网站策划资料方案,网站建设佰金手指科杰三,wordpress 改成 中文,莆田seo全网营销在Linux中使用 make 命令来编译程序#xff0c;特别是大程序#xff1b;而 make 命令所执行的动作 依赖于 Makefile 文件。以下是最简单的Makefile文件#xff1a; 首先#xff0c;包含如下文件#xff1a; Makefile文件内容如下所示#xff1a; 然后#xff0c;直接执行… 在Linux中使用 make 命令来编译程序特别是大程序而 make 命令所执行的动作 依赖于 Makefile 文件。以下是最简单的Makefile文件 首先包含如下文件 Makefile文件内容如下所示 然后直接执行 make 命令编译程序 执行 make clean 命令 即可清除编译出来的结果 make命令 根据文件更新的时间戳来决定哪些文件需要重新编译这使得可以避免已经编译过的、没有变化的程序可以大大提高编译效率。
一、Makefile规则与示例讲解
1、规则
规则如下所示
目标(target): 依赖(prerequiries)
tab命令(command)
需要特别注意必须以Tab键进行缩进不能以空格键缩进。 如果 “依赖文件” 比 “目标文件” 更加新那么执行 “命令” 来重新生成 “目标文件”。 命令执行的两个条件依赖文件比目标文件新或者是 目标文件没有生成。
这里介绍两个函数
1$(foreach var, list, text) 简单地说就是 for each var in list, change it to text。 其实就是 对 list 中的每一个元素取出来赋给 var然后把 var 改为 text 所描述的形式。
如
objs : a.o b.o
dep_files : $(foreach f, $(objs), .$(f).d) //最终 dep_files : .a.o.d .b.o.d
2$(wildcard pattern) pattern 所列出的文件是否存在把存在的文件都列出来。
如
src_files : $( wildcard *.c) //最终 src_files 中列出了当前目录下的所有.c文件
2、示例讲解
1简单粗暴效率低
test : main.c sub.c sub.hgcc -o test main.c sub.c
2效率高相似规则太多太啰嗦不支持检测头文件
test : main.o sub.ogcc -o test main.o sub.omain.o : main.cgcc -c -o main.o main.csub.o : sub.cgcc -c -o sub.o sub.cclean:rm *.o test -f
3效率高精炼不支持头文件检测
test : main.o sub.ogcc -o test main.o sub.o%.o : %.cgcc -c -o $ $clean:rm *.o test -f
4效率高精炼支持头文件检测但是需要手动添加头文件规则
test : main.o sub.ogcc -o test main.o sub.o%.o : %.cgcc -c -o $ $sub.o : sub.hclean:rm *.o test -f
5效率高精炼支持自动检测头文件
objs : main.o sub.o
test : $(objs)gcc -o test $^# 需要判断是否存在依赖文件
# .main.o.d .sub.o.d
dep_files : $(foreach f, $(objs), .$(f).d)
dep_files : $(wildcard $(dep_files))# 把依赖文件包含进来
ifneq ($(dep_files),)include $(dep_files)
endif%.o : %.cgcc -Wp,-MD,.$.d -c -o $ $
clean:rm *.o test -f
distclean:rm $(dep_files) *.o test -f
二、通用Makefile的使用
1、解析
1make 命令的使用 执行 make 命令时它会去 当前目录下 查找名为 “Makefile” 的文件并根据它的指示去执行操作生成 第一个 目标。 我们可以使用 “ -f ” 选项指定文件不再使用名为 “Makefile” 的文件比如
make -f Makefile.buid 我们也可以使用 “ -C ”选项指定目录切换到其他目录里去比如
make -C a/ -f Makefile.buid 我们可以指定目标不再 默认生成 第一个 目标
make -C a/ -f Makefile.buid other_target
2即时变量、延时变量、自动变量 延迟赋值在Makefile运行时才会被赋值: 立即赋值立即赋值是在真正运行前就会被赋值? 空赋值如果变量没有设置过才会被赋值 追加赋值可以理解为字符串的加操作 变量的定义语法如下所示
A xxx // 延时变量
B ? xxx // 延时变量只有第一次定义时赋值才成功如果曾定义过此赋值无效
C : xxx // 立即变量
D yyy // 如果 D 在前面是延时变量那么现在它还是延时变量
// 如果 D 在前面是立即变量那么现在它还是立即变量 上面语句中变量A是延时变量它的值在使用时才展开、才确定。比如
A $
test:echo $A 上述Makefile中变量A的值在执行时才确定它等于test是延时变量。 如果使用 “ A : $ ”这是立即变量这时 $ 为空所以A的值就为 空。
自动变量 Makefile有很多自动变量这里只介绍几个常用的分别是 $、$^、$其它的可以去参考Makefile文档。
$ 表示第一个依赖的文件例如
test: test.o test2.o echo $
test.o:
test2.o: 最终的结果是打印了 test.o也就是test第一个依赖。
$^ 表示所有依赖例如
test: test.o test2.o echo $^
test.o:
test2.o: 最终结果是打印了 test.o test2.o是test全部的依赖。
$ 表示目标例如
test: test.o test2.o echo $
test.o:
test2.o: 最终结果打印的是 test也就是Makefile中的目标。 3变量的导出export 在编译时我们会不断地使用 make -C dir 切换到其它目录执行其他目录里的 Makefile。如果 想让某个变量的值在所有目录中都可见要把它 export 出来。比如
CC $(CROSS_COMPILE)gcc 上面的CC变量表示编译器在整个过程中都是一样的。定义它之后要使用 “ export CC ” 把它导出来。
4Makefile中可以使用shell命令
比如
TOPDIR : $(shell pwd) 这是一个即时变量TOPDIR 等于 shell 命令pwd 的结果。
5在Makefile中怎么放置 第一个 目标 执行 make 命令时如果不指定目标那么它默认是去生成 第一个 目标。 所以 “ 第一个目标 ” 位置很重要。有时候不太方便把 第一个目标 完整地放在文件前面这时可以在文件的前面直接放置目标在后面再完善它的依赖与命令。比如
First_target: // 这句话放在前面// 其他代码比如 include 其他文件得到后面的 xxx 变量
First_target : $(xxx) $(yyy) // 在文件的后面再来完善command
6假想目标 比如我们的 Makefile 文件中有这样的目标
clean:rm -f $(shell find -name *.o)rm -f $(TARGET) 如果当前目录下恰好有名为 “ clean ” 的文件那么执行 “ make clean ” 时它就不会执行上面那些删除命令。 这时我们需要把 “ clean ” 这个目标设置为 “ 假想目标 ”这样可以确保执行 “ make clean ” 时那些删除命令肯定可以得到执行。 使用下面的语句 把 “ clean ” 设置为假想目标
.PHONY : clean
2、通用Makefile的设计思想
1在Makefile文件中确定要编译的文件、目录
比如
obj-y main.o
obj-y a/ “ Makefile ” 文件总是被 “ Makefile.build ” 包含的。
2在 Makefile.build 中设置编译规则有 3条规则 怎么编译子目录进入子目录编译
$(subdir-y):make -C $ -f $(TOPDIR)/Makefile.build 怎么编译当前目录中的文件
%.o : %.c$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$) -Wp,-MD,$(dep_file) -c -o $ $ 当前目录下的 .o 和 子目录下的 build-in.o 要打包起来
built-in.o : $(cur_objs) $(subdir_objs)$(LD) -r -o $ $^
3顶层 Makefile 中把顶层目录的 build-in.o 链接成 APP
$(TARGET) : built-in.o$(CC) $(LDFLAGS) -o $(TARGET) built-in.o
4情景演绎
使用情景 ./Makefile
CROSS_COMPILE
AS $(CROSS_COMPILE)as
LD $(CROSS_COMPILE)ld
CC $(CROSS_COMPILE)gcc
CPP $(CC) -E
AR $(CROSS_COMPILE)ar
NM $(CROSS_COMPILE)nmSTRIP $(CROSS_COMPILE)strip
OBJCOPY $(CROSS_COMPILE)objcopy
OBJDUMP $(CROSS_COMPILE)objdumpexport AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMPCFLAGS : -Wall -O2 -g
CFLAGS -I $(shell pwd)/includeLDFLAGS : export CFLAGS LDFLAGSTOPDIR : $(shell pwd)
export TOPDIRTARGET : testobj-y main.o
obj-y sub.o
obj-y a/all : start_recursive_build $(TARGET)echo $(TARGET) has been built!start_recursive_build:make -C ./ -f $(TOPDIR)/Makefile.build$(TARGET) : built-in.o$(CC) -o $(TARGET) built-in.o $(LDFLAGS)clean:rm -f $(shell find -name *.o)rm -f $(TARGET)distclean:rm -f $(shell find -name *.o)rm -f $(shell find -name *.d)rm -f $(TARGET)
./a/Makefile EXTRA_CFLAGS : -D DEBUG
CFLAGS_sub3.o : -D DEBUG_SUB3obj-y sub2.o
obj-y sub3.o Makefile.build
PHONY : __build__build:obj-y :
subdir-y :
EXTRA_CFLAGS :include Makefile__subdir-y : $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y $(__subdir-y)subdir_objs : $(foreach f,$(subdir-y),$(f)/built-in.o)cur_objs : $(filter-out %/, $(obj-y))
dep_files : $(foreach f,$(cur_objs),.$(f).d)
dep_files : $(wildcard $(dep_files))ifneq ($(dep_files),)include $(dep_files)
endifPHONY $(subdir-y)__build : $(subdir-y) built-in.o debugdebug:echo cur_objs $(cur_objs)echo obj-y $(obj-y)echo subdir_objs $(subdir_objs)echo subdir-y $(subdir-y)$(subdir-y):echo subdir-y $(subdir-y)make -C $ -f $(TOPDIR)/Makefile.buildbuilt-in.o : $(cur_objs) $(subdir_objs)$(LD) -r -o $ $^dep_file .$.d%.o : %.c$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$) -Wp,-MD,$(dep_file) -c -o $ $.PHONY : $(PHONY)
对上述情景的理解如下 整理简化大致是这样的 首先在顶层执行 make执行顶层Makefile第一个目标为 all而其第一个依赖为 start_recursive_build 接着根据这个依赖会去调用 make -C ./ -f $(TOPDIR)/Makefile.build 可以替换为 make -C ./ -f $(shell pwd)/Makefile.build 即在当前目录下去执行 Makefile.build。 因为 Makefile.build 脚本中包含了 include Makefile所以这时候会根据顶层Makefile给 Makefile.build 中的 obj- 和 subdir-y 进行赋值那么就有 obj-y main.o sub.o a/ cur_objs main.o sub.o subdir-y a subdir_objs a/built-in.o 第一个目标为 __build 且其第一个依赖为 $(subdir-y)也就是a目录先运行第一个依赖的命令然后调用命令为 make -C $ -f $(TOPDIR)/Makefile.build替换后为 make -C a -f $(shell pwd)/Makefile.build。意思为去子目录a下运行Makefile.build。 接着进入子目录运行 Makefile.build同样根据子目录下的 Makefile 去给 obj- 和 subdir-y 进行赋值由于没有子目录了所以 subdir-y 为空如下所示 obj-y sub2.o sub3.o cur_objs sub2.o sub3.o subdir_objs subdir-y 第一个目标还是 __build其依赖项两个$(subdir-y)和built-in.o。前者为空因此不需要再次递归调用了。第二个目标是 built-in.o找到其对应的依赖为$(cur_objs) $(subdir_objs)而subdir_objs又为空因此不需要执行。 由于已经子目录下已经执行完了退回顶层目录文件夹下 的 Makefile.build 脚本中继续执行这时由于 __build 目标的第一个依赖 $(subdir-y) 已经执行结束则开始执行第二个依赖 built-in.o 而此时对应的两个依赖$(cur_objs) $(subdir_objs)第一个生成了 %.o 第二个为 a/built-in.o 已经在上一步生成了。因此链接生成顶层的 built-in.o 此时目标生成退出 Makefile.build 脚本回到顶层的 Makefile 中。 回到顶层的 Makefile 继续执行第一个目标 all 的 第二个依赖 $(TARGET) 而 $(TARGET) 的依赖 built-in.o 也生成了执行最后的命令 $(CC) $(LDFLAGS) -o $(TARGET) built-in.o 就结束 顶层Makefile 脚本了。