rule的顺序是无关紧要的,除了决定default goal之外。如果第一个rule有多个targets,只有第一个target被当成default goal。
.开头的target不会被选中为default goal,除非它包含斜杠/。定义pattern rule的target也不影响default goal。
长这样:
1 2 3 | targets : prerequisites recipe ... |
或者这样:
1 2 3 | targets : prerequisites ; recipe recipe ... |
targets是space分开的文件名,可以使用wildcard characters。
1 2 3 4 5 6 7 8 9 | $ ls a inc.mk Makefile $ cat Makefile *: echo all $ make make: 'a' is up to date. |
如上,可以使用*作为targets。
recipe以TAB键开始,可以使用.RECIPEPREFIX变量修改默认行为。由于$被做为变量引用符,如果想使用$字面量,可以使用两个$,即:$$
有两种prerequisites:normal prerequisites,order-only prerequisites,语法:
targets : normal-prerequisites | order-only-prerequisites
order-only prerequisites这种依赖,会被执行,但是target是否更新,与这种依赖无关。
Makefile里的wildcard charcters有* ? [...]
targets和prerequisites中的wildcard expansion是由make执行的。recipes里面的则是由shell执行的。
定义变量的时候,wildcard expansion不生效:
objects = *.o
是真实的字符串*.o,但是如果objects变量用在target或者prerequisite,那么wildcard expansion将在那里发生。
使用VPATH变量,来指定make搜索的目录。如果prerequisite不在当前目录,make就会尝试在VPATH指定的目录去搜索。
VPATH中,变量用冒号:或者空白分开。
4.4.2 vpath directive
可以按文件的类型指定不同的搜索目录,有3种vpath directive
vpath pattern directories
指定pattern类型文件的搜索目录
vpath %.o obj/
vpath pattern
清除pattern关联的搜索路径
vpath
清除之前vpath指定的所有搜索路径
4.4.3 How Directory Searches are Performed
对于VPATH,如果target要被rebuilt,那么target是本地的,被搜索的路径被thrown away。而依赖则是搜索的路径。
1 2 3 4 5 6 7 8 9 10 11 12 | $ cat Makefile VPATH = obj/ a: b @echo target: $@ @echo prerequisite: $< $ touch obj/b $ make target: a prerequisite: obj/b |
可以发现依赖为obj/b
4.4.5 Directory Search and Implicit Rules
VPATH或者vpath也会应用到implicit rules。
有些target本身不是一个文件,你想它们总是会被执行。那么就把它声明为PHONY目标,一个例子足以说明一切:
1 2 3 | .PHONY: clean clean: rm *.o temp |
.PHONY目标的隐含规则搜索被跳过。这可以提升一些性能。
一个真实文件的目标不应该依赖于一个phony 目标。
如果一个rule没有依赖或者recipe。且rule的target文件不存在,则make认为这个rule的target总是过时,意味着依赖这个rule的targets总是会执行它们的recipe。
1 2 3 | clean: FORCE rm $(objects) FORCE: |
主要是为了书写方便。
一个target可以在多个rule中存在,每个rule的依赖都会合并成一个依赖列表,但是只能有一个rule有recipe,如果有多个rule有recipe,make选择最后一个,同时给出警告信息。
1 2 3 4 5 6 7 8 9 10 11 | $ cat Makefile a: c a: b @echo target: $@ @echo prerequisite: $? $ touch b c $ make target: a prerequisite: b c |
如果没有显示规则有recipe,make搜隐式规则来找到一个。
静态模式规则是这样一种规则,它有多个静态目标,它们的依赖可以根据目标的名字pattern来匹配,不同目标的依赖必须是相似的,不一定要相同。
语法:
1 2 3 | targets ...: target-pattern: prereq-patterns ... recipe ... |
target可以包含通配符。
target-pattern和prereq pattern用来确定每个target如何计算依赖。
每个pattern通常包含一个%。%可以匹配target name的任何部分,这部分被叫做stem。pattern的其它部分必须严格匹配。
prereq pattern中的%被替换为target pattern中匹配的部分。
一个例子足以说明问题:
1 2 3 4 5 6 | objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ |
每个target必须匹配target-pattern,如果不匹配会给出警告。
双冒号规则,在target name后面有两个冒号。
当一个目标出现在多个rule中时,它们的类型必须相同,要么都是普通的,要么都是双冒号的。
如果是双冒号的,每个rule是独立的,它们依赖各自的依赖,它们执行各自的recipe。就好像它们的target名字不同一样。
如果双冒号 rule没有任何依赖,那么这个rule的recipe总是会执行,即使target文件已经存在,这和单冒号的不同。
每个双冒号规则,应该指定一个recipe,如果没有隐式的规则将被应用,如果存在的话。
recipe以tab字符开始,任何以tab开始的行且出现在一个rule context都被当成recipe的一部分。
rule context是rule开始后,直到另外一个rule或者变量定义。
recipe lines中的空行和注释被忽略。
以tab开始的空行不是空的,它是一个empty recipe。
recipe中的backslash/newline对不会被make移除,如何处理是shell的事情。
如果backslash/newline后面的字符是一个recipe prefix character,那这个字符会被移除。
空白字符绝不会添加到recipe中。
简单,没啥特别的,要产生shell中的$,可以使用两个$$
recipe line前面加一个@,可以防止回显。-s或者--silent选项可以让make阻止所有的回显。
每一行,在一个子shell中执行。
-j, --jobs 选项控制并行执行的个数。
在特别的makefile中,可以通过.NOTPARALLEL伪目标来阻止并行执行。
job slots
如果-j后面没接参数,则并行没有限制。
recipe line前面加一个破折号-,忽略这个行的错误。
有时候失败了,但是目标文件的时间戳已经更新了,下次可能不会在重新编译目标文件,这可能是一个错误。
.DELETE_ON_ERROR伪目标,可以在失败的时候删除目标文件。
使用$(MAKE)变量,而不要直接使用make,它可以保证使用同一个make。
另外,在recipe line中使用MAKE变量,会和在recipe line的前面+有一样的效果。
-t (--touch) -n(--just-print) -q (--question)
这些选项不会执行recipe,但是使用了+的recipe line会被执行。比如你使用了-t选项来touch过期的目标,对于+开始后的行,它们会被执行,比如+make -C subdir,这通常是你想要的结果,因为你也想更新子目录的过期目标。看例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | $ cat Makefile .PHONY: all all: echo all +make -f inc.mk $ cat inc.mk suball: echo suball $ ls inc.mk Makefile $ make -t make -f inc.mk make[1]: Entering directory '/work/learn/make' touch suball make[1]: Leaving directory '/work/learn/make' |
可以看到make -f inc.mk执行了,但是echo all没有执行。
export变量到sub-make,使用export语法
export variable ...
想要阻止变量被exported,使用unexport语法
unexport variable
如果想默认所有变量都导出,执行
export
选项自动通过MAKEFLAGS传递给sub-make。如果5.7中的例子,MAKEFLAGS的值为tw。
同样的,命令行中的变量定义也通过MAKEFLAGS传递给sub-make。make对待MAKEFLAGS中的变量就好像定义在命令行上一样。
-j选项比较特殊,主make和子make会沟通,保证最多N个job同时执行。
如果你不想传递任何标记下去,可以改变MAKEFLAGS的值:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
-w 或 --print-directory,进入目录时打印,make -C时会自动打开这个选项。
--no-print-directory关闭这个选项。
将recipe lines定义在一个define变量中,然后将其扩展到recipe中:
1 2 3 4 5 6 7 | define run-yacc = yacc $(firstword $^) mv y.tab.c $@ endef foo.c : foo.y $(run-yacc) |
define在定义的额时候,不会扩展变量引用和和函数调用。另外define是recursively expanded 的。
define里面的每一行,都作为一个recipe行,可以为某些行加上@,那取消回显,只正对该行有效。
整个灌装的也可以加@,如
1 2 | foo.c : foo.y @$(run-yacc) |
那么所有行都取消回显。
定义个什么也不做的recipes有时很有用:
target: ;
当然,你也可以使用一行,只有recipe prefix字符,但是在看起来很迷惑,不好识别是空行,还是空recipe