Dig*_*uma 11 parallel-processing makefile gnu-make
我有一个项目,其中包括一个代码生成器,它只需一次调用代码生成器就可以从一个输入文件生成几个.c和.h文件.我有一个规则,其中.c和.h文件作为多个目标,输入文件作为先决条件,配方是代码生成器的调用.然后我有进一步的规则来编译和链接生成的.c文件.
这可以正常使用-j因子为1,但如果我增加j因子,我发现我得到了代码生成器的多次调用,直到-j因子或预期目标文件的数量,以最小者为准.这很糟糕,因为代码生成器的多次调用可能会因生成的代码被多次写入而导致失败.
我不打算在这里发布我的实际(大)代码,但我已经能够构建一个似乎表现出相同行为的小例子.
Makefile看起来像这样:
output.concat: output5 output4 output3 output2 output1
cat $^ > $@
output1 output2 output3 output4 output5: input
./frob input
clean:
rm -rf output*
对于此示例,我编写了一个简单的shell脚本,frob而不是代码生成器,它从一个输入文件生成多个输出文件:
#!/bin/bash
for i in {1..5}; do
{
echo "This is output${i}, generated from ${1}. input was:"
cat ${1}
} > output${i}
done
当我使用非统一-j因子运行此Makefile时,我得到以下输出:
$ make -j2 ./frob input ./frob input cat output5 output4 output3 output2 output1 > output.concat $
我们看到./frob这里被调用了两次,这很糟糕.有没有什么方法可以构造这个规则,使配方只被调用一次,即使是非统一-j因子?
我考虑过更改规则,以便只有一个预期的输出文件是目标,然后添加另一个没有配方的规则,使其目标是剩余的预期输出文件,先决条件是第一个预期的输出文件.但我不确定这会起作用,因为我不知道我是否可以保证生成文件的顺序,因此可能最终会产生循环依赖.
Mad*_*ist 16
这就是定义make的工作方式.像这样的规则:
foo bar baz : boz ; $(BUILDIT)
Run Code Online (Sandbox Code Playgroud)
完全等同于编写这三条规则:
foo : boz ; $(BUILDIT)
bar : boz ; $(BUILDIT)
baz : boz ; $(BUILDIT)
Run Code Online (Sandbox Code Playgroud)
没有办法(在GNU make中)定义具有所需特征的显式规则; 也就是说,一次调用配方将构建所有三个目标.
但是,如果输出文件和输入文件共享一个公共库,则可以编写如下的模式规则:
%.foo %.bar %.baz : %.boz ; $(BUILDIT)
Run Code Online (Sandbox Code Playgroud)
奇怪的是,对于具有多个目标的隐式规则,GNU make假定对配方的单个调用将构建所有目标,并且它将完全按照您的需要运行.
Iva*_*sev 11
从输入文件a b ?中并行地正确生成和更新多个目标:make -ji1 i2
all: a b c
.INTERMEDIATE: d
a: d
b: d
c: d
d: i1 i2
cat i1 i2 > a
cat i1 i2 > b
cat i1 i2 > c
Run Code Online (Sandbox Code Playgroud)
如果a,b,c缺少任何一个,d则重新制作伪目标.d永远不会创建该文件; 单一规则d可以避免对配方进行多次并行调用.
.INTERMEDIATE确保丢失的文件d不会触发d配方.
"John Graham-Cumming - GNU Make Book"一书中的多个目标的其他一些方法,第92-96页.
@MadScientist的答案是有希望的 - 我想我可以使用它.与此同时,我一直在玩这个,并提出了一个不同的可能解决方案,正如问题所暗示的那样.我可以将规则分为两部分,如下所示:
INPUT_FILE = input
OUTPUT_FILES = output5 output4 output3 output2 output1
OUTPUT_FILE1 = $(firstword $(OUTPUT_FILES))
OUTPUT_FILES_REST = $(wordlist 2,$(words $(OUTPUT_FILES)),$(OUTPUT_FILES))
$(OUTPUT_FILE1): $(INPUT_FILE)
./frob $<
touch $(OUTPUT_FILES_REST)
$(OUTPUT_FILES_REST): $(OUTPUT_FILE1)
Run Code Online (Sandbox Code Playgroud)
仅将一个输出文件作为目标可以解决可能存在的并行问题.然后我们将这个输出文件作为其余输出文件的先决条件.重要的是,在frob配方中,除了第一个输出文件之外,我们触摸所有输出文件,因此我们保证第一个输出文件的时间戳比其余的都要长.