foreach 中用于显式 make 规则的条件变量 (ifeq)

Conditional variables (ifeq) in foreach for explicit make rules

我无法找出在 GNU Make 3.81 中使用带有 foreach 循环的条件定义变量的正确语法。

一个简单的 makefile

SET := A B C

define da_loop

ifeq ($(S), A)
    T := equals_A
else
    T := not_equals_A
endif

out_$(S):
    echo "$(S) $$(T)"

endef

$(foreach S, $(SET), $(eval $(call da_loop, $S)))

预期输出:

$ make out_A out_B out_C
echo "A equals_A"
A equals_A
echo "B not_equals_A"
B not_equals_A
echo "C not_equals_A"
C not_equals_A

实际输出:

$ make out_A out_B out_C
echo "A not_equals_A"
A not_equals_A
echo "B not_equals_A"
B not_equals_A
echo "C not_equals_A"
C not_equals_A

将 "eval" 更改为 "info" 在我看来应该可行:

ifeq (A, A)
        T := equals_A
else
        T := not_equals_A
endif

out_A:
        echo "A $(T)"


ifeq (B, A)
        T := equals_A
else
        T := not_equals_A
endif

out_B:
        echo "B $(T)"


ifeq (C, A)
        T := equals_A
else
        T := not_equals_A
endif

out_C:
        echo "C $(T)"

我已经尝试了所有我能想到的额外“$”、引号、= 与 := 的组合,但 none 仍然有效。有什么想法吗?


ifeq 当然有另一种语法(带引号),
在引用的文本中没有剥离白色-space!

问题与循环、eval等无关。问题比这更简单、更根本:在make中,变量是全局定义的,并且所有规则仅在整个 makefile 被解析后才被调用。 info 技巧实际上确实向您展示了问题所在。让我们稍微简化一下:

T := equals_A
out_A:
        echo "A $(T)"

T := not_equals_A
out_B:
        echo "B $(T)"

T := not_equals_A
out_C:
        echo "C $(T)"

问题是所有变量赋值都是在读入 makefile 时发生的,但是配方的扩展直到很久以后才发生,当 make 正在构建目标并决定 运行 配方时。因此,您的 makefile 可以等效地写为:

T := equals_A
T := not_equals_A
T := not_equals_A

out_A:
        echo "A $(T)"
out_B:
        echo "B $(T)"
out_C:
        echo "C $(T)"

现在您可以了解为什么会出现这种行为了。

有很多方法可以 "fix" 这个,哪一个最合适取决于你真正想在你的真实 makefile 中做什么。一个非常简单的选项是使用 target-specific variables,像这样:

SET := A B C

define da_loop

ifeq ($(S), A)
    out_$(S): T := equals_A
else
    out_$(S): T := not_equals_A
endif

out_$(S):
        echo "$(S) $$(T)"

endef

$(foreach S, $(SET), $(eval $(call da_loop, $S)))

通过使用它,您已指定每个 T 变量都绑定到该特定目标的范围,并且每个目标都可以有不同的值。其他选项将构造变量名,或者使用 $(if ...) 函数将值直接扩展到配方中,而不是将其设置为单独的变量。