如何处理具有多个有序目标的并行调用

时间:2017-09-29 08:39:10

标签: makefile gnu-make

GNU make允许1)并行执行和2)在同一个调用中指定几个目标:

make -j4 clean all

但是,由于GNU使目标并行化,因此可能会出现一些竞争条件。插图:

$ cat Makefile
clean:
    @sleep 1 && rm -f foo

all: foo
    @sleep 2 && cat foo

foo:
    @echo '$@' > $@
$ make -j4 clean ; make -j4 all
foo
$ make -j4 clean all
cat: foo: No such file or directory
Makefile:5: recipe for target 'all' failed
make: *** [all] Error 1

是否有一种很好的方法可以强制实现目标之间的顺序,但是仍然可以从每个目标的并行加速中受益?在上面的示例中,最好等到cleanall开始之前完成,以避免竞争条件。

如图所示,单独的make调用按预期工作,但这并非100%令人满意:

  1. 有些目标可以同时调用,有些则不能。因此,完全禁止多个目标可被视为限制性太强。但是识别所有有效和无效的组合是棘手且容易出错的。
  2. 为了完全避免这个问题,可以警告所有潜在用户,这样的Makefile在并行模式下不支持多个目标调用,但某些用户不可避免地会忽略此警告。
  3. 比赛条件并不总是导致错误。有些人显然可以无缝地工作,但会产生错误的结果。

2 个答案:

答案 0 :(得分:1)

恕我直言,这似乎根源于一个问题,即在编程语言(在本例中为shell)中,我们能够形成依赖性,这些依赖性与make可以处理的性质有着根本不同的性质。在您的示例中,cleanfoo不存在存在依赖关系,而all具有逆依赖关系。如果你同时激活这两个目标,这似乎超越了make的理论基础 - 我不知道是否存在能够处理这种关系的合理理论。我能提出的一切都是明确的表述:

.PHONY: all clean

clean:
    @sleep 1
    rm -f foo

all: foo $(filter clean,$(MAKECMDGOALS))
    @sleep 2
    cat foo

foo: $(filter clean,$(MAKECMDGOALS))
    @echo Creating $@
    @echo '$@' > $@

我认为这肯定是一个有趣的问题。

答案 1 :(得分:0)

我找到了一种解决方法(但我并不是100%确信它是最好的解决方案,并且它没有隐藏的缺点)。我们的想法是使用MAKECMDGOALS GNU make变量和条件来强制多个目标的序列化:

ifeq ($(words $(MAKECMDGOALS)),1)
.PHONY: all clean

clean:
    @sleep 1 && rm -f foo

all: foo
    @sleep 2 && cat foo

foo:
    @echo '$@' > $@
else
.NOTPARALLEL:

%:
    @$(MAKE) $@
endif

当然,条件的条件可能更复杂,例如,测试其中一个目标是否匹配clean ...