summaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-02-24 06:19:49 (GMT)
committerSteven Knight <knight@baldmt.com>2004-02-24 06:19:49 (GMT)
commit7e47444082fbd90c50717050873e7c2b3b00a6ea (patch)
tree21ab8d3cc408214f1e550bf4df7a27c3b45115fe /src/engine
parent027b93825c3b594b46e5edb9bf33af8089cbf7a4 (diff)
downloadSCons-7e47444082fbd90c50717050873e7c2b3b00a6ea.zip
SCons-7e47444082fbd90c50717050873e7c2b3b00a6ea.tar.gz
SCons-7e47444082fbd90c50717050873e7c2b3b00a6ea.tar.bz2
Handle recursive substitution in overrides.
Diffstat (limited to 'src/engine')
-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
7 files changed, 193 insertions, 14 deletions
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index a626204..c6bc2a9 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 330bb7c..a5cf171 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 8151f87..b636f60 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 d43f7e2..9c64012 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 2e053be..63b945f 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 6643393..4ca25b2 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 ce82013..d981abc 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')