我正在尝试在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
。
答案 0 :(得分:0)
我的上述多目标模式规则存在两个问题。其中一个(事后看来)应该很明显,第二个...应该不是很多。我将在有关in/*.txt
和out/*.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.txt
和out/c.txt
)的方法。make
为out/a.txt
选择了$@
。我不知道为什么不out/b.txt
,但是out/a.txt
确实比in/header
还老。make
必须设置$?
,并且由于任何原因它都不会将其设置为in/header
。可能是因为out/a.txt
不依赖于in/header
,尽管我仍然觉得这种推理很奇怪,因为in/header
是make
要使用此规则的唯一原因。$?
为空时,我的演示配方刚刚崩溃。我的原始配方调用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