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

Makefile 如何强制gnu make不并行构建配方?,makefile,parallel-processing,gnu-make,Makefile,Parallel Processing,Gnu Make,我如何告诉gnu make不要并行地构建一些配方。假设我有以下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

我如何告诉gnu make不要并行地构建一些配方。假设我有以下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 $@ $<
sources=a.xxx b.xxx c.xxx
目标=程序
全部:$(目标)
$(目标):$(patsubst%.xxx,%.o,$(来源))
$(CXX)-o$@$<
%.o:%.cpp
$(CXX)-c-o$@$<
%.cpp:%.xxx
我的预处理器-o$@$<
但是,
my pre-processor
命令使用固定名称创建临时文件(我无法更改此名称)。如果我只使用make而不使用
-j
参数,那么这工作正常。但是,如果使用了
-j
选项,构建有时会失败,因为对
my pre-processor
的两次并发调用会覆盖它们的临时文件


我想知道是否有一种方法可以告诉make,它不能构建并尝试并行执行
%.cpp:%.xxx
配方。

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


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

这是一个可怕的难题,但它可以完成以下任务:

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中有效,我不知道其他版本。)

您最好研究一下
automake
附带的
ylwrap
工具的操作:它解决了
lex
yacc
旧版本的大多数相同问题:

解决方案1 GNU Make包含特殊的内置伪目标
。NOTPARALLEL

例如:

.PHONY: all clean

.NOTPARALLEL:

anotherTarget: dependency1

解决方案2 您还可以在命令行上使用
-j
--jobs[=]
标志,其中
n
是允许并行运行的往复次数

用法:
make-j
make-jobs=

例如:
make-j1
make--jobs=1

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


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

例如:
MAKEFLAGS:=-j1

MAKEFLAGS:=--jobs=1

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

要指定确切的顺序,可以使用。以下是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)
OBJDIR:=OBJDIR
OBJS:=$(addprefix$(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o:%.c
$(COMPILE.c)$(输出选项)$<
全部:$(OBJS)
$(OBJS):|$(OBJDIR)
$(OBJDIR):
mkdir$(OBJDIR)

由于比赛条件,我不得不在
make dist
规则中使用仅限订单的先决条件。
dist
配方取决于
distclean
diff
规则,而
diff
规则执行一个
svn diff-r XXX
来显示确切的变化。有时,make会删除它刚刚创建的差异,因为清除规则将在差异规则之后运行。

我遇到了相同的问题(我的软件签名服务不接受多个并发请求)。我的解决方案是在recepie中添加互斥:

%.cpp : %.xxx
    @ until mkdir /tmp/make.lock 2>/dev/null ; do sleep 2 ; done
    my-pre-processor -o $@ $<
    @ rmdir /tmp/make.lock
%.cpp:%.xxx
@直到mkdir/tmp/make.lock 2>/dev/null;做睡眠2;完成
我的预处理器-o$@$<
@rmdir/tmp/make.lock

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

考虑为预处理器创建一个包装器脚本,在临时目录中完成所有工作。这将使
Makefile
更整洁。五年前的问题和正确答案在我需要它的同一天出现。谢谢不幸的是,所有这些解决方案都强制在整个Makefile上进行完全顺序的构建。然而,问题是关于仅停用单个配方的并行性。对于这个特定问题(运行程序的多个副本会覆盖临时文件),另一个解决方案可能是创建一个带有随机名称的临时目录(例如,使用
mktemp-d
)并从其中运行该工具。然后,您可以并行运行该工具的多个副本,从而加快构建速度,并假设它将其文件写入当前目录,它们将不会被覆盖。
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)
%.cpp : %.xxx
    @ until mkdir /tmp/make.lock 2>/dev/null ; do sleep 2 ; done
    my-pre-processor -o $@ $<
    @ rmdir /tmp/make.lock