summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CHANGES.txt3
-rw-r--r--src/engine/SCons/Environment.py10
-rw-r--r--src/engine/SCons/EnvironmentTests.py40
-rw-r--r--src/engine/SCons/Executor.py13
-rw-r--r--src/engine/SCons/ExecutorTests.py4
-rw-r--r--src/engine/SCons/Node/NodeTests.py11
-rw-r--r--src/engine/SCons/Util.py49
-rw-r--r--src/engine/SCons/UtilTests.py80
-rw-r--r--test/CCFLAGS.py10
-rw-r--r--test/LIBS.py44
-rw-r--r--test/overrides.py10
11 files changed, 242 insertions, 32 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 42c5de1d..be8dfa04 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -125,7 +125,8 @@ RELEASE 0.95 - XXX
- Refactor construction variable expansion to support recursive
expansion of variables (e.g. CCFLAGS = "$CCFLAGS -g") without going
- into an infinite loop.
+ into an infinite loop. Support this in all construction variable
+ overrides, as well as when copying Environments.
- Fix calling Configure() from more than one subsidiary SConscript file.
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index a6262046..c6bc2a99 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -505,7 +505,10 @@ class Base:
apply_tools(clone, tools, toolpath)
# Apply passed-in variables after the new tools.
- apply(clone.Replace, (), kw)
+ new = {}
+ for key, value in kw.items():
+ new[key] = SCons.Util.scons_subst_once(value, self, key)
+ apply(clone.Replace, (), new)
return clone
def Detect(self, progs):
@@ -561,7 +564,10 @@ class Base:
env = copy.copy(self)
env._dict = copy.copy(self._dict)
env['__env__'] = env
- env._dict.update(overrides)
+ new = {}
+ for key, value in overrides.items():
+ new[key] = SCons.Util.scons_subst_once(value, self, key)
+ env._dict.update(new)
return env
else:
return self
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 330bb7c2..a5cf1716 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -1019,6 +1019,16 @@ class EnvironmentTestCase(unittest.TestCase):
assert env3.get('BAR') is 2
assert env3.get('BAZ') is 3
+ # Ensure that recursive variable substitution when copying
+ # environments works properly.
+ env1 = Environment(CCFLAGS = '-DFOO', XYZ = '-DXYZ')
+ env2 = env1.Copy(CCFLAGS = '$CCFLAGS -DBAR',
+ XYZ = ['-DABC', 'x $XYZ y', '-DDEF'])
+ x = env2.get('CCFLAGS')
+ assert x == '-DFOO -DBAR', x
+ x = env2.get('XYZ')
+ assert x == ['-DABC', 'x -DXYZ y', '-DDEF'], x
+
def test_Detect(self):
"""Test Detect()ing tools"""
test = TestCmd.TestCmd(workdir = '')
@@ -1122,16 +1132,28 @@ class EnvironmentTestCase(unittest.TestCase):
def test_Override(self):
"Test overriding construction variables"
- env = Environment(ONE=1, TWO=2)
- assert env['ONE'] == 1
- assert env['TWO'] == 2
- env2 = env.Override({'TWO':'10'})
- assert env2['ONE'] == 1
- assert env2['TWO'] == '10'
- assert env['TWO'] == 2
+ env = Environment(ONE=1, TWO=2, THREE=3, FOUR=4)
+ assert env['ONE'] == 1, env['ONE']
+ assert env['TWO'] == 2, env['TWO']
+ assert env['THREE'] == 3, env['THREE']
+ assert env['FOUR'] == 4, env['FOUR']
+
+ env2 = env.Override({'TWO' : '10',
+ 'THREE' :'x $THREE y',
+ 'FOUR' : ['x', '$FOUR', 'y']})
+ assert env2['ONE'] == 1, env2['ONE']
+ assert env2['TWO'] == '10', env2['TWO']
+ assert env2['THREE'] == 'x 3 y', env2['THREE']
+ assert env2['FOUR'] == ['x', 4, 'y'], env2['FOUR']
+
+ assert env['ONE'] == 1, env['ONE']
+ assert env['TWO'] == 2, env['TWO']
+ assert env['THREE'] == 3, env['THREE']
+ assert env['FOUR'] == 4, env['FOUR']
+
env2.Replace(ONE = "won")
- assert env2['ONE'] == "won"
- assert env['ONE'] == 1
+ assert env2['ONE'] == "won", env2['ONE']
+ assert env['ONE'] == 1, env['ONE']
assert env['__env__'] is env, env['__env__']
assert env2['__env__'] is env2, env2['__env__']
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
index 8151f87d..b636f606 100644
--- a/src/engine/SCons/Executor.py
+++ b/src/engine/SCons/Executor.py
@@ -69,6 +69,12 @@ class Executor:
# The normal case: use the Environment that was
# used to specify how these targets will be built.
env = self.env
+
+ # Create the build environment instance with appropriate
+ # overrides. These get evaluated against the current
+ # environment's construction variables so that users can
+ # add to existing values by referencing the variable in
+ # the expansion.
overrides = {}
overrides.update(self.builder.overrides)
overrides.update(self.overrides)
@@ -78,8 +84,13 @@ class Executor:
pass
else:
overrides.update(generate_build_dict())
- overrides.update(SCons.Util.subst_dict(self.targets, self.sources))
self.build_env = env.Override(overrides)
+
+ # Now update the build environment with the things that we
+ # don't want expanded against the current construction
+ # variables.
+ self.build_env._update(SCons.Util.subst_dict(self.targets,
+ self.sources))
return self.build_env
def get_action_list(self, target):
diff --git a/src/engine/SCons/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py
index d43f7e20..9c640121 100644
--- a/src/engine/SCons/ExecutorTests.py
+++ b/src/engine/SCons/ExecutorTests.py
@@ -34,10 +34,12 @@ class MyEnvironment:
def __init__(self, **kw):
self._dict = {}
self._dict.update(kw)
+ def __getitem__(self, key):
+ return self._dict[key]
def Override(self, overrides):
d = self._dict.copy()
d.update(overrides)
- return d
+ return apply(MyEnvironment, (), d)
def _update(self, dict):
self._dict.update(dict)
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 2e053bed..63b945ff 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -78,10 +78,19 @@ class MyNonGlobalAction:
return [self]
class Environment:
+ def __init__(self, **kw):
+ self._dict = {}
+ self._dict.update(kw)
+ def __getitem__(self, key):
+ return self._dict[key]
def Dictionary(self, *args):
return {}
def Override(self, overrides):
- return overrides
+ d = self._dict.copy()
+ d.update(overrides)
+ return apply(Environment, (), d)
+ def _update(self, dict):
+ self._dict.update(dict)
class Builder:
def __init__(self):
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 66433938..4ca25b2a 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -788,6 +788,55 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di
return ls.data
+def scons_subst_once(strSubst, env, key):
+ """Perform single (non-recursive) substitution of a single
+ construction variable keyword.
+
+ This is used when setting a variable when copying or overriding values
+ in an Environment. We want to capture (expand) the old value before
+ we override it, so people can do things like:
+
+ env2 = env.Copy(CCFLAGS = '$CCFLAGS -g')
+
+ We do this with some straightforward, brute-force code here...
+ """
+ matchlist = ['$' + key, '${' + key + '}']
+ if is_List(strSubst):
+ result = []
+ for arg in strSubst:
+ if is_String(arg):
+ if arg in matchlist:
+ arg = env[key]
+ if is_List(arg):
+ result.extend(arg)
+ else:
+ result.append(arg)
+ else:
+ r = []
+ for a in _separate_args.findall(arg):
+ if a in matchlist:
+ a = env[key]
+ if is_List(a):
+ r.extend(string.join(map(str, a)))
+ else:
+ r.append(str(a))
+ result.append(string.join(r, ''))
+ else:
+ result.append(arg)
+ return result
+ elif is_String(strSubst):
+ result = []
+ for a in _separate_args.findall(strSubst):
+ if a in matchlist:
+ a = env[key]
+ if is_List(a):
+ result.extend(string.join(map(str, a)))
+ else:
+ result.append(str(a))
+ return string.join(result, '')
+ else:
+ return strSubst
+
def render_tree(root, child_func, prune=0, margin=[0], visited={}):
"""
Render a tree of nodes into an ASCII tree view.
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index ce82013d..d981abc4 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -63,6 +63,9 @@ class DummyEnv:
return self.dict
return self.dict[key]
+ def __getitem__(self, key):
+ return self.dict[key]
+
def sig_dict(self):
dict = self.dict.copy()
dict["TARGETS"] = 'tsig'
@@ -289,6 +292,9 @@ class UtilTestCase(unittest.TestCase):
'$S', 'x y',
'$LS', 'x y',
'$L', 'x y',
+ '$S z', 'x y z',
+ '$LS z', 'x y z',
+ '$L z', 'x y z',
#cs, 'cs',
#cl, 'cl',
'$CS', 'cs',
@@ -590,11 +596,20 @@ class UtilTestCase(unittest.TestCase):
#'$R', [[]],
#['$R'], [[]],
'$S', [['x', 'y']],
+ '$S z', [['x', 'y', 'z']],
['$S'], [['x', 'y']],
+ ['$S z'], [['x', 'y z']], # XXX - IS THIS BEST?
+ ['$S', 'z'], [['x', 'y', 'z']],
'$LS', [['x y']],
+ '$LS z', [['x y', 'z']],
['$LS'], [['x y']],
+ ['$LS z'], [['x y z']],
+ ['$LS', 'z'], [['x y', 'z']],
'$L', [['x', 'y']],
+ '$L z', [['x', 'y', 'z']],
['$L'], [['x', 'y']],
+ ['$L z'], [['x', 'y z']], # XXX - IS THIS BEST?
+ ['$L', 'z'], [['x', 'y', 'z']],
cs, [['cs']],
[cs], [['cs']],
cl, [['cl']],
@@ -756,6 +771,71 @@ class UtilTestCase(unittest.TestCase):
else:
raise AssertionError, "did not catch expected SyntaxError"
+ def test_subst_once(self):
+ """Testing the scons_subst_once() method"""
+
+ loc = {
+ 'CCFLAGS' : '-DFOO',
+ 'ONE' : 1,
+ 'RECURSE' : 'r $RECURSE r',
+ 'LIST' : ['a', 'b', 'c'],
+ }
+
+ env = DummyEnv(loc)
+
+ cases = [
+ '$CCFLAGS -DBAR',
+ 'OTHER_KEY',
+ '$CCFLAGS -DBAR',
+
+ '$CCFLAGS -DBAR',
+ 'CCFLAGS',
+ '-DFOO -DBAR',
+
+ 'x $ONE y',
+ 'ONE',
+ 'x 1 y',
+
+ 'x $RECURSE y',
+ 'RECURSE',
+ 'x r $RECURSE r y',
+
+ '$LIST',
+ 'LIST',
+ 'a b c',
+
+ ['$LIST'],
+ 'LIST',
+ ['a', 'b', 'c'],
+
+ ['x', '$LIST', 'y'],
+ 'LIST',
+ ['x', 'a', 'b', 'c', 'y'],
+
+ ['x', 'x $LIST y', 'y'],
+ 'LIST',
+ ['x', 'x a b c y', 'y'],
+
+ ['x', 'x $CCFLAGS y', 'y'],
+ 'LIST',
+ ['x', 'x $CCFLAGS y', 'y'],
+
+ ['x', 'x $RECURSE y', 'y'],
+ 'LIST',
+ ['x', 'x $RECURSE y', 'y'],
+ ]
+
+ failed = 0
+ while cases:
+ input, key, expect = cases[:3]
+ result = scons_subst_once(input, env, key)
+ if result != expect:
+ if failed == 0: print
+ print " input %s (%s) => %s did not match %s" % (repr(input), repr(key), repr(result), repr(expect))
+ failed = failed + 1
+ del cases[:3]
+ assert failed == 0, "%d subst() cases failed" % failed
+
def test_splitext(self):
assert splitext('foo') == ('foo','')
assert splitext('foo.bar') == ('foo','.bar')
diff --git a/test/CCFLAGS.py b/test/CCFLAGS.py
index 60fb74ea..66640104 100644
--- a/test/CCFLAGS.py
+++ b/test/CCFLAGS.py
@@ -45,6 +45,8 @@ foo.Object(target = 'foo%s', source = 'prog.c')
bar.Object(target = 'bar%s', source = 'prog.c')
foo.Program(target = 'foo', source = 'foo%s')
bar.Program(target = 'bar', source = 'bar%s')
+foo.Program(target = 'prog', source = 'prog.c',
+ CCFLAGS = '$CCFLAGS -DBAR $BAZ', BAZ = '-DBAZ')
""" % (fooflags, barflags, _obj, _obj, _obj, _obj))
test.write('prog.c', r"""
@@ -58,6 +60,9 @@ main(int argc, char *argv[])
#ifdef BAR
printf("prog.c: BAR\n");
#endif
+#ifdef BAZ
+ printf("prog.c: BAZ\n");
+#endif
exit (0);
}
""")
@@ -67,6 +72,11 @@ test.run(arguments = '.')
test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n")
test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n")
+test.run(program = test.workpath('prog'), stdout = """\
+prog.c: FOO
+prog.c: BAR
+prog.c: BAZ
+""")
test.write('SConstruct', """
bar = Environment(CCFLAGS = '%s')
diff --git a/test/LIBS.py b/test/LIBS.py
index e66899e7..645c625e 100644
--- a/test/LIBS.py
+++ b/test/LIBS.py
@@ -121,8 +121,8 @@ test.run(program=foo4_exe, stdout='sub1/bar.c\nsub1/baz.c\n')
#
test.write('SConstruct', """
-env = Environment()
-env.Program(target='foo1', source='foo1.c', LIBS=['baz', 'bar'], LIBPATH = '.')
+env = Environment(LIBS=['baz'])
+env.Program(target='foo1', source='foo1.c', LIBS=['$LIBS', 'bar'], LIBPATH = '.')
SConscript('sub1/SConscript', 'env')
SConscript('sub2/SConscript', 'env')
""")
@@ -187,7 +187,18 @@ libraries = (['libtest_component2',
'libtest_component1'])
# To remove the dependency problem, you should rename blender to mlender.
-Program (source='', target='blender', LIBS=libraries, LIBPREFIX='lib', LIBPATH=libpath)
+Program (source='main.c', target='blender', LIBS=libraries, LIBPREFIX='lib', LIBPATH=libpath, CPPPATH=['src/component2'])
+""")
+
+test.write('main.c', """\
+#include <stdio.h>
+#include "message2.h"
+
+int main (void)
+{
+ DisplayMessage2();
+ exit (0);
+}
""")
test.write(['src', 'SConscript'], """\
@@ -196,38 +207,41 @@ SConscript(['component1/SConscript',
""")
test.write(['src', 'component1', 'SConscript'], """\
-source_files = ['message.c']
-Library (target='../../lib/libtest_component1', source=source_files)
+source_files = ['message1.c']
+Library (target='../../lib/libtest_component1', source=source_files, LINKFLAGS='')
""")
-test.write(['src', 'component1', 'message.c'], """\
+test.write(['src', 'component1', 'message1.c'], """\
#include <stdio.h>
-void DisplayMessage (void)
+void DisplayMessage1 (void)
{
printf ("src/component1/message.c\\n");
}
""")
-test.write(['src', 'component1', 'message.h'], """\
-void DisplayMessage (void);
+test.write(['src', 'component1', 'message1.h'], """\
+void DisplayMessage1 (void);
""")
test.write(['src', 'component2', 'SConscript'], """\
-source_files = ['hello.c']
+source_files = ['message2.c']
include_paths = ['../component1']
Library (target='../../lib/libtest_component2', source=source_files, CPPPATH=include_paths)
""")
-test.write(['src', 'component2', 'hello.c'], """\
+test.write(['src', 'component2', 'message2.h'], """\
+void DisplayMessage2 (void);
+""")
+
+test.write(['src', 'component2', 'message2.c'], """\
#include <stdio.h>
-#include "message.h"
+#include "message1.h"
-int main (void)
+int DisplayMessage2 (void)
{
- DisplayMessage();
+ DisplayMessage1();
printf ("src/component2/hello.c\\n");
- exit (0);
}
""")
diff --git a/test/overrides.py b/test/overrides.py
index d9b6fdea..a247f7d3 100644
--- a/test/overrides.py
+++ b/test/overrides.py
@@ -34,14 +34,18 @@ test = TestSCons.TestSCons()
python = TestSCons.python
test.write('SConstruct', """
-env = Environment(LIBS=['a'])
+env = Environment(CCFLAGS='-DFOO', LIBS=['a'])
def build(target, source, env):
print "env['CC'] =", env['CC']
+ print "env['CCFLAGS'] =", env['CCFLAGS']
print "env['LIBS'] =", env['LIBS']
builder = Builder(action=build, CC='buildcc', LIBS='buildlibs')
env['BUILDERS']['Build'] = builder
-foo = env.Build('foo.out', 'foo.in', CC='mycc', LIBS = env['LIBS']+['b'])
+foo = env.Build('foo.out', 'foo.in',
+ CC='mycc',
+ CCFLAGS='$CCFLAGS -DBAR',
+ LIBS = env['LIBS']+['b'])
bar = env.Build('bar.out', 'bar.in')
Default([foo, bar])
""")
@@ -52,9 +56,11 @@ test.write('bar.in', "bar.in\n")
test.run(arguments = "-Q", stdout = """\
build("foo.out", "foo.in")
env['CC'] = mycc
+env['CCFLAGS'] = -DFOO -DBAR
env['LIBS'] = ['a', 'b']
build("bar.out", "bar.in")
env['CC'] = buildcc
+env['CCFLAGS'] = -DFOO
env['LIBS'] = buildlibs
""")