【问题标题】:How do you define pattern-specific variables for makefile pattern rules containing a patterned prerequisite?您如何为包含模式化先决条件的 makefile 模式规则定义特定于模式的变量?
【发布时间】:2016-11-25 14:40:54
【问题描述】:

我知道您可以为没有先决条件的规则定义特定于模式的变量,如下所示:

%.o: var = 2

这会将变量 var 设置为 2,用于适用于所有以 .o 结尾的目标的配方。这在GNU Make documentation 中有明确说明。但是如何为包含模式先决条件的模式规则定义特定于模式的变量,如下所示:

%.o: %.c

假设我有这部分的makefile:

%.o: %.c
    (recipe here)

%.o: %.b
    (recipe here)

我只想为%.o: %.b 规则定义一个特定于模式的变量,但我不知道该怎么做(如果可能的话)。我想做这样的事情,但当然不行:

%.o: %.c
    (recipe here)

%.o: %.b: var = 2
    (recipe here)

有没有办法做到这一点?

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    您只能为目标设置变量,而不是规则。 %.o: %b 是一条规则,其中 %.o 是目标模式(因此称为“特定于模式”的名称)。

    解决此问题的常用方法是在配方中使用以太硬编码值或使用特定于规则的标志(在您的情况下可能是 CVARBVAR)。

    编辑: 从头开始​​。想出了一个解决方法。

    这可以通过利用变量的递归评估来完成。

    all: a.o b.o c.o
    
    $(shell touch a.xx b.yy c.zz)
    
    ##
    # Create rule-specific variable... rules
    #
    # @param 1 Target.
    # @param 2 Prerequisite.
    # @param 3 Variable name.
    # @param 4 Variable value.
    #
    rule-var = $(eval $(rule-var-body))
    define rule-var-body
    $1: private $3 = $$(if $$(<:$2=),$(or $(value rule-var-$1-$3),$(value $3)),$4)
    $2: $3 = $4
    rule-var-$1-$3 = $$(if $$(<:$2=),$(or $(value rule-var-$1-$3),$(value $3)),$4)
    endef
    
    VAR = $(VAR_DEFAULT)
    
    # Declare couple of test values
    $(call rule-var,%.o,%.x,VAR,x-value)
    $(call rule-var,%.o,%.y,VAR,y-value)
    VAR_DEFAULT := z-value
    
    ECHO_RULE_RECIPE = @echo -e '$@: $^\t(VAR = $(VAR))'
    
    %.o: %.x
        $(ECHO_RULE_RECIPE)
    %.o: %.y
        $(ECHO_RULE_RECIPE)
    %.o: %.z
        $(ECHO_RULE_RECIPE)
    %.x: %.xx
        $(ECHO_RULE_RECIPE)
    %.y: %.yy
        $(ECHO_RULE_RECIPE)
    %.z: %.zz
        $(ECHO_RULE_RECIPE)
    

    输出是:

    a.x: a.xx       (VAR = x-value)
    a.o: a.x        (VAR = x-value)
    b.y: b.yy       (VAR = y-value)
    b.o: b.y        (VAR = y-value)
    c.z: c.zz       (VAR = z-value)
    c.o: c.z        (VAR = z-value)
    

    操作的大脑是宏rule-var。它将变量值包装在匹配if-else 表达式的先决条件中。该表达式还保存在 rule-var-$1-$3 变量中,用于其他特定于规则的值。

    $$(if $$(&lt;:$2=),$(or $(value rule-var-$1-$3),$(value $3)),$4)去混淆:

    $$(if $$(&lt;:$2=), 将通过将其模式 ($2) 替换为空字符串来测试第一个先决条件值 ($&lt;)。

    • 如果模式匹配,请使用$(or $(value rule-var-$1-$3),$(value $3))。这是全局变量阴影的一种解决方法。在您的示例中,%.o: %.c 没有声明 var,因此它应该使用全局值,但两个规则共享相同的目标,它是不可见的。两者都由值引用,单个$ 在变量替换阶段扩展表达式。所以结果很整洁,or 免费。

      • 如果$(value rule-var-$1-$3) 不为零,请使用它。也就是说,之前已经为此目标和变量名调用过函数。
      • 否则使用变量的全局值 ($(value $3))。
    • 否则使用提供的值 ($4)。

    不幸的是,当被继承时,这个 if-else 怪物将无法正确扩展,因此它被声明为 private 并使用简单的第二条规则进行修复。

    在本例中,将声明以下 3 条规则。

    %.o: private VAR = $(if $(<:%.y=),$(if $(<:%.x=),$(VAR_DEFAULT),x-value),y-value)
    %.y: VAR = y-value
    %.x: VAR = x-value
    

    限制

    即使有变通方法,变量的全局对应项仍然会被隐藏。如果您需要一个默认值,请在调用rule-var之前分配它。全局值被复制为特定规则变量的一部分,但在使用之前不会扩展。

    【讨论】:

    • 我正在尝试遵循语法,但我有点挣扎。 $$(if $$(&lt;:$2=),$(or $(value rule-var-$3),$(value $3)),$4) 这条线让我感到震惊。我认为它可以归结为if prereq1:prereq1=,但我之前没有见过这种语法,也没有见过以or 开头的语法。你能再解释一下那行吗?
    • @smeep 你去吧。
    • 你清楚地知道你的东西。我仍在努力解决您的答案,但在这样做的同时可能找到了另一种解决方案,我已将其发布。
    【解决方案2】:

    我找到了另一种解决方案,尽管根据进行了多少先决条件比较,它可能会有点难以阅读。也许有办法重构它。

    # Return 1 if prerequisite ends in `.c`.
    # Return 2 if prerequisite ends in `.b`.
    build-var = $(if $(filter %.c,$<),1,$(if $(filter %.b,$<),2))
    
    %.o: private var = $(build-var)
    %.o: %.c
        (recipe here)
    
    %.o: %.b
        (recipe here)
    

    对于以.o 结尾的任何目标,都会调用指令var = $(build-var)。但是当build-var被扩展时,它的价值来自于检查先决条件的结尾,看看它是什么类型的文件。

    如果if 语句的第一个参数是非空字符串,则其计算结果为真,如果在我的示例中,前提条件包含.c,则filter 返回非空字符串。因此,遍历build-var 行,如果先决条件(由自动变量$&lt; 表示)以.c 结尾,则返回值1。否则,如果先决条件以 .b 结尾,则启动第二条 if 语句,该语句返回值 2。如果先决条件不是以 .c.b 结尾,则 build-var 设置为空(空字符串)。

    【讨论】:

      猜你喜欢
      • 2015-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-08
      相关资源
      最近更新 更多