Recursive variable substitution with env.subst

1.3k Views Asked by At

According to the scons documentation, the subst method will recursively interpolate construction variables. However, it does not seem to be recursive:

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

Prints:

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 is correctly evaluated during compilation as a part of CPPDEFINES, but not in the print statement. How can I get subst to fully evaluate foobar?

2

There are 2 best solutions below

5
On BEST ANSWER

Using the expression

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

, as suggested by Kenny Ostrom, doesn't help either. It yields a syntax error because the subst method doesn't really handle nested braces too well.

The actual question is: Why do we see different outputs when using subst in the SConstruct directly, and when it gets used within a build command?

If we add

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

to the SConstruct, we see the same output ${foo + 2} for FOOBAR. The difference at build time is, that the internal variable $_CPPDEFFLAGS is declared in terms of the _defines method:

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

(from a print e.Dump()). This _defines method runs all variables through subst_path a second time, such that one can use variables in include paths, for example.

So the subst method is doing the right thing, you just have to evaluate again:

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

to get the same output.

0
On

Just to make it clear what dirkbaechle said; we can achieve this by simply doing interpolation and evaluation in two separate steps (by calling subst twice). This allows us to have arbitrary complex expressions:

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

Prints:

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.

Thanks again, dirkbaechle!