1 如果一个target有非空recipe,则下列情况下被认为是过时的,并执行recipe。
a. target是一个PHONY目标,则target总是过时的。
b. target文件不存在,则target是过时的。
c. target文件存在,但是target没有依赖,则不是过时的。
d. target存在,且有依赖,且存在一个依赖比它新,则target是过时的:
I. PHONY依赖总是最新的。
II. 依赖文件不存在。
III. 依赖存在,但是比它新。
如果target没有过时,则打印:
'target' is up to date
2. 如果一个target有空的recipe,且没有任何依赖及其子孙依赖的recipe被执行,则打印:
'target' is up to date
空recipe有两种写法
s: a;
或者
s:
TAB
3. 如果一个target没有recipe,如果没有任何依赖(或依赖的子孙依赖)的recipe被执行,则打印:
make: Nothing to be done for 'target'.
4. 如果一个文件存在,但没有这个文件作为目标的rule,则打印:
make: Nothing to be done for 'target'.
总结:
PHONY目标总是最新的,任何目标依赖PHONY目标都是过时的,如果有recipe,总是会执行。
其它目标总是把其当作一个文件,通过比较文件的时间戳来确定目标是否过时。
依赖总是会被检查,如果依赖本身过时了,其recipe会被执行。
依赖执行完后,比较依赖文件和目标文件的时间戳,来确定目标是否过时:
1. 如果目标文件不存在,则目标是过时的。
2. 如果依赖文件不存在,则目标是过时的。
3. 如果两个都存在,则比较两个文件的时间戳。
4. 如果没有依赖,则看目标文件是否存在。
举个例子:
1 2 3 4 5 6 7 8 9 | $ cat Makefile a: b @echo a b: FORCE @echo b FORCE: ; |
然后执行
1 2 3 4 5 6 | $ touch b $ $ touch a $ $ make a b |
可以看到,在执行make之前,已经有a,b文件了,且a比b新。但是b目标仍然执行了,因为b依赖于一个不存在的FORCE目标。执行b的recipe后,比较a和b的时间戳,a更新一些,所以a的recipe没有被执行。
target中包含%的rule,是pattern rule。Make自带的implicit rule也属于pattern rule。
1 当没有任何非pattern rule能匹配target时,才会去尝试匹配pattern rule。没有recipe的非pattern rule和没有recipe的双冒号rule,也会进行模式匹配。
1 2 3 4 5 6 7 8 9 | $ cat Makefile a: b %: @echo $@ $ make b a |
a,b文件不存在,可以看到a匹配了%,输出a。
2 空recipe的非pattern rule的目标不会进行模式匹配
1 2 3 4 5 6 7 8 | $ cat Makefile a: ; %: @echo $@ $ make make: 'a' is up to date. |
这里a会匹配普通目标,而不会匹配pattern rule。
3 模式匹配,只是为target寻找一个rule,这个target是否过时的判断还是和普通rule一样的。
1 2 3 4 5 6 7 8 9 10 11 | $ cat Makefile %: @echo $@ $ touch testfile $ make testfile make: 'testfile' is up to date. $ rm testfile $ make testfile testfile |
可以看到,如果testfile存在,则testfile没有过时,recipe照样不执行。
4 模式规则,只有依赖都存在或者可以被Made,模式规则才匹配。
1 2 3 4 5 6 7 8 9 10 11 12 | $ cat Makefile %.x: %.y @echo $@ $ rm -f a.y $ make a.x make: *** No rule to make target 'a.x'. Stop. $ touch a.y $ make a.x a.x |
可以看到没有a.y时,a.x不匹配,有a.y时,才匹配。
5 模式规则的定义是没有限制的,同一个target pattern可以依赖不同的prerequisite patterns,甚至可以定义多个相同的模式规则,只是recipe不同。
1 2 3 4 5 6 7 | $ cat Makefile %.x: %.y @echo $@ %.x: %.z @echo z $@ |
6 当一个target匹配多个pattern rule时,取匹配部分最短的pattern rule。长度相同的,选择最先出现的。如果写一个和implicit相同的pattern rule,则可以覆盖implicit。
1 2 3 4 5 6 7 8 9 10 11 12 | $ cat Makefile %.o: a.c @echo $@ $ touch b.c $ make b.o cc -c -o b.o b.c $ touch a.c $ make a.o a.o |
可以看到执行b.o时,匹配的是implicit rule,但是执行a.o时,选择了定义的pattern rule
7 Chains of pattern rule
模式匹配是可以组成链的,比如:
1 2 3 4 5 6 7 8 9 10 11 12 | $ cat Makefile %: %.o touch $@ %.o: touch $@ $ make x touch x.o touch x rm x.o |
但是,在一个chain里,一个pattern rule不能匹配多次。比如%: %2,不能无限匹配x: x2: x22。
如上模式匹配链中,出现了rm x.o,这是因为,如果依赖的目标不存在,并且通过一个模式匹配得到,那么这个依赖就是intermediate file,GNU Make会删除intermediate file。
同时,GNU Make不会和intermediate file比较时间戳,而是和intermediate file的依赖比较,来决定是否更新目标,如果不需要更新,则依赖的recipe也不会执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $ cat Makefile %: %.o touch $@ %.o: %.c touch $@ %.c: ; $(MAKEFILE_LIST): ; $ touch x.c $ make -r x touch x.o touch x rm x.o $ make -r x make: 'x' is up to date. |
如上x->x.o->x.c,x.o是一个中间文件,开始时没有x,所以x.o被执行。再次执行后,x比x.c新,x.o也不会执行了。
如上
1 | $(MAKEFILE_LIST): ; |
是为了防止Makefile remake匹配到模式rule,而更新。
8 terminal pattern rule
使用双冒号将pattern rule定义为terminal rule,terminal rule要求依赖必须存在,或者能通过非pattern rule被Make,不允许依赖再进行模式匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | $ cat Makefile %:: %.o @echo %: $@ b.o: a.o: @echo a.o %.o: @echo %.o: $@ $ make a a.o %: a $ make b %: b $ make c make: *** No rule to make target 'c'. Stop. |
比如foo.c,它可能通过foo.c.p foo.c.c等编译而来,但是某些场景下,编译foo.o就是依赖存在的源文件foo.c,此时我们可以定义为%.o:: %.c,避免GNU Make去进行pattern rule匹配,提升速度。
9 Matching-anything rule
即单个%作为target pattern,它能匹配任何目标。和其它pattern rule不同,GNU Make对Matching-anything rule做了一个限制,如果是非terminal rule,那么它不能匹配其它pattern rule中的目标,即使这个目标由于依赖不存在不能匹配任何其它pattern rule。
1 2 3 4 5 6 7 8 9 10 11 | $ cat Makefile %: @echo %: $@ %.o: %.c @echo %.o: $@ $ rm -f a.o a.c $ make a.o make: *** No rule to make target 'a.o'. Stop. |
如上,a.o虽然不能匹配%.o: %.c,因为a.c不存在,但是也不能匹配%,如果将%修改为terminal rule,即%::,那么此时a.o能匹配%。
如果想要一个模式不匹配non-terminal match-anything rules,可简单的定义一个空的pattern rule,如:
%p:
10 Canceling Implicit Rules
只要重新定义相同的target pattern和prerequisites,但是没有recipe即可。注意Implicit rule也是pattern rule,所以这种方法也可以取消pattern rule。如:
1 | %.o : %.s |
参考:
GNU Make手册