我可以通过名称来引用$ call的位置参数吗?

时间:2017-07-31 22:50:16

标签: makefile

能够通过$(eval $(call))定义规则系列非常有用,如下所示:

define SIMPLE_TEMPLATE
  foo_$(1):
        echo foo $(1)
endef

$(foreach _,A B C,$(eval $(call SIMPLE_TEMPLATE,$_)))

这会创建目标foo_Afoo_Bfoo_C。在一个更复杂的模板中,引用一个名称调用参数可能会很好;即$(message)而不是$(1)。像这样:

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 {{1调用。同样,如果你在循环中追加额外的传递,这似乎也有效。但感觉不对。有没有正确的"这样做的方式 - 而且不必进行额外的黑客攻击?

1 个答案:

答案 0 :(得分:3)

使变量定义和make函数在不同的可能时间递归扩展,具体取决于它们的定义方式(A := ...A = ...)和where(在目标中,先决条件,配方......) )。 This section of GNU make manual简要解释了这一点。

我看到你的第二个Makefile有两个不同的问题:

  1. $(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

    但是在这里,你遇到了第二个问题:

  2. 您的不同规则共享相同的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 ),它也是相同的。

  3. 要解决第二个问题,您可以为所有目标使用相同的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}}

    可能更接近你的预期。