使用 .INTERMEDIATE 在 makefile 中进行不可靠的并行构建?
Unreliable parallel builds in a makefile with .INTERMEDIATE?
我有一个生成多个输出文件的工具,众所周知,它很难在 make 中建模。我用的是GNU Makefile rule generating a few targets from a single source file的食谱,看起来简单可靠。 几乎 有效。
不幸的是,我看到了一些关于并行构建的非常奇怪的行为,有时似乎会删除依赖项;我不明白为什么。
这是我的测试用例:
out3: out1 out2
touch out3
.INTERMEDIATE: out.intermediate
out1 out2: out.intermediate
out.intermediate: in
touch out1 out2
如果我构建一次,它就可以工作:
$ touch in
$ make -f test.mk out3 -j4
touch out1 out2
touch out3
out1
和out2
一起建,一次,不错;然后 out3
是根据结果构建的。
现在我触摸输入文件,模拟增量构建,然后重试:
$ touch in
$ make -f test.mk out3 -j4
touch out1 out2
那是重建 out1
和 out2
,正确...但它没有重建 out3
,它应该有。然后,如果我再做一次构建:
$ make -f test.mk out3 -j4
touch out3
...它赶上了。
这仅适用于并行构建。 -j1
构建工作正常。
这很糟糕 --- 我需要能够依赖正确的构建。有人知道发生了什么事吗?
这是 GNU Make 4.1。
你link的SO回答有误;令人惊讶的是,这么多人显然已经成功地使用了它。
发生的事情(您可以将 -rRd
选项添加到您的 make 调用中以查看更多详细信息)是 GNU make 的目录缓存的结果。 GNU make 不希望文件系统状态以除您的 makefile 描述之外的任何方式改变,因此它缓存目录的内容以提供显着的性能提升(对于大型 makefiles/directories)。
基本上,当制定 运行 规则时:
out.intermediate: in
touch out1 out2
预计此配方不会更新除规则中列出的目标以外的任何目标:out.intermediate
。如果其他文件 out1
和 out2
已经内化在目录缓存中(它们将是因为它们存在并且我们已经将它们检查为 out3
的先决条件),那么 make won不要返回文件系统查看它们是否已更新:根据 makefile,make "knows" 它们不能更改,因为没有规则 运行 可以更改它们。
有一个简单的单字符修复程序可以使所有这些工作正常进行。更改此行:
out1 out2: out.intermediate
对此:
out1 out2: out.intermediate ;
如果你想更明确,你也可以使用这个:
out1 out2: out.intermediate
@:
甚至这个,用于调试:
out1 out2: out.intermediate
@echo Do nothing
所有这些的共同点是您现在不仅定义了目标和先决条件之间的依赖关系,而且还给出了一个 recipe make 应该调用该规则。即使配方是空的(如第一个示例),所以 make 实际上 运行 没有任何命令,make 仍然会推断 out1
and/or out2
已更改,它将使这些目标的缓存修改时间无效,并从文件系统中重新检索它们。
我有一个生成多个输出文件的工具,众所周知,它很难在 make 中建模。我用的是GNU Makefile rule generating a few targets from a single source file的食谱,看起来简单可靠。 几乎 有效。
不幸的是,我看到了一些关于并行构建的非常奇怪的行为,有时似乎会删除依赖项;我不明白为什么。
这是我的测试用例:
out3: out1 out2
touch out3
.INTERMEDIATE: out.intermediate
out1 out2: out.intermediate
out.intermediate: in
touch out1 out2
如果我构建一次,它就可以工作:
$ touch in
$ make -f test.mk out3 -j4
touch out1 out2
touch out3
out1
和out2
一起建,一次,不错;然后 out3
是根据结果构建的。
现在我触摸输入文件,模拟增量构建,然后重试:
$ touch in
$ make -f test.mk out3 -j4
touch out1 out2
那是重建 out1
和 out2
,正确...但它没有重建 out3
,它应该有。然后,如果我再做一次构建:
$ make -f test.mk out3 -j4
touch out3
...它赶上了。
这仅适用于并行构建。 -j1
构建工作正常。
这很糟糕 --- 我需要能够依赖正确的构建。有人知道发生了什么事吗?
这是 GNU Make 4.1。
你link的SO回答有误;令人惊讶的是,这么多人显然已经成功地使用了它。
发生的事情(您可以将 -rRd
选项添加到您的 make 调用中以查看更多详细信息)是 GNU make 的目录缓存的结果。 GNU make 不希望文件系统状态以除您的 makefile 描述之外的任何方式改变,因此它缓存目录的内容以提供显着的性能提升(对于大型 makefiles/directories)。
基本上,当制定 运行 规则时:
out.intermediate: in
touch out1 out2
预计此配方不会更新除规则中列出的目标以外的任何目标:out.intermediate
。如果其他文件 out1
和 out2
已经内化在目录缓存中(它们将是因为它们存在并且我们已经将它们检查为 out3
的先决条件),那么 make won不要返回文件系统查看它们是否已更新:根据 makefile,make "knows" 它们不能更改,因为没有规则 运行 可以更改它们。
有一个简单的单字符修复程序可以使所有这些工作正常进行。更改此行:
out1 out2: out.intermediate
对此:
out1 out2: out.intermediate ;
如果你想更明确,你也可以使用这个:
out1 out2: out.intermediate
@:
甚至这个,用于调试:
out1 out2: out.intermediate
@echo Do nothing
所有这些的共同点是您现在不仅定义了目标和先决条件之间的依赖关系,而且还给出了一个 recipe make 应该调用该规则。即使配方是空的(如第一个示例),所以 make 实际上 运行 没有任何命令,make 仍然会推断 out1
and/or out2
已更改,它将使这些目标的缓存修改时间无效,并从文件系统中重新检索它们。