我有一个生成多个输出文件的工具,众所周知,这很难在make中建模。我正在使用GNU Makefile规则中的配方,该规则从单个源文件生成一些目标,这似乎简单可靠。而且几乎可以正常工作。
不幸的是,关于并行构建,我看到了一些非常奇怪的行为,有时似乎会删除依赖关系。我不明白为什么。
这是我的测试用例:
out3: out1 out2
touch out3
.INTERMEDIATE: out.intermediate
out1 out2: out.intermediate
out.intermediate: in
touch out1 out2
Run Code Online (Sandbox Code Playgroud)
如果我构建一次,它将起作用:
$ touch in
$ make -f test.mk out3 -j4
touch out1 out2
touch out3
Run Code Online (Sandbox Code Playgroud)
out1并且out2一起建造一次,这很好;然后out3根据结果构建。
现在,我触摸输入文件,模拟增量构建,然后重试:
$ touch in
$ make -f test.mk out3 -j4
touch out1 out2
Run Code Online (Sandbox Code Playgroud)
这是重建out1和out2,正确的...但它一直没有重建out3,它应该有。然后,如果我执行另一个构建:
$ make -f test.mk out3 -j4
touch out3
Run Code Online (Sandbox Code Playgroud)
...然后赶上了。
这仅适用于并行构建。-j1建立良好的工作。
这很糟糕---我需要能够依靠正确的构建。有谁知道发生了什么吗?
这是GNU Make 4.1。
您链接到的SO答案有错误;令人惊讶的是,如此多的人显然已经成功地使用了它。
发生的事情(您可以在-rRdmake调用中添加选项以更详细地了解)是GNU make的目录缓存的结果。GNU make并不期望文件系统状态会以您的makefile描述之外的任何方式发生变化,因此它缓存目录的内容以显着提高性能(对于大型makefile /目录)。
基本上,当make运行规则时:
out.intermediate: in
touch out1 out2
Run Code Online (Sandbox Code Playgroud)
它不会期望此食谱会更新规则中列出的目标以外的任何目标out.intermediate。如果其他文件out1和out2目录中的高速缓存已经内化(他们会因为他们的存在,我们已经检查了他们的先决条件out3),那么让不回去到文件系统,看看他们是否已经更新是否:使“知道”它们不能更改,因为根据makefile,它运行的规则不可能对其进行更改。
有一个简单的单字符修复程序可以使所有这些工作正常进行。更改此行:
out1 out2: out.intermediate
Run Code Online (Sandbox Code Playgroud)
对此:
out1 out2: out.intermediate ;
Run Code Online (Sandbox Code Playgroud)
如果您想更加明确,也可以使用以下命令:
out1 out2: out.intermediate
@:
Run Code Online (Sandbox Code Playgroud)
甚至用于调试:
out1 out2: out.intermediate
@echo Do nothing
Run Code Online (Sandbox Code Playgroud)
所有这些的共同点在于,您现在不仅定义了目标和前提条件之间的依赖关系,而且还给出了make应该为该规则调用的配方。即使配方为空(如第一个示例中所示),因此make实际上并未运行任何命令,make仍将推断出时间戳记可能已更改out1和/或out2已更改,并且它将使缓存的修改时间无效这些目标,然后从文件系统中重新获取它们。