【问题标题】:Can I refer to the positional arguments of $call by name instead?我可以按名称引用 $call 的位置参数吗?
【发布时间】:2017-07-31 22:50:16
【问题描述】:

能够通过 $(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) 分配一个值,但直到下一个 eval 调用才感知到该分配。同样,如果您通过循环附加额外的通道,这似乎确实有效。但是感觉不对。有没有“正确”的方法来做到这一点 - 并且不必进行额外的黑客攻击?

【问题讨论】:

    标签: makefile


    【解决方案1】:

    根据定义方式(A := ...A = ...)和位置(在目标、先决条件、配方...)中,生成变量定义和生成函数在不同的可能时间递归扩展。 This section of GNU make manual 简要说明了这一点。

    我发现您的第二个 Makefile 存在 2 个不同的问题:

    1. SIMPLE_TEMPLATE 中的 $(MSG) 在扩展 $(foreach _,A B C,... 期间被扩展,而不是在正常解析结果时作为 make 语法。让我们一步一步来看看:

      • foreach的扩展:

        $(eval $(call SIMPLE_TEMPLATE,A)))
        $(eval $(call SIMPLE_TEMPLATE,B)))
        $(eval $(call SIMPLE_TEMPLATE,C)))
        
      • eval 的扩展很特殊,它扩展了它的参数并将其实例化为一个 make 构造。所以,要理解,我们必须先扩展call。每个都替换SIMPLE_TEMPLATE 定义中的$(1)。例如,传递给第一个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
      

      并将其实例化为 make 构造。这些 make 构造再次扩展(与任何其他 make 构造一样),但它不再更改任何内容。同样,第三个eval 实例化:

          MSG := C
          foo_B       
              echo foo B
      

      所以,总而言之,将被实例化为 make 构造的是:

      MSG := A
      foo_:
          echo foo 
      
      MSG := B
      foo_A:
          echo foo A
      
      MSG := C
      foo_B:
          echo foo B
      

      而且您没有任何foo_C 目标(但您有一个不需要的foo_ 目标...)

      要在$(foreach... 扩展期间避免$(MSG) 的第一次和过早扩展,您可以将$ 符号加倍:

      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)
      

      (每个 $$ 吃一个 $)并将其实例化为 make 构造。这些新的 make 构造也将被扩展,就像任何其他 make 构造一样,它将给出:

      MSG := A
      foo_A:
          echo foo $(MSG)
      

      (配方中的$(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)
      

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

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

    要解决第二个问题,您可以为所有目标使用相同的 make 变量名称,但为其分配特定于目标的值,例如:

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

    (注意配方中的$$)扩展为:

    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
    

    可能更接近您的预期。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-11
      • 2017-07-31
      • 2014-01-26
      相关资源
      最近更新 更多