我有以下用于从某些模板生成文件的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}
不是有效的规则名称,您不能在依赖项列表中使用$@
或任何其他(清洁?)方式。
答案 0 :(得分:4)
在我看来,这就是你要找的东西:
#
# 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)
会在第一次跨越该行时进行评估(这是&#34;第一次扩展&#34;)。 当时 %
没有任何价值,因此wildcard
大致会在命令行中执行类似ls %*_tpl
的操作。
出于速度原因,默认情况下make不会让您有机会在第一次扩展之后进行任何评估。如果您想要以后的机会,则必须指定.SECONDEXPANSION
,这将启用第二次扩展处理。 Make仍像往常一样执行第一次扩展。这就是您需要$$(wildcard
的原因:在第一次扩展期间,它会转换为$(wildcard
。在第二次展开时,make $(wildcard %*_tpl)
,用实际的词干替换%
,然后用实际的词干执行wildcard
函数而不是文字%
。
$(TARGETS)
在模式规则中?可以写出模式规则:
%: $$(wildcard %*_tpl)
./generate $^ -o $@
没有$(TARGETS)
。但是,这条规则什么都不做,因为它是"match-anything rule"。基本上,如果make在表面上采用了这样的规则,那么计算成本就会很高,而且很可能不是Makefile
的作者真的意味着应用这个规则任何文件。所以这样的规则带有限制,这在Makefile中使它变得无用。
添加$(TARGETS)
会使其成为static pattern rule,这不是匹配任何规则。在目标模式前添加$(TARGETS)
会告知该规则仅适用于这些目标,而不适用于其他目标。