summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Environment.py
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 /src/engine/SCons/Environment.py
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.
Diffstat (limited to 'src/engine/SCons/Environment.py')
-rw-r--r--src/engine/SCons/Environment.py279
1 files changed, 160 insertions, 119 deletions
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))