Makefile 如何处理具有多个有序目标的并行make调用

Makefile 如何处理具有多个有序目标的并行make调用,makefile,gnu-make,Makefile,Gnu Make,GNU make允许1)并行执行和2)在同一调用中指定多个目标: make -j4 clean all 但是,当GNU make将目标并行化时,可能会出现一些竞争条件。说明: $ cat Makefile clean: @sleep 1 && rm -f foo all: foo @sleep 2 && cat foo foo: @echo '$@' > $@ $ make -j4 clean ; make -j4 all fo

GNU make允许1)并行执行和2)在同一调用中指定多个目标:

make -j4 clean all
但是,当GNU make将目标并行化时,可能会出现一些竞争条件。说明:

$ cat Makefile
clean:
    @sleep 1 && rm -f foo

all: foo
    @sleep 2 && cat foo

foo:
    @echo '$@' > $@
$ make -j4 clean ; make -j4 all
foo
$ make -j4 clean all
cat: foo: No such file or directory
Makefile:5: recipe for target 'all' failed
make: *** [all] Error 1
有没有一种很好的方法可以在目标之间建立一个顺序,但仍然可以从每个目标的并行加速中获益?在上面的示例中,最好等到
clean
完成后再开始
all
,以避免出现竞态条件

如图所示,单独的make调用按预期工作,但这并不是100%令人满意:

  • 有些目标可以同时调用,有些则不能。因此,完全禁止多个目标可能被视为限制性太强。但是识别所有有效和无效的组合是很棘手的,而且容易出错
  • 为了完全避免这个问题,可以警告所有潜在的Makefile用户,并行模式下不支持多目标调用,但是一些用户不可避免地会忽略这个警告
  • 比赛条件并不总是导致错误。有些显然可以无缝工作,但会产生错误的结果
  • 我找到了一个解决方法(但我并不100%相信它是最好的解决方案,而且它没有隐藏的缺点)。其思想是使用
    MAKECMDGOALS
    GNU make变量和条件强制序列化多个目标:

    ifeq ($(words $(MAKECMDGOALS)),1)
    .PHONY: all clean
    
    clean:
        @sleep 1 && rm -f foo
    
    all: foo
        @sleep 2 && cat foo
    
    foo:
        @echo '$@' > $@
    else
    .NOTPARALLEL:
    
    %:
        @$(MAKE) $@
    endif
    

    当然,条件条件的条件可能更复杂,例如,测试其中一个目标是否匹配
    clean
    ..

    IMHO这似乎是源于编程语言中的问题(在本例中为shell)我们能够制定出与make能够处理的依赖关系本质上不同的依赖关系。在您的示例中,
    clean
    依赖于
    foo
    的不存在,而
    all
    具有相反的依赖性。如果你同时使这两个目标活跃起来,这似乎超越了制造理论的基础——我不知道是否存在一个可以处理这种关系的合理理论。我能想到的只是明确的表述:

    .PHONY: all clean
    
    clean:
        @sleep 1
        rm -f foo
    
    all: foo $(filter clean,$(MAKECMDGOALS))
        @sleep 2
        cat foo
    
    foo: $(filter clean,$(MAKECMDGOALS))
        @echo Creating $@
        @echo '$@' > $@
    

    我认为这确实是一个有趣的问题。

    也许你可以打电话给
    makeclean&&makeall-j4
    ?或者有一个特殊的
    rebuild
    目标,该目标以以下顺序将
    clean
    all
    作为先决条件。我不知道还有其他解决方案。@TimF不幸的是,您的
    重建
    目标无法解决问题。在我的简单示例中,调用
    make-j4rebuild
    时仍然会出现错误。单独的make调用是一个选项(如我最初的问题所示),但我正在寻找比指示我的makefile的任何潜在用户不支持多目标调用更好的方法……那么我想知道是否有人有其他解决方案,因为我尝试了构建设置,我也遇到了同样的问题。但我真的怀疑有没有解决办法。顺便说一下,我还将一个Makefile分发给一组用户,他们总是(直观地)在两个单独的命令中生成
    clean
    all
    ,所以在我的情况下这不是问题。@RenaudPacalet我的GNUmake失败,即使我将
    clean
    作为
    all
    的先决条件,因为它可能会假设从foo开始的DAG不会干扰clean的DAG,这是由于文件系统的不可见依赖关系造成的。@Vroomfondel是的,将
    clean
    作为
    all
    的先决条件并不能解决问题。而且,不管怎么说,即使它起作用了,那也真是太过分了:每次都必须重新构建所有内容。是的,我还尝试使用
    MAKECMDGOALS
    。但是你用它看起来比我的好。谢谢你这个好主意。我们甚至可以通过一个额外的规则来改进它:
    all foo:$(filter clean,$(MAKECMDGOALS))
    和所有其他不变的规则。很好的hack,太糟糕了,它不依赖于本机GNU make功能,因为它依赖于操作系统(在Unix环境中本机等待2ms这样简单的事情在Windows环境中变得很痛苦)@TimF uhh。。。我想你没有领会我和雷诺的观点。这个脚本充当依赖矛盾问题的演示者。
    sleep
    只是一个占位符,用于“导致并行线程之间竞争条件的任何处理都会评估相互矛盾的依赖关系树”。