summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-12-29 16:26:58 (GMT)
committerSteven Knight <knight@baldmt.com>2004-12-29 16:26:58 (GMT)
commit9113805b081ef58fdf56bd5b5a9be6afad0b7a41 (patch)
treeb5a11d8944eca035fdedf45dcb8de3af4108245a
parent36e665069d121ee715f4fba0c304fb9afe00583f (diff)
downloadSCons-9113805b081ef58fdf56bd5b5a9be6afad0b7a41.zip
SCons-9113805b081ef58fdf56bd5b5a9be6afad0b7a41.tar.gz
SCons-9113805b081ef58fdf56bd5b5a9be6afad0b7a41.tar.bz2
Enhance OverrideEnvironment, and rename the base class to an enhanced and maybe-even-useful SubstitutionEnvironment, to eliminate copying of construction environment variables.
-rw-r--r--src/engine/SCons/Action.py25
-rw-r--r--src/engine/SCons/ActionTests.py29
-rw-r--r--src/engine/SCons/Environment.py279
-rw-r--r--src/engine/SCons/EnvironmentTests.py348
-rw-r--r--src/engine/SCons/Executor.py11
-rw-r--r--src/engine/SCons/Options/OptionsTests.py4
-rw-r--r--src/engine/SCons/Scanner/Prog.py7
-rw-r--r--src/engine/SCons/Util.py44
-rw-r--r--src/engine/SCons/UtilTests.py95
9 files changed, 551 insertions, 291 deletions
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 6db1e6a..83e4208 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -415,7 +415,7 @@ class CommandAction(_ActionAction):
return result
return 0
- def get_contents(self, target, source, env, dict=None):
+ def get_contents(self, target, source, env):
"""Return the signature contents of this action's command line.
This strips $(-$) and everything in between the string,
@@ -426,7 +426,7 @@ class CommandAction(_ActionAction):
cmd = string.join(map(str, cmd))
else:
cmd = str(cmd)
- return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source, dict)
+ return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source)
class CommandGeneratorAction(ActionBase):
"""Class for command-generator actions."""
@@ -464,13 +464,13 @@ class CommandGeneratorAction(ActionBase):
return act(target, source, env, errfunc, presub,
show, execute, chdir)
- def get_contents(self, target, source, env, dict=None):
+ def get_contents(self, target, source, env):
"""Return the signature contents of this action's command line.
This strips $(-$) and everything in between the string,
since those parts don't affect signatures.
"""
- return self._generate(target, source, env, 1).get_contents(target, source, env, dict=None)
+ return self._generate(target, source, env, 1).get_contents(target, source, env)
# Ooh, polymorphism -- pretty scary, eh, kids?
#
@@ -516,9 +516,9 @@ class LazyAction(CommandGeneratorAction, CommandAction):
c = self.get_parent_class(env)
return apply(c.__call__, args, kw)
- def get_contents(self, target, source, env, dict=None):
+ def get_contents(self, target, source, env):
c = self.get_parent_class(env)
- return c.get_contents(self, target, source, env, dict)
+ return c.get_contents(self, target, source, env)
class FunctionAction(_ActionAction):
"""Class for Python function actions."""
@@ -569,7 +569,7 @@ class FunctionAction(_ActionAction):
raise SCons.Errors.BuildError(node=target, errstr=e.strerror)
return result
- def get_contents(self, target, source, env, dict=None):
+ def get_contents(self, target, source, env):
"""Return the signature contents of this callable action.
By providing direct access to the code object of the
@@ -590,7 +590,7 @@ class FunctionAction(_ActionAction):
# This is weird, just do the best we can.
contents = str(self.execfunction)
else:
- contents = gc(target, source, env, dict)
+ contents = gc(target, source, env)
return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
self.varlist)))
@@ -618,14 +618,13 @@ class ListAction(ActionBase):
a.presub_lines(env),
self.list))
- def get_contents(self, target, source, env, dict=None):
+ def get_contents(self, target, source, env):
"""Return the signature contents of this action list.
Simple concatenation of the signatures of the elements.
"""
- dict = SCons.Util.subst_dict(target, source)
- return string.join(map(lambda x, t=target, s=source, e=env, d=dict:
- x.get_contents(t, s, e, d),
+ return string.join(map(lambda x, t=target, s=source, e=env:
+ x.get_contents(t, s, e),
self.list),
"")
@@ -651,7 +650,7 @@ class ActionCaller:
self.parent = parent
self.args = args
self.kw = kw
- def get_contents(self, target, source, env, dict=None):
+ def get_contents(self, target, source, env):
actfunc = self.parent.actfunc
try:
# "self.actfunc" is a function.
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 705daff..4c1d521 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -128,11 +128,11 @@ class Environment:
for k, v in kw.items():
self.d[k] = v
# Just use the underlying scons_subst*() utility methods.
- def subst(self, strSubst, raw=0, target=[], source=[], dict=None):
- return SCons.Util.scons_subst(strSubst, self, raw, target, source, dict)
+ def subst(self, strSubst, raw=0, target=[], source=[]):
+ return SCons.Util.scons_subst(strSubst, self, raw, target, source, self.d)
subst_target_source = subst
- def subst_list(self, strSubst, raw=0, target=[], source=[], dict=None):
- return SCons.Util.scons_subst_list(strSubst, self, raw, target, source, dict)
+ def subst_list(self, strSubst, raw=0, target=[], source=[]):
+ return SCons.Util.scons_subst_list(strSubst, self, raw, target, source, self.d)
def __getitem__(self, item):
return self.d[item]
def __setitem__(self, item, value):
@@ -1088,7 +1088,7 @@ class CommandActionTestCase(unittest.TestCase):
# Make sure that CommandActions use an Environment's
# subst_target_source() method for substitution.
class SpecialEnvironment(Environment):
- def subst_target_source(self, strSubst, raw=0, target=[], source=[], dict=None):
+ def subst_target_source(self, strSubst, raw=0, target=[], source=[]):
return 'subst_target_source: ' + strSubst
c = a.get_contents(target=DummyNode('ttt'), source = DummyNode('sss'),
@@ -1112,14 +1112,10 @@ class CommandActionTestCase(unittest.TestCase):
a = SCons.Action.CommandAction(["$TARGET"])
c = a.get_contents(target=t, source=s, env=env)
assert c == "t1", c
- c = a.get_contents(target=t, source=s, env=env, dict={})
- assert c == "", c
a = SCons.Action.CommandAction(["$TARGETS"])
c = a.get_contents(target=t, source=s, env=env)
assert c == "t1 t2 t3 t4 t5 t6", c
- c = a.get_contents(target=t, source=s, env=env, dict={})
- assert c == "", c
a = SCons.Action.CommandAction(["${TARGETS[2]}"])
c = a.get_contents(target=t, source=s, env=env)
@@ -1132,14 +1128,10 @@ class CommandActionTestCase(unittest.TestCase):
a = SCons.Action.CommandAction(["$SOURCE"])
c = a.get_contents(target=t, source=s, env=env)
assert c == "s1", c
- c = a.get_contents(target=t, source=s, env=env, dict={})
- assert c == "", c
a = SCons.Action.CommandAction(["$SOURCES"])
c = a.get_contents(target=t, source=s, env=env)
assert c == "s1 s2 s3 s4 s5 s6", c
- c = a.get_contents(target=t, source=s, env=env, dict={})
- assert c == "", c
a = SCons.Action.CommandAction(["${SOURCES[2]}"])
c = a.get_contents(target=t, source=s, env=env)
@@ -1253,8 +1245,6 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
a = SCons.Action.CommandGeneratorAction(f)
c = a.get_contents(target=[], source=[], env=env)
assert c == "guux FFF BBB test", c
- c = a.get_contents(target=[], source=[], env=env, dict={})
- assert c == "guux FFF BBB test", c
class FunctionActionTestCase(unittest.TestCase):
@@ -1381,8 +1371,6 @@ class FunctionActionTestCase(unittest.TestCase):
c = a.get_contents(target=[], source=[], env=Environment())
assert c in matches, repr(c)
- c = a.get_contents(target=[], source=[], env=Environment(), dict={})
- assert c in matches, repr(c)
a = SCons.Action.FunctionAction(GlobalFunc, varlist=['XYZ'])
@@ -1394,7 +1382,7 @@ class FunctionActionTestCase(unittest.TestCase):
assert c in matches_foo, repr(c)
class Foo:
- def get_contents(self, target, source, env, dict=None):
+ def get_contents(self, target, source, env):
return 'xyzzy'
a = SCons.Action.FunctionAction(Foo())
c = a.get_contents(target=[], source=[], env=Environment())
@@ -1481,9 +1469,6 @@ class ListActionTestCase(unittest.TestCase):
c = a.get_contents(target=[], source=[], env=Environment(s = self))
assert self.foo==1, self.foo
assert c == "xyz", c
- c = a.get_contents(target=[], source=[], env=Environment(s = self), dict={})
- assert self.foo==1, self.foo
- assert c == "xyz", c
class LazyActionTestCase(unittest.TestCase):
def test___init__(self):
@@ -1533,8 +1518,6 @@ class LazyActionTestCase(unittest.TestCase):
env = Environment(FOO = [["This", "is", "a", "test"]])
c = a.get_contents(target=[], source=[], env=env)
assert c == "This is a test", c
- c = a.get_contents(target=[], source=[], env=env, dict={})
- assert c == "This is a test", c
class ActionCallerTestCase(unittest.TestCase):
def test___init__(self):
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 75b1eca..4af4ced 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -214,18 +214,91 @@ class BuilderDict(UserDict):
for i, v in dict.items():
self.__setitem__(i, v)
-class _Environment:
- """Abstract base class for different flavors of construction
- environment objects.
-
- This collects common methods that need to be used by all types of
- construction environment (proxies as well as "real" environments)
- so that construction variable substitution and translation from
- strings into Nodes happen in accordance with the object's other
- rules--in other words, for the case of proxies, wherever we need to
- do something like env.subst(), env.arg2nodes() and fetch attributes or
- values like the wrapper proxy, not the underlying wrapped environment.
+class SubstitutionEnvironment:
+ """Base class for different flavors of construction environments.
+
+ This class contains a minimal set of methods that handle contruction
+ variable expansion and conversion of strings to Nodes, which may or
+ may not be actually useful as a stand-alone class. Which methods
+ ended up in this class is pretty arbitrary right now. They're
+ basically the ones which we've empirically determined are common to
+ the different construction environment subclasses, and most of the
+ others that use or touch the underlying dictionary of construction
+ variables.
+
+ Eventually, this class should contain all the methods that we
+ determine are necessary for a "minimal" interface to the build engine.
+ A full "native Python" SCons environment has gotten pretty heavyweight
+ with all of the methods and Tools and construction variables we've
+ jammed in there, so it would be nice to have a lighter weight
+ alternative for interfaces that don't need all of the bells and
+ whistles. (At some point, we'll also probably rename this class
+ "Base," since that more reflects what we want this class to become,
+ but because we've released comments that tell people to subclass
+ Environment.Base to create their own flavors of construction
+ environment, we'll save that for a future refactoring when this
+ class actually becomes useful.)
"""
+ def __init__(self, **kw):
+ """Initialization of an underlying SubstitutionEnvironment class.
+ """
+ if __debug__: logInstanceCreation(self)
+ self.fs = SCons.Node.FS.default_fs
+ self.ans = SCons.Node.Alias.default_ans
+ self.lookup_list = SCons.Node.arg2nodes_lookups
+ self._dict = kw.copy()
+ self._dict['__env__'] = self
+
+ def __cmp__(self, other):
+ # Since an Environment now has an '__env__' construction variable
+ # that refers to itself, delete that variable to avoid infinite
+ # loops when comparing the underlying dictionaries in some Python
+ # versions (*cough* 1.5.2 *cough*)...
+ sdict = self._dict.copy()
+ del sdict['__env__']
+ odict = other._dict.copy()
+ del odict['__env__']
+ return cmp(sdict, odict)
+
+ def __delitem__(self, key):
+ del self._dict[key]
+
+ def __getitem__(self, key):
+ return self._dict[key]
+
+ def __setitem__(self, key, value):
+ if key in reserved_construction_var_names:
+ SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
+ "Ignoring attempt to set reserved variable `%s'" % key)
+ elif key == 'BUILDERS':
+ try:
+ bd = self._dict[key]
+ for k in bd.keys():
+ del bd[k]
+ except KeyError:
+ self._dict[key] = BuilderDict(kwbd, self)
+ self._dict[key].update(value)
+ elif key == 'SCANNERS':
+ self._dict[key] = value
+ self.scanner_map_delete()
+ else:
+ if not SCons.Util.is_valid_construction_var(key):
+ raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
+ self._dict[key] = value
+
+ def get(self, key, default=None):
+ "Emulates the get() method of dictionaries."""
+ return self._dict.get(key, default)
+
+ def has_key(self, key):
+ return self._dict.has_key(key)
+
+ def items(self):
+ "Emulates the items() method of dictionaries."""
+ result = self._dict.items()
+ result = filter(lambda t: t[0] != '__env__', result)
+ return result
+
def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
if node_factory is _null:
node_factory = self.fs.File
@@ -268,7 +341,13 @@ class _Environment:
return nodes
- def subst(self, string, raw=0, target=None, source=None, dict=None, conv=None):
+ def gvars(self):
+ return self._dict
+
+ def lvars(self):
+ return {}
+
+ def subst(self, string, raw=0, target=None, source=None, conv=None):
"""Recursively interpolates construction variables from the
Environment into the specified string, returning the expanded
result. Construction variables are specified by a $ prefix
@@ -278,21 +357,25 @@ class _Environment:
may be surrounded by curly braces to separate the name from
trailing characters.
"""
- return SCons.Util.scons_subst(string, self, raw, target, source, dict, conv)
+ gvars = self.gvars()
+ lvars = self.lvars()
+ return SCons.Util.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
- def subst_kw(self, kw, raw=0, target=None, source=None, dict=None):
+ def subst_kw(self, kw, raw=0, target=None, source=None):
nkw = {}
for k, v in kw.items():
- k = self.subst(k, raw, target, source, dict)
+ k = self.subst(k, raw, target, source)
if SCons.Util.is_String(v):
- v = self.subst(v, raw, target, source, dict)
+ v = self.subst(v, raw, target, source)
nkw[k] = v
return nkw
- def subst_list(self, string, raw=0, target=None, source=None, dict=None, conv=None):
+ def subst_list(self, string, raw=0, target=None, source=None, conv=None):
"""Calls through to SCons.Util.scons_subst_list(). See
the documentation for that function."""
- return SCons.Util.scons_subst_list(string, self, raw, target, source, dict, conv)
+ gvars = self.gvars()
+ lvars = self.lvars()
+ return SCons.Util.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
def subst_path(self, path):
"""Substitute a path list, turning EntryProxies into Nodes
@@ -335,7 +418,30 @@ class _Environment:
subst_target_source = subst
-class Base(_Environment):
+ def Override(self, overrides):
+ """
+ Produce a modified environment whose variables are overriden by
+ the overrides dictionaries. "overrides" is a dictionary that
+ will override the variables of this environment.
+
+ This function is much more efficient than Copy() or creating
+ a new Environment because it doesn't copy the construction
+ environment dictionary, it just wraps the underlying construction
+ environment, and doesn't even create a wrapper object if there
+ are no overrides.
+ """
+ if overrides:
+ o = copy_non_reserved_keywords(overrides)
+ overrides = {}
+ for key, value in o.items():
+ overrides[key] = SCons.Util.scons_subst_once(value, self, key)
+ if overrides:
+ env = OverrideEnvironment(self, overrides)
+ return env
+ else:
+ return self
+
+class Base(SubstitutionEnvironment):
"""Base class for "real" construction Environments. These are the
primary objects used to communicate dependency and construction
information to the build engine.
@@ -365,6 +471,16 @@ class Base(_Environment):
toolpath=[],
options=None,
**kw):
+ """
+ Initialization of a basic SCons construction environment,
+ including setting up special construction variables like BUILDER,
+ PLATFORM, etc., and searching for and applying available Tools.
+
+ Note that we do *not* call the underlying base class
+ (SubsitutionEnvironment) initialization, because we need to
+ initialize things in a very specific order that doesn't work
+ with the much simpler base class initialization.
+ """
if __debug__: logInstanceCreation(self)
self.fs = SCons.Node.FS.default_fs
self.ans = SCons.Node.Alias.default_ans
@@ -408,58 +524,9 @@ class Base(_Environment):
if options:
options.Update(self)
- def __cmp__(self, other):
- # Since an Environment now has an '__env__' construction variable
- # that refers to itself, delete that variable to avoid infinite
- # loops when comparing the underlying dictionaries in some Python
- # versions (*cough* 1.5.2 *cough*)...
- sdict = self._dict.copy()
- del sdict['__env__']
- odict = other._dict.copy()
- del odict['__env__']
- return cmp(sdict, odict)
-
- def __getitem__(self, key):
- return self._dict[key]
-
- def __setitem__(self, key, value):
- if key in reserved_construction_var_names:
- SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
- "Ignoring attempt to set reserved variable `%s'" % key)
- elif key == 'BUILDERS':
- try:
- bd = self._dict[key]
- for k in bd.keys():
- del bd[k]
- except KeyError:
- self._dict[key] = BuilderDict(kwbd, self)
- self._dict[key].update(value)
- elif key == 'SCANNERS':
- self._dict[key] = value
- self.scanner_map_delete()
- else:
- if not SCons.Util.is_valid_construction_var(key):
- raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
- self._dict[key] = value
-
- def __delitem__(self, key):
- del self._dict[key]
-
- def items(self):
- "Emulates the items() method of dictionaries."""
- return self._dict.items()
-
- def has_key(self, key):
- return self._dict.has_key(key)
-
- def get(self, key, default=None):
- "Emulates the get() method of dictionaries."""
- return self._dict.get(key, default)
-
#######################################################################
# Utility methods that are primarily for internal use by SCons.
- # These begin with lower-case letters. Note that the subst() method
- # is actually already out of the closet and used by people.
+ # These begin with lower-case letters.
#######################################################################
def get_calculator(self):
@@ -722,31 +789,6 @@ class Base(_Environment):
if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
return path
- def Override(self, overrides):
- """
- Produce a modified environment whose variables
- are overriden by the overrides dictionaries.
-
- overrides - a dictionary that will override
- the variables of this environment.
-
- This function is much more efficient than Copy()
- or creating a new Environment because it doesn't do
- a deep copy of the dictionary, and doesn't do a copy
- at all if there are no overrides.
- """
-
- if overrides:
- o = copy_non_reserved_keywords(overrides)
- overrides = {}
- for key, value in o.items():
- overrides[key] = SCons.Util.scons_subst_once(value, self, key)
- if overrides:
- env = OverrideEnvironment(self, overrides)
- return env
- else:
- return self
-
def ParseConfig(self, command, function=None):
"""
Use the specified function to parse the output of the command
@@ -1340,21 +1382,25 @@ class Base(_Environment):
"""
return SCons.Node.Python.Value(value)
-class OverrideEnvironment(_Environment):
- """A proxy that overrides variables in a wrapped "real"
- (Environment.Base) construction environment by returning values from
- an overrides dictionary in preference to values from the underlying
- subject environment.
+class OverrideEnvironment(SubstitutionEnvironment):
+ """A proxy that overrides variables in a wrapped construction
+ environment by returning values from an overrides dictionary in
+ preference to values from the underlying subject environment.
This is a lightweight (I hope) proxy that passes through most use of
attributes to the underlying Environment.Base class, but has just
enough additional methods defined to act like a real construction
- environment with overridden values.
-
- Note that because we subclass _Environment, this class also has
- inherited arg2nodes() and subst*() methods. Those methods can't
+ environment with overridden values. It can wrap either a Base
+ construction environment, or another OverrideEnvironment, which
+ can in turn nest arbitrary OverrideEnvironments...
+
+ Note that we do *not* call the underlying base class
+ (SubsitutionEnvironment) initialization, because we get most of those
+ from proxying the attributes of the subject construction environment.
+ But because we subclass SubstitutionEnvironment, this class also
+ has inherited arg2nodes() and subst*() methods; those methods can't
be proxied because they need *this* object's methods to fetch the
- overridden values.
+ values from the overrides dictionary.
"""
def __init__(self, subject, overrides={}):
if __debug__: logInstanceCreation(self, 'OverrideEnvironment')
@@ -1385,7 +1431,7 @@ class OverrideEnvironment(_Environment):
pass
return self.__dict__['__subject'].__delitem__(key)
def get(self, key, default=None):
- "Emulates the get() method of dictionaries."""
+ """Emulates the get() method of dictionaries."""
try:
return self.__dict__['overrides'][key]
except KeyError:
@@ -1397,7 +1443,7 @@ class OverrideEnvironment(_Environment):
except KeyError:
return self.__dict__['__subject'].has_key(key)
def items(self):
- "Emulates the items() method of dictionaries."""
+ """Emulates the items() method of dictionaries."""
return self.Dictionary().items()
# Overridden private construction environment methods.
@@ -1407,20 +1453,15 @@ class OverrideEnvironment(_Environment):
"""
self.__dict__['overrides'].update(dict)
+ def gvars(self):
+ return self.__dict__['__subject'].gvars()
+
+ def lvars(self):
+ lvars = self.__dict__['__subject'].lvars()
+ lvars.update(self.__dict__['overrides'])
+ return lvars
+
# Overridden public construction environment methods.
- def Dictionary(self, *args):
- if not args:
- result = self.__dict__['__subject'].Dictionary().copy()
- result.update(self.__dict__['overrides'])
- return result
- dlist = map(lambda k, s=self: s.__getitem__(k), args)
- if len(dlist) == 1:
- dlist = dlist[0]
- return dlist
- def Override(self, overrides):
- kw = copy_non_reserved_keywords(overrides)
- self.__dict__['overrides'].update(our_deepcopy(kw))
- return self
def Replace(self, **kw):
kw = copy_non_reserved_keywords(kw)
self.__dict__['overrides'].update(our_deepcopy(kw))
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 71383e7..92b89c0 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -146,38 +146,74 @@ class DummyNode:
-class EnvironmentTestCase(unittest.TestCase):
+class SubstitutionTestCase(unittest.TestCase):
def test___init__(self):
- """Test construction Environment creation
+ """Test initializing a SubstitutionEnvironment
+ """
+ env = SubstitutionEnvironment()
+ assert env['__env__'] is env, env['__env__']
- Create two with identical arguments and check that
- they compare the same.
+ def test___cmp__(self):
+ """Test comparing SubstitutionEnvironments
"""
- env1 = Environment(XXX = 'x', YYY = 'y')
- env2 = Environment(XXX = 'x', YYY = 'y')
- assert env1 == env2, diff_env(env1, env2)
- assert env1['__env__'] is env1, env1['__env__']
- assert env2['__env__'] is env2, env2['__env__']
+ env1 = SubstitutionEnvironment(XXX = 'x')
+ env2 = SubstitutionEnvironment(XXX = 'x')
+ env3 = SubstitutionEnvironment(XXX = 'xxx')
+ env4 = SubstitutionEnvironment(XXX = 'x', YYY = 'x')
+
+ assert env1 == env2
+ assert env1 != env3
+ assert env1 != env4
+
+ def test___delitem__(self):
+ """Test deleting a variable from a SubstitutionEnvironment
+ """
+ env1 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+ env2 = SubstitutionEnvironment(XXX = 'x')
+ del env1['YYY']
+ assert env1 == env2
+
+ def test___getitem__(self):
+ """Test deleting a variable from a SubstitutionEnvironment
+ """
+ env = SubstitutionEnvironment(XXX = 'x')
+ assert env['XXX'] == 'x', env['XXX']
+
+ def test___setitem__(self):
+ """Test deleting a variable from a SubstitutionEnvironment
+ """
+ env1 = SubstitutionEnvironment(XXX = 'x')
+ env2 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+ env1['YYY'] = 'y'
+ assert env1 == env2
def test_get(self):
- """Test the get() method."""
- env = Environment(aaa = 'AAA')
+ """Test the SubstitutionEnvironment get() method
+ """
+ env = SubstitutionEnvironment(XXX = 'x')
+ assert env.get('XXX') == 'x', env.get('XXX')
+ assert env.get('YYY') is None, env.get('YYY')
- x = env.get('aaa')
- assert x == 'AAA', x
- x = env.get('aaa', 'XXX')
- assert x == 'AAA', x
- x = env.get('bbb')
- assert x is None, x
- x = env.get('bbb', 'XXX')
- assert x == 'XXX', x
+ def test_has_key(self):
+ """Test the SubstitutionEnvironment has_key() method
+ """
+ env = SubstitutionEnvironment(XXX = 'x')
+ assert env.has_key('XXX')
+ assert not env.has_key('YYY')
+
+ def test_items(self):
+ """Test the SubstitutionEnvironment items() method
+ """
+ env = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+ items = env.items()
+ assert items == [('XXX','x'), ('YYY','y')], items
def test_arg2nodes(self):
"""Test the arg2nodes method
"""
- env = Environment()
+ env = SubstitutionEnvironment()
dict = {}
class X(SCons.Node.Node):
pass
@@ -245,7 +281,7 @@ class EnvironmentTestCase(unittest.TestCase):
else:
return None
- env_ll = env.Copy()
+ env_ll = SubstitutionEnvironment()
env_ll.lookup_list = [lookup_a, lookup_b]
nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
@@ -295,23 +331,35 @@ class EnvironmentTestCase(unittest.TestCase):
assert not hasattr(nodes[1], 'bbbb'), nodes[0]
assert nodes[1].c == 1, nodes[1]
+ def test_gvars(self):
+ """Test the base class gvars() method"""
+ env = SubstitutionEnvironment()
+ gvars = env.gvars()
+ assert gvars == {'__env__' : env}, gvars
+
+ def test_lvars(self):
+ """Test the base class lvars() method"""
+ env = SubstitutionEnvironment()
+ lvars = env.lvars()
+ assert lvars == {}, lvars
+
def test_subst(self):
"""Test substituting construction variables within strings
Check various combinations, including recursive expansion
of variables into other variables.
"""
- env = Environment(AAA = 'a', BBB = 'b')
+ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
assert mystr == "a aA b", mystr
# Changed the tests below to reflect a bug fix in
# subst()
- env = Environment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
assert mystr == "b bA bB b", mystr
- env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
assert mystr == "c cA cB c", mystr
@@ -320,28 +368,26 @@ class EnvironmentTestCase(unittest.TestCase):
s1 = DummyNode('s1')
s2 = DummyNode('s2')
- env = Environment(AAA = 'aaa')
+ env = SubstitutionEnvironment(AAA = 'aaa')
s = env.subst('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
assert s == "aaa t1 s1 s2", s
s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
assert s == "aaa t1 t2 s1", s
- s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2], dict={})
- assert s == "aaa", s
- # Test callables in the Environment
+ # Test callables in the SubstitutionEnvironment
def foo(target, source, env, for_signature):
assert str(target) == 't', target
assert str(source) == 's', source
return env["FOO"]
- env = Environment(BAR=foo, FOO='baz')
+ env = SubstitutionEnvironment(BAR=foo, FOO='baz')
t = DummyNode('t')
s = DummyNode('s')
subst = env.subst('test $BAR', target=t, source=s)
assert subst == 'test baz', subst
- # Test not calling callables in the Environment
+ # Test not calling callables in the SubstitutionEnvironment
if 0:
# This will take some serious surgery to subst() and
# subst_list(), so just leave these tests out until we can
@@ -349,7 +395,7 @@ class EnvironmentTestCase(unittest.TestCase):
def bar(arg):
pass
- env = Environment(BAR=bar, FOO='$BAR')
+ env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
subst = env.subst('$BAR', call=None)
assert subst is bar, subst
@@ -358,8 +404,8 @@ class EnvironmentTestCase(unittest.TestCase):
assert subst is bar, subst
def test_subst_kw(self):
- """Test substituting construction variables within dictionaries"""
- env = Environment(AAA = 'a', BBB = 'b')
+ """Test substituting construction variables within dictionaries"""
+ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
kw = env.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'})
assert len(kw) == 2, kw
assert kw['a'] == 'aaa', kw['a']
@@ -368,21 +414,21 @@ class EnvironmentTestCase(unittest.TestCase):
def test_subst_list(self):
"""Test substituting construction variables in command lists
"""
- env = Environment(AAA = 'a', BBB = 'b')
+ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
l = env.subst_list("$AAA ${AAA}A $BBBB $BBB")
assert l == [["a", "aA", "b"]], l
# Changed the tests below to reflect a bug fix in
# subst()
- env = Environment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
assert l == [["b", "bA", "bB", "b"]], l
- env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
assert l == [["c", "cA", "cB", "c"]], mystr
- env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
+ env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
lst = env.subst_list([ "$AAA", "B $CCC" ])
assert lst == [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst
@@ -391,28 +437,26 @@ class EnvironmentTestCase(unittest.TestCase):
s1 = DummyNode('s1')
s2 = DummyNode('s2')
- env = Environment(AAA = 'aaa')
+ env = SubstitutionEnvironment(AAA = 'aaa')
s = env.subst_list('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
assert s == [["aaa", "t1", "s1", "s2"]], s
s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
assert s == [["aaa", "t1", "t2", "s1"]], s
- s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2], dict={})
- assert s == [["aaa"]], s
- # Test callables in the Environment
+ # Test callables in the SubstitutionEnvironment
def foo(target, source, env, for_signature):
assert str(target) == 't', target
assert str(source) == 's', source
return env["FOO"]
- env = Environment(BAR=foo, FOO='baz')
+ env = SubstitutionEnvironment(BAR=foo, FOO='baz')
t = DummyNode('t')
s = DummyNode('s')
lst = env.subst_list('test $BAR', target=t, source=s)
assert lst == [['test', 'baz']], lst
- # Test not calling callables in the Environment
+ # Test not calling callables in the SubstitutionEnvironment
if 0:
# This will take some serious surgery to subst() and
# subst_list(), so just leave these tests out until we can
@@ -420,7 +464,7 @@ class EnvironmentTestCase(unittest.TestCase):
def bar(arg):
pass
- env = Environment(BAR=bar, FOO='$BAR')
+ env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
subst = env.subst_list('$BAR', call=None)
assert subst is bar, subst
@@ -440,7 +484,7 @@ class EnvironmentTestCase(unittest.TestCase):
class MyObj:
pass
- env = Environment(FOO='foo', BAR='bar', PROXY=MyProxy('my1'))
+ env = SubstitutionEnvironment(FOO='foo', BAR='bar', PROXY=MyProxy('my1'))
r = env.subst_path('$FOO')
assert r == ['foo'], r
@@ -459,7 +503,7 @@ class EnvironmentTestCase(unittest.TestCase):
def __str__(self):
return self.s
- env = Environment(FOO=StringableObj("foo"),
+ env = SubstitutionEnvironment(FOO=StringableObj("foo"),
BAR=StringableObj("bar"))
r = env.subst_path([ "${FOO}/bar", "${BAR}/baz" ])
@@ -471,6 +515,70 @@ class EnvironmentTestCase(unittest.TestCase):
r = env.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ])
assert r == [ "bar/foo/bar", "baz/bar/baz" ]
+ def test_subst_target_source(self):
+ """Test the base environment subst_target_source() method"""
+ env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
+ mystr = env.subst_target_source("$AAA ${AAA}A $BBBB $BBB")
+ assert mystr == "a aA b", mystr
+
+ def test_Override(self):
+ "Test overriding construction variables"
+ env = SubstitutionEnvironment(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", env2['ONE']
+ assert env['ONE'] == 1, env['ONE']
+
+ assert env['__env__'] is env, env['__env__']
+ assert env2['__env__'] is env2, env2['__env__']
+
+
+
+class BaseTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test construction Environment creation
+
+ Create two with identical arguments and check that
+ they compare the same.
+ """
+ env1 = Environment(XXX = 'x', YYY = 'y')
+ env2 = Environment(XXX = 'x', YYY = 'y')
+ assert env1 == env2, diff_env(env1, env2)
+
+ assert env1['__env__'] is env1, env1['__env__']
+ assert env2['__env__'] is env2, env2['__env__']
+
+ def test_get(self):
+ """Test the get() method."""
+ env = Environment(aaa = 'AAA')
+
+ x = env.get('aaa')
+ assert x == 'AAA', x
+ x = env.get('aaa', 'XXX')
+ assert x == 'AAA', x
+ x = env.get('bbb')
+ assert x is None, x
+ x = env.get('bbb', 'XXX')
+ assert x == 'XXX', x
+
def test_Builder_calls(self):
"""Test Builder calls through different environments
"""
@@ -916,6 +1024,14 @@ def exists(env):
x = s("${_concat(PRE, LIST, SUF, __env__)}")
assert x == 'preasuf prebsuf', x
+ def test_gvars(self):
+ """Test the Environment gvars() method"""
+ env = Environment(XXX = 'x', YYY = 'y', ZZZ = 'z')
+ gvars = env.gvars()
+ assert gvars['XXX'] == 'x', gvars['XXX']
+ assert gvars['YYY'] == 'y', gvars['YYY']
+ assert gvars['ZZZ'] == 'z', gvars['ZZZ']
+
def test__update(self):
"""Test the _update() method"""
env = Environment(X = 'x', Y = 'y', Z = 'z')
@@ -1307,34 +1423,6 @@ def exists(env):
assert None == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
- def test_Override(self):
- "Test overriding construction variables"
- 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", env2['ONE']
- assert env['ONE'] == 1, env['ONE']
-
- assert env['__env__'] is env, env['__env__']
- assert env2['__env__'] is env2, env2['__env__']
-
def test_ParseConfig(self):
"""Test the ParseConfig() method"""
env = Environment(ASFLAGS='assembler',
@@ -2634,6 +2722,110 @@ f5: \
+class OverrideEnvironmentTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test OverrideEnvironment initialization"""
+ env = Environment(XXX = 'x', YYY = 'y')
+ env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
+ env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3'})
+ assert env['XXX'] == 'x', env['XXX']
+ assert env2['XXX'] == 'x2', env2['XXX']
+ assert env3['XXX'] == 'x3', env3['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+ assert env2['YYY'] == 'y', env2['YYY']
+ assert env3['YYY'] == 'y3', env3['YYY']
+
+ def test_get(self):
+ """Test the OverrideEnvironment get() method"""
+ env = Environment(XXX = 'x', YYY = 'y')
+ env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
+ env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
+ assert env.get('XXX') == 'x', env.get('XXX')
+ assert env2.get('XXX') == 'x2', env2.get('XXX')
+ assert env3.get('XXX') == 'x3', env3.get('XXX')
+ assert env.get('YYY') == 'y', env.get('YYY')
+ assert env2.get('YYY') == 'y', env2.get('YYY')
+ assert env3.get('YYY') == 'y3', env3.get('YYY')
+ assert env.get('ZZZ') == None, env.get('ZZZ')
+ assert env2.get('ZZZ') == None, env2.get('ZZZ')
+ assert env3.get('ZZZ') == 'z3', env3.get('ZZZ')
+
+ def test_has_key(self):
+ """Test the OverrideEnvironment has_key() method"""
+ env = Environment(XXX = 'x', YYY = 'y')
+ env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
+ env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
+ assert env.has_key('XXX'), env.has_key('XXX')
+ assert env2.has_key('XXX'), env2.has_key('XXX')
+ assert env3.has_key('XXX'), env3.has_key('XXX')
+ assert env.has_key('YYY'), env.has_key('YYY')
+ assert env2.has_key('YYY'), env2.has_key('YYY')
+ assert env3.has_key('YYY'), env3.has_key('YYY')
+ assert not env.has_key('ZZZ'), env.has_key('ZZZ')
+ assert not env2.has_key('ZZZ'), env2.has_key('ZZZ')
+ assert env3.has_key('ZZZ'), env3.has_key('ZZZ')
+
+ def test_items(self):
+ """Test the OverrideEnvironment items() method"""
+ env = Environment(WWW = 'w', XXX = 'x', YYY = 'y')
+ env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
+ env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
+ items = env.items()
+ assert items == {'WWW' : 'w', 'XXX' : 'x', 'YYY' : 'y'}, items
+ items = env2.items()
+ assert items == {'WWW' : 'w', 'XXX' : 'x2', 'YYY' : 'y'}, items
+ items = env3.items()
+ assert items == {'WWW' : 'w', 'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items
+
+ def test_gvars(self):
+ """Test the OverrideEnvironment gvars() method"""
+ env = Environment(XXX = 'x', YYY = 'y')
+ env2 = OverrideEnvironment(env, {'xxx' : 'x2'})
+ env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3'})
+ gvars = env.gvars()
+ assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
+ gvars = env2.gvars()
+ assert gvars == {'XXX' : 'x2', 'YYY' : 'y'}, gvars
+ gvars = env3.gvars()
+ assert gvars == {'XXX' : 'x3', 'YYY' : 'y3'}, gvars
+
+ def test_lvars(self):
+ """Test the OverrideEnvironment lvars() method"""
+ env = Environment(XXX = 'x', YYY = 'y')
+ env2 = OverrideEnvironment(env, {'xxx' : 'x2'})
+ env3 = OverrideEnvironment(env2, {'xxx' : 'x3', 'YYY' : 'y3'})
+ lvars = env.lvars()
+ assert lvars == {}, lvars
+ lvars = env2.lvars()
+ assert lvars == {'XXX' : 'x2', 'YYY' : 'y'}, lvars
+ lvars = env3.lvars()
+ assert lvars == {'XXX' : 'x3', 'YYY' : 'y3'}, lvars
+
+ def test_Replace(self):
+ """Test the OverrideEnvironment Replace() method"""
+ env = Environment(XXX = 'x', YYY = 'y')
+ env2 = OverrideEnvironment(env, {'xxx' : 'x2'})
+ env3 = OverrideEnvironment(env2, {'xxx' : 'x3', 'YYY' : 'y3'})
+ assert env['XXX'] == 'x', env['XXX']
+ assert env2['XXX'] == 'x2', env2['XXX']
+ assert env3['XXX'] == 'x3', env3['XXX']
+ assert env['YYY'] == 'y', env['YYY']
+ assert env2['YYY'] == 'y', env2['YYY']
+ assert env3['YYY'] == 'y3', env3['YYY']
+
+ env.Replace(YYY = 'y4')
+
+ assert env['XXX'] == 'x', env['XXX']
+ assert env2['XXX'] == 'x2', env2['XXX']
+ assert env3['XXX'] == 'x3', env3['XXX']
+ assert env['YYY'] == 'y4', env['YYY']
+ assert env2['YYY'] == 'y4', env2['YYY']
+ assert env3['YYY'] == 'y3', env3['YYY']
+
+
+
+
class NoSubstitutionProxyTestCase(unittest.TestCase):
def test___init__(self):
@@ -2680,7 +2872,7 @@ class NoSubstitutionProxyTestCase(unittest.TestCase):
assert x == '$XXX', x
x = proxy.subst('$YYY', raw=7, target=None, source=None,
- dict=None, conv=None,
+ conv=None,
extra_meaningless_keyword_argument=None)
assert x == '$YYY', x
@@ -2714,8 +2906,7 @@ class NoSubstitutionProxyTestCase(unittest.TestCase):
x = proxy.subst_list('$XXX')
assert x == [[]], x
- x = proxy.subst_list('$YYY', raw=0, target=None, source=None,
- dict=None, conv=None)
+ x = proxy.subst_list('$YYY', raw=0, target=None, source=None, conv=None)
assert x == [[]], x
def test_subst_target_source(self):
@@ -2739,7 +2930,8 @@ class NoSubstitutionProxyTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.TestSuite()
- tclasses = [ EnvironmentTestCase,
+ tclasses = [ SubstitutionTestCase,
+ BaseTestCase,
NoSubstitutionProxyTestCase ]
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
index 6a8fe83..1cb449c 100644
--- a/src/engine/SCons/Executor.py
+++ b/src/engine/SCons/Executor.py
@@ -81,9 +81,14 @@ class Executor:
env = self.env or SCons.Defaults.DefaultEnvironment()
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.
+ # Update the overrides with the $TARGET/$SOURCE variables for
+ # this target+source pair, so that evaluations of arbitrary
+ # Python functions have them in the __env__ environment
+ # they're passed. Note that the underlying substitution
+ # functions also override these with their own $TARGET/$SOURCE
+ # expansions, which is *usually* duplicated effort, but covers
+ # a corner case where an Action is called directly from within
+ # a function action with different target and source lists.
self.build_env._update(SCons.Util.subst_dict(self.targets,
self.sources))
return self.build_env
diff --git a/src/engine/SCons/Options/OptionsTests.py b/src/engine/SCons/Options/OptionsTests.py
index 4248dc3..b99aa98 100644
--- a/src/engine/SCons/Options/OptionsTests.py
+++ b/src/engine/SCons/Options/OptionsTests.py
@@ -37,15 +37,13 @@ class Environment:
def __init__(self):
self.dict = {}
def subst(self, x):
- return SCons.Util.scons_subst(x, self)
+ return SCons.Util.scons_subst(x, self, gvars=self.dict)
def __setitem__(self, key, value):
self.dict[key] = value
def __getitem__(self, key):
return self.dict[key]
def has_key(self, key):
return self.dict.has_key(key)
- def Dictionary(self):
- return self.dict
def check(key, value, env):
diff --git a/src/engine/SCons/Scanner/Prog.py b/src/engine/SCons/Scanner/Prog.py
index fa36cad..512e512 100644
--- a/src/engine/SCons/Scanner/Prog.py
+++ b/src/engine/SCons/Scanner/Prog.py
@@ -47,9 +47,8 @@ def scan(node, env, libpath = (), fs = SCons.Node.FS.default_fs):
for libraries specified in the LIBS variable, returning any
files it finds as dependencies.
"""
-
try:
- libs = env.Dictionary('LIBS')
+ libs = env['LIBS']
except KeyError:
# There are no LIBS in this environment, so just return a null list:
return []
@@ -61,14 +60,14 @@ def scan(node, env, libpath = (), fs = SCons.Node.FS.default_fs):
libs = [libs]
try:
- prefix = env.Dictionary('LIBPREFIXES')
+ prefix = env['LIBPREFIXES']
if not SCons.Util.is_List(prefix):
prefix = [ prefix ]
except KeyError:
prefix = [ '' ]
try:
- suffix = env.Dictionary('LIBSUFFIXES')
+ suffix = env['LIBSUFFIXES']
if not SCons.Util.is_List(suffix):
suffix = [ suffix ]
except KeyError:
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 982fdec..25713a8 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -551,7 +551,7 @@ _separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str)
# space characters in the string result from the scons_subst() function.
_space_sep = re.compile(r'[\t ]+(?![^{]*})')
-def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None, conv=None, gvars=None):
+def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
"""Expand a string containing construction variable substitutions.
This is the work-horse function for substitutions in file names
@@ -693,12 +693,22 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=No
else:
return self.expand(args, lvars)
- if dict is None:
- dict = subst_dict(target, source)
if conv is None:
conv = _strconv[mode]
- if gvars is None:
- gvars = env.Dictionary()
+
+ # Doing this every time is a bit of a waste, since the Executor
+ # has typically already populated the OverrideEnvironment with
+ # $TARGET/$SOURCE variables. We're keeping this (for now), though,
+ # because it supports existing behavior that allows us to call
+ # an Action directly with an arbitrary target+source pair, which
+ # we use in Tool/tex.py to handle calling $BIBTEX when necessary.
+ # If we dropped that behavior (or found another way to cover it),
+ # we could get rid of this call completely and just rely on the
+ # Executor setting the variables.
+ d = subst_dict(target, source)
+ if d:
+ lvars = lvars.copy()
+ lvars.update(d)
# We're (most likely) going to eval() things. If Python doesn't
# find a __builtin__ value in the global dictionary used for eval(),
@@ -709,7 +719,7 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=No
gvars['__builtin__'] = __builtin__
ss = StringSubber(env, mode, target, source, conv, gvars)
- result = ss.substitute(strSubst, dict)
+ result = ss.substitute(strSubst, lvars)
try:
del gvars['__builtin__']
@@ -729,7 +739,7 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=No
return result
-def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None, conv=None, gvars=None):
+def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
"""Substitute construction variables in a string (or list or other
object) and separate the arguments into a command list.
@@ -943,12 +953,22 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di
self.add_strip(x)
self.in_strip = None
- if dict is None:
- dict = subst_dict(target, source)
if conv is None:
conv = _strconv[mode]
- if gvars is None:
- gvars = env.Dictionary()
+
+ # Doing this every time is a bit of a waste, since the Executor
+ # has typically already populated the OverrideEnvironment with
+ # $TARGET/$SOURCE variables. We're keeping this (for now), though,
+ # because it supports existing behavior that allows us to call
+ # an Action directly with an arbitrary target+source pair, which
+ # we use in Tool/tex.py to handle calling $BIBTEX when necessary.
+ # If we dropped that behavior (or found another way to cover it),
+ # we could get rid of this call completely and just rely on the
+ # Executor setting the variables.
+ d = subst_dict(target, source)
+ if d:
+ lvars = lvars.copy()
+ lvars.update(d)
# We're (most likely) going to eval() things. If Python doesn't
# find a __builtin__ value in the global dictionary used for eval(),
@@ -959,7 +979,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di
gvars['__builtins__'] = __builtins__
ls = ListSubber(env, mode, target, source, conv, gvars)
- ls.substitute(strSubst, dict, 0)
+ ls.substitute(strSubst, lvars, 0)
try:
del gvars['__builtins__']
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index c28db37..b6b0424 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -327,7 +327,8 @@ class UtilTestCase(unittest.TestCase):
'$CALLABLE', 'callable-1',
]
- kwargs = {'target' : target, 'source' : source}
+ kwargs = {'target' : target, 'source' : source,
+ 'gvars' : env.Dictionary()}
failed = 0
while cases:
@@ -341,6 +342,11 @@ class UtilTestCase(unittest.TestCase):
del cases[:2]
assert failed == 0, "%d subst() cases failed" % failed
+ # The expansion dictionary no longer comes from the construction
+ # environment automatically.
+ s = scons_subst('$AAA', env)
+ assert s == '', s
+
# Tests of the various SUBST_* modes of substitution.
subst_cases = [
"test $xxx",
@@ -396,20 +402,22 @@ class UtilTestCase(unittest.TestCase):
"| | c 1",
]
+ gvars = env.Dictionary()
+
failed = 0
while subst_cases:
input, eraw, ecmd, esig = subst_cases[:4]
- result = scons_subst(input, env, mode=SUBST_RAW)
+ result = scons_subst(input, env, mode=SUBST_RAW, gvars=gvars)
if result != eraw:
if failed == 0: print
print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
failed = failed + 1
- result = scons_subst(input, env, mode=SUBST_CMD)
+ result = scons_subst(input, env, mode=SUBST_CMD, gvars=gvars)
if result != ecmd:
if failed == 0: print
print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
failed = failed + 1
- result = scons_subst(input, env, mode=SUBST_SIG)
+ result = scons_subst(input, env, mode=SUBST_SIG, gvars=gvars)
if result != esig:
if failed == 0: print
print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
@@ -428,8 +436,8 @@ class UtilTestCase(unittest.TestCase):
result = scons_subst("$TARGET $SOURCES", env,
target=[t1, t2],
source=[s1, s2],
- dict={})
- assert result == " ", result
+ gvars={})
+ assert result == "t1 s1 s2", result
result = scons_subst("$TARGET $SOURCES", env, target=[], source=[])
assert result == " ", result
@@ -438,7 +446,8 @@ class UtilTestCase(unittest.TestCase):
# Test interpolating a callable.
newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS",
- env, target=MyNode('t'), source=MyNode('s'))
+ env, target=MyNode('t'), source=MyNode('s'),
+ gvars=gvars)
assert newcom == "test foo baz s t", newcom
# Test that we handle syntax errors during expansion as expected.
@@ -457,25 +466,27 @@ class UtilTestCase(unittest.TestCase):
n1 = MyNode('n1')
env = DummyEnv({'NODE' : n1})
- node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s)
+ gvars = env.Dictionary()
+ node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
assert node is n1, node
- node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s)
+ node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
assert node is n1, node
- node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s)
+ node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
assert node is n1, node
# Test returning a function.
#env = DummyEnv({'FUNCTION' : foo})
- #func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None)
+ #gvars = env.Dictionary()
+ #func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None, gvars=gvars)
#assert func is function_foo, func
- #func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None)
+ #func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None, gvars=gvars)
#assert func is function_foo, func
- #func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None)
+ #func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None, gvars=gvars)
#assert func is function_foo, func
# Test supplying an overriding gvars dictionary.
env = DummyEnv({'XXX' : 'xxx'})
- result = scons_subst('$XXX', env)
+ result = scons_subst('$XXX', env, gvars=env.Dictionary())
assert result == 'xxx', result
result = scons_subst('$XXX', env, gvars={'XXX' : 'yyy'})
assert result == 'yyy', result
@@ -694,7 +705,9 @@ class UtilTestCase(unittest.TestCase):
'$CALLABLE', [['callable-2']],
]
- kwargs = {'target' : target, 'source' : source}
+ gvars = env.Dictionary()
+
+ kwargs = {'target' : target, 'source' : source, 'gvars' : gvars}
failed = 0
while cases:
@@ -708,31 +721,38 @@ class UtilTestCase(unittest.TestCase):
del cases[:2]
assert failed == 0, "%d subst_list() cases failed" % failed
+ # The expansion dictionary no longer comes from the construction
+ # environment automatically.
+ s = scons_subst_list('$AAA', env)
+ assert s == [[]], s
+
t1 = MyNode('t1')
t2 = MyNode('t2')
s1 = MyNode('s1')
s2 = MyNode('s2')
result = scons_subst_list("$TARGET $SOURCES", env,
target=[t1, t2],
- source=[s1, s2])
+ source=[s1, s2],
+ gvars=gvars)
assert result == [['t1', 's1', 's2']], result
result = scons_subst_list("$TARGET $SOURCES", env,
target=[t1, t2],
source=[s1, s2],
- dict={})
- assert result == [[]], result
+ gvars={})
+ assert result == [['t1', 's1', 's2']], result
# Test interpolating a callable.
_t = DummyNode('t')
_s = DummyNode('s')
cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
- env, target=_t, source=_s)
+ env, target=_t, source=_s,
+ gvars=gvars)
assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
# Test escape functionality.
def escape_func(foo):
return '**' + foo + '**'
- cmd_list = scons_subst_list("abc $LITERALS xyz", env)
+ cmd_list = scons_subst_list("abc $LITERALS xyz", env, gvars=gvars)
assert cmd_list == [['abc',
'foo\nwith\nnewlines',
'bar\nwith\nnewlines',
@@ -746,7 +766,7 @@ class UtilTestCase(unittest.TestCase):
c = cmd_list[0][3].escape(escape_func)
assert c == 'xyz', c
- cmd_list = scons_subst_list("abc${LITERALS}xyz", env)
+ cmd_list = scons_subst_list("abc${LITERALS}xyz", env, gvars=gvars)
c = cmd_list[0][0].escape(escape_func)
assert c == '**abcfoo\nwith\nnewlines**', c
c = cmd_list[0][1].escape(escape_func)
@@ -807,23 +827,25 @@ class UtilTestCase(unittest.TestCase):
[["|", "|", "c", "1"]],
]
- r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW)
+ gvars = env.Dictionary()
+
+ r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW, gvars=gvars)
assert r == [[]], r
failed = 0
while subst_list_cases:
input, eraw, ecmd, esig = subst_list_cases[:4]
- result = scons_subst_list(input, env, mode=SUBST_RAW)
+ result = scons_subst_list(input, env, mode=SUBST_RAW, gvars=gvars)
if result != eraw:
if failed == 0: print
print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
failed = failed + 1
- result = scons_subst_list(input, env, mode=SUBST_CMD)
+ result = scons_subst_list(input, env, mode=SUBST_CMD, gvars=gvars)
if result != ecmd:
if failed == 0: print
print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
failed = failed + 1
- result = scons_subst_list(input, env, mode=SUBST_SIG)
+ result = scons_subst_list(input, env, mode=SUBST_SIG, gvars=gvars)
if result != esig:
if failed == 0: print
print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
@@ -847,16 +869,17 @@ class UtilTestCase(unittest.TestCase):
n1 = MyNode('n1')
env = DummyEnv({'NODE' : n1})
- node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s)
+ gvars=env.Dictionary()
+ node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
assert node == [[n1]], node
- node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s)
+ node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
assert node == [[n1]], node
- node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s)
+ node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
assert node == [[n1]], node
# Test supplying an overriding gvars dictionary.
env = DummyEnv({'XXX' : 'xxx'})
- result = scons_subst_list('$XXX', env)
+ result = scons_subst_list('$XXX', env, gvars=env.Dictionary())
assert result == [['xxx']], result
result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'})
assert result == [['yyy']], result
@@ -1290,28 +1313,28 @@ class UtilTestCase(unittest.TestCase):
def test_Literal(self):
"""Test the Literal() function."""
input_list = [ '$FOO', Literal('$BAR') ]
- dummy_env = DummyEnv({ 'FOO' : 'BAZ', 'BAR' : 'BLAT' })
+ gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
def escape_func(cmd):
return '**' + cmd + '**'
- cmd_list = scons_subst_list(input_list, dummy_env)
+ cmd_list = scons_subst_list(input_list, None, gvars=gvars)
cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
def test_SpecialAttrWrapper(self):
"""Test the SpecialAttrWrapper() function."""
input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
- dummy_env = DummyEnv({ 'FOO' : 'BAZ', 'BAR' : 'BLAT' })
+ gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
def escape_func(cmd):
return '**' + cmd + '**'
- cmd_list = scons_subst_list(input_list, dummy_env)
+ cmd_list = scons_subst_list(input_list, None, gvars=gvars)
cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
- cmd_list = scons_subst_list(input_list, dummy_env, mode=SUBST_SIG)
+ cmd_list = scons_subst_list(input_list, None, mode=SUBST_SIG, gvars=gvars)
cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
assert cmd_list == ['BAZ', '**BLEH**'], cmd_list
@@ -1584,10 +1607,10 @@ class UtilTestCase(unittest.TestCase):
cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test")
- newcmd = scons_subst(cmd, env)
+ newcmd = scons_subst(cmd, env, gvars=env.Dictionary())
assert newcmd == 'test foo bar call test', newcmd
- cmd_list = scons_subst_list(cmd, env)
+ cmd_list = scons_subst_list(cmd, env, gvars=env.Dictionary())
assert len(cmd_list) == 1, cmd_list
assert cmd_list[0][0] == "test", cmd_list[0][0]
assert cmd_list[0][1] == "foo", cmd_list[0][1]