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

时间:2010-12-03 14:18:40

标签: makefile parallel-processing 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 $@ $<

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

我想知道是否有办法告诉make它不能构建试图并行化%.cpp : %.xxx食谱的执行。

6 个答案:

答案 0 :(得分:14)

解决方案1 ​​

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

示例:

.PHONY: all clean

.NOTPARALLEL:

anotherTarget: dependency1

解决方案2

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

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

实施例: make -j 1make --jobs=1

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


解决方案3

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

实施例: MAKEFLAGS := -j 1 要么 MAKEFLAGS := --jobs=1

答案 1 :(得分:8)

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

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

  

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

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规则之后运行。

答案 2 :(得分: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

此外,由于您使用的是语法但不是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...

提供自定义包含目录时可能会喜欢的内容

答案 3 :(得分:6)

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

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 :(得分:3)

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

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

答案 5 :(得分:0)

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

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

这允许makefile的所有其他部分并行运行,但是此时只有一个“ my-pre-processor”正在运行。