在编译大型C++开发项目中,MakeFile接触的比较多,而之前都是简单程序不需要复杂编译,所以没什么太多了解,因此对MakeFile的语法以及规则进行了学习总结,主要参考自《Makefile学习写法》这本书。
一、程序的编译和链接
- 首先将源文件编译成中间代码文件,即Object File,称为编译(compile)。然后将大量的Object File合成执行文件,称为链接(link)。由于编译生成的中间目标文件太多,在链接时需要指出中间目标文件名,因此要给中间目标文件打包,称为库文件(windows下为.lib文件,UNIX下是.a文件)。
- 具体过程:源文件首先会生成中间目标文件(编译),再由中间目标文件生成执行文件(链接)。在编译过程中,编译器只检测程序语法,和函数、变量是否被声明。在链接程序时,会在所有的object file中寻找函数的出现。
二、Makefile介绍
- Makefile语法规则:
target ... : prerequisites ...
command
target是一个目标文件,可以是object file,也可以是执行文件,还可以是一个标签。prequisites是生成target所需要的文件或目标,command是make需要执行的命令。其核心是:说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。
- 对于一些target冒号后面没有依赖文件,只有命令时,make不会自动寻找文件的依赖性,也不会自动执行其后所定义的命令。要执行该命令,需要在make命令后明确指出该label名字,比如make clean。
- make工作方式:make会一层一层寻找文件的依赖关系,直到最终编译出第一个目标文件。make不关心命令错误或者编译成功与否,只负责文件的依赖性。
- makefile使用变量:其变量是一个字符串形式,在makefile中可以通过$(objects)的方式来调用。
- make自动推导:它可以自动推导文件以及文件依赖关系后面的命令,make看到一个[.o]文件,将会自动把[.c]文件加在依赖关系中。
- make的隐晦规则:“.PHONY”表示某个target是一个伪目标文件。
- 清空目标文件规则(clean从来都是放在文件最后):
.PHONY : clean
clean :
-rm edit $(objects)
三、Makefile总述
-
Makefile主要包含了5个东西:显式规则、隐晦规则、变量定义、文件指示、注释。另外,在Makefile中的命令,必须要以Tab键开始。
-
引用其他makefile:使用include关键字将其他makefile引入,其语法为include <filename> 。
-
make工作方式:
(1)读入所有的makefile文件。
(2)读入被include的其他makefile。
(3)初始化文件中的变量。
(4)推导隐晦规则,并分析所有规则。
(5)为所有的目标文件创建依赖关系链。
(6)根据依赖关系,决定哪些目标要重新生成。
(7)执行生成命令。
四、Makefile书写规则和命令
- 规则包含两个部分,一个是依赖关系,一个是生成目标的方法。在Makefile中,只应该有一个最终目标,其他目标都是连带出来的。一般来说,第一条规则中的目标将被确立为最终目标。
- 通配符:* ? […]。如果让objects的值是所有[.o]的文件名的集合,可以表示成:
objects := $(wildcard *.o)
-
文件搜索:使用特殊变量VPATH来完成该功能。例如:VPATH = src:…/headers,指定了两个目录,目录由冒号进行分隔。另外,也可以使用vpath关键字来设置文件搜索路径,vpath %.h …/headers表示make在…/headers目录下搜索所有以.h结尾的文件。
-
多目标:使用自动化变量“$@”来表示目前规则中所有目标的集合。
-
静态模式:
<targets ...>: <target-pattern>: <prereq-patterns ...> <commands> ....
targets定义了一系列的目标文件,可以有通配符,是目标的集合。targets-pattern指明了targets的模式,即目标集模式。prereq-patterns是目标的依赖模式。举例如下:
objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
"$<“表示所有的依赖目标集合,”$@"表示目标集合。
-
命令执行:如果让上一条命令的结果应用在下一条命令时,应该使用分号分隔这两条命令。
-
命令包定义:为一些相同命令序列定义一个变量,定义这种命令序列的语法以“define”开始,以“endef”结束。
define two-lines echo foo echo $(bar) endef
五、Makefile变量、条件判断和函数
-
使用":="操作符这种变量定义方法,使得前面的变量不能使用后面的变量,只能使用前面已经定义好的变量。
-
“?=”操作符表示未定义前则被赋值,否则不进行操作。
-
变量追加值:使用"+="操作符。
-
条件判断关键字:ifeq表示条件语句的开始,else表示条件表达式为假的情况,endif表示一个条件语句的结束,ifdef表示测试一个变量是否有值。例子如下:
libs_for_gcc = -lgnu normal_libs = ifeq ($(CC),gcc) libs = $(libs_for_gcc) else libs = $(normal_libs) endif foo: $(objects) $(CC) -o foo $(objects) $(libs)
-
函数调用:$(<function> <arguments>),参数间用逗号进行分隔,函数名和参数之间用空格分隔,函数调用以$开头。替换函数subst的使用示例如下:
comma := , empty := space := $(empty) $(empty) foo := a b c bar := $(subst $(space),$(comma),$(foo))
-
foreach函数:$(foreach ,,
),将参数中的单词逐一取出放到参数 所指定的变量中去,然后再执行 所包含的表达式。。每一次 会返回一个字符串,循环过程中, 的所返回的每个字符串会以空格分隔,最后当整个循环结束时, 所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。 -
隐含规则使用变量:CC(C语言的编译程序gcc)、CXX(C++的编译程序g++)、RM(删除文件命令rm -f)。
-
自动化变量(将模式中所定义的一系列文件自动取出):
(1)$@:规则中的目标文件集。
(2)$<:依赖目标中的第一个目标名字。
(3)$?:所有比目标新的依赖目标集合。
(4)$^:所有的依赖目标集合。