代码之家  ›  专栏  ›  技术社区  ›  code_fodder

并行调用所有子服务器上的gnumake(-j),然后最后运行链接器规则(即顺序重要)

  •  2
  • code_fodder  · 技术社区  · 7 年前

    我有一个C++ MaFrimeProject。它适用于非平行建筑。它的工作99%为并行建设…唯一的问题是,我不能让最后一个可执行链接行运行(它必须是最后发生的事情)。

    我有一些限制:我不想在我的链接线上有任何虚假的依赖,因为这会导致它每次都重新链接。也就是说,一旦我的目标被建立起来,当我重新建立的时候,它就不应该被重新连接。

    这里是(稍微做作)最小的例子。请不要试图在它上面挖个洞,它实际上是为了显示问题,它不是真实的,但我显示的问题是。你应该能够运行这个程序,看到和我一样的问题。

    # Set the default goal to build.
    .DEFAULT_GOAL = build
    
    #pretend subdirs (these don't really exist but it does not matter so long as they always try to be built)
    MAKE_SUB_DIRS = 1 2 3
    
    #pretend shared objects that are created by the pretend makefile sub directories (above)
    OUTPUTS = out1.so out2.so out3.so
    
    # Top level build goal - depends on all of the subdir makes and the target.out
    .PHONY: build
    build: $(MAKE_SUB_DIRS) target.out
        @echo build finished
    
    # Takes 1 second to build each of these pretend sub make directories. PHONY so always runs
    .PHONY: $(MAKE_SUB_DIRS)
    $(MAKE_SUB_DIRS):
        @if [ ! -f out$@.so ] ; then echo making $@... ; sleep 1 ; echo a > out$@.so ; fi
    
    # The main target, pretending that it needs out1,2 and 3 to link
    # Should only run when target.out does not exist
    # No PHONY deps allowed here
    target.out:
        @echo linking $@...
        @ls $(OUTPUTS) > /dev/null
        @cat $(OUTPUTS) > target.out
    
    # Clean for convinience
    clean:
        @rm -rf *.so target.out
    

    现在,我真的不在乎 make 工作,我想要的是 make -j 工作。这是我试图运行的:

    admin@osboxes:~/sandbox$ make clean 
    admin@osboxes:~/sandbox$ 
    admin@osboxes:~/sandbox$ make -j     - 1st attempt
    making 1...
    making 2...
    linking target.out...
    making 3...
    ls: cannot access 'out1.so': No such file or directory
    ls: cannot access 'out2.so': No such file or directory
    ls: cannot access 'out3.so': No such file or directory
    makefile:24: recipe for target 'target.out' failed
    make: *** [target.out] Error 2
    make: *** Waiting for unfinished jobs....
    admin@osboxes:~/sandbox$ 
    admin@osboxes:~/sandbox$ make -j     - 2nd attempt
    linking target.out...
    build finished
    admin@osboxes:~/sandbox$ 
    admin@osboxes:~/sandbox$ make -j     - 3rd attempt
    build finished
    admin@osboxes:~/sandbox$
    

    所以我强调了我的三次尝试。

    • 尝试1:您可以看到构建的所有4个依赖项同时启动(大约)。因为每个 makeing x... 花1秒钟 linking 几乎是瞬间,我们看到了我的错误。然而,这三个“库”都是正确构建的。
    • 尝试2:只有当库不存在时才会被创建(这是bash代码——假装做了makefile可能做的事情)。在这种情况下,它们已经被创建。所以链接现在就通过了,因为它只需要存在库。
    • 尝试3:没有任何事情发生,因为没有任何事情需要发生:)

    所以你可以看到所有的步骤都在那里,这只是命令它们的问题。我想要做亚德士的 1, 2, 3 以任何顺序并行构建,然后只在它们全部完成之后,我希望 target.out 运行(即链接器)。

    但我不想这样称呼它: $(MAKE) target.out 因为在我真正的makefile中我有很多变量,所有的设置…

    我试过看(从其他答案看) .NOT_PARALLEL 使用DEP ORDER运算符 | (pipe),我试着点了一大堆规则让target.out成为最后一个……但是 -j 选择权只是把所有这些都翻遍,破坏了我的订单:(…必须有一些简单的方法来做到这一点?

    3 回复  |  直到 7 年前
        1
  •  1
  •   Renaud Pacalet    7 年前

    编辑 :添加将变量传递到Sub Make的方法示例。通过添加优化一点 $(SUBDIRS) 到的先决条件 build 而不是按照它的配方制作。

    我不确定我是否完全理解您的组织,但处理子目录的一个解决方案如下。我假设,有点像在您的示例中,构建子目录 foo 生产 foo.o 在顶部目录中。我还假设您的top makefile定义变量( VAR1 , VAR2 …)创建子目录时要传递给子目录的。

    VAR1    := some-value
    VAR2    := some-other-value
    ...
    SUBDIRS := foo bar baz
    SUBOBJS := $(patsubst %,%.o,$(SUBDIRS))
    
    .PHONY: build clean $(SUBDIRS)
    
    build: $(SUBDIRS)
        $(MAKE) top
    
    $(SUBDIRS):
        $(MAKE) -C $@ VAR1=$(VAR1) VAR2=$(VAR2) ...
    
    top: top.o $(SUBOBJS)
        $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS)
    
    top.o: top.cc
        $(CXX) $(CXXFLAGS) -c $< -o $@
    
    clean:
        rm -f top top.o $(SUBOBJS)
        for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done
    

    这是并行安全的,并保证只有在所有子构建完成后才会进行链接。请注意,您也可以 export 要传递给Sub Make的变量,而不是在命令行上传递它们:

    VAR1    := some-value
    VAR2    := some-other-value
    ...
    export VAR1 VAR2 ...
    
        2
  •  1
  •   Chris Dodd    7 年前

    通常,您只需添加lib文件作为target.out的先决条件:

    target.out: $(OUTPUTS)
            @echo linking $@...
    

    问题是,如果任何输出lib文件较新,这将重新链接target.out。通常情况下,这是您想要的(如果lib已更改,则需要重新链接目标),但您明确表示不需要。

    GNUmake提供了一个名为“仅订购先决条件”的扩展,您在 | :

    target.out: | $(OUTPUTS)
            @echo linking $@...
    

    现在,只有当target.out不存在时,它才会被重新链接,但在这种情况下,它仍然会等到 $(OUTPUTS) 已经完成建造

    如果你 $(OUTPUT) 文件是由子目录生成的,您可能会发现需要类似以下规则:

    .PHONY: $(OUTPUT)
    $(OUTPUT):
            $(MAKE) -C $$(dirname $@) $@
    

    调用递归make,除非您有其他将在子目录中调用make的规则

        3
  •  0
  •   code_fodder    7 年前

    好吧,所以我找到了“A”解决方案…但它有点违背我的意愿,因此很难看(但不是那样 那个 丑陋的):

    这个 只有 我能理解的确保并行构建顺序的方法(再次从我读到的其他答案中)如下:

    rule: un ordered deps
    rule:
        @echo this will happen last
    

    在这里将进行(或制作)三次DEP。在任何顺序,然后最后回音线将运行。

    然而,我要做的是一个规则,特别是这样,它检查是否有任何更改或文件不存在-然后,仅在那时,运行该规则。

    我所知道的从另一个规则的预兆中运行规则的唯一方法是递归地调用make。但是,我只在同一个makefile上递归调用make时会遇到以下问题:

    1. 默认情况下不传入变量
    2. 许多相同的规则将被重新定义(不允许或不需要)

    所以我想到了这个:

    生成文件:

    # Set the default goal to build.
    .DEFAULT_GOAL = build
    
    #pretend subdirs (these don't really exist but it does not matter so long as they always try to be built)
    MAKE_SUB_DIRS = 1 2 3
    
    #pretend shared objects that are created by the pretend makefile sub directories (above)
    OUTPUTS = out1.so out2.so out3.so
    
    # Top level build goal - depends on all of the subdir makes and the target.out
    export OUTPUTS
    .PHONY: build
    build: $(MAKE_SUB_DIRS)
        @$(MAKE) -f link.mk target.out --no-print-directory
        @echo build finished
    
    # Takes 1 second to build each of these pretend sub make directories. PHONY so always runs
    .PHONY: $(MAKE_SUB_DIRS)
    $(MAKE_SUB_DIRS):
        @if [ ! -f out$@.so ] ; then echo making $@... ; sleep 1 ; echo a > out$@.so ; fi
    
    # Clean for convinience
    clean:
        @rm -rf *.so target.out
    

    MK:

    # The main target, pretending that it needs out1,2 and 3 to link
    # Should only run when target.out does not exist
    # No PHONY deps allowed here
    target.out:
        @echo linking $@...
        @ls $(OUTPUTS) > /dev/null
        @cat $(OUTPUTS) > target.out
    

    因此,在这里,我将链接器规则放入一个名为link.mk的单独make file中,这样可以避免对同一个文件进行递归make调用(因此使用重新定义的规则)。但我必须导出我需要传递的所有变量…这很难看,如果这些变量发生变化,会增加一些维护开销。

    …但是…它的工作原理是:

    我不会很快标记这个,因为我希望一些天才会指出一个更整洁/更好的方法来做这件事…