Makefile使用--just-print时file函数报错,求解决方案
我有一个编译C/C++项目的Makefile,因最终链接命令行字符数超出shell限制,参考方案改用传递给链接器的参数列表文件。
现在编写了如下规则:
$(OBJECT_LIST): $(OBJECT_FILES) $(file > $@,$^)
其中OBJECT_LIST是存储目标文件列表的文件路径,OBJECT_FILES是原本要直接传递给链接器的目标文件列表。
我的IDE使用--just-print选项确定需索引的文件,执行上述规则时出现类似错误:
open: objects.in: No such file or directory. Stop.
这是一次空运行(由--just-print触发),相关目录并未实际创建,为何make仍尝试执行file函数?该如何解决?
原因与解决方案
原因
Make的$(file)函数属于立即执行函数,它在Make解析Makefile的阶段就会被执行,而非像规则里的shell命令那样,仅在实际执行规则的阶段才触发。因此即使开启--just-print(或-n)空运行模式,Make依然会调用$(file)尝试写入文件,此时若存放OBJECT_LIST的目录未创建,就会抛出找不到路径的错误。
解决方案
方案1:用shell命令替代$(file)函数
将$(file)函数替换为shell的重定向命令,这样在空运行模式下,命令只会被打印而不会实际执行:
$(OBJECT_LIST): $(OBJECT_FILES) @echo "$^" > $@
如果目标文件列表中存在带空格的文件名,建议使用printf避免空格分割问题:
$(OBJECT_LIST): $(OBJECT_FILES) @printf "%s\n" $^ > $@
方案2:判断运行模式,仅非空运行时执行$(file)
通过检查MAKEFLAGS变量判断是否处于空运行模式,仅在非空运行时执行$(file)操作:
$(OBJECT_LIST): $(OBJECT_FILES) $(if $(filter --just-print -n,$(MAKEFLAGS)),,$(file > $@,$^))
逻辑说明:如果MAKEFLAGS包含--just-print或-n,则跳过文件写入;否则执行$(file)函数生成目标文件列表。
方案3:提前创建目录(适配$(file)函数场景)
若必须使用$(file)函数,可在生成文件前先创建对应目录,同样加入空运行判断:
$(OBJECT_LIST): $(OBJECT_FILES) $(if $(filter --just-print -n,$(MAKEFLAGS)),,@mkdir -p $(dir $@)) $(if $(filter --just-print -n,$(MAKEFLAGS)),,$(file > $@,$^))
非空运行时会先创建目录,避免写入文件时的路径错误;空运行时则跳过所有实际操作。
内容的提问来源于stack exchange,提问作者Patrick Wright

