Makefile中的复杂模式规则

Makefile中的复杂模式规则,makefile,Makefile,我使用以下makefile从一些模板生成文件,生成的文件有两个可能的扩展名: %.tex: %.tex*_tpl ./generate $@_tpl -o $@ %.xml: %.xml*_tpl ./generate $@_tpl -o $@ 这里的依赖项列表将匹配像a.tex\u tpl,a.tex-subpart1\u tpl,a.tex-subpart2\u tpl 虽然这样做有效,但有没有办法避免重复?例如,通过在规则名称中匹配*。{tex,xml},并在依赖项列表

我使用以下makefile从一些模板生成文件,生成的文件有两个可能的扩展名:

%.tex: %.tex*_tpl
    ./generate $@_tpl -o $@

%.xml: %.xml*_tpl
    ./generate $@_tpl -o $@
这里的依赖项列表将匹配像
a.tex\u tpl
a.tex-subpart1\u tpl
a.tex-subpart2\u tpl

虽然这样做有效,但有没有办法避免重复?例如,通过在规则名称中匹配
*。{tex,xml}
,并在依赖项列表中使用整个匹配的名称?看起来像这样的东西:

%.{tex,xml}: $@_tpl
    ./generate $< -o $@
%.{tex,xml}:$@
/生成$<-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 $@
关键是允许在第二个扩展阶段评估
$$(通配符%*\u tpl)
。顺便说一句,double
$
不是打字错误;它保护表达式在第一次展开时不被计算

如果我用以下文件填充目录:

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
为什么要进行第二次扩张? 如果没有第二次扩展,您必须在依赖项中包含
$(通配符%*\u tpl)
,因为使用
$$
通配符函数将永远不会执行。相反,make将把
$$(通配符..)
字面上看作依赖项,这显然是错误的

好的,所以在第一次运行该行时(这是“第一次扩展”)将对
$(通配符%*\u tpl)
进行评估。那时,
%
还没有值,所以
通配符
大概是在命令行上执行类似于
ls%*\u tpl
的操作

出于速度的原因,默认情况下,“make”不会在第一次扩展之后为您提供执行任何评估的机会。如果希望以后有机会,则必须指定
.SECONDEXPANSION
,这将打开第二次扩展处理。Make仍然像往常一样执行firts扩展。这就是为什么您需要有
$$(通配符)
:在第一次扩展期间,它被转换为
$(通配符)
。在第二次扩展时,请查看
$(通配符%*\tpl)
,将
%
替换为实际的词干,然后使用实际的词干而不是文本
%
执行
通配符
函数

为什么模式规则中的
$(目标)
? 模式规则可以写为:

%: $$(wildcard %*_tpl)
    ./generate $^ -o $@
没有
$(目标)
。但是,这个规则不会起任何作用,因为它是一个。基本上,如果make按照表面上的价值使用了这样一个规则,那么计算成本将是巨大的,而且很可能
Makefile
的作者并不是真的想将这个规则应用于任何文件。因此,这样一个规则带有限制,这在这里的Makefile使它变得无用


$(目标)
添加到一个,这不是一个匹配任何东西的规则。在目标模式前面添加
$(目标)
告诉make,该规则仅适用于这些目标,而不适用于其他任何目标。

我想不出任何东西可以使它更干净,尽管这取决于您所说的“干净”是什么意思。您可以将配方放入一个变量中:
GENERATE=./GENERATE$@\u tpl-o$@
然后在每个命令中使用
$(GENERATE)
变量;这将减少更新配方所需的位置数。这确实更好,谢谢!我真的不知道该称之为“cleaner”,我只能想到一些模糊的描述,如“short and not hacky”。这可能符合“hacky”:D,但听起来很棒!我今天没有更多的时间来测试它,但我会尽快给你回复,谢谢!编辑:并且您的makefile开头的注释是正确的,如果我不清楚,很抱歉。唯一不同的是,
生成
脚本只需要一个文件(
$@_tpl
)作为输入,此文件有说明(jinja的
包括
)包括其他(子部分_tpl)但这不应该是一个问题。除了GNU Make guru之外,这并不干净。我实际上非常喜欢它,尤其是它通过查看
\u tpl
文件中的内容来生成可用目标的列表。顺便说一下,我不需要将这些目标限制为xml和tex文件,这样我甚至可以将其缩短为
目标:=$(patsubst%\u tpl,%,$(wildcard*\u tpl))
。我有两个问题:1)是什么使得
.SECONDEXPANSION
成为强制性的?是不是在
%
扩展之后必须扩展的
通配符
函数?2)我在
$(目标)
规则中找不到关于额外
:%:
的文档,是什么?(我想它是从一个目标列表中创建了一个模式规则?。@Dettoler如果没有第二次扩展,你会把
$(通配符%*\u tpl)
放进去,因为使用
$
通配符函数将永远不会执行。现在
$(通配符%*\u tpl)
将在make第一次运行该行时进行计算。那时
%
还没有值,因此
通配符
将大致执行类似于命令行中的
ls%*\u tpl
的操作。在第二次扩展时,
%
将替换为实际的干,然后
通配符
已执行。@Louis谢谢,这是我(模糊地)的想法。'$(目标):'之后的额外“%:”是什么?在哪里记录了它?(或者概念的名称是什么?)