如何强制gnu make不能并行构建配方?

Syl*_*sne 12 parallel-processing makefile gnu-make

我怎么能告诉gnu不要并行构建一些配方.假设我有以下makefile:

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 $@ $<
Run Code Online (Sandbox Code Playgroud)

但是,该my-pre-processor命令创建具有固定名称的临时文件(我无法更改此名称).如果我只使用没有-j参数的make,这工作正常.但是,如果使用该-j选项,则构建有时会失败,因为两次并发调用会my-pre-processor覆盖其临时文件.

我想知道是否有办法告诉make它必须不构建并行化%.cpp : %.xxx配方执行的尝试.

Sha*_*Lee 17

解决方案1

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

例:

.PHONY: all clean

.NOTPARALLEL:

anotherTarget: dependency1
Run Code Online (Sandbox Code Playgroud)

解决方案2

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

用法: make -j <n>make --jobs=<n>

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

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


解决方案3

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

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

  • 不幸的是,所有这些解决方案都迫使在整个Makefile上进行完全顺序的构建。但是,问题是仅针对单个配方停用并行性。 (2认同)

jww*_*jww 11

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

要指定确切的顺序,可以使用仅订单的先决条件.这是GNU make的手册页:

但是,有时您可能希望对要调用的规则强制执行特定排序,而不强制在执行其中一个规则时更新目标.在这种情况下,您希望定义仅订单的先决条件.可以通过在先决条件列表中放置管道符号(|)来指定仅订单的先决条件:管道符号左侧的任何先决条件都是正常的; 右边的任何先决条件都是仅限订单:

targets : normal-prerequisites | order-only-prerequisites
Run Code Online (Sandbox Code Playgroud)

以下是他们提供的示例:

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)
Run Code Online (Sandbox Code Playgroud)

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

  • 五年前的问题和正确答案在我需要它的同一天出现。谢谢! (2认同)

den*_*ane 6

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

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
Run Code Online (Sandbox Code Playgroud)

此外,由于您使用的是语法但不是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
Run Code Online (Sandbox Code Playgroud)

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

.cpp.o:
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
Run Code Online (Sandbox Code Playgroud)

用户在需要提供自定义包含目录时可能会喜欢的内容 -L...

  • 考虑为预处理器创建一个包装器脚本,该脚本执行临时目录中的所有工作.这将使`Makefile`保持整洁. (5认同)

Bet*_*eta 6

这是一个可怕的kludge,但它将完成这项工作:

b.cpp: a.cpp

c.cpp: b.cpp
Run Code Online (Sandbox Code Playgroud)

或者,如果实际上有很多这些,你可以喝几杯烈酒并做到这一点:

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)))
Run Code Online (Sandbox Code Playgroud)

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