Makefile依赖项foo.o排首位,为何all.o构建先执行?
问题描述
现有如下Makefile文件:
objects = foo.o bar.o all.o # 第1行 all: $(objects) # 以下文件通过隐式规则完成编译 foo.o: foo.c bar.o: bar.c all.o: all.c all.c: echo "int main() { return 0; }" > all.c %.c: touch $@ clean: rm -f *.c *.o all
执行make命令后得到如下输出:
echo "int main() { return 0; }" > all.c cc -c -o all.o all.c touch foo.c cc -c -o foo.o foo.c touch bar.c cc -c -o bar.o bar.c cc all.o foo.o bar.o -o all
核心疑问:Makefile第1行定义的objects变量中foo.o是目标all的第一个依赖项,为何实际执行构建时,依赖all.c的all.o对应的构建流程会最先运行?
原因解答
这个现象是GNU Make的规则解析和依赖检查逻辑导致的,和很多人直觉里「依赖严格按书写顺序从左到右构建」的认知有偏差,核心逻辑如下:
- GNU Make不会拿到Makefile就直接按书写顺序跑命令,它启动后首先会进入预扫描阶段:遍历所有内置隐式规则和用户自定义规则,把所有能生成最终目标(Makefile里定义的第一个目标,这里就是
all)的可能构建路径全部找出来,先做一轮依赖存在性、新旧程度的检查。 - 你的Makefile里,
all.c有专门写死的显式构建规则,foo.c、bar.c则只能靠通配的%.c模式规则生成。预扫描阶段Make会优先匹配内置的「直接从同名.c文件编译生成可执行文件」的隐式规则(对应%: %.c的内置规则),这个规则会触发Make先查找all.c是否存在、是否需要更新,发现你写了all.c的显式构建规则,就会先执行命令生成all.c;接着顺着依赖链查到all.o依赖已经生成的all.c,就会顺手先把all.o编译出来。 - 等预扫描完所有可能的构建路径,Make确认你自己写的
all: $(objects)显式规则优先级更高,应该用三个.o文件链接生成最终程序,才会回头按你写的依赖列表顺序处理剩下的foo.o和bar.o:先通过%.c规则touch生成foo.c、编译出foo.o,再touch生成bar.c、编译出bar.o,最后执行链接命令生成all。
你可以自己验证这个逻辑:执行make的时候加上
-r参数关闭所有内置隐式规则,再跑就会看到构建顺序完全和依赖列表一致:先生成foo.c编译foo.o,再生成bar.c编译bar.o,最后生成all.c编译all.o再链接。因为没了内置规则的预扫描触发,Make就不会提前去碰all.c和all.o了。
内容的提问来源于stack exchange,提问作者yapkm01
相关产品推荐
相关产品推荐

