在我看来,这可以满足您的需求:
#
# 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 first 运行穿过那条线时进行评估(这是“第一次扩展”)。 那时% 还没有任何价值,所以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) 表明该规则仅适用于这些 目标,而不适用于其他目标。