makefile定义了一种语言来描述源代码、中间文件及可执行文件之间的关系。如果命令行指定了目标,则更新指定的目标,如果没有,则取第一个目标,也即默认目标。1.1 目标与依赖makefile包含构造程序的一组规则,规则包含三个部分:目标、依赖及执行命令。target: prereq1 prereq2commandstarget是需要构造的东西,依赖则是在创建target前必须存在的东西。commands为将依赖生成为target的shell命令。1.2 依赖检查
对于-l<NAME>的依赖,make有特殊的支持,它表示依赖库文件。
make会查找libNAME.so,如果找不到,则查找libNAME.a。1.3 减少重编
文件修改时,只会影响相关的编译,整个项目一般不会从头构造。
1.4 调用makemake target 更新指定的target。如果目标是最新的,则什么也不做。如果指定的target在makefile里不存在,也没有隐含规则,则提示无规则。make有很多命令行参数,最有用的是 --just-print (或 -n)它使make显示编译目标使用的命令,但不实际执行。命令行的变量可以覆盖makefile里的变量。1.5 makefile基本语法更完整的形式(不是最完整的):target1 target2 target3: prerequisite1 prerequisite2command1command2command3target可以有多个,依赖可以没有。如果没有依赖,则只有不存在的target会更新。command必须以tab开始。make另启动子shell来执行command。长的行可以用反斜杠分隔,依赖也可以使用。2. 规则规则有不同种类 * 显式规则,明确指定目标和依赖 * 模式规则,使用通配符 * 隐式规则,makefile内置的模式规则或者扩展名规则2.1 显式规则大多数规则是显式规则。规则可以有多个目标,表示每个目标的依赖是相同的。target1 target2: prereq等价于target1: prereqtarget2: prereq规则不需要一次写全,make每次搜索目标文件时,会将目标和依赖加到依赖关系图中。如果目标在依赖关系图中已存在,则将依赖添加到图中。vpath.o: vpath.c make.h config.h getopt.h gettext.h dep.hvpath.o: filedef.h hash.h job.h commands.h variable.h vpath.h2.1.1 通配make的通配符与bash一样:~, * , ?, [...], [^...]*.*表示所有包含.的文件,?表示任意一个字符,[...]表示一组字符,选择相反的字符集用[^...]~用于表示当前用户的home目录,~user表示那个用户user的home目录。目标或是依赖里的通配由make处理,而命令里的通配由shell处理。而make在读取makefile时会立即展开通配符。2.1.2 Phony目标不代表文件的目标即为phony目标,比如all和clean。一般情况下,phony总会执行,因为相关的命令不产生目标名。但是make无法区分文件目标和phony目标,假如一个phony目标恰好对应了一个文件名,make会把假的目标加到依赖图里。为了避免该问题,GNU make引入了一个特殊的目标:.PHONY来告诉make这个目标不是一个真实文件,可以用它来修饰任何一个伪目标,形式如下: .PHONY: clean clean: command这样,即使存在一个叫clean的文件,make也会执行clean目标的命令。将phony目标作为真实文件的依赖没有多大意义,因为phony总是旧的,这会导致目标文件重新生成。常用的做法是将phony作为目标。按习惯,有一些标准的phony目标: * all:执行所有任务构建程序 * install:安装程序 * clean:删除从源码生成的二进制文件 * distclean:删除不在源码发布范围内的所有产生文件 * TAGS:创建tag表给编辑器使用 * info:从Texinfo源码创建GNU info文件 * check:运行与程序相关的测试TAGS不是一个phony目标,因为ctags和etags的输出就是一个TAGS文件。2.1.3 空目标空目标与phony目标类似,phony target总是过时的,导至其依赖被重新生成。有时有些命令没有输出文件,希望只在有些情况下去执行,并且不想更新依赖,此时可以创建一个空文件的规则(也称为cookie):
prog: size prog.o
$(CC) $(LDFLAGS) -o $@ $^
size: prog.o
size $^
touch size
在这例里,size规则用touch生成了一个文件名为size的空文件,这个空文件充当了一个时间戳的功能。只有在prog.o更新时,size规则才会执行。除非prog.o更新了,否则size规则不会导致prog被重编。也就是说这个规则在prog.o更新prog时执行,起到了特定条件下执行命令的目的。空文件与自动变量$?结合时特别有用。$?表示比目标新的依赖集合。 print: *.[hc] lpr $? touch $@这个例子打印自上次执行print以来,变化过的文件。通常,空文件是用来标记一个特定的时间。2.2 变量变量最简单的形式:$(variable-name)表示希望展开名为variable-name的变量。变量名需要用$()引用,但单字符变量可以省去括号。makefile有自定义的一些变量。2.2.1 自动变量当规则匹配时,make会设置自动变量,它们提供了目标和依赖的清单,避免显式指定文件名,代码重复,且对通用的模式规则很重要。有6个重要的自动变量:$@表示target的文件名$%the filename element of an archive member specification.可能跟库有关系。$<第一个依赖的文件名$?比目标新的所有依赖$^所有依赖的文件名(会去掉重复项)$+与$^类似,但包含重复项$*目标文件名的根。根一般是不含后缀名的文件名。不建议在pattern rule之外使用。自动变量只有在规则匹配时才会生成,因此只有在命令区才可以使用。2.3 用VPATH和vpath查找文件make只会在当前目录查找源文件VPATH = src告诉make,当前目录找不到文件时,到src里去找VPATH变量包含了一个路径列表,make需要一个文件时就从这个列表里找。这个只对目标和依赖有效,命令区里的文件名是不用的。VPATH有缺点:路径多的时候,效率会低。不同路径下有重名时,只会取第一个,vpath更好一些。vpath pattern directory-list前面的VPATH可以改写为:vpath %.c srcvpath %.h includevpath是否也存在重名的问题呢?2.4 模式规则make的内置规则都是模式规则,除了文件的根以%替代外,与普通规则无异(根是指后缀之前的部分)。可通过make --print-data-base查看make的内置规则。可以修改内置规则的命令区。2.4.1 模式模式规则里,%基本上与unix shell的* 相同,代表任意数量的任意字符。%可以放在任意位置,但只能出现一次。除%外的字符则用于匹配文件名。模式可以包含前缀、后缀和前后缀。当make查找模式规则时,首先查找可以匹配的target。如果找到,在前缀和后缀之间的部分将作为名字的根。然后make查找对应的依赖,将根替换到依赖模式里。根至少包含一个字符。模式可以只包含%,一般这种用法是生成一个可执行程序。2.4.2 静态模式规则静态模式规则是指只对特定目标生效的规则,如$(OBJECTS): %.o: %.c$(CC) -c $(CFLAGS) $< -o $@与普通的模式规则不同的只有前面的$(OBJECTS):限定,它指定该规则只对$(OBJECTS)变量列出的目标生效。2.4.3 后缀规则后缀规则是早期的(已经过时的)定义隐含规则的方式。其它版本的makefile不支持GNU make的模式规则,仍能在其它makefile里见到。后缀规则包含一个或多个串接的后缀,用作目标:.c.o:$(COMPILE.c) $(OUTPUT_OPTION) $<这里的依赖在前,目标在后,它等价于:%.o: %.c$(COMPILE.c) $(OUTPUT_OPTION) $<将目标的后缀替换为依赖的后缀得到依赖,在有后缀名在已知清