制作重拍图案规则的目标,即使它比先决条件要新

时间:2019-04-17 14:09:24

标签: makefile

我正在尝试在Makefile中使用“伪”模式规则,以通过一次调用编译器来编译我的所有Erlang源文件。它可以工作,但是由于某种原因,make决定始终重新制作目标,即使它比其先决条件要新。这是怎么回事?

这是我的Makefile的相关部分:

.SUFFIXES: .erl .beam .yrl .d .app .app.src
.PHONY: all build tests clean docs distclean realclean

build: $(ERL_OBJECTS)

$(ERL_NON_PRE_OBJECTS:.beam=.bea%): $(ERL_NON_PRE_SOURCES)
    $(ERLC) -pa $(EBIN_DIR) $(ERLC_FLAGS) -o $(EBIN_DIR) $(filter $(SRC_DIR)/%.erl,$?)

这是make -d -p build输出的相关部分:

Considering target file 'build'.
 File 'build' does not exist.
  Considering target file 'ebin/field_usage.beam'.
   Looking for an implicit rule for 'ebin/field_usage.beam'.
   Trying pattern rule with stem 'm'.
   Trying rule prerequisite 'src/field_usage.erl'.
   Trying rule prerequisite 'src/bo_lib.erl'.
   Trying rule prerequisite 'src/bo.erl'.
   Trying rule prerequisite 'src/flags.erl'.
   Trying rule prerequisite 'src/bos_utilities.erl'.
   Trying rule prerequisite 'src/generate_bo_template.erl'.
   Trying rule prerequisite 'src/bo_mappings.erl'.
   Trying rule prerequisite 'src/document_bos.erl'.
   Trying rule prerequisite 'src/types.erl'.
   Trying rule prerequisite 'src/document_config_bos.erl'.
   Trying rule prerequisite 'src/bo_transform.erl'.
   Found an implicit rule for 'ebin/field_usage.beam'.
    [...]
   Finished prerequisites of target file 'ebin/field_usage.beam'.
   Prerequisite 'src/field_usage.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/bo_lib.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/bo.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/flags.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/bos_utilities.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/generate_bo_template.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/bo_mappings.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/document_bos.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/types.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/document_config_bos.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/bo_transform.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'src/field_usage.erl' is older than target 'ebin/field_usage.beam'.
   Prerequisite 'ebin' is order-only for target 'ebin/field_usage.beam'.
  Must remake target 'ebin/field_usage.beam'.

[...]

ERL_OBJECTS := ebin/field_usage.beam ebin/bo_lib.beam ebin/bo.beam ebin/flags.beam ebin/bos_utilities.beam ebin/generate_bo_template.beam ebin/bo_mappings.beam ebin/document_bos.beam ebin/types.beam ebin/document_config_bos.beam ebin/bo_transform.beam

ERL_NON_PRE_OBJECTS := ebin/field_usage.beam ebin/bo_lib.beam ebin/bo.beam ebin/flags.beam ebin/bos_utilities.beam ebin/generate_bo_template.beam ebin/bo_mappings.beam ebin/document_bos.beam ebin/types.beam ebin/document_config_bos.beam ebin/bo_transform.beam

ERL_NON_PRE_SOURCES := src/field_usage.erl src/bo_lib.erl src/bo.erl src/flags.erl src/bos_utilities.erl src/generate_bo_template.erl src/bo_mappings.erl src/document_bos.erl src/types.erl src/document_config_bos.erl src/bo_transform.erl

ebin/field_usage.beam: src/field_usage.erl src/bo_lib.erl src/bo.erl src/flags.erl src/bos_utilities.erl src/generate_bo_template.erl src/bo_mappings.erl src/document_bos.erl src/types.erl src/document_config_bos.erl src/bo_transform.erl src/field_usage.erl | ebin
#  Implicit rule search has been done.
#  Implicit/static pattern stem: 'm'
#  Also makes: ebin/bo_transform.beam ebin/document_config_bos.beam ebin/types.beam ebin/document_bos.beam ebin/bo_mappings.beam ebin/generate_bo_template.beam ebin/bos_utilities.beam ebin/flags.beam ebin/bo.beam ebin/bo_lib.beam
#  Last modified 2019-04-10 13:04:00.763145368
#  File has been updated.
#  Successfully updated.
# automatic
# @ := ebin/field_usage.beam
# automatic
# % :=
# automatic
# * := m
# automatic
# + := src/field_usage.erl src/bo_lib.erl src/bo.erl src/flags.erl src/bos_utilities.erl src/generate_bo_template.erl src/bo_mappings.erl src/document_bos.erl src/types.erl src/document_config_bos.erl src/bo_transform.erl src/field_usage.erl
# automatic
# | := ebin
# automatic
# < := src/field_usage.erl
# automatic
# ^ := src/field_usage.erl src/bo_lib.erl src/bo.erl src/flags.erl src/bos_utilities.erl src/generate_bo_template.erl src/bo_mappings.erl src/document_bos.erl src/types.erl src/document_config_bos.erl src/bo_transform.erl
# automatic
# ? :=
# variable set hash-table stats:
# Load=8/32=25%, Rehash=0, Collisions=2/31=6%
#  recipe to execute (from '/mybuildpath/make/stdapp.mk', line 408):
    $(PROGRESS)
    $(ERLC) -pa $(EBIN_DIR) $(ERLC_FLAGS) -o $(EBIN_DIR) $(filter $(SRC_DIR)/%.erl,$?)

对我来说这毫无意义。当ebin/field_usage.beam比其所有依赖项都新时,为什么还要重建它? $?甚至将make设置为空。

我认为,这是由于以下情况造成的:每个已编译的.beam模块都比其相应的.erl源要新,但是有.erl个源要比其他{{ 1}}。至少在配方中添加.beams即可解决问题。

但是,我无法通过简单的示例来重现空白的touch $(ERL_NON_PRE_OBJECTS)情况:

$?

例如FILES := a b c INS := $(FILES:%=in/%.txt) OUTS := $(FILES:%=out/%.txt) .PHONY: build build: $(OUTS) $(OUTS): | out out: mkdir -p $@ $(OUTS:.txt=.tx%): $(INS) echo building $@ from $? cp -t out $? 将打印make; touch in/b.txt; make。这指出了我的食谱存在的问题,但是无论时间戳如何,我都没有设法得到一个空的building out/a.txt from in/b.txt

1 个答案:

答案 0 :(得分:0)

我的上述多目标模式规则存在两个问题。其中一个(事后看来)应该很明显,第二个...应该不是很多。我将在有关in/*.txtout/*.txt文件的简化示例中解释这些问题。

1)多目标模式规则的每个目标取决于每个先决条件

换句话说,每个目标都必须比最新的先决条件要新。我可能知道out/a.txt是根据in/a.txt构建的,而out/b.txt是根据in/b.txt构建的,但是该规则告诉了与make不同的地方。它告诉您是否in/a.txt已更新,它必须重建out/中的所有三个文件,因为它们都依赖于in/a.txt。因此,如果配方仅更新out/a.txt,则下次您运行make时,它将尝试再次重建其余文件:

$ touch in/a.txt
$ make
building out/a.txt from in/a.txt
$ make
building out/b.txt from in/a.txt

请注意,$@始终设置为多目标模式规则的第一个目标,该目标并不比所有先决条件都要新。即使存在多个此类目标,make也会为out/c.txt选择$@。我不知道为什么make$@选择目标。

解决方案::始终touch -c在食谱末尾的每个目标(-c确保您不会以这种方式创建新文件;以防其中存在错误您的食谱,并且您没有建立目标之一,那么应该最好没有目标文件完成,而不是在磁盘上放置假的,空的目标文件。

$(OUTS:.txt=.tx%): $(INS)
    echo building $@ from $?
    cp -t out $?
    @touch -c $(OUTS)

2)其他依赖性(例如包含)可能导致$?为空

让我们考虑输入文件可以包含一些头文件的情况!这些包含项引入了其他依赖性,例如:

out/b.txt: in/header

现在,我们可以重现空白的$?场景:

$ touch in/header
$ make
building out/a.txt from
cp: missing file operand
Try 'cp --help' for more information.
make: *** [Makefile:15: out/a.txt] Error 1

发生了什么事?

  • out/b.txt必须重建,因为它比in/header(它的依赖项)更旧。
  • 多目标模式规则提供了重建out/b.txt(以及out/a.txtout/c.txt)的方法。
  • 由于某些原因,makeout/a.txt选择了$@。我不知道为什么不out/b.txt,但是out/a.txt确实比in/header还老。
  • 现在make必须设置$?,并且由于任何原因它都不会将其设置为in/header。可能是因为out/a.txt不依赖于in/header,尽管我仍然觉得这种推理很奇怪,因为in/headermake要使用此规则的唯一原因。
  • $?为空时,我的演示配方刚刚崩溃。我的原始配方调用erlc $?至少没有崩溃,因为erlc可以在没有任何实际文件重新编译的情况下被调用。但是它也没有重新编译任何模块,这是一个问题,因为之前介绍的touch -c会隐藏此错误。基本上,如果项目中还有其他依赖项(例如头文件),则这种使用多目标模式规则的方法破坏增量构建

请注意,$?不一定为空:如果将$@设置为直接依赖于修改后的标头的文件,则将从一开始就按照我的期望进行设置:

$ touch in/header out/a.txt
$ make
building out/b.txt from in/header

解决方案:没有解决此问题的简便方法。到目前为止,我最好的解决方案是引入一些标记文件,这些文件可以快速生成并直接映射到输入文件。

FILES := a b c
INS := $(FILES:%=in/%.txt)
OUTS := $(FILES:%=out/%.txt)
MARKS := $(FILES:%=tmp/%.mark)

mark2input = $(1:tmp/%.mark=in/%.txt)

.PHONY: build
build: $(OUTS)

$(OUTS): | out

out tmp:
    mkdir -p $@

# Regular pattern rule to create a marker file
# (with a very quick command) when an input file
# has to be recompiled.
tmp/%.mark: in/%.txt | tmp
    @echo marking $< to recompile
    $(file >$@)

# Multi-target pattern rule declares output files
# depending on marker files. The recipe however
# compiles the input files the markers correspond to.
$(OUTS:.txt=.tx%): $(MARKS)
    @echo recompiling $(call mark2input,$?)
    cp -t out $(call mark2input,$?)
    @touch -c $(OUTS)

# Dependencies must be declared on the marker files!
tmp/b.mark: in/header

这是一个复杂的Makefile,但至少增量构建可以很好地工作:

$ touch in/include
$ make
marking in/b.txt to recompile
recompiling in/b.txt