问题描述
我使用以下makefile从某些模板生成文件,生成的文件具有两个可能的扩展名:
%.tex: %.tex*_tpl
./generate $@_tpl -o $@
%.xml: %.xml*_tpl
./generate $@_tpl -o $@
此处的依赖项列表将匹配a.tex_tpl
,a.tex-subpart1_tpl
,a.tex-subpart2_tpl
之类的内容.
这是可行的,但是有办法避免重复吗?例如,通过在规则名称中匹配*.{tex,xml}
并在依赖项列表中使用整个匹配的名称?看起来像这样:
%.{tex,xml}: $@_tpl
./generate $< -o $@
(尽管我知道%.{tex,xml}
不是有效的规则名称,并且您不能在依赖项列表中使用$@
)
或其他任何方法(更清洁?).
在我看来,这可以满足您的需求:
#
# I've assumed that files of the form:
#
# a.xml_tpl
# b.tex_tpl
#
# determine what targets you want to build
#
TARGETS:=$(patsubst %_tpl,%,$(wildcard *.xml_tpl *.tex_tpl))
.PHONY: all
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): %: $$(wildcard %*_tpl)
./generate $^ -o $@
关键是使用 .SECONDEXPANSION
以便在第二个扩展阶段评估$$(wildcard %*_tpl)
.顺便说一句,双精度$
不是错字.它可以防止表达式在第一次扩展时被求值.
如果我用这些文件填充目录:
a.tex-subpart1_tpl
a.tex_tpl
a.xml-subpart1_tpl
a.xml-subpart2_tpl
a.xml_tpl
并运行make -n
,我可以在控制台上找到它:
./generate a.xml_tpl a.xml-subpart1_tpl a.xml-subpart2_tpl -o a.xml
./generate a.tex_tpl a.tex-subpart1_tpl -o a.tex
为什么要进行第二次扩展?
如果没有第二个扩展,则必须在依赖项中包含$(wildcard %*_tpl)
,因为对于$$
,通配符函数将永远不会执行.相反,make会将$$(wildcard..)
字面意义上的视为依赖项,这显然是错误的.
好,因此$(wildcard %*_tpl)
将在make第一次越过该行时进行评估(这是第一次扩展"). 当时 %
尚无任何价值,因此wildcard
大致会像在命令行中执行ls %*_tpl
一样.
出于速度原因,默认情况下,make不会给您机会在第一次扩展后进行任何评估.如果您希望以后有机会,则必须指定.SECONDEXPANSION
,这将打开第二个扩展处理. Make仍然像往常一样执行点火扩展.这就是为什么您需要$$(wildcard
的原因:在第一次扩展时将其转换为$(wildcard
.在第二次扩展时,make看到$(wildcard %*_tpl)
,将%
替换为实际的词干,然后使用实际的词干而不是文字%
执行wildcard
函数. /p>
为什么$(TARGETS)
在模式规则中?
模式规则可以写为:
%: $$(wildcard %*_tpl)
./generate $^ -o $@
没有$(TARGETS)
.但是,此规则无济于事,因为它是匹配项-任何规则" .基本上,如果make在面值上采用这样的规则,则计算成本将是可观的,并且Makefile
really 的作者很可能并非打算将该规则应用于任何文件.因此,这样的规则带有限制,在此处的Makefile中使其无用.
添加$(TARGETS)
使其成为静态模式规则,它不是任何匹配规则.在目标模式前面加上$(TARGETS)
可以使该规则仅适用于这些目标,而没有其他内容.
I have the following makefile I use to generate files from some templates, the generated files have two possible extensions:
%.tex: %.tex*_tpl
./generate $@_tpl -o $@
%.xml: %.xml*_tpl
./generate $@_tpl -o $@
The dependency list will here match things like a.tex_tpl
, a.tex-subpart1_tpl
, a.tex-subpart2_tpl
.
While this works, is there a way to avoid repetition? For example by matching *.{tex,xml}
in the rule name and use the whole matched name in the dependency list? Something that would look like that:
%.{tex,xml}: $@_tpl
./generate $< -o $@
(Though I know %.{tex,xml}
is not a valid rule name and you can't use $@
in the dependency list)
Or any other (cleaner?) way.
Seems to me this does what you are looking for:
#
# I've assumed that files of the form:
#
# a.xml_tpl
# b.tex_tpl
#
# determine what targets you want to build
#
TARGETS:=$(patsubst %_tpl,%,$(wildcard *.xml_tpl *.tex_tpl))
.PHONY: all
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): %: $$(wildcard %*_tpl)
./generate $^ -o $@
The key is to use .SECONDEXPANSION
to allow $$(wildcard %*_tpl)
to be evaluated in a second expansion phase. The double $
is not a typo, by the way; it protects the expression from being evaluated at the time of the first expansion.
If I populate a directory with these files:
a.tex-subpart1_tpl
a.tex_tpl
a.xml-subpart1_tpl
a.xml-subpart2_tpl
a.xml_tpl
and run make -n
, I get this on the console:
./generate a.xml_tpl a.xml-subpart1_tpl a.xml-subpart2_tpl -o a.xml
./generate a.tex_tpl a.tex-subpart1_tpl -o a.tex
Why the Second Expansion?
Without the second expansion, you'd have to have $(wildcard %*_tpl)
in the dependency because with the $$
the wildcard function would never execute. Instead, make would treat $$(wildcard..)
literally as the dependency, which is obviously wrong.
Ok, so $(wildcard %*_tpl)
would be evaluated at the time make first runs across that line (this is the "first expansion"). At that time %
has no value yet so wildcard
would roughly be doing something like what would be ls %*_tpl
at the command line.
For reasons of speed, make does not by default give you the opportunity to do any evaluation later than during the first expansion. If you want a later opportunity you have to specify .SECONDEXPANSION
, which turns on the second expansion processing. Make still performs the firts expansion as usual. This is why you need to have $$(wildcard
: it is transformed to $(wildcard
during the first expansion. At the time of the second expansion make sees $(wildcard %*_tpl)
, replaces %
with the actual stem and then executes the wildcard
function with the actual stem rather than with a literal %
.
Why $(TARGETS)
in the Pattern Rule?
The pattern rule could be written:
%: $$(wildcard %*_tpl)
./generate $^ -o $@
without $(TARGETS)
. However, this rule would do nothing, as it would be a "match-anything rule". Basically, if make took such a rule at face value, then the computation cost would be significant, and most likely it is not the case that the author of the Makefile
really means to apply this rule to any file whatsoever. So such a rule comes with restrictions, which in the Makefile here make it useless.
Adding $(TARGETS)
makes it into a static pattern rule, which is not a match-anything rule. The addition of $(TARGETS)
in front of the target pattern tells make that the rule applies only to these targets, and nothing else.
这篇关于Makefile中的复杂模式规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!