Makefile 的一个陷阱
前言(Intro)
问题是这样的,我写了一个 Makefile,大致内容如下,
1 | |
然而执行 make b 却只打印出 b;
执行 make c1 的结果却是 make: Nothing to be done for 'c1'。
为何会无法执行 c1 和 c2?!
找原因
Nothing to be done for 的出现,有可能是因为没有可执行的命令。
但在这里似乎不是这个问题。
为了寻找这个问题,我从最简单的写法开始推:
1 | |
1 | |
正常。
1 | |
1 | |
正常。
1 | |
1 | |
正常。
1 | |
1 | |
不正常。
跟之前的相比,变化的是 .PHONY 中添加了 c1 和 c2 项。这个问题一定跟 .PHONY 相关。
原因
参见 GNU - makefile。有这么一行:
The implicit rule search (see Implicit Rules) is skipped for .PHONY targets. This is why declaring a target as .PHONY is good for performance, even if you are not worried about the actual file existing.
所以,当用 % 这种使用通配符的规则(即 Implicit Rule),只要对应的实际项存在于 .PHONY 中,这条规则就会被跳过。
读文档不仔细,就容易踩到坑。
Makefile:怪我咯?
其他
在调试的过程中,还发现其他几点需要注意的地方。
一、
我用 $(warning) 来打印调试变量,奇怪的是在先决条件 (prerequisites) 中使用 warning 无法打印出 % 的值。
比如
1 | |
make a.c 打印出的是 Makefile:1: %.o,然而去掉 warning,实际上 make a.c 是可以执行到 %.o 里的命令的。
我猜可能是 a.c 对应的值还没映射到 %, warning 就打印了。或者因为 % 是个特殊的变量,从而 warning 无法打印。具体没有研究过,瞎猜的。
二、
1 | |
这里虽然 b 不在 .PHONY 里,但是 make b 依然可以无限次执行。原因在于 c 是伪命令,会影响到 b。
只要把 c 从 .PHONY 中去掉,执行 make b(已经存在 b 文件的情况下),就会得到 make: 'b' is up to date. 的提示。
所以写 Makefile 的时候要注意链式依赖里有没有依赖伪命令,以此提高执行效率。