多作业的依赖性排序错误

时间:2017-06-06 15:10:09

标签: makefile gnu-make

下面的Makefile必须创建(多个)输出目录,并从上面目录中的输入生成这些目录中的输出。因此,在输入时,dir n 存在,并且dir n /file.foo存在。构建必须创建dir n /out/file.bar。

此Makefile在作为单个作业运行时有效(请注意,它会在$(shell)中创建两个必需的源目录和文件)。它可能有效,因为makedirsall的第一个/最左边的先决条件。但是,它不适用于多作业(即make -j4 /无论如何)。

有关如何修复依赖项以确保输出目录在需要之前生成的任何想法?

修改

我应该已经明确表示我已尝试过各种仅限订单的先决条件解决方案,但我无法做到这一点并保证目标实际重建(仅限订单点通常是阻止重建,不强制执行依赖顺序)。如果您有OO解决方案,请检查它!感谢。

# expected output:
# made directories
# copying dir1/out/../file.foo to dir1/out/file.bar
# copying dir2/out/../file.foo to dir2/out/file.bar
# created all output files
# done

    $(shell mkdir dir1 >& /dev/null; touch dir1/file.foo; \
            mkdir dir2 >& /dev/null; touch dir2/file.foo)

    OUTDIRS = dir1/out dir2/out
    OUTPUTS = dir1/out/file.bar dir2/out/file.bar 

    .DEFAULT_GOAL := all

    .PHONY: makedirs $(OUTDIRS)

    .SUFFIXES: .foo .bar

    %.bar : ../%.foo
        @echo "copying $< to $@"
        @cp $< $@

    all : makedirs outputs
        @echo "done"

    outputs : $(OUTPUTS)
        @echo "created all output files"

    makedirs : $(OUTDIRS)
        @mkdir -p $(OUTDIRS)
        @echo "made directories"

    clean :
        @rm -rf dir1 dir2

1 个答案:

答案 0 :(得分:2)

make $(OUTPUTS)对目录本身有order-only依赖:

 $(OUTDIRS) : 
      mkdir -p $@

 $(OUTPUTS) : | $(OUTDIRS)

这将保证在$(OUTPUTS)之前创建目录,但如果目录比目标更新,则不会导致输出重建(这很重要,因为每次设置目录的时间戳时都会设置文件被添加到它...)。

注意:您还可以在输出配方中添加mkdir -p,如果每次运行输出规则时都不会创建目录,但我更喜欢上述方法。

注意2:在现有的makefile中,您还可以添加一行:$(OUTPUTS): makedirs,这将强制makedirs规则在构建任何输出之前运行,但同样,我更喜欢以上解决方案: - )

----编辑:-----

当时有点奇怪 - 您使用的是什么版本的品牌?我刚刚运行了以下内容:在创建目录时注意sleep 1,这意味着如果存在并发问题,它肯定会命中:

$(shell mkdir dir1 >& /dev/null; touch dir1/file.foo; \
        mkdir dir2 >& /dev/null; touch dir2/file.foo)


OUTDIRS = dir1/out dir2/out
OUTPUTS = dir1/out/file.bar dir2/out/file.bar

.DEFAULT_GOAL := all

$(OUTPUTS) : | $(OUTDIRS)

$(OUTDIRS) :
        @echo "making $@"
        sleep 1
        mkdir -p $@
        @echo "done making $@"


%.bar : ../%.foo
        @echo "copying $< to $@"
        @cp $< $@

all : outputs
        @echo "done $@"

outputs : $(OUTPUTS)
        @echo "created all output files"

clean :
        @rm -rf dir1 dir2

输出结果为:

~/sandbox/tmp20> make -j -f Makefile2
making dir1/out
making dir2/out
sleep 1
sleep 1
mkdir -p dir1/out
mkdir -p dir2/out
done making dir1/out
done making dir2/out
copying dir1/out/../file.foo to dir1/out/file.bar
copying dir2/out/../file.foo to dir2/out/file.bar
created all output files
done all

请注意,它会同时构建dir1/outdir2/out,并且在两者都完成之前不会运行模式规则。我还验证了我在note2中提到的解决方案也有效(至少在我的机器上......)。

在执行模式规则时,您可以指定模式之外的依赖关系,因此您可以执行以下操作:

foo.o: foo.h

%.o: %.c
    recipe here...

如果foo.o较新,则会重建foo.h,如果在构建foo.h之前不存在foo.o,则会尝试构建smb_username