我可以改为通过名称引用$ call的位置参数吗? [英] Can I refer to the positional arguments of $call by name instead?

查看:70
本文介绍了我可以改为通过名称引用$ call的位置参数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

能够通过$(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_Afoo_Bfoo_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_Afoo_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:

  1. SIMPLE_TEMPLATE中的$(MSG)$(foreach _,A B C,...的扩展过程中扩展,而不是在正常解析结果作为make语法的过程中扩展.让我们看一下此分步操作:

  1. The $(MSG) in your SIMPLE_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屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆