能够通过$(eval $(call))定义规则系列非常有用,如下所示:
define SIMPLE_TEMPLATE
foo_$(1):
echo foo $(1)
endef
$(foreach _,A B C,$(eval $(call SIMPLE_TEMPLATE,$_)))
这会创建目标foo_A
,foo_B
和foo_C
。在一个更复杂的模板中,引用一个名称调用参数可能会很好;即$(message)
而不是$(1)
。像这样:
define SIMPLE_TEMPLATE
MSG := $1
foo_$(MSG):
echo foo $(MSG)
endef
$(foreach _,A B C,$(eval $(call SIMPLE_TEMPLATE,$_)))
几乎有效。 foo_A
和foo_B
按预期工作,但foo_C
没有。有趣的是,将A B C
更改为A B C D
会导致目标foo_C
开始工作。以下是我理解发生了什么的方法:每次通过eval
都会将值分配给$(MSG)
,但直到 next {{1调用。同样,如果你在循环中追加额外的传递,这似乎也有效。但感觉不对。有没有正确的"这样做的方式 - 而且不必进行额外的黑客攻击?
答案 0 :(得分:3)
使变量定义和make函数在不同的可能时间递归扩展,具体取决于它们的定义方式(A := ...
或A = ...
)和where(在目标中,先决条件,配方......) )。 This section of GNU make manual简要解释了这一点。
我看到你的第二个Makefile有两个不同的问题:
$(MSG)
中的SIMPLE_TEMPLATE
在$(foreach _,A B C,...
扩展期间展开,而不是在正常解析结果为make语法的过程中。让我们一步一步看看:
扩展foreach
:
$(eval $(call SIMPLE_TEMPLATE,A)))
$(eval $(call SIMPLE_TEMPLATE,B)))
$(eval $(call SIMPLE_TEMPLATE,C)))
eval
的扩展是特殊的,它扩展了它的参数并将其实例化为make构造。因此,要理解,我们必须先扩展call
。每个都替换$(1)
定义中的SIMPLE_TEMPLATE
。例如,传递给第一个eval
的是:
MSG := A
foo_$(MSG):
echo foo $(MSG)
但是,eval
参数的扩展会继续,直到没有任何内容可以展开。对MSG
的引用(尚未定义)将替换为空字符串,并且最终实例化的make构造为:
MSG := A
foo_:
echo foo
在echo foo
末尾有一个(不可见)空格。作为任何make构造,它们会依次扩展,但这并没有改变任何东西,因为没有其他东西可以扩展。在扩展第二个eval
期间,MSG
具有值A
,这要归功于第一个eval
。因此,第二个 MSG := B
foo_$(MSG)
echo foo $(MSG)
收到:
call
来自 MSG := B
foo_A
echo foo A
,将其展开为:
eval
并将其实例化为make构造。这些make构造再次扩展(与任何其他make构造一样),但它不再改变任何东西。同样,第三个 MSG := C
foo_B
echo foo B
实例化:
MSG := A
foo_:
echo foo
MSG := B
foo_A:
echo foo A
MSG := C
foo_B:
echo foo B
所以,总而言之,将实例化为make构造的是:
foo_C
你没有任何foo_
目标(但你有一个不受欢迎的$(MSG)
目标......)
要在$(foreach...
扩展期间逃避$
的第一次和过早扩展,您可以将define SIMPLE_TEMPLATE
MSG := $(1)
foo_$$(MSG):
echo foo $$(MSG)
endef
符号加倍:
call
如果我们一步一步地运行,第一个MSG := A
foo_$$(MSG):
echo foo $$(MSG)
将通过:
eval
到第一个MSG := A
foo_$(MSG):
echo foo $(MSG)
,它会将其扩展为:
$
(每$$
吃一个MSG := A
foo_A:
echo foo $(MSG)
)并将其实例化为make构造。这些新的make构造也将像任何其他make构造一样进行扩展,它将提供:
$(MSG)
(配方中的$(foreach _,A B C,...
尚未扩展,因为在配方中,扩展将延迟到第二阶段,并且仅在选择执行配方时才会发生。首先扩展MSG := A
foo_A:
echo foo $(MSG)
MSG := B
foo_B:
echo foo $(MSG)
MSG := C
foo_C:
echo foo $(MSG)
所以你拥有的是:
MSG
但是在这里,你遇到了第二个问题:
您的不同规则共享相同的C
制作变量。在第一阶段之后,其值将被解析为MSG := C
foo_A:
echo foo $(MSG)
foo_B:
echo foo $(MSG)
foo_C:
echo foo $(MSG)
,而您所拥有的相当于:
$ make foo_A foo_B foo_C
echo foo C
foo C
echo foo C
foo C
echo foo C
foo C
结果将是:
MSG = ...
可能不是你想要的。请注意,即使使用递归扩展变量(define SIMPLE_TEMPLATE
foo_$(1): MSG := $(1)
foo_$(1):
echo foo $$(MSG)
endef
),它也是相同的。
要解决第二个问题,您可以为所有目标使用相同的make变量名称,但为其指定特定于目标的值,如:
$$
(注意食谱中的foo_A: MSG := A
foo_A:
echo foo $(MSG)
...
)扩展为:
define SIMPLE_TEMPLATE
MSG_$(1) := $(1)
foo_$(1):
echo foo $$(MSG_$(1))
endef
或者,也许,使用不同的,构造的变量名称:
MSG_A := A
foo_A:
echo foo $(MSG_A)
...
扩展为:
$ make foo_A foo_B foo_C
echo foo A
foo A
echo foo B
foo B
echo foo C
foo C
最终都以:
运行{{1}}
可能更接近你的预期。