env.subst 的递归变量替换

Recursive variable substitution with env.subst

根据scons documentationsubst方法将递归地插入构造变量。不过好像不是递归的:

e = Environment(CPPDEFINES = ["FOOBAR=${foobar}"])

e["foo"] = 1
e["bar"] = "${foo + 1}"
e["foobar"] = "$${foo + ${bar}}"

# I want this to print "foobar: 3"
print "foobar:", e.subst("${foobar}")

e.Program("test.c")

打印:

scons: Reading SConscript files ...
foobar: ${foo + 2}
scons: done reading SConscript files.
scons: Building targets ...
gcc -o test.o -c -DFOOBAR=3 test.c
gcc -o test test.o
scons: done building targets.

foobar 在编译期间作为 CPPDEFINES 的一部分被正确评估,但不在 print 语句中。我怎样才能 subst 全面评估 foobar?

使用表达式

e["foobar"] = "${foo + ${bar}}" 

,如 Kenny Ostrom 所建议的,也无济于事。它会产生语法错误,因为 subst 方法并不能很好地处理嵌套大括号。

实际的问题是:为什么我们在 SConstruct 中直接使用 subst 和在构建命令中使用它时会看到不同的输出?

如果我们加上

print "CPPDEFINES:", e.subst("$CPPDEFINES")

SConstruct,我们看到 FOOBAR 的相同输出 ${foo + 2}。构建时的区别在于,内部变量 $_CPPDEFFLAGS 是根据 _defines 方法声明的:

'_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}'

(来自 print e.Dump())。此 _defines 方法通过 subst_path 第二次运行所有变量,例如可以在包含路径中使用变量。

所以subst方法是正确的,你只需要再次评估:

print "foobar:", e.subst(e.subst("${foobar}"))

获得相同的输出。

只是为了弄清楚 dirkbaechle 所说的话;我们可以通过 只需在两个单独的步骤中进行插值和评估(通过调用 subst 两次)。这允许我们有任意复杂的表达式:

# Notice how we wrap foobar in $$
e = Environment(CPPDEFINES = ["FOOBARBAZ=$${${foobarbaz}}"])

e["foo"] = 1
e["bar"] = "($foo + 1)"
e["foobar"] = "($foo + $bar)"
e["foobarbaz"] = "($foobar + $foobar)"

print "foobarbaz after substituting once:", e.subst("$${${foobarbaz}}")
print "foobarbaz after substituting twice:", e.subst(e.subst("$${${foobarbaz}}"))

e.Program("test.c")

打印:

scons: Reading SConscript files ...
foobarbaz after substituting once: ${((1 + (1 + 1)) + (1 + (1 + 1)))}
foobarbaz after substituting twice: 6
scons: done reading SConscript files.
scons: Building targets ...
gcc -o test.o -c -DFOOBARBAZ=6 test.c
gcc -o test test.o
scons: done building targets.

再次感谢 dirkbaechle!