【问题标题】:How can I force gnu make to not build recipe in parallel?如何强制 gnu make 不并行构建配方?
【发布时间】:2010-12-03 14:18:40
【问题描述】:

我如何告诉 gnu make 不要并行构建一些配方。假设我有以下生成文件:

sources = a.xxx b.xxx c.xxx
target  = program

all : $(target)

$(target) : $(patsubst %.xxx,%.o,$(sources))
    $(CXX) -o $@ $<

%.o : %.cpp
    $(CXX) -c -o $@ $<

%.cpp : %.xxx
    my-pre-processor -o $@ $<

但是,my-pre-processor 命令会创建具有固定名称的临时文件(我无法更改)。如果我只使用不带-j 参数的make,这可以正常工作。但是,如果使用 -j 选项,构建有时会失败,因为 my-pre-processor 的两个并发调用会覆盖它们的临时文件。

我想知道是否有办法告诉 make 它不能构建 try 以并行化 %.cpp : %.xxx 配方的执行。

【问题讨论】:

  • 对于这个特殊问题(临时文件被程序的多个副本覆盖)另一个解决方案可能是创建一个具有随机名称的临时目录(例如mktemp -d)并运行该工具从里面。然后您可以并行运行该工具的多个副本,从而加快构建速度,并假设它将其文件写入当前目录,它们不会被覆盖。

标签: makefile parallel-processing gnu-make


【解决方案1】:

解决方案 1

GNU Make 包含特殊的内置伪目标.NOTPARALLEL

例子:

.PHONY: all clean

.NOTPARALLEL:

anotherTarget: dependency1

解决方案 2

您还可以在命令行上使用-j &lt;n&gt;,--jobs[=&lt;n&gt;] 标志,其中n 是允许并行运行的收件人数。

用法: make -j &lt;n&gt;make --jobs=&lt;n&gt;

示例: make -j 1make --jobs=1

注意:省略 &lt;n&gt; 将允许执行任意数量的配方,仅受系统可用资源的限制


解决方案 3

最后,您可以将解决方案 2 中的命令行标志分配给 Makefile 中的 MAKEFLAGS 变量

示例: MAKEFLAGS := -j 1 或者 MAKEFLAGS := --jobs=1

【讨论】:

  • 不幸的是,所有这些解决方案都强制在整个 Makefile 上完全按顺序构建。然而,问题是询问是否只为单个配方停用并行性。
【解决方案2】:

一个相关的解决方案是指定您想要构建事物的确切顺序(而不是说“不要并行构建”)。

要指定确切的顺序,您可以使用 order-only prerequisites。这是 GNU make 的手册页:

但是,有时您会遇到想要强加 在不强制执行的情况下对要调用的规则进行特定排序 如果执行这些规则之一,则要更新的目标。在这种情况下, 您想定义仅限订单的先决条件。仅订购的先决条件 可以通过在先决条件中放置管道符号 (|) 来指定 列表:管道符号左侧的任何先决条件都是正常的;任何 右侧的先决条件是仅订购的:

targets : normal-prerequisites | order-only-prerequisites

这是他们提供的示例:

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

$(OBJDIR)/%.o : %.c
        $(COMPILE.c) $(OUTPUT_OPTION) $<

all: $(OBJS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
        mkdir $(OBJDIR)

由于竞争条件,我不得不在 make dist 规则上使用仅订单先决条件。 dist 配方依赖于 distcleandiff 规则,diff 规则执行 svn diff -r XXX 以显示确切的更改。有时,make 会删除它刚刚创建的 diff,因为 clean 规则会在 diff 规则之后运行。

【讨论】:

  • 五年前的问题和正确答案出现在我需要它的同一天。谢谢!
【解决方案3】:

这是一个可怕的组合,但它会完成工作:

b.cpp: a.cpp

c.cpp: b.cpp

或者如果真的有很多,你可以喝几杯烈性酒然后这样做:

c-sources = $(sources:.xxx=.cpp)

ALLBUTFIRST = $(filter-out $(firstword $(c-sources)), $(c-sources))
ALLBUTLAST = $(filter-out $(lastword $(c-sources)), $(c-sources))
PAIRS = $(join $(ALLBUTLAST),$(addprefix :,$(ALLBUTFIRST)))

$(foreach pair,$(PAIRS),$(eval $(pair)))

(这适用于 GNUMake,我不知道其他版本。)

【讨论】:

    【解决方案4】:

    如果临时文件是在当前工作目录中创建的,您也许可以使用子目录(不漂亮,但很少见):

    sources = a.xxx b.xxx c.xxx
    target  = program
    
    all : $(target)
    
    $(target) : $(patsubst %.xxx,%.o,$(sources))
        $(CXX) -o $@ $<
    
    %.o : %.cpp
        $(CXX) -c -o $@ $<
    
    %.cpp : %.xxx
        mkdir $@.d
        s=`realpath $<` && cd $@.d && my-pre-processor -o ../$@ "$${s}" || { $(RM) -r $@.d && false; }
        $(RM) -r $@.d
    

    另外,由于您使用的是语法而非 GNU make 独有的功能,请注意以下等效的 Makefile 应该更便于移植

    sources = a.xxx b.xxx c.xxx
    target  = program
    
    all : $(target)
    
    $(target) : $(sources:.xxx=.o)
        $(CXX) -o $@ $<
    
    .cpp.o:
        $(CXX) -c -o $@ $<
    
    .xxx.cpp:
        mkdir $@.d
        s=`realpath $<` && cd $@.d && my-pre-processor -o ../$@ "$${s}" || { $(RM) -r $@.d && false; }
        $(RM) -r $@.d
    
    .PHONY: all
    .SUFFIXES: .xxx .cpp .o
    

    还要注意 GNU make 的内在 .cpp.o: 规则允许用户在命令行上指定标志,(类似于)

    .cpp.o:
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
    

    当您的用户需要通过-L... 提供自定义包含目录时,他们可能会喜欢它

    【讨论】:

    • 考虑为在临时目录中完成所有工作的预处理器制作一个包装脚本。这将使Makefile 更整洁。
    【解决方案5】:

    你最好研究一下automake附带的ylwrap工具的操作:它解决了旧版本lexyacc的大部分相同问题:

    http://git.savannah.gnu.org/cgit/automake.git/tree/lib/ylwrap

    【讨论】:

      【解决方案6】:

      在 linux 上,您可以使用 flock file -c "command": https://man7.org/linux/man-pages/man1/flock.1.html。我已经使用flock 解决了 GPU 资源同步的问题。

      ###############################
      %.wav: %.txt
          flock .cuda.lock -c "python synthesize.py --in $< --out $@ --dev cuda:0"
      ################################
      %.mp3: %.wav
          ffmpeg -i $< $@
      ################################
      

      如果我打电话:make 1.mp3 2.mp3 3.mp3 -j3 - 它将同步 python,但不会同步 ffmpeg

      【讨论】:

        【解决方案7】:

        我遇到了同样的问题(我的 sw 签名服务不接受多个并发请求)。我的解决方案是在接收器中添加一个互斥锁:

        %.cpp : %.xxx
            @ until mkdir /tmp/make.lock 2>/dev/null ; do sleep 2 ; done
            my-pre-processor -o $@ $<
            @ rmdir /tmp/make.lock
        

        这允许 makefile 的所有其他部分并行运行,但此时将只有一个“我的预处理器”在运行。

        【讨论】:

          猜你喜欢
          • 2022-08-16
          • 2022-01-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多