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 仍然有效。有什么想法吗?
问题与循环、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 ...)
函数将值直接扩展到配方中,而不是将其设置为单独的变量。
我无法找出在 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 仍然有效。有什么想法吗?
问题与循环、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 ...)
函数将值直接扩展到配方中,而不是将其设置为单独的变量。