使用.INTERMEDIATE在makefile中建立不可靠的并行构建吗?

Dav*_*ven 3 makefile gnu-make

我有一个生成多个输出文件的工具,众所周知,这很难在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)

这是重建out1out2,正确的...但它一直没有重建out3,它应该有。然后,如果我执行另一个构建:

$ make -f test.mk out3 -j4
touch out3
Run Code Online (Sandbox Code Playgroud)

...然后赶上了。

适用于并行构建。-j1建立良好的工作。

这很糟糕---我需要能够依靠正确的构建。有谁知道发生了什么吗?

这是GNU Make 4.1。

Mad*_*ist 7

您链接到的SO答案有错误;令人惊讶的是,如此多的人显然已经成功地使用了它。

发生的事情(您可以在-rRdmake调用中添加选项以更详细地了解)是GNU make的目录缓存的结果。GNU make并不期望文件系统状态会以您的makefile描述之外的任何方式发生变化,因此它缓存目录的内容以显着提高性能(对于大型makefile /目录)。

基本上,当make运行规则时:

out.intermediate: in
        touch out1 out2
Run Code Online (Sandbox Code Playgroud)

它不会期望此食谱会更新规则中列出的目标以外的任何目标out.intermediate。如果其他文件out1out2目录中的高速缓存已经内化(他们会因为他们的存在,我们已经检查了他们的先决条件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已更改,并且它将使缓存的修改时间无效这些目标,然后从文件系统中重新获取它们。