我正在查看以下玩具Makefile来跟踪错误:
all: hey bye
hey bye: hi
cat hi
touch hey bye
在此方案中,我事先创建了hi
文件。
然后我运行make -j 2 all
并得到以下输出:
cat hi
cat hi
hey
touch hey bye
hey
touch hey bye
而如果我用make -j 1 all
运行它,命令只执行一次。调试输出非常冗长,但据我了解,它执行了两次,因为它试图满足两个不同作业的hey
和bye
。
如何防止这种情况发生?这是正确的行为吗?为什么它认为一次运行两次命令会使其更快地完成?
行为是正确的,因为你误解了你所说的内容。
您可以在 GNU 制作手册中阅读详细信息。
此规则:
hey bye: hi
...
不告诉 make 对配方的一次调用将构建两个目标。 相反,make 解释它的方式与你写的完全一样:
hey: hi
...
bye: hi
...
也就是说,您已经声明了两个完全不同的目标,其中每个目标都将通过单独调用...
来创建。
当您在没有-j
的情况下运行 make 时,它会看到hey
已过期并运行配方。 然后它查看bye
,现在(因为以前的食谱刚刚更新了它(它是最新的,所以它似乎可以工作。
但是,如果您使用-j2
运行,make 会看到hey
已过时并开始该作业,然后寻找更多工作。 现在,当它检查bye
时,hey
的食谱尚未更新它,因此认为它仍然过时并启动了配方的另一个实例。
解决此问题的最佳方法取决于您尚未提供的实际使用情况的详细信息。
如果输入和输出与目标和先决条件的名称相关,则可以编写模式规则。 与显式规则不同,多个目标模式告诉 make 对配方的一次调用会构建所有目标:
%.x %.y : %.z
...
如果这行不通,并且您愿意将构建限制为使用 GNU make 4.3 或更高版本,您可以利用"分组目标"功能:
hey bye &: hi
...
如果这不可行,则必须使用哨兵目标,例如:
all: hey bye
hey bye: .sentinel ;
.sentinel: hi
...
@touch $@