如何为包含模式化先决条件的makefile模式规则定义特定于模式的变量?

时间:2016-07-21 20:30:41

标签: makefile gnu-make

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

%.o: var = 2

这将为适用于以.o结尾的所有目标的食谱将变量var设置为2。 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)

有办法做到这一点吗?

2 个答案:

答案 0 :(得分: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 $$(<:$2=),$(or $(value rule-var-$1-$3),$(value $3)),$4)去混淆:

$$(if $$(<:$2=),将使用空字符串替换它的模式($<)来测试第一个先决条件值($2)。

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

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

不幸的是,当继承时,这个if-else monstrosity不会正确扩展,因此它被声明为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。全局值将作为特定于规则的变量的一部分进行复制,但在使用之前不会进行扩展。

答案 1 :(得分:0)

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

# 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)

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

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