Makefile 多作业生成的依赖项排序错误

Makefile 多作业生成的依赖项排序错误,makefile,gnu-make,Makefile,Gnu Make,下面的Makefile必须创建(多个)输出目录,并根据上面目录中的输入在这些目录中生成输出。因此,在输入时,dirn存在,dirn/file.foo存在。构建必须创建dirn/out/file.bar 此Makefile在作为单个作业运行时工作(请注意,它在$(shell)中创建两个必需的源目录和文件)。这大概是因为makedirs是all的第一个/最左边的先决条件。但是,它不适用于多任务make(即make-j4/whatever) 关于如何修复依赖关系以确保在需要输出目录之前生成它们,您有什

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

此Makefile在作为单个作业运行时工作(请注意,它在
$(shell)
中创建两个必需的源目录和文件)。这大概是因为
makedirs
all
的第一个/最左边的先决条件。但是,它不适用于多任务make(即
make-j4
/whatever)

关于如何修复依赖关系以确保在需要输出目录之前生成它们,您有什么想法吗

编辑

我应该清楚地表明,我已经尝试了各种仅顺序的先决条件解决方案,但我无法做到这一点并保证目标实际重建(顺序问题通常只是为了防止重建,而不是强制执行依赖顺序)。如果您有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
#预期输出:
#制作目录
#正在将dir1/out/./file.foo复制到dir1/out/file.bar
#正在将dir2/out/./file.foo复制到dir2/out/file.bar
#创建了所有输出文件
#完成
$(shell mkdir dir1>&/dev/null;触摸dir1/file.foo\
mkdir dir2>&/dev/null;触摸dir2/file.foo)
OUTDIRS=dir1/out dir2/out
输出=dir1/out/file.bar dir2/out/file.bar
.默认目标:=全部
.PHONY:makedirs$(OUTDIRS)
.后缀:.foo.bar
%.bar:../.foo
@回显“将$<复制到$@”
@cp$<$@
全部:makedirs输出
@回音“完成”
产出:$(产出)
@echo“已创建所有输出文件”
makedirs:$(OUTDIRS)
@mkdir-p$(OUTDIRS)
@echo“制作目录”
清洁:
@rm-rf dir1 dir2
使
$(输出)
依赖于目录本身:

 $(OUTDIRS) : 
      mkdir -p $@

 $(OUTPUTS) : | $(OUTDIRS)
这将确保在
$(OUTPUTS)
之前创建目录,但如果目录比目标更新,则不会导致重新生成输出(这很重要,因为每次向其添加文件时都会设置目录的时间戳…)

注意:您还可以在输出配方中添加一个
mkdir-p
,如果每次运行输出规则时目录还不存在,那么它将创建目录,但我更喜欢上面的方法

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

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

那么有点奇怪--您使用的是哪种版本的make?我刚刚运行了以下命令:注意创建目录时的
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
请注意,它同时生成
dir1/out
dir2/out
,并且在这两个过程完成之前不会运行模式规则。我还验证了我在note2中提到的解决方案也可以工作(至少在我的机器上…)

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

foo.o: foo.h

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

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

谢谢,但是纯订单依赖关系的问题是它不能保证重建目标,只能保证创建目录。我无法在我的原始Makefile中获得可用的OOD,但我还没有尝试过这个简化版本-您尝试过对上面的文件进行特定更改吗?注意:我不能这样做,因为模式规则。如果目录不存在,则模式中的干匹配将失败(请参阅“..”)。似乎没有任何方法可以解决这个问题(无论如何,我可以找到)。注2:我认为这不会起作用,因为
$(输出)
必须由模式规则而不是新规则构建。我尝试了各种组合,并将
makedirs
放入原始Makefile中的模式规则中,但没有一个有效。抱歉,刚刚回到这个问题,运行3.82。我使用
-j
获得与您相同的输出,但它不作为单个作业工作(无
-j
)-它只复制
dir2
,而忽略
dir1
。只是想找到它…这真的很奇怪(在3.81上也复制了它)。我运行了一个
--debug=vi
,似乎make说它必须重新生成
dir1/out/file.bar
,然后说它在不运行任何规则的情况下重新生成了它。。。不幸的是,我现在的带宽有限,但如果你真的弄明白了,请发帖,因为我很好奇。。。
foo.o: foo.h

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