Gnu Make:中间文件丢失时重建

问题描述 投票:0回答:4

我使用 Make 来管理数据工作流程(而不是构建软件项目)。我有这样的模式规则:

%.B: %.A
    foo $^ > $@

%.C: %.B
    bar $^ > $@

.SECONDARY:

现在,如果我注意到某些

.B
文件存在问题(例如
foo
中的错误),我会删除特定的
.B
文件并再次执行
make
,当然相应的
 .C
文件仍然存在,并且比其
.A
文件更新,因此不会发生任何事情。

有没有一种好方法可以在缺少依赖项时强制重建

.C
文件?本质上,我想我正在寻找一种方法来将
.B
文件提升到完整目标,而不是次要或中间目标。

makefile gnu-make
4个回答
4
投票

我从未见过这个问题,尽管我经常这样做。 也许那是因为我从不使用模式规则。 只需获取目标列表并使用static模式规则即可。 也许就像使用

$(wildcard)
获取来源列表一样简单。

# List of sources
As := 1.A 2.A 3.A

${As}: ; touch $@

# List of targets
Bs := ${As:.A=.B}
Cs := ${Bs:.B=.C}

# Static pattern rules
${Bs}: %.B: %.A ; touch $@
${Cs}: %.C: %.B ; touch $@

.PHONY: all
all: ${Cs}

导致

$ make all --warn
touch 1.A
touch 1.B
touch 1.C
touch 2.A
touch 2.B
touch 2.C
touch 3.A
touch 3.B
touch 3.C
$ make all --warn
make: Nothing to be done for 'all'.
$ rm 2.B
$ make all --warn
touch 2.B
touch 2.C

乔布斯是个好人。


1
投票

我认为如果您想确保构建完成后它们存在,则必须列出实际的

.B
文件作为
all
目标(或其他目标,这是在运行期间构建的)的先决条件。我不确定还有其他方法可以做到吗


1
投票

似乎您添加了

.SECONDARY
目标以避免删除 %.B 文件,这会导致问题。所以你需要不同的解决方法而不是
.SECONDARY
。 下面的怎么样:

%.B: %.A; cat $^ > $@ && echo B >> $@

%.C: %.B; cat $^ > $@ && echo C >> $@

$(foreach   C,\
            $(sort $(filter %.C,$(MAKECMDGOALS))),\
            $(eval $C: $(patsubst %.C,%.B,$C))\
            $(eval $(patsubst %.C,%.B,$C): $(patsubst %.C,%.A,$C)))

运行上面的makefile:

bash#
$ touch doc.A

bash#
$ make doc.C
cat doc.A > doc.B && echo B >> doc.B
cat doc.B > doc.C && echo C >> doc.C

bash#
$ ls doc.*
doc.A  doc.B  doc.C

bash#
$ rm doc.B

bash#
$ make doc.C
cat doc.A > doc.B && echo B >> doc.B
cat doc.B > doc.C && echo C >> doc.C

bash#
$ ls doc.*
doc.A  doc.B  doc.C

bash#
$ touch doc.A

bash#
$ make doc.C
cat doc.A > doc.B && echo B >> doc.B
cat doc.B > doc.C && echo C >> doc.C

bash#
$ touch doc.B

bash#
$ make doc.C
cat doc.B > doc.C && echo C >> doc.C

该解决方法为命令行上指定的所有 %.C 目标生成显式规则链。不寻常使用 GNU Make 的不寻常解决方法!


1
投票

我已经解决了类似的问题:

# Avoids collisions with filenames
.PHONY: all

# Stops intermediate files being deleted (e.g. content/X.html)
.SECONDARY:

PUBLIC_FROM_ORIGINAL := $(foreach ...)
PUBLIC_FROM_CONTENT := $(foreach ...)
CONTENT_FROM_ORIGINAL := $(foreach ...)
PUBLIC_ALL = $(PUBLIC_FROM_CONTENT) $(PUBLIC_FROM_ORIGINAL)

all: content public

public: $(PUBLIC_ALL)
content: $(CONTENT_FROM_ORIGINAL) $(PUBLIC_FROM_CONTENT)

内容文件是中间文件,但由于内容文件是通过 make all 重建的,因此如果删除它们,它们会被恢复(无论下游文件如何),并且对它们的更改会传播到公共文件。

所以我认为同样的方法应该适合你,使用这样的东西:

%.B: %.A
    foo $^ > $@

%.C: %.B
    bar $^ > $@

make all: %.B %.C

.SECONDARY:

我还没有对此进行测试,因此您可能需要使用变量来代替模式。

© www.soinside.com 2019 - 2024. All rights reserved.