我可以改为通过名称引用$ call的位置参数吗? [英] Can I refer to the positional arguments of $call by name instead?
问题描述
能够通过$(eval $(call))定义规则族非常有用,就像这样:
It's very useful to be able to define families of rules via $(eval $(call )), like so:
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)
.像这样:
This creates the targets foo_A
, foo_B
, and foo_C
. In a more complex template, it could be nice to refer to the argument to call by a name; i.e. $(message)
rather than $(1)
. Something like this:
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 eval
调用之前,该分配不会被感知.同样,如果您在循环中附加了额外的传递,这似乎确实可行.但这感觉不对.是否有一种正确"的方法来执行此操作-而不必进行额外的通行破解?
That almost works. foo_A
and foo_B
work as expected but foo_C
does not. Interestingly, changing A B C
to A B C D
causes target foo_C
to start working. Here is how I understand what's going on: each pass through eval
assigns a value to $(MSG)
, but that assignment is not perceived until the next eval
call. Again, this does appear to work, if you append the extra pass through the loop. But it feels wrong. Is there a "correct" way to do this - and without having to do the extra-pass hack?
推荐答案
根据变量定义的方式(A := ...
或A = ...
)和位置(目标,先决条件,配方...). GNU制作手册的这一部分对此进行了简要说明.
Make variable definitions and make functions are recursively expanded at different possible times, depending on the way they are defined (A := ...
or A = ...
) and where (in targets, pre-requisites, recipes...). This section of GNU make manual briefly explains this.
我看到第二个Makefile有两个不同的问题:
I see 2 different problems with your second Makefile:
-
SIMPLE_TEMPLATE
中的$(MSG)
在$(foreach _,A B C,...
的扩展过程中扩展,而不是在正常解析结果作为make语法的过程中扩展.让我们看一下此分步操作:
The
$(MSG)
in yourSIMPLE_TEMPLATE
is expanded during expansion of the$(foreach _,A B C,...
, not during the normal parsing of the result as make syntax. Let's look at this step-by step:
-
foreach
的扩展:
$(eval $(call SIMPLE_TEMPLATE,A)))
$(eval $(call SIMPLE_TEMPLATE,B)))
$(eval $(call SIMPLE_TEMPLATE,C)))
eval
的扩展是特殊的,它扩展其参数并将其实例化为make结构.因此,要理解,我们必须首先扩展call
.在SIMPLE_TEMPLATE
的定义中,每个替换$(1)
.例如,传递给第一个eval
的是:
The expansion of the eval
is special, it expands its parameter and instantiates it as a make construct. So, to understand, we must expand the call
first. Each substitutes the $(1)
in the definition of SIMPLE_TEMPLATE
. What is passed to the first eval
, for instance, is:
MSG := A
foo_$(MSG):
echo foo $(MSG)
但是,eval
参数的扩展将继续,直到没有剩余可扩展的为止.尚未定义的对MSG
的引用将替换为空字符串,并且最终实例化的make构造为:
But, the expansion of the eval
parameter continues until there is nothing left to expand. The references to MSG
, which is not yet defined, are replaced by the empty string and the make constructs that are finally instantiated are:
MSG := A
foo_:
echo foo
在echo foo
的末尾带有一个(不可见的)空格.作为任何make构造,它们都会依次扩展,但这不会更改任何内容,因为没有其他要扩展的内容.在第二个eval
的扩展过程中,这次,由于第一个MSG
具有值A
.因此,第二个eval
收到:
with one (invisible) space at the end of echo foo
. As any make construct they are expanded in turn but this does not change anything because there is nothing else to expand. During the expansion of the second eval
, this time, MSG
has value A
, thanks to the first one. So, the second eval
receives:
MSG := B
foo_$(MSG)
echo foo $(MSG)
call
,将其展开为:
MSG := B
foo_A
echo foo A
并将其实例化为make构造.这些make构造将再次进行扩展(就像其他任何make构造一样),但不会再进行任何更改.同样,第三个eval
实例化:
and instantiates it as make constructs. These make constructs are expanded once more (as any other make constructs) but it does not change anything any more. Similarly, the third eval
instantiates:
MSG := C
foo_B
echo foo B
因此,总而言之,将实例化为make构造的是:
So, all in all, what will be instantiated as make constructs is:
MSG := A
foo_:
echo foo
MSG := B
foo_A:
echo foo A
MSG := C
foo_B:
echo foo B
您没有任何foo_C
目标(但是您有不需要的foo_
目标...)
And you do not have any foo_C
target (but you have an unwanted foo_
target...)
要在扩展$(foreach...
的过程中避开$(MSG)
的第一个和太早的扩展,可以将$
符号加倍:
To escape the first and too early expansion of $(MSG)
during the expansion of $(foreach...
you can double the $
signs:
define SIMPLE_TEMPLATE
MSG := $(1)
foo_$$(MSG):
echo foo $$(MSG)
endef
如果我们逐步进行此操作,则第一个call
将通过:
If we run this step-by-step, the first call
will pass:
MSG := A
foo_$$(MSG):
echo foo $$(MSG)
到第一个eval
,它将扩展为:
to the first eval
, which will expand it as:
MSG := A
foo_$(MSG):
echo foo $(MSG)
(每个$$
吃一个$
)并将其实例化为make构造.这些新的make构造也将像其他任何make构造一样进行扩展,从而提供:
(eating one $
per $$
) and instantiate it as make constructs. These new make constructs will also be expanded, as any other make construct, which will give:
MSG := A
foo_A:
echo foo $(MSG)
(配方中的$(MSG)
尚未扩展,因为在配方中,扩展被推迟到第二阶段,并且仅在选择了要执行的配方时才发生).在第一次扩展$(foreach _,A B C,...
之后,您将拥有的是:
(the $(MSG)
in the recipe is not expanded yet because, in recipes, the expansion is deferred to the second phase and happens only when the recipe is selected for execution). After first expansion of the $(foreach _,A B C,...
what you have is thus:
MSG := A
foo_A:
echo foo $(MSG)
MSG := B
foo_B:
echo foo $(MSG)
MSG := C
foo_C:
echo foo $(MSG)
但是在这里,您遇到了第二个问题:
But here, you hit the second problem:
您的不同规则共享相同的MSG
make变量.在第一阶段之后,其值将解析为C
,您所拥有的等同于:
Your different rules share the same MSG
make variable. After the first phase its value is thus resolved as C
and what you have is equivalent to:
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 = ...
),也将是相同的.
Probably not what you want. Note that even with a recursively expanded variable (MSG = ...
), it would be the same.
要解决第二个问题,您可以为所有目标使用相同的make变量名称,但为目标指定特定的值,例如:
To solve this second problem you could use the same make variable name for all targets but assign it target-specific values, like in:
define SIMPLE_TEMPLATE
foo_$(1): MSG := $(1)
foo_$(1):
echo foo $$(MSG)
endef
(请注意食谱中的$$
),其扩展为:
(note the $$
in the recipe) which expands as:
foo_A: MSG := A
foo_A:
echo foo $(MSG)
...
或者,也许使用不同的,构造的变量名称:
Or, maybe, use different, constructed, variable names:
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
可能更接近您的期望.
这篇关于我可以改为通过名称引用$ call的位置参数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!