ILD

target is up to date 、nothing to be done、Pattern rule 分析
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2021-3-13 站点:Inside Linux Development

Target更新

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没有被执行。


Pattern rule

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手册



Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.