为什么我需要 eval 来设置 makefile 函数中的变量?

Why do I need eval to set variable in makefile function?

如果我在 gnu makefile 的顶部有以下内容:

$(if _,a:=1)
$(info a=$a)

编译失败(make-3.81: *** No rule to make target '=1', needed by 'a'. Stop.,或者 *** missing separator. Stop. 如果我排除了冒号)。我知道我可以通过使用 eval 来解决这个问题,但我不明白为什么需要它。这不应该扩展为 a:=1,被解析,并将 a 设置为 1 吗?

在您的例子中,if 函数作为它的第二个参数 (a:=1) 求值,但这不会作为 make 变量赋值依次求值。使用您的 make 版本,它成为规则。这可能是由于 make 在语法上解析 makefile 的方式。它不认为此 if 语句可以是变量赋值,因为(在扩展之前)它看起来像有效的 make 变量赋值的 none。所以 if 语句得到了扩展,但是 make 将结果视为变量赋值已经太迟了...

您可以使用:

a := $(if _,1)

或:

ifneq (_,)
a := 1
endif

或:

$(if _,$(eval a := 1))

或:

$(eval $(if _,a := 1))

最后两种形式之所以有效,是因为根据定义,eval 函数参数扩展的结果被处理为普通的 make 语句。

I'm not understanding why it's required

嗯,就是这样的设计。

评估程序总是执行(递归)扩展(除了 "the initial evaluation",后续扩展可以被 = 有效停止,即 "lazy assignment"),但扩展程序从不评估, 除非明确告知(基本上, $(eval ...) 就是这样一个从扩展切换到评估的命令)。

考虑一下:

x := 0
y := 1
# z is set to "y<space><equal><space><dollar>x"
z = y = $x

# [0] [1] [y = $x]
$(info [$x] [$y] [$(value z)])

# error as "y = $x" will not be re-evaluated
#$z

# effectively sets y to "0"
$(eval $z)

# [0] [0] [y = $x]
$(info [$x] [$y] [$(value z)])

从 make 的角度来看 $(if cond,x=y)$z 没有太大区别 --- 它已扩展但未评估。无论站在哪里。只需将 $(...) 形式的任何内容视为 "data",而不是 "code"。