以下是GNU Make manual section on Secondary Expansion(略微简化)的示例:
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
# $$@ expands to the target ("foo" in this case)
foo: $$(patsubst %.c,%.o,$$($$@_SRCS))
这很有效;它构建bar.o
和baz.o
:
cc -c -o bar.o bar.c
cc -c -o baz.o baz.c
但如果我稍微调整一下这个例子,patsubst
就会停止工作:
all: foo.a
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
# $$* expands to the stem of the match ("foo" in this case).
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $@ $^
它不再构建bar.o
和baz.o
,而是直接使用*.c
文件作为先决条件!
ar rcs foo.a bar.c baz.c
请注意,$$($$*_SRCS)
部分显然正常工作,因为它找到了foo_SRCS
并将其作为先决条件。但由于某种原因,patsubst
部分已成为无操作!它不是将%.c
替换为%.o
,而是直接使用foo_SRCS
。
这里发生了什么?我怎样才能让我的榜样起作用?
编辑:我有一个理论认为%
内的patsubst
字符是使用词干匹配(foo)提前评估的,所以patsubst本身看起来像这样:
$(patsubst foo.c,foo.o,bar.c baz.c)
为了测试这个理论,我在foo.c
添加了一个名为foo_SRCS
的文件:
all: foo.a
foo_SRCS := foo.c bar.c baz.c
.SECONDEXPANSION:
# $$* expands to the stem of the match ("foo" in this case).
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $@ $^
这导致了更奇怪的事情:
make: *** No rule to make target `foo.a', needed by `all'. Stop.
答案 0 :(得分:4)
make正在读取百分比字符作为与词干中的通配符的匹配,并且正在被词干匹配替换。如果您查看示例的make -p
输出,您会看到解析后的目标行如下所示:
%.a: $(patsubst %.c,%.o,$($*_SRCS))
就制作而言,这只是一组非常奇怪的图案目标(或类似的东西)。
如果你以类似于逃避$
进行评估的方式转义make解析中的百分比字符,你可以得到你想要的工作:
pc := %
$$(patsubst $$(pc).c,$$(pc).o,$$($$*_SRCS))
有关添加的信息substitution references(即$(foo_SRCS:.c=.o)
)可用于此类转换,而不是对patsubst的较长调用。但是,在这种情况下,虽然它在这种情况下可以使用:
(通过c := :
)进行类似的转义,但它似乎并不是目标的唯一先决条件( Makefile:23: *** commands commence before first target. Stop.
错误,我不太了解)至少使用GNU Make 3.81。
答案 1 :(得分:4)
您混合了三个功能不佳的功能:secondary expansion,pattern rules和patsubst。我将尝试详细解释在评估代码时所做的工作(AFAIUI)。
让我们从你的第一个Makefile开始:
foo_SRCS := bar.c baz.c
.SECONDEXPANSION:
%.a: $$(patsubst %.c,%.o,$$($$*_SRCS))
ar rcs $@ $^
阅读阶段。所有美元符号都已转义,因此此处不会进行任何评估。 Make在其数据库中输入以下规则:
%.a: $(patsubst %.c,%.o,$($*_SRCS))
模式替换。就make而言,这只是另一种模式规则,目标为%.a
,两个先决条件由空格分隔:$(patsubst
和{{1 }}
%.c,%.o,$($*_SRCS))
与目标模式匹配,因此每个先决条件中的第一个 foo.a
将被%
替换。规则变为:
foo
目标更新阶段。当您请求二次扩展时,将在目标更新阶段再次评估该模式:
foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
因此最终执行命令
foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
==> foo.a: $(patsubst foo.c,%.o,bar.c baz.c)
==> foo.a: bar.c baz.c
那么foo.c呢?如果你把foo.c添加到foo_SRCS,那么二级扩展就像这样:
ar rcs foo.a bar.c baz.c
规则失败,因为make不知道如何构建 foo.a: $(patsubst foo.c,%.o,$($*_SRCS))
==> foo.a: $(patsubst foo.c,%.o,foo.c bar.c baz.c)
==> foo.a: %.o bar.c baz.c
。
解决方法。您可以使用反斜杠转义%.o
字符:
%