| Index: third_party/scons/scons-local/SCons/Environment.py
|
| ===================================================================
|
| --- third_party/scons/scons-local/SCons/Environment.py (revision 7290)
|
| +++ third_party/scons/scons-local/SCons/Environment.py (working copy)
|
| @@ -1,2252 +1,2253 @@
|
| -"""SCons.Environment
|
| -
|
| -Base class for construction Environments. These are
|
| -the primary objects used to communicate dependency and
|
| -construction information to the build engine.
|
| -
|
| -Keyword arguments supplied when the construction Environment
|
| -is created are construction variables used to initialize the
|
| -Environment
|
| -"""
|
| -
|
| -#
|
| -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
|
| -#
|
| -# Permission is hereby granted, free of charge, to any person obtaining
|
| -# a copy of this software and associated documentation files (the
|
| -# "Software"), to deal in the Software without restriction, including
|
| -# without limitation the rights to use, copy, modify, merge, publish,
|
| -# distribute, sublicense, and/or sell copies of the Software, and to
|
| -# permit persons to whom the Software is furnished to do so, subject to
|
| -# the following conditions:
|
| -#
|
| -# The above copyright notice and this permission notice shall be included
|
| -# in all copies or substantial portions of the Software.
|
| -#
|
| -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
| -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
| -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
| -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
| -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
| -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
| -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| -#
|
| -
|
| -__revision__ = "src/engine/SCons/Environment.py 3603 2008/10/10 05:46:45 scons"
|
| -
|
| -
|
| -import copy
|
| -import os
|
| -import sys
|
| -import re
|
| -import shlex
|
| -import string
|
| -from UserDict import UserDict
|
| -
|
| -import SCons.Action
|
| -import SCons.Builder
|
| -from SCons.Debug import logInstanceCreation
|
| -import SCons.Defaults
|
| -import SCons.Errors
|
| -import SCons.Memoize
|
| -import SCons.Node
|
| -import SCons.Node.Alias
|
| -import SCons.Node.FS
|
| -import SCons.Node.Python
|
| -import SCons.Platform
|
| -import SCons.SConsign
|
| -import SCons.Subst
|
| -import SCons.Tool
|
| -import SCons.Util
|
| -import SCons.Warnings
|
| -
|
| -class _Null:
|
| - pass
|
| -
|
| -_null = _Null
|
| -
|
| -_warn_copy_deprecated = True
|
| -_warn_source_signatures_deprecated = True
|
| -_warn_target_signatures_deprecated = True
|
| -
|
| -CleanTargets = {}
|
| -CalculatorArgs = {}
|
| -
|
| -semi_deepcopy = SCons.Util.semi_deepcopy
|
| -
|
| -# Pull UserError into the global name space for the benefit of
|
| -# Environment().SourceSignatures(), which has some import statements
|
| -# which seem to mess up its ability to reference SCons directly.
|
| -UserError = SCons.Errors.UserError
|
| -
|
| -def alias_builder(env, target, source):
|
| - pass
|
| -
|
| -AliasBuilder = SCons.Builder.Builder(action = alias_builder,
|
| - target_factory = SCons.Node.Alias.default_ans.Alias,
|
| - source_factory = SCons.Node.FS.Entry,
|
| - multi = 1,
|
| - is_explicit = None,
|
| - name='AliasBuilder')
|
| -
|
| -def apply_tools(env, tools, toolpath):
|
| - # Store the toolpath in the Environment.
|
| - if toolpath is not None:
|
| - env['toolpath'] = toolpath
|
| -
|
| - if not tools:
|
| - return
|
| - # Filter out null tools from the list.
|
| - for tool in filter(None, tools):
|
| - if SCons.Util.is_List(tool) or type(tool)==type(()):
|
| - toolname = tool[0]
|
| - toolargs = tool[1] # should be a dict of kw args
|
| - tool = apply(env.Tool, [toolname], toolargs)
|
| - else:
|
| - env.Tool(tool)
|
| -
|
| -# These names are controlled by SCons; users should never set or override
|
| -# them. This warning can optionally be turned off, but scons will still
|
| -# ignore the illegal variable names even if it's off.
|
| -reserved_construction_var_names = \
|
| - ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
|
| -
|
| -def copy_non_reserved_keywords(dict):
|
| - result = semi_deepcopy(dict)
|
| - for k in result.keys():
|
| - if k in reserved_construction_var_names:
|
| - SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
|
| - "Ignoring attempt to set reserved variable `%s'" % k)
|
| - del result[k]
|
| - return result
|
| -
|
| -def _set_reserved(env, key, value):
|
| - msg = "Ignoring attempt to set reserved variable `%s'" % key
|
| - SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg)
|
| -
|
| -def _set_BUILDERS(env, key, value):
|
| - try:
|
| - bd = env._dict[key]
|
| - for k in bd.keys():
|
| - del bd[k]
|
| - except KeyError:
|
| - bd = BuilderDict(kwbd, env)
|
| - env._dict[key] = bd
|
| - bd.update(value)
|
| -
|
| -def _del_SCANNERS(env, key):
|
| - del env._dict[key]
|
| - env.scanner_map_delete()
|
| -
|
| -def _set_SCANNERS(env, key, value):
|
| - env._dict[key] = value
|
| - env.scanner_map_delete()
|
| -
|
| -
|
| -
|
| -# The following is partly based on code in a comment added by Peter
|
| -# Shannon at the following page (there called the "transplant" class):
|
| -#
|
| -# ASPN : Python Cookbook : Dynamically added methods to a class
|
| -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
|
| -#
|
| -# We had independently been using the idiom as BuilderWrapper, but
|
| -# factoring out the common parts into this base class, and making
|
| -# BuilderWrapper a subclass that overrides __call__() to enforce specific
|
| -# Builder calling conventions, simplified some of our higher-layer code.
|
| -
|
| -class MethodWrapper:
|
| - """
|
| - A generic Wrapper class that associates a method (which can
|
| - actually be any callable) with an object. As part of creating this
|
| - MethodWrapper object an attribute with the specified (by default,
|
| - the name of the supplied method) is added to the underlying object.
|
| - When that new "method" is called, our __call__() method adds the
|
| - object as the first argument, simulating the Python behavior of
|
| - supplying "self" on method calls.
|
| -
|
| - We hang on to the name by which the method was added to the underlying
|
| - base class so that we can provide a method to "clone" ourselves onto
|
| - a new underlying object being copied (without which we wouldn't need
|
| - to save that info).
|
| - """
|
| - def __init__(self, object, method, name=None):
|
| - if name is None:
|
| - name = method.__name__
|
| - self.object = object
|
| - self.method = method
|
| - self.name = name
|
| - setattr(self.object, name, self)
|
| -
|
| - def __call__(self, *args, **kwargs):
|
| - nargs = (self.object,) + args
|
| - return apply(self.method, nargs, kwargs)
|
| -
|
| - def clone(self, new_object):
|
| - """
|
| - Returns an object that re-binds the underlying "method" to
|
| - the specified new object.
|
| - """
|
| - return self.__class__(new_object, self.method, self.name)
|
| -
|
| -class BuilderWrapper(MethodWrapper):
|
| - """
|
| - A MethodWrapper subclass that that associates an environment with
|
| - a Builder.
|
| -
|
| - This mainly exists to wrap the __call__() function so that all calls
|
| - to Builders can have their argument lists massaged in the same way
|
| - (treat a lone argument as the source, treat two arguments as target
|
| - then source, make sure both target and source are lists) without
|
| - having to have cut-and-paste code to do it.
|
| -
|
| - As a bit of obsessive backwards compatibility, we also intercept
|
| - attempts to get or set the "env" or "builder" attributes, which were
|
| - the names we used before we put the common functionality into the
|
| - MethodWrapper base class. We'll keep this around for a while in case
|
| - people shipped Tool modules that reached into the wrapper (like the
|
| - Tool/qt.py module does, or did). There shouldn't be a lot attribute
|
| - fetching or setting on these, so a little extra work shouldn't hurt.
|
| - """
|
| - def __call__(self, target=None, source=_null, *args, **kw):
|
| - if source is _null:
|
| - source = target
|
| - target = None
|
| - if not target is None and not SCons.Util.is_List(target):
|
| - target = [target]
|
| - if not source is None and not SCons.Util.is_List(source):
|
| - source = [source]
|
| - return apply(MethodWrapper.__call__, (self, target, source) + args, kw)
|
| -
|
| - def __repr__(self):
|
| - return '<BuilderWrapper %s>' % repr(self.name)
|
| -
|
| - def __str__(self):
|
| - return self.__repr__()
|
| -
|
| - def __getattr__(self, name):
|
| - if name == 'env':
|
| - return self.object
|
| - elif name == 'builder':
|
| - return self.method
|
| - else:
|
| - raise AttributeError, name
|
| -
|
| - def __setattr__(self, name, value):
|
| - if name == 'env':
|
| - self.object = value
|
| - elif name == 'builder':
|
| - self.method = value
|
| - else:
|
| - self.__dict__[name] = value
|
| -
|
| - # This allows a Builder to be executed directly
|
| - # through the Environment to which it's attached.
|
| - # In practice, we shouldn't need this, because
|
| - # builders actually get executed through a Node.
|
| - # But we do have a unit test for this, and can't
|
| - # yet rule out that it would be useful in the
|
| - # future, so leave it for now.
|
| - #def execute(self, **kw):
|
| - # kw['env'] = self.env
|
| - # apply(self.builder.execute, (), kw)
|
| -
|
| -class BuilderDict(UserDict):
|
| - """This is a dictionary-like class used by an Environment to hold
|
| - the Builders. We need to do this because every time someone changes
|
| - the Builders in the Environment's BUILDERS dictionary, we must
|
| - update the Environment's attributes."""
|
| - def __init__(self, dict, env):
|
| - # Set self.env before calling the superclass initialization,
|
| - # because it will end up calling our other methods, which will
|
| - # need to point the values in this dictionary to self.env.
|
| - self.env = env
|
| - UserDict.__init__(self, dict)
|
| -
|
| - def __semi_deepcopy__(self):
|
| - return self.__class__(self.data, self.env)
|
| -
|
| - def __setitem__(self, item, val):
|
| - try:
|
| - method = getattr(self.env, item).method
|
| - except AttributeError:
|
| - pass
|
| - else:
|
| - self.env.RemoveMethod(method)
|
| - UserDict.__setitem__(self, item, val)
|
| - BuilderWrapper(self.env, val, item)
|
| -
|
| - def __delitem__(self, item):
|
| - UserDict.__delitem__(self, item)
|
| - delattr(self.env, item)
|
| -
|
| - def update(self, dict):
|
| - for i, v in dict.items():
|
| - self.__setitem__(i, v)
|
| -
|
| -
|
| -
|
| -_is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
|
| -
|
| -def is_valid_construction_var(varstr):
|
| - """Return if the specified string is a legitimate construction
|
| - variable.
|
| - """
|
| - return _is_valid_var.match(varstr)
|
| -
|
| -
|
| -
|
| -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.)
|
| - """
|
| -
|
| - if SCons.Memoize.use_memoizer:
|
| - __metaclass__ = SCons.Memoize.Memoized_Metaclass
|
| -
|
| - def __init__(self, **kw):
|
| - """Initialization of an underlying SubstitutionEnvironment class.
|
| - """
|
| - if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
|
| - self.fs = SCons.Node.FS.get_default_fs()
|
| - self.ans = SCons.Node.Alias.default_ans
|
| - self.lookup_list = SCons.Node.arg2nodes_lookups
|
| - self._dict = kw.copy()
|
| - self._init_special()
|
| - self.added_methods = []
|
| - #self._memo = {}
|
| -
|
| - def _init_special(self):
|
| - """Initial the dispatch tables for special handling of
|
| - special construction variables."""
|
| - self._special_del = {}
|
| - self._special_del['SCANNERS'] = _del_SCANNERS
|
| -
|
| - self._special_set = {}
|
| - for key in reserved_construction_var_names:
|
| - self._special_set[key] = _set_reserved
|
| - self._special_set['BUILDERS'] = _set_BUILDERS
|
| - self._special_set['SCANNERS'] = _set_SCANNERS
|
| -
|
| - # Freeze the keys of self._special_set in a list for use by
|
| - # methods that need to check. (Empirically, list scanning has
|
| - # gotten better than dict.has_key() in Python 2.5.)
|
| - self._special_set_keys = self._special_set.keys()
|
| -
|
| - def __cmp__(self, other):
|
| - return cmp(self._dict, other._dict)
|
| -
|
| - def __delitem__(self, key):
|
| - special = self._special_del.get(key)
|
| - if special:
|
| - special(self, key)
|
| - else:
|
| - del self._dict[key]
|
| -
|
| - def __getitem__(self, key):
|
| - return self._dict[key]
|
| -
|
| - def __setitem__(self, key, value):
|
| - # This is heavily used. This implementation is the best we have
|
| - # according to the timings in bench/env.__setitem__.py.
|
| - #
|
| - # The "key in self._special_set_keys" test here seems to perform
|
| - # pretty well for the number of keys we have. A hard-coded
|
| - # list works a little better in Python 2.5, but that has the
|
| - # disadvantage of maybe getting out of sync if we ever add more
|
| - # variable names. Using self._special_set.has_key() works a
|
| - # little better in Python 2.4, but is worse then this test.
|
| - # So right now it seems like a good trade-off, but feel free to
|
| - # revisit this with bench/env.__setitem__.py as needed (and
|
| - # as newer versions of Python come out).
|
| - if key in self._special_set_keys:
|
| - self._special_set[key](self, key, value)
|
| - else:
|
| - # If we already have the entry, then it's obviously a valid
|
| - # key and we don't need to check. If we do check, using a
|
| - # global, pre-compiled regular expression directly is more
|
| - # efficient than calling another function or a method.
|
| - if not self._dict.has_key(key) \
|
| - and not _is_valid_var.match(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):
|
| - return self._dict.items()
|
| -
|
| - def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
|
| - if node_factory is _null:
|
| - node_factory = self.fs.File
|
| - if lookup_list is _null:
|
| - lookup_list = self.lookup_list
|
| -
|
| - if not args:
|
| - return []
|
| -
|
| - args = SCons.Util.flatten(args)
|
| -
|
| - nodes = []
|
| - for v in args:
|
| - if SCons.Util.is_String(v):
|
| - n = None
|
| - for l in lookup_list:
|
| - n = l(v)
|
| - if not n is None:
|
| - break
|
| - if not n is None:
|
| - if SCons.Util.is_String(n):
|
| - # n = self.subst(n, raw=1, **kw)
|
| - kw['raw'] = 1
|
| - n = apply(self.subst, (n,), kw)
|
| - if node_factory:
|
| - n = node_factory(n)
|
| - if SCons.Util.is_List(n):
|
| - nodes.extend(n)
|
| - else:
|
| - nodes.append(n)
|
| - elif node_factory:
|
| - # v = node_factory(self.subst(v, raw=1, **kw))
|
| - kw['raw'] = 1
|
| - v = node_factory(apply(self.subst, (v,), kw))
|
| - if SCons.Util.is_List(v):
|
| - nodes.extend(v)
|
| - else:
|
| - nodes.append(v)
|
| - else:
|
| - nodes.append(v)
|
| -
|
| - return nodes
|
| -
|
| - 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
|
| - in the string and begin with an initial underscore or
|
| - alphabetic character followed by any number of underscores
|
| - or alphanumeric characters. The construction variable names
|
| - may be surrounded by curly braces to separate the name from
|
| - trailing characters.
|
| - """
|
| - gvars = self.gvars()
|
| - lvars = self.lvars()
|
| - lvars['__env__'] = self
|
| - return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
|
| -
|
| - 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)
|
| - if SCons.Util.is_String(v):
|
| - v = self.subst(v, raw, target, source)
|
| - nkw[k] = v
|
| - return nkw
|
| -
|
| - def subst_list(self, string, raw=0, target=None, source=None, conv=None):
|
| - """Calls through to SCons.Subst.scons_subst_list(). See
|
| - the documentation for that function."""
|
| - gvars = self.gvars()
|
| - lvars = self.lvars()
|
| - lvars['__env__'] = self
|
| - return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
|
| -
|
| - def subst_path(self, path, target=None, source=None):
|
| - """Substitute a path list, turning EntryProxies into Nodes
|
| - and leaving Nodes (and other objects) as-is."""
|
| -
|
| - if not SCons.Util.is_List(path):
|
| - path = [path]
|
| -
|
| - def s(obj):
|
| - """This is the "string conversion" routine that we have our
|
| - substitutions use to return Nodes, not strings. This relies
|
| - on the fact that an EntryProxy object has a get() method that
|
| - returns the underlying Node that it wraps, which is a bit of
|
| - architectural dependence that we might need to break or modify
|
| - in the future in response to additional requirements."""
|
| - try:
|
| - get = obj.get
|
| - except AttributeError:
|
| - obj = SCons.Util.to_String_for_subst(obj)
|
| - else:
|
| - obj = get()
|
| - return obj
|
| -
|
| - r = []
|
| - for p in path:
|
| - if SCons.Util.is_String(p):
|
| - p = self.subst(p, target=target, source=source, conv=s)
|
| - if SCons.Util.is_List(p):
|
| - if len(p) == 1:
|
| - p = p[0]
|
| - else:
|
| - # We have an object plus a string, or multiple
|
| - # objects that we need to smush together. No choice
|
| - # but to make them into a string.
|
| - p = string.join(map(SCons.Util.to_String_for_subst, p), '')
|
| - else:
|
| - p = s(p)
|
| - r.append(p)
|
| - return r
|
| -
|
| - subst_target_source = subst
|
| -
|
| - def backtick(self, command):
|
| - import subprocess
|
| - # common arguments
|
| - kw = { 'stdout' : subprocess.PIPE,
|
| - 'stderr' : subprocess.PIPE,
|
| - 'universal_newlines' : True,
|
| - }
|
| - # if the command is a list, assume it's been quoted
|
| - # othewise force a shell
|
| - if not SCons.Util.is_List(command): kw['shell'] = True
|
| - # run constructed command
|
| - #TODO(1.5) p = SCons.Action._subproc(self, command, **kw)
|
| - p = apply(SCons.Action._subproc, (self, command), kw)
|
| - out,err = p.communicate()
|
| - status = p.wait()
|
| - if err:
|
| - sys.stderr.write(err)
|
| - if status:
|
| - raise OSError("'%s' exited %d" % (command, status))
|
| - return out
|
| -
|
| - def AddMethod(self, function, name=None):
|
| - """
|
| - Adds the specified function as a method of this construction
|
| - environment with the specified name. If the name is omitted,
|
| - the default name is the name of the function itself.
|
| - """
|
| - method = MethodWrapper(self, function, name)
|
| - self.added_methods.append(method)
|
| -
|
| - def RemoveMethod(self, function):
|
| - """
|
| - Removes the specified function's MethodWrapper from the
|
| - added_methods list, so we don't re-bind it when making a clone.
|
| - """
|
| - is_not_func = lambda dm, f=function: not dm.method is f
|
| - self.added_methods = filter(is_not_func, self.added_methods)
|
| -
|
| - 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 Clone() 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 not overrides: return self
|
| - o = copy_non_reserved_keywords(overrides)
|
| - if not o: return self
|
| - overrides = {}
|
| - merges = None
|
| - for key, value in o.items():
|
| - if key == 'parse_flags':
|
| - merges = value
|
| - else:
|
| - overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
|
| - env = OverrideEnvironment(self, overrides)
|
| - if merges: env.MergeFlags(merges)
|
| - return env
|
| -
|
| - def ParseFlags(self, *flags):
|
| - """
|
| - Parse the set of flags and return a dict with the flags placed
|
| - in the appropriate entry. The flags are treated as a typical
|
| - set of command-line flags for a GNU-like toolchain and used to
|
| - populate the entries in the dict immediately below. If one of
|
| - the flag strings begins with a bang (exclamation mark), it is
|
| - assumed to be a command and the rest of the string is executed;
|
| - the result of that evaluation is then added to the dict.
|
| - """
|
| - dict = {
|
| - 'ASFLAGS' : SCons.Util.CLVar(''),
|
| - 'CFLAGS' : SCons.Util.CLVar(''),
|
| - 'CCFLAGS' : SCons.Util.CLVar(''),
|
| - 'CPPDEFINES' : [],
|
| - 'CPPFLAGS' : SCons.Util.CLVar(''),
|
| - 'CPPPATH' : [],
|
| - 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
|
| - 'FRAMEWORKS' : SCons.Util.CLVar(''),
|
| - 'LIBPATH' : [],
|
| - 'LIBS' : [],
|
| - 'LINKFLAGS' : SCons.Util.CLVar(''),
|
| - 'RPATH' : [],
|
| - }
|
| -
|
| - # The use of the "me" parameter to provide our own name for
|
| - # recursion is an egregious hack to support Python 2.1 and before.
|
| - def do_parse(arg, me, self = self, dict = dict):
|
| - # if arg is a sequence, recurse with each element
|
| - if not arg:
|
| - return
|
| -
|
| - if not SCons.Util.is_String(arg):
|
| - for t in arg: me(t, me)
|
| - return
|
| -
|
| - # if arg is a command, execute it
|
| - if arg[0] == '!':
|
| - arg = self.backtick(arg[1:])
|
| -
|
| - # utility function to deal with -D option
|
| - def append_define(name, dict = dict):
|
| - t = string.split(name, '=')
|
| - if len(t) == 1:
|
| - dict['CPPDEFINES'].append(name)
|
| - else:
|
| - dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
|
| -
|
| - # Loop through the flags and add them to the appropriate option.
|
| - # This tries to strike a balance between checking for all possible
|
| - # flags and keeping the logic to a finite size, so it doesn't
|
| - # check for some that don't occur often. It particular, if the
|
| - # flag is not known to occur in a config script and there's a way
|
| - # of passing the flag to the right place (by wrapping it in a -W
|
| - # flag, for example) we don't check for it. Note that most
|
| - # preprocessor options are not handled, since unhandled options
|
| - # are placed in CCFLAGS, so unless the preprocessor is invoked
|
| - # separately, these flags will still get to the preprocessor.
|
| - # Other options not currently handled:
|
| - # -iqoutedir (preprocessor search path)
|
| - # -u symbol (linker undefined symbol)
|
| - # -s (linker strip files)
|
| - # -static* (linker static binding)
|
| - # -shared* (linker dynamic binding)
|
| - # -symbolic (linker global binding)
|
| - # -R dir (deprecated linker rpath)
|
| - # IBM compilers may also accept -qframeworkdir=foo
|
| -
|
| - params = shlex.split(arg)
|
| - append_next_arg_to = None # for multi-word args
|
| - for arg in params:
|
| - if append_next_arg_to:
|
| - if append_next_arg_to == 'CPPDEFINES':
|
| - append_define(arg)
|
| - elif append_next_arg_to == '-include':
|
| - t = ('-include', self.fs.File(arg))
|
| - dict['CCFLAGS'].append(t)
|
| - elif append_next_arg_to == '-isysroot':
|
| - t = ('-isysroot', arg)
|
| - dict['CCFLAGS'].append(t)
|
| - dict['LINKFLAGS'].append(t)
|
| - elif append_next_arg_to == '-arch':
|
| - t = ('-arch', arg)
|
| - dict['CCFLAGS'].append(t)
|
| - dict['LINKFLAGS'].append(t)
|
| - else:
|
| - dict[append_next_arg_to].append(arg)
|
| - append_next_arg_to = None
|
| - elif not arg[0] in ['-', '+']:
|
| - dict['LIBS'].append(self.fs.File(arg))
|
| - elif arg[:2] == '-L':
|
| - if arg[2:]:
|
| - dict['LIBPATH'].append(arg[2:])
|
| - else:
|
| - append_next_arg_to = 'LIBPATH'
|
| - elif arg[:2] == '-l':
|
| - if arg[2:]:
|
| - dict['LIBS'].append(arg[2:])
|
| - else:
|
| - append_next_arg_to = 'LIBS'
|
| - elif arg[:2] == '-I':
|
| - if arg[2:]:
|
| - dict['CPPPATH'].append(arg[2:])
|
| - else:
|
| - append_next_arg_to = 'CPPPATH'
|
| - elif arg[:4] == '-Wa,':
|
| - dict['ASFLAGS'].append(arg[4:])
|
| - dict['CCFLAGS'].append(arg)
|
| - elif arg[:4] == '-Wl,':
|
| - if arg[:11] == '-Wl,-rpath=':
|
| - dict['RPATH'].append(arg[11:])
|
| - elif arg[:7] == '-Wl,-R,':
|
| - dict['RPATH'].append(arg[7:])
|
| - elif arg[:6] == '-Wl,-R':
|
| - dict['RPATH'].append(arg[6:])
|
| - else:
|
| - dict['LINKFLAGS'].append(arg)
|
| - elif arg[:4] == '-Wp,':
|
| - dict['CPPFLAGS'].append(arg)
|
| - elif arg[:2] == '-D':
|
| - if arg[2:]:
|
| - append_define(arg[2:])
|
| - else:
|
| - append_next_arg_to = 'CPPDEFINES'
|
| - elif arg == '-framework':
|
| - append_next_arg_to = 'FRAMEWORKS'
|
| - elif arg[:14] == '-frameworkdir=':
|
| - dict['FRAMEWORKPATH'].append(arg[14:])
|
| - elif arg[:2] == '-F':
|
| - if arg[2:]:
|
| - dict['FRAMEWORKPATH'].append(arg[2:])
|
| - else:
|
| - append_next_arg_to = 'FRAMEWORKPATH'
|
| - elif arg == '-mno-cygwin':
|
| - dict['CCFLAGS'].append(arg)
|
| - dict['LINKFLAGS'].append(arg)
|
| - elif arg == '-mwindows':
|
| - dict['LINKFLAGS'].append(arg)
|
| - elif arg == '-pthread':
|
| - dict['CCFLAGS'].append(arg)
|
| - dict['LINKFLAGS'].append(arg)
|
| - elif arg[:5] == '-std=':
|
| - dict['CFLAGS'].append(arg) # C only
|
| - elif arg[0] == '+':
|
| - dict['CCFLAGS'].append(arg)
|
| - dict['LINKFLAGS'].append(arg)
|
| - elif arg in ['-include', '-isysroot', '-arch']:
|
| - append_next_arg_to = arg
|
| - else:
|
| - dict['CCFLAGS'].append(arg)
|
| -
|
| - for arg in flags:
|
| - do_parse(arg, do_parse)
|
| - return dict
|
| -
|
| - def MergeFlags(self, args, unique=1, dict=None):
|
| - """
|
| - Merge the dict in args into the construction variables of this
|
| - env, or the passed-in dict. If args is not a dict, it is
|
| - converted into a dict using ParseFlags. If unique is not set,
|
| - the flags are appended rather than merged.
|
| - """
|
| -
|
| - if dict is None:
|
| - dict = self
|
| - if not SCons.Util.is_Dict(args):
|
| - args = self.ParseFlags(args)
|
| - if not unique:
|
| - apply(self.Append, (), args)
|
| - return self
|
| - for key, value in args.items():
|
| - if not value:
|
| - continue
|
| - try:
|
| - orig = self[key]
|
| - except KeyError:
|
| - orig = value
|
| - else:
|
| - if not orig:
|
| - orig = value
|
| - elif value:
|
| - # Add orig and value. The logic here was lifted from
|
| - # part of env.Append() (see there for a lot of comments
|
| - # about the order in which things are tried) and is
|
| - # used mainly to handle coercion of strings to CLVar to
|
| - # "do the right thing" given (e.g.) an original CCFLAGS
|
| - # string variable like '-pipe -Wall'.
|
| - try:
|
| - orig = orig + value
|
| - except (KeyError, TypeError):
|
| - try:
|
| - add_to_orig = orig.append
|
| - except AttributeError:
|
| - value.insert(0, orig)
|
| - orig = value
|
| - else:
|
| - add_to_orig(value)
|
| - t = []
|
| - if key[-4:] == 'PATH':
|
| - ### keep left-most occurence
|
| - for v in orig:
|
| - if v not in t:
|
| - t.append(v)
|
| - else:
|
| - ### keep right-most occurence
|
| - orig.reverse()
|
| - for v in orig:
|
| - if v not in t:
|
| - t.insert(0, v)
|
| - self[key] = t
|
| - return self
|
| -
|
| -# def MergeShellPaths(self, args, prepend=1):
|
| -# """
|
| -# Merge the dict in args into the shell environment in env['ENV'].
|
| -# Shell path elements are appended or prepended according to prepend.
|
| -
|
| -# Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
|
| -
|
| -# Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'})
|
| -# prepends /usr/local/lib to env['ENV']['LIBPATH'].
|
| -# """
|
| -
|
| -# for pathname, pathval in args.items():
|
| -# if not pathval:
|
| -# continue
|
| -# if prepend:
|
| -# apply(self.PrependENVPath, (pathname, pathval))
|
| -# else:
|
| -# apply(self.AppendENVPath, (pathname, pathval))
|
| -
|
| -
|
| -# Used by the FindSourceFiles() method, below.
|
| -# Stuck here for support of pre-2.2 Python versions.
|
| -def build_source(ss, result):
|
| - for s in ss:
|
| - if isinstance(s, SCons.Node.FS.Dir):
|
| - build_source(s.all_children(), result)
|
| - elif s.has_builder():
|
| - build_source(s.sources, result)
|
| - elif isinstance(s.disambiguate(), SCons.Node.FS.File):
|
| - result.append(s)
|
| -
|
| -def default_decide_source(dependency, target, prev_ni):
|
| - f = SCons.Defaults.DefaultEnvironment().decide_source
|
| - return f(dependency, target, prev_ni)
|
| -
|
| -def default_decide_target(dependency, target, prev_ni):
|
| - f = SCons.Defaults.DefaultEnvironment().decide_target
|
| - return f(dependency, target, prev_ni)
|
| -
|
| -def default_copy_from_cache(src, dst):
|
| - f = SCons.Defaults.DefaultEnvironment().copy_from_cache
|
| - return f(src, dst)
|
| -
|
| -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.
|
| -
|
| - Keyword arguments supplied when the construction Environment
|
| - is created are construction variables used to initialize the
|
| - Environment.
|
| - """
|
| -
|
| - if SCons.Memoize.use_memoizer:
|
| - __metaclass__ = SCons.Memoize.Memoized_Metaclass
|
| -
|
| - memoizer_counters = []
|
| -
|
| - #######################################################################
|
| - # This is THE class for interacting with the SCons build engine,
|
| - # and it contains a lot of stuff, so we're going to try to keep this
|
| - # a little organized by grouping the methods.
|
| - #######################################################################
|
| -
|
| - #######################################################################
|
| - # Methods that make an Environment act like a dictionary. These have
|
| - # the expected standard names for Python mapping objects. Note that
|
| - # we don't actually make an Environment a subclass of UserDict for
|
| - # performance reasons. Note also that we only supply methods for
|
| - # dictionary functionality that we actually need and use.
|
| - #######################################################################
|
| -
|
| - def __init__(self,
|
| - platform=None,
|
| - tools=None,
|
| - toolpath=None,
|
| - variables=None,
|
| - parse_flags = 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, 'Environment.Base')
|
| - self._memo = {}
|
| - self.fs = SCons.Node.FS.get_default_fs()
|
| - self.ans = SCons.Node.Alias.default_ans
|
| - self.lookup_list = SCons.Node.arg2nodes_lookups
|
| - self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
|
| - self._init_special()
|
| - self.added_methods = []
|
| -
|
| - # We don't use AddMethod, or define these as methods in this
|
| - # class, because we *don't* want these functions to be bound
|
| - # methods. They need to operate independently so that the
|
| - # settings will work properly regardless of whether a given
|
| - # target ends up being built with a Base environment or an
|
| - # OverrideEnvironment or what have you.
|
| - self.decide_target = default_decide_target
|
| - self.decide_source = default_decide_source
|
| -
|
| - self.copy_from_cache = default_copy_from_cache
|
| -
|
| - self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
|
| -
|
| - if platform is None:
|
| - platform = self._dict.get('PLATFORM', None)
|
| - if platform is None:
|
| - platform = SCons.Platform.Platform()
|
| - if SCons.Util.is_String(platform):
|
| - platform = SCons.Platform.Platform(platform)
|
| - self._dict['PLATFORM'] = str(platform)
|
| - platform(self)
|
| -
|
| - # Apply the passed-in and customizable variables to the
|
| - # environment before calling the tools, because they may use
|
| - # some of them during initialization.
|
| - if kw.has_key('options'):
|
| - # Backwards compatibility: they may stll be using the
|
| - # old "options" keyword.
|
| - variables = kw['options']
|
| - del kw['options']
|
| - apply(self.Replace, (), kw)
|
| - keys = kw.keys()
|
| - if variables:
|
| - keys = keys + variables.keys()
|
| - variables.Update(self)
|
| -
|
| - save = {}
|
| - for k in keys:
|
| - try:
|
| - save[k] = self._dict[k]
|
| - except KeyError:
|
| - # No value may have been set if they tried to pass in a
|
| - # reserved variable name like TARGETS.
|
| - pass
|
| -
|
| - SCons.Tool.Initializers(self)
|
| -
|
| - if tools is None:
|
| - tools = self._dict.get('TOOLS', None)
|
| - if tools is None:
|
| - tools = ['default']
|
| - apply_tools(self, tools, toolpath)
|
| -
|
| - # Now restore the passed-in and customized variables
|
| - # to the environment, since the values the user set explicitly
|
| - # should override any values set by the tools.
|
| - for key, val in save.items():
|
| - self._dict[key] = val
|
| -
|
| - # Finally, apply any flags to be merged in
|
| - if parse_flags: self.MergeFlags(parse_flags)
|
| -
|
| - #######################################################################
|
| - # Utility methods that are primarily for internal use by SCons.
|
| - # These begin with lower-case letters.
|
| - #######################################################################
|
| -
|
| - def get_builder(self, name):
|
| - """Fetch the builder with the specified name from the environment.
|
| - """
|
| - try:
|
| - return self._dict['BUILDERS'][name]
|
| - except KeyError:
|
| - return None
|
| -
|
| - def get_CacheDir(self):
|
| - try:
|
| - path = self._CacheDir_path
|
| - except AttributeError:
|
| - path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
|
| - try:
|
| - if path == self._last_CacheDir_path:
|
| - return self._last_CacheDir
|
| - except AttributeError:
|
| - pass
|
| - cd = SCons.CacheDir.CacheDir(path)
|
| - self._last_CacheDir_path = path
|
| - self._last_CacheDir = cd
|
| - return cd
|
| -
|
| - def get_factory(self, factory, default='File'):
|
| - """Return a factory function for creating Nodes for this
|
| - construction environment.
|
| - """
|
| - name = default
|
| - try:
|
| - is_node = issubclass(factory, SCons.Node.Node)
|
| - except TypeError:
|
| - # The specified factory isn't a Node itself--it's
|
| - # most likely None, or possibly a callable.
|
| - pass
|
| - else:
|
| - if is_node:
|
| - # The specified factory is a Node (sub)class. Try to
|
| - # return the FS method that corresponds to the Node's
|
| - # name--that is, we return self.fs.Dir if they want a Dir,
|
| - # self.fs.File for a File, etc.
|
| - try: name = factory.__name__
|
| - except AttributeError: pass
|
| - else: factory = None
|
| - if not factory:
|
| - # They passed us None, or we picked up a name from a specified
|
| - # class, so return the FS method. (Note that we *don't*
|
| - # use our own self.{Dir,File} methods because that would
|
| - # cause env.subst() to be called twice on the file name,
|
| - # interfering with files that have $$ in them.)
|
| - factory = getattr(self.fs, name)
|
| - return factory
|
| -
|
| - memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
|
| -
|
| - def _gsm(self):
|
| - try:
|
| - return self._memo['_gsm']
|
| - except KeyError:
|
| - pass
|
| -
|
| - result = {}
|
| -
|
| - try:
|
| - scanners = self._dict['SCANNERS']
|
| - except KeyError:
|
| - pass
|
| - else:
|
| - # Reverse the scanner list so that, if multiple scanners
|
| - # claim they can scan the same suffix, earlier scanners
|
| - # in the list will overwrite later scanners, so that
|
| - # the result looks like a "first match" to the user.
|
| - if not SCons.Util.is_List(scanners):
|
| - scanners = [scanners]
|
| - else:
|
| - scanners = scanners[:] # copy so reverse() doesn't mod original
|
| - scanners.reverse()
|
| - for scanner in scanners:
|
| - for k in scanner.get_skeys(self):
|
| - result[k] = scanner
|
| -
|
| - self._memo['_gsm'] = result
|
| -
|
| - return result
|
| -
|
| - def get_scanner(self, skey):
|
| - """Find the appropriate scanner given a key (usually a file suffix).
|
| - """
|
| - return self._gsm().get(skey)
|
| -
|
| - def scanner_map_delete(self, kw=None):
|
| - """Delete the cached scanner map (if we need to).
|
| - """
|
| - try:
|
| - del self._memo['_gsm']
|
| - except KeyError:
|
| - pass
|
| -
|
| - def _update(self, dict):
|
| - """Update an environment's values directly, bypassing the normal
|
| - checks that occur when users try to set items.
|
| - """
|
| - self._dict.update(dict)
|
| -
|
| - def get_src_sig_type(self):
|
| - try:
|
| - return self.src_sig_type
|
| - except AttributeError:
|
| - t = SCons.Defaults.DefaultEnvironment().src_sig_type
|
| - self.src_sig_type = t
|
| - return t
|
| -
|
| - def get_tgt_sig_type(self):
|
| - try:
|
| - return self.tgt_sig_type
|
| - except AttributeError:
|
| - t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
|
| - self.tgt_sig_type = t
|
| - return t
|
| -
|
| - #######################################################################
|
| - # Public methods for manipulating an Environment. These begin with
|
| - # upper-case letters. The essential characteristic of methods in
|
| - # this section is that they do *not* have corresponding same-named
|
| - # global functions. For example, a stand-alone Append() function
|
| - # makes no sense, because Append() is all about appending values to
|
| - # an Environment's construction variables.
|
| - #######################################################################
|
| -
|
| - def Append(self, **kw):
|
| - """Append values to existing construction variables
|
| - in an Environment.
|
| - """
|
| - kw = copy_non_reserved_keywords(kw)
|
| - for key, val in kw.items():
|
| - # It would be easier on the eyes to write this using
|
| - # "continue" statements whenever we finish processing an item,
|
| - # but Python 1.5.2 apparently doesn't let you use "continue"
|
| - # within try:-except: blocks, so we have to nest our code.
|
| - try:
|
| - orig = self._dict[key]
|
| - except KeyError:
|
| - # No existing variable in the environment, so just set
|
| - # it to the new value.
|
| - self._dict[key] = val
|
| - else:
|
| - try:
|
| - # Check if the original looks like a dictionary.
|
| - # If it is, we can't just try adding the value because
|
| - # dictionaries don't have __add__() methods, and
|
| - # things like UserList will incorrectly coerce the
|
| - # original dict to a list (which we don't want).
|
| - update_dict = orig.update
|
| - except AttributeError:
|
| - try:
|
| - # Most straightforward: just try to add them
|
| - # together. This will work in most cases, when the
|
| - # original and new values are of compatible types.
|
| - self._dict[key] = orig + val
|
| - except (KeyError, TypeError):
|
| - try:
|
| - # Check if the original is a list.
|
| - add_to_orig = orig.append
|
| - except AttributeError:
|
| - # The original isn't a list, but the new
|
| - # value is (by process of elimination),
|
| - # so insert the original in the new value
|
| - # (if there's one to insert) and replace
|
| - # the variable with it.
|
| - if orig:
|
| - val.insert(0, orig)
|
| - self._dict[key] = val
|
| - else:
|
| - # The original is a list, so append the new
|
| - # value to it (if there's a value to append).
|
| - if val:
|
| - add_to_orig(val)
|
| - else:
|
| - # The original looks like a dictionary, so update it
|
| - # based on what we think the value looks like.
|
| - if SCons.Util.is_List(val):
|
| - for v in val:
|
| - orig[v] = None
|
| - else:
|
| - try:
|
| - update_dict(val)
|
| - except (AttributeError, TypeError, ValueError):
|
| - if SCons.Util.is_Dict(val):
|
| - for k, v in val.items():
|
| - orig[k] = v
|
| - else:
|
| - orig[val] = None
|
| - self.scanner_map_delete(kw)
|
| -
|
| - def AppendENVPath(self, name, newpath, envname = 'ENV',
|
| - sep = os.pathsep, delete_existing=1):
|
| - """Append path elements to the path 'name' in the 'ENV'
|
| - dictionary for this environment. Will only add any particular
|
| - path once, and will normpath and normcase all paths to help
|
| - assure this. This can also handle the case where the env
|
| - variable is a list instead of a string.
|
| -
|
| - If delete_existing is 0, a newpath which is already in the path
|
| - will not be moved to the end (it will be left where it is).
|
| - """
|
| -
|
| - orig = ''
|
| - if self._dict.has_key(envname) and self._dict[envname].has_key(name):
|
| - orig = self._dict[envname][name]
|
| -
|
| - nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing)
|
| -
|
| - if not self._dict.has_key(envname):
|
| - self._dict[envname] = {}
|
| -
|
| - self._dict[envname][name] = nv
|
| -
|
| - def AppendUnique(self, delete_existing=0, **kw):
|
| - """Append values to existing construction variables
|
| - in an Environment, if they're not already there.
|
| - If delete_existing is 1, removes existing values first, so
|
| - values move to end.
|
| - """
|
| - kw = copy_non_reserved_keywords(kw)
|
| - for key, val in kw.items():
|
| - if not self._dict.has_key(key) or self._dict[key] in ('', None):
|
| - self._dict[key] = val
|
| - elif SCons.Util.is_Dict(self._dict[key]) and \
|
| - SCons.Util.is_Dict(val):
|
| - self._dict[key].update(val)
|
| - elif SCons.Util.is_List(val):
|
| - dk = self._dict[key]
|
| - if not SCons.Util.is_List(dk):
|
| - dk = [dk]
|
| - if delete_existing:
|
| - dk = filter(lambda x, val=val: x not in val, dk)
|
| - else:
|
| - val = filter(lambda x, dk=dk: x not in dk, val)
|
| - self._dict[key] = dk + val
|
| - else:
|
| - dk = self._dict[key]
|
| - if SCons.Util.is_List(dk):
|
| - # By elimination, val is not a list. Since dk is a
|
| - # list, wrap val in a list first.
|
| - if delete_existing:
|
| - dk = filter(lambda x, val=val: x not in val, dk)
|
| - self._dict[key] = dk + [val]
|
| - else:
|
| - if not val in dk:
|
| - self._dict[key] = dk + [val]
|
| - else:
|
| - if delete_existing:
|
| - dk = filter(lambda x, val=val: x not in val, dk)
|
| - self._dict[key] = dk + val
|
| - self.scanner_map_delete(kw)
|
| -
|
| - def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
|
| - """Return a copy of a construction Environment. The
|
| - copy is like a Python "deep copy"--that is, independent
|
| - copies are made recursively of each objects--except that
|
| - a reference is copied when an object is not deep-copyable
|
| - (like a function). There are no references to any mutable
|
| - objects in the original Environment.
|
| - """
|
| - clone = copy.copy(self)
|
| - clone._dict = semi_deepcopy(self._dict)
|
| -
|
| - try:
|
| - cbd = clone._dict['BUILDERS']
|
| - except KeyError:
|
| - pass
|
| - else:
|
| - clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
|
| -
|
| - # Check the methods added via AddMethod() and re-bind them to
|
| - # the cloned environment. Only do this if the attribute hasn't
|
| - # been overwritten by the user explicitly and still points to
|
| - # the added method.
|
| - clone.added_methods = []
|
| - for mw in self.added_methods:
|
| - if mw == getattr(self, mw.name):
|
| - clone.added_methods.append(mw.clone(clone))
|
| -
|
| - clone._memo = {}
|
| -
|
| - # Apply passed-in variables before the tools
|
| - # so the tools can use the new variables
|
| - kw = copy_non_reserved_keywords(kw)
|
| - new = {}
|
| - for key, value in kw.items():
|
| - new[key] = SCons.Subst.scons_subst_once(value, self, key)
|
| - apply(clone.Replace, (), new)
|
| -
|
| - apply_tools(clone, tools, toolpath)
|
| -
|
| - # apply them again in case the tools overwrote them
|
| - apply(clone.Replace, (), new)
|
| -
|
| - # Finally, apply any flags to be merged in
|
| - if parse_flags: clone.MergeFlags(parse_flags)
|
| -
|
| - if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
|
| - return clone
|
| -
|
| - def Copy(self, *args, **kw):
|
| - global _warn_copy_deprecated
|
| - if _warn_copy_deprecated:
|
| - msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
|
| - SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
|
| - _warn_copy_deprecated = False
|
| - return apply(self.Clone, args, kw)
|
| -
|
| - def _changed_build(self, dependency, target, prev_ni):
|
| - if dependency.changed_state(target, prev_ni):
|
| - return 1
|
| - return self.decide_source(dependency, target, prev_ni)
|
| -
|
| - def _changed_content(self, dependency, target, prev_ni):
|
| - return dependency.changed_content(target, prev_ni)
|
| -
|
| - def _changed_source(self, dependency, target, prev_ni):
|
| - target_env = dependency.get_build_env()
|
| - type = target_env.get_tgt_sig_type()
|
| - if type == 'source':
|
| - return target_env.decide_source(dependency, target, prev_ni)
|
| - else:
|
| - return target_env.decide_target(dependency, target, prev_ni)
|
| -
|
| - def _changed_timestamp_then_content(self, dependency, target, prev_ni):
|
| - return dependency.changed_timestamp_then_content(target, prev_ni)
|
| -
|
| - def _changed_timestamp_newer(self, dependency, target, prev_ni):
|
| - return dependency.changed_timestamp_newer(target, prev_ni)
|
| -
|
| - def _changed_timestamp_match(self, dependency, target, prev_ni):
|
| - return dependency.changed_timestamp_match(target, prev_ni)
|
| -
|
| - def _copy_from_cache(self, src, dst):
|
| - return self.fs.copy(src, dst)
|
| -
|
| - def _copy2_from_cache(self, src, dst):
|
| - return self.fs.copy2(src, dst)
|
| -
|
| - def Decider(self, function):
|
| - copy_function = self._copy2_from_cache
|
| - if function in ('MD5', 'content'):
|
| - if not SCons.Util.md5:
|
| - raise UserError, "MD5 signatures are not available in this version of Python."
|
| - function = self._changed_content
|
| - elif function == 'MD5-timestamp':
|
| - function = self._changed_timestamp_then_content
|
| - elif function in ('timestamp-newer', 'make'):
|
| - function = self._changed_timestamp_newer
|
| - copy_function = self._copy_from_cache
|
| - elif function == 'timestamp-match':
|
| - function = self._changed_timestamp_match
|
| - elif not callable(function):
|
| - raise UserError, "Unknown Decider value %s" % repr(function)
|
| -
|
| - # We don't use AddMethod because we don't want to turn the
|
| - # function, which only expects three arguments, into a bound
|
| - # method, which would add self as an initial, fourth argument.
|
| - self.decide_target = function
|
| - self.decide_source = function
|
| -
|
| - self.copy_from_cache = copy_function
|
| -
|
| - def Detect(self, progs):
|
| - """Return the first available program in progs.
|
| - """
|
| - if not SCons.Util.is_List(progs):
|
| - progs = [ progs ]
|
| - for prog in progs:
|
| - path = self.WhereIs(prog)
|
| - if path: return prog
|
| - return None
|
| -
|
| - def Dictionary(self, *args):
|
| - if not args:
|
| - return self._dict
|
| - dlist = map(lambda x, s=self: s._dict[x], args)
|
| - if len(dlist) == 1:
|
| - dlist = dlist[0]
|
| - return dlist
|
| -
|
| - def Dump(self, key = None):
|
| - """
|
| - Using the standard Python pretty printer, dump the contents of the
|
| - scons build environment to stdout.
|
| -
|
| - If the key passed in is anything other than None, then that will
|
| - be used as an index into the build environment dictionary and
|
| - whatever is found there will be fed into the pretty printer. Note
|
| - that this key is case sensitive.
|
| - """
|
| - import pprint
|
| - pp = pprint.PrettyPrinter(indent=2)
|
| - if key:
|
| - dict = self.Dictionary(key)
|
| - else:
|
| - dict = self.Dictionary()
|
| - return pp.pformat(dict)
|
| -
|
| - def FindIxes(self, paths, prefix, suffix):
|
| - """
|
| - Search a list of paths for something that matches the prefix and suffix.
|
| -
|
| - paths - the list of paths or nodes.
|
| - prefix - construction variable for the prefix.
|
| - suffix - construction variable for the suffix.
|
| - """
|
| -
|
| - suffix = self.subst('$'+suffix)
|
| - prefix = self.subst('$'+prefix)
|
| -
|
| - for path in paths:
|
| - dir,name = os.path.split(str(path))
|
| - if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
|
| - return path
|
| -
|
| - def ParseConfig(self, command, function=None, unique=1):
|
| - """
|
| - Use the specified function to parse the output of the command
|
| - in order to modify the current environment. The 'command' can
|
| - be a string or a list of strings representing a command and
|
| - its arguments. 'Function' is an optional argument that takes
|
| - the environment, the output of the command, and the unique flag.
|
| - If no function is specified, MergeFlags, which treats the output
|
| - as the result of a typical 'X-config' command (i.e. gtk-config),
|
| - will merge the output into the appropriate variables.
|
| - """
|
| - if function is None:
|
| - def parse_conf(env, cmd, unique=unique):
|
| - return env.MergeFlags(cmd, unique)
|
| - function = parse_conf
|
| - if SCons.Util.is_List(command):
|
| - command = string.join(command)
|
| - command = self.subst(command)
|
| - return function(self, self.backtick(command))
|
| -
|
| - def ParseDepends(self, filename, must_exist=None, only_one=0):
|
| - """
|
| - Parse a mkdep-style file for explicit dependencies. This is
|
| - completely abusable, and should be unnecessary in the "normal"
|
| - case of proper SCons configuration, but it may help make
|
| - the transition from a Make hierarchy easier for some people
|
| - to swallow. It can also be genuinely useful when using a tool
|
| - that can write a .d file, but for which writing a scanner would
|
| - be too complicated.
|
| - """
|
| - filename = self.subst(filename)
|
| - try:
|
| - fp = open(filename, 'r')
|
| - except IOError:
|
| - if must_exist:
|
| - raise
|
| - return
|
| - lines = SCons.Util.LogicalLines(fp).readlines()
|
| - lines = filter(lambda l: l[0] != '#', lines)
|
| - tdlist = []
|
| - for line in lines:
|
| - try:
|
| - target, depends = string.split(line, ':', 1)
|
| - except (AttributeError, TypeError, ValueError):
|
| - # Python 1.5.2 throws TypeError if line isn't a string,
|
| - # Python 2.x throws AttributeError because it tries
|
| - # to call line.split(). Either can throw ValueError
|
| - # if the line doesn't split into two or more elements.
|
| - pass
|
| - else:
|
| - tdlist.append((string.split(target), string.split(depends)))
|
| - if only_one:
|
| - targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
|
| - if len(targets) > 1:
|
| - raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
|
| - for target, depends in tdlist:
|
| - self.Depends(target, depends)
|
| -
|
| - def Platform(self, platform):
|
| - platform = self.subst(platform)
|
| - return SCons.Platform.Platform(platform)(self)
|
| -
|
| - def Prepend(self, **kw):
|
| - """Prepend values to existing construction variables
|
| - in an Environment.
|
| - """
|
| - kw = copy_non_reserved_keywords(kw)
|
| - for key, val in kw.items():
|
| - # It would be easier on the eyes to write this using
|
| - # "continue" statements whenever we finish processing an item,
|
| - # but Python 1.5.2 apparently doesn't let you use "continue"
|
| - # within try:-except: blocks, so we have to nest our code.
|
| - try:
|
| - orig = self._dict[key]
|
| - except KeyError:
|
| - # No existing variable in the environment, so just set
|
| - # it to the new value.
|
| - self._dict[key] = val
|
| - else:
|
| - try:
|
| - # Check if the original looks like a dictionary.
|
| - # If it is, we can't just try adding the value because
|
| - # dictionaries don't have __add__() methods, and
|
| - # things like UserList will incorrectly coerce the
|
| - # original dict to a list (which we don't want).
|
| - update_dict = orig.update
|
| - except AttributeError:
|
| - try:
|
| - # Most straightforward: just try to add them
|
| - # together. This will work in most cases, when the
|
| - # original and new values are of compatible types.
|
| - self._dict[key] = val + orig
|
| - except (KeyError, TypeError):
|
| - try:
|
| - # Check if the added value is a list.
|
| - add_to_val = val.append
|
| - except AttributeError:
|
| - # The added value isn't a list, but the
|
| - # original is (by process of elimination),
|
| - # so insert the the new value in the original
|
| - # (if there's one to insert).
|
| - if val:
|
| - orig.insert(0, val)
|
| - else:
|
| - # The added value is a list, so append
|
| - # the original to it (if there's a value
|
| - # to append).
|
| - if orig:
|
| - add_to_val(orig)
|
| - self._dict[key] = val
|
| - else:
|
| - # The original looks like a dictionary, so update it
|
| - # based on what we think the value looks like.
|
| - if SCons.Util.is_List(val):
|
| - for v in val:
|
| - orig[v] = None
|
| - else:
|
| - try:
|
| - update_dict(val)
|
| - except (AttributeError, TypeError, ValueError):
|
| - if SCons.Util.is_Dict(val):
|
| - for k, v in val.items():
|
| - orig[k] = v
|
| - else:
|
| - orig[val] = None
|
| - self.scanner_map_delete(kw)
|
| -
|
| - def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep,
|
| - delete_existing=1):
|
| - """Prepend path elements to the path 'name' in the 'ENV'
|
| - dictionary for this environment. Will only add any particular
|
| - path once, and will normpath and normcase all paths to help
|
| - assure this. This can also handle the case where the env
|
| - variable is a list instead of a string.
|
| -
|
| - If delete_existing is 0, a newpath which is already in the path
|
| - will not be moved to the front (it will be left where it is).
|
| - """
|
| -
|
| - orig = ''
|
| - if self._dict.has_key(envname) and self._dict[envname].has_key(name):
|
| - orig = self._dict[envname][name]
|
| -
|
| - nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing)
|
| -
|
| - if not self._dict.has_key(envname):
|
| - self._dict[envname] = {}
|
| -
|
| - self._dict[envname][name] = nv
|
| -
|
| - def PrependUnique(self, delete_existing=0, **kw):
|
| - """Prepend values to existing construction variables
|
| - in an Environment, if they're not already there.
|
| - If delete_existing is 1, removes existing values first, so
|
| - values move to front.
|
| - """
|
| - kw = copy_non_reserved_keywords(kw)
|
| - for key, val in kw.items():
|
| - if not self._dict.has_key(key) or self._dict[key] in ('', None):
|
| - self._dict[key] = val
|
| - elif SCons.Util.is_Dict(self._dict[key]) and \
|
| - SCons.Util.is_Dict(val):
|
| - self._dict[key].update(val)
|
| - elif SCons.Util.is_List(val):
|
| - dk = self._dict[key]
|
| - if not SCons.Util.is_List(dk):
|
| - dk = [dk]
|
| - if delete_existing:
|
| - dk = filter(lambda x, val=val: x not in val, dk)
|
| - else:
|
| - val = filter(lambda x, dk=dk: x not in dk, val)
|
| - self._dict[key] = val + dk
|
| - else:
|
| - dk = self._dict[key]
|
| - if SCons.Util.is_List(dk):
|
| - # By elimination, val is not a list. Since dk is a
|
| - # list, wrap val in a list first.
|
| - if delete_existing:
|
| - dk = filter(lambda x, val=val: x not in val, dk)
|
| - self._dict[key] = [val] + dk
|
| - else:
|
| - if not val in dk:
|
| - self._dict[key] = [val] + dk
|
| - else:
|
| - if delete_existing:
|
| - dk = filter(lambda x, val=val: x not in val, dk)
|
| - self._dict[key] = val + dk
|
| - self.scanner_map_delete(kw)
|
| -
|
| - def Replace(self, **kw):
|
| - """Replace existing construction variables in an Environment
|
| - with new construction variables and/or values.
|
| - """
|
| - try:
|
| - kwbd = kw['BUILDERS']
|
| - except KeyError:
|
| - pass
|
| - else:
|
| - kwbd = semi_deepcopy(kwbd)
|
| - del kw['BUILDERS']
|
| - self.__setitem__('BUILDERS', kwbd)
|
| - kw = copy_non_reserved_keywords(kw)
|
| - self._update(semi_deepcopy(kw))
|
| - self.scanner_map_delete(kw)
|
| -
|
| - def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
|
| - """
|
| - Replace old_prefix with new_prefix and old_suffix with new_suffix.
|
| -
|
| - env - Environment used to interpolate variables.
|
| - path - the path that will be modified.
|
| - old_prefix - construction variable for the old prefix.
|
| - old_suffix - construction variable for the old suffix.
|
| - new_prefix - construction variable for the new prefix.
|
| - new_suffix - construction variable for the new suffix.
|
| - """
|
| - old_prefix = self.subst('$'+old_prefix)
|
| - old_suffix = self.subst('$'+old_suffix)
|
| -
|
| - new_prefix = self.subst('$'+new_prefix)
|
| - new_suffix = self.subst('$'+new_suffix)
|
| -
|
| - dir,name = os.path.split(str(path))
|
| - if name[:len(old_prefix)] == old_prefix:
|
| - name = name[len(old_prefix):]
|
| - if name[-len(old_suffix):] == old_suffix:
|
| - name = name[:-len(old_suffix)]
|
| - return os.path.join(dir, new_prefix+name+new_suffix)
|
| -
|
| - def SetDefault(self, **kw):
|
| - for k in kw.keys():
|
| - if self._dict.has_key(k):
|
| - del kw[k]
|
| - apply(self.Replace, (), kw)
|
| -
|
| - def _find_toolpath_dir(self, tp):
|
| - return self.fs.Dir(self.subst(tp)).srcnode().abspath
|
| -
|
| - def Tool(self, tool, toolpath=None, **kw):
|
| - if SCons.Util.is_String(tool):
|
| - tool = self.subst(tool)
|
| - if toolpath is None:
|
| - toolpath = self.get('toolpath', [])
|
| - toolpath = map(self._find_toolpath_dir, toolpath)
|
| - tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
|
| - tool(self)
|
| -
|
| - def WhereIs(self, prog, path=None, pathext=None, reject=[]):
|
| - """Find prog in the path.
|
| - """
|
| - if path is None:
|
| - try:
|
| - path = self['ENV']['PATH']
|
| - except KeyError:
|
| - pass
|
| - elif SCons.Util.is_String(path):
|
| - path = self.subst(path)
|
| - if pathext is None:
|
| - try:
|
| - pathext = self['ENV']['PATHEXT']
|
| - except KeyError:
|
| - pass
|
| - elif SCons.Util.is_String(pathext):
|
| - pathext = self.subst(pathext)
|
| - prog = self.subst(prog)
|
| - path = SCons.Util.WhereIs(prog, path, pathext, reject)
|
| - if path: return path
|
| - return None
|
| -
|
| - #######################################################################
|
| - # Public methods for doing real "SCons stuff" (manipulating
|
| - # dependencies, setting attributes on targets, etc.). These begin
|
| - # with upper-case letters. The essential characteristic of methods
|
| - # in this section is that they all *should* have corresponding
|
| - # same-named global functions.
|
| - #######################################################################
|
| -
|
| - def Action(self, *args, **kw):
|
| - def subst_string(a, self=self):
|
| - if SCons.Util.is_String(a):
|
| - a = self.subst(a)
|
| - return a
|
| - nargs = map(subst_string, args)
|
| - nkw = self.subst_kw(kw)
|
| - return apply(SCons.Action.Action, nargs, nkw)
|
| -
|
| - def AddPreAction(self, files, action):
|
| - nodes = self.arg2nodes(files, self.fs.Entry)
|
| - action = SCons.Action.Action(action)
|
| - uniq = {}
|
| - for executor in map(lambda n: n.get_executor(), nodes):
|
| - uniq[executor] = 1
|
| - for executor in uniq.keys():
|
| - executor.add_pre_action(action)
|
| - return nodes
|
| -
|
| - def AddPostAction(self, files, action):
|
| - nodes = self.arg2nodes(files, self.fs.Entry)
|
| - action = SCons.Action.Action(action)
|
| - uniq = {}
|
| - for executor in map(lambda n: n.get_executor(), nodes):
|
| - uniq[executor] = 1
|
| - for executor in uniq.keys():
|
| - executor.add_post_action(action)
|
| - return nodes
|
| -
|
| - def Alias(self, target, source=[], action=None, **kw):
|
| - tlist = self.arg2nodes(target, self.ans.Alias)
|
| - if not SCons.Util.is_List(source):
|
| - source = [source]
|
| - source = filter(None, source)
|
| -
|
| - if not action:
|
| - if not source:
|
| - # There are no source files and no action, so just
|
| - # return a target list of classic Alias Nodes, without
|
| - # any builder. The externally visible effect is that
|
| - # this will make the wrapping Script.BuildTask class
|
| - # say that there's "Nothing to be done" for this Alias,
|
| - # instead of that it's "up to date."
|
| - return tlist
|
| -
|
| - # No action, but there are sources. Re-call all the target
|
| - # builders to add the sources to each target.
|
| - result = []
|
| - for t in tlist:
|
| - bld = t.get_builder(AliasBuilder)
|
| - result.extend(bld(self, t, source))
|
| - return result
|
| -
|
| - nkw = self.subst_kw(kw)
|
| - nkw.update({
|
| - 'action' : SCons.Action.Action(action),
|
| - 'source_factory' : self.fs.Entry,
|
| - 'multi' : 1,
|
| - 'is_explicit' : None,
|
| - })
|
| - bld = apply(SCons.Builder.Builder, (), nkw)
|
| -
|
| - # Apply the Builder separately to each target so that the Aliases
|
| - # stay separate. If we did one "normal" Builder call with the
|
| - # whole target list, then all of the target Aliases would be
|
| - # associated under a single Executor.
|
| - result = []
|
| - for t in tlist:
|
| - # Calling the convert() method will cause a new Executor to be
|
| - # created from scratch, so we have to explicitly initialize
|
| - # it with the target's existing sources, plus our new ones,
|
| - # so nothing gets lost.
|
| - b = t.get_builder()
|
| - if b is None or b is AliasBuilder:
|
| - b = bld
|
| - else:
|
| - nkw['action'] = b.action + action
|
| - b = apply(SCons.Builder.Builder, (), nkw)
|
| - t.convert()
|
| - result.extend(b(self, t, t.sources + source))
|
| - return result
|
| -
|
| - def AlwaysBuild(self, *targets):
|
| - tlist = []
|
| - for t in targets:
|
| - tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
| - for t in tlist:
|
| - t.set_always_build()
|
| - return tlist
|
| -
|
| - def BuildDir(self, *args, **kw):
|
| - if kw.has_key('build_dir'):
|
| - kw['variant_dir'] = kw['build_dir']
|
| - del kw['build_dir']
|
| - return apply(self.VariantDir, args, kw)
|
| -
|
| - def Builder(self, **kw):
|
| - nkw = self.subst_kw(kw)
|
| - return apply(SCons.Builder.Builder, [], nkw)
|
| -
|
| - def CacheDir(self, path):
|
| - import SCons.CacheDir
|
| - if not path is None:
|
| - path = self.subst(path)
|
| - self._CacheDir_path = path
|
| -
|
| - def Clean(self, targets, files):
|
| - global CleanTargets
|
| - tlist = self.arg2nodes(targets, self.fs.Entry)
|
| - flist = self.arg2nodes(files, self.fs.Entry)
|
| - for t in tlist:
|
| - try:
|
| - CleanTargets[t].extend(flist)
|
| - except KeyError:
|
| - CleanTargets[t] = flist
|
| -
|
| - def Configure(self, *args, **kw):
|
| - nargs = [self]
|
| - if args:
|
| - nargs = nargs + self.subst_list(args)[0]
|
| - nkw = self.subst_kw(kw)
|
| - nkw['_depth'] = kw.get('_depth', 0) + 1
|
| - try:
|
| - nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
|
| - except KeyError:
|
| - pass
|
| - return apply(SCons.SConf.SConf, nargs, nkw)
|
| -
|
| - def Command(self, target, source, action, **kw):
|
| - """Builds the supplied target files from the supplied
|
| - source files using the supplied action. Action may
|
| - be any type that the Builder constructor will accept
|
| - for an action."""
|
| - bkw = {
|
| - 'action' : action,
|
| - 'target_factory' : self.fs.Entry,
|
| - 'source_factory' : self.fs.Entry,
|
| - }
|
| - try: bkw['source_scanner'] = kw['source_scanner']
|
| - except KeyError: pass
|
| - else: del kw['source_scanner']
|
| - bld = apply(SCons.Builder.Builder, (), bkw)
|
| - return apply(bld, (self, target, source), kw)
|
| -
|
| - def Depends(self, target, dependency):
|
| - """Explicity specify that 'target's depend on 'dependency'."""
|
| - tlist = self.arg2nodes(target, self.fs.Entry)
|
| - dlist = self.arg2nodes(dependency, self.fs.Entry)
|
| - for t in tlist:
|
| - t.add_dependency(dlist)
|
| - return tlist
|
| -
|
| - def Dir(self, name, *args, **kw):
|
| - """
|
| - """
|
| - s = self.subst(name)
|
| - if SCons.Util.is_Sequence(s):
|
| - result=[]
|
| - for e in s:
|
| - result.append(apply(self.fs.Dir, (e,) + args, kw))
|
| - return result
|
| - return apply(self.fs.Dir, (s,) + args, kw)
|
| -
|
| - def NoClean(self, *targets):
|
| - """Tags a target so that it will not be cleaned by -c"""
|
| - tlist = []
|
| - for t in targets:
|
| - tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
| - for t in tlist:
|
| - t.set_noclean()
|
| - return tlist
|
| -
|
| - def NoCache(self, *targets):
|
| - """Tags a target so that it will not be cached"""
|
| - tlist = []
|
| - for t in targets:
|
| - tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
| - for t in tlist:
|
| - t.set_nocache()
|
| - return tlist
|
| -
|
| - def Entry(self, name, *args, **kw):
|
| - """
|
| - """
|
| - s = self.subst(name)
|
| - if SCons.Util.is_Sequence(s):
|
| - result=[]
|
| - for e in s:
|
| - result.append(apply(self.fs.Entry, (e,) + args, kw))
|
| - return result
|
| - return apply(self.fs.Entry, (s,) + args, kw)
|
| -
|
| - def Environment(self, **kw):
|
| - return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
|
| -
|
| - def Execute(self, action, *args, **kw):
|
| - """Directly execute an action through an Environment
|
| - """
|
| - action = apply(self.Action, (action,) + args, kw)
|
| - result = action([], [], self)
|
| - if isinstance(result, SCons.Errors.BuildError):
|
| - errstr = result.errstr
|
| - if result.filename:
|
| - errstr = result.filename + ': ' + errstr
|
| - sys.stderr.write("scons: *** %s\n" % errstr)
|
| - return result.status
|
| - else:
|
| - return result
|
| -
|
| - def File(self, name, *args, **kw):
|
| - """
|
| - """
|
| - s = self.subst(name)
|
| - if SCons.Util.is_Sequence(s):
|
| - result=[]
|
| - for e in s:
|
| - result.append(apply(self.fs.File, (e,) + args, kw))
|
| - return result
|
| - return apply(self.fs.File, (s,) + args, kw)
|
| -
|
| - def FindFile(self, file, dirs):
|
| - file = self.subst(file)
|
| - nodes = self.arg2nodes(dirs, self.fs.Dir)
|
| - return SCons.Node.FS.find_file(file, tuple(nodes))
|
| -
|
| - def Flatten(self, sequence):
|
| - return SCons.Util.flatten(sequence)
|
| -
|
| - def GetBuildPath(self, files):
|
| - result = map(str, self.arg2nodes(files, self.fs.Entry))
|
| - if SCons.Util.is_List(files):
|
| - return result
|
| - else:
|
| - return result[0]
|
| -
|
| - def Glob(self, pattern, ondisk=True, source=False, strings=False):
|
| - return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
|
| -
|
| - def Ignore(self, target, dependency):
|
| - """Ignore a dependency."""
|
| - tlist = self.arg2nodes(target, self.fs.Entry)
|
| - dlist = self.arg2nodes(dependency, self.fs.Entry)
|
| - for t in tlist:
|
| - t.add_ignore(dlist)
|
| - return tlist
|
| -
|
| - def Literal(self, string):
|
| - return SCons.Subst.Literal(string)
|
| -
|
| - def Local(self, *targets):
|
| - ret = []
|
| - for targ in targets:
|
| - if isinstance(targ, SCons.Node.Node):
|
| - targ.set_local()
|
| - ret.append(targ)
|
| - else:
|
| - for t in self.arg2nodes(targ, self.fs.Entry):
|
| - t.set_local()
|
| - ret.append(t)
|
| - return ret
|
| -
|
| - def Precious(self, *targets):
|
| - tlist = []
|
| - for t in targets:
|
| - tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
| - for t in tlist:
|
| - t.set_precious()
|
| - return tlist
|
| -
|
| - def Repository(self, *dirs, **kw):
|
| - dirs = self.arg2nodes(list(dirs), self.fs.Dir)
|
| - apply(self.fs.Repository, dirs, kw)
|
| -
|
| - def Requires(self, target, prerequisite):
|
| - """Specify that 'prerequisite' must be built before 'target',
|
| - (but 'target' does not actually depend on 'prerequisite'
|
| - and need not be rebuilt if it changes)."""
|
| - tlist = self.arg2nodes(target, self.fs.Entry)
|
| - plist = self.arg2nodes(prerequisite, self.fs.Entry)
|
| - for t in tlist:
|
| - t.add_prerequisite(plist)
|
| - return tlist
|
| -
|
| - def Scanner(self, *args, **kw):
|
| - nargs = []
|
| - for arg in args:
|
| - if SCons.Util.is_String(arg):
|
| - arg = self.subst(arg)
|
| - nargs.append(arg)
|
| - nkw = self.subst_kw(kw)
|
| - return apply(SCons.Scanner.Base, nargs, nkw)
|
| -
|
| - def SConsignFile(self, name=".sconsign", dbm_module=None):
|
| - if not name is None:
|
| - name = self.subst(name)
|
| - if not os.path.isabs(name):
|
| - name = os.path.join(str(self.fs.SConstruct_dir), name)
|
| - if name:
|
| - name = os.path.normpath(name)
|
| - sconsign_dir = os.path.dirname(name)
|
| - if sconsign_dir and not os.path.exists(sconsign_dir):
|
| - self.Execute(SCons.Defaults.Mkdir(sconsign_dir))
|
| - SCons.SConsign.File(name, dbm_module)
|
| -
|
| - def SideEffect(self, side_effect, target):
|
| - """Tell scons that side_effects are built as side
|
| - effects of building targets."""
|
| - side_effects = self.arg2nodes(side_effect, self.fs.Entry)
|
| - targets = self.arg2nodes(target, self.fs.Entry)
|
| -
|
| - for side_effect in side_effects:
|
| - if side_effect.multiple_side_effect_has_builder():
|
| - raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
|
| - side_effect.add_source(targets)
|
| - side_effect.side_effect = 1
|
| - self.Precious(side_effect)
|
| - for target in targets:
|
| - target.side_effects.append(side_effect)
|
| - return side_effects
|
| -
|
| - def SourceCode(self, entry, builder):
|
| - """Arrange for a source code builder for (part of) a tree."""
|
| - entries = self.arg2nodes(entry, self.fs.Entry)
|
| - for entry in entries:
|
| - entry.set_src_builder(builder)
|
| - return entries
|
| -
|
| - def SourceSignatures(self, type):
|
| - global _warn_source_signatures_deprecated
|
| - if _warn_source_signatures_deprecated:
|
| - msg = "The env.SourceSignatures() method is deprecated;\n" + \
|
| - "\tconvert your build to use the env.Decider() method instead."
|
| - SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
|
| - _warn_source_signatures_deprecated = False
|
| - type = self.subst(type)
|
| - self.src_sig_type = type
|
| - if type == 'MD5':
|
| - if not SCons.Util.md5:
|
| - raise UserError, "MD5 signatures are not available in this version of Python."
|
| - self.decide_source = self._changed_content
|
| - elif type == 'timestamp':
|
| - self.decide_source = self._changed_timestamp_match
|
| - else:
|
| - raise UserError, "Unknown source signature type '%s'" % type
|
| -
|
| - def Split(self, arg):
|
| - """This function converts a string or list into a list of strings
|
| - or Nodes. This makes things easier for users by allowing files to
|
| - be specified as a white-space separated list to be split.
|
| - The input rules are:
|
| - - A single string containing names separated by spaces. These will be
|
| - split apart at the spaces.
|
| - - A single Node instance
|
| - - A list containing either strings or Node instances. Any strings
|
| - in the list are not split at spaces.
|
| - In all cases, the function returns a list of Nodes and strings."""
|
| - if SCons.Util.is_List(arg):
|
| - return map(self.subst, arg)
|
| - elif SCons.Util.is_String(arg):
|
| - return string.split(self.subst(arg))
|
| - else:
|
| - return [self.subst(arg)]
|
| -
|
| - def TargetSignatures(self, type):
|
| - global _warn_target_signatures_deprecated
|
| - if _warn_target_signatures_deprecated:
|
| - msg = "The env.TargetSignatures() method is deprecated;\n" + \
|
| - "\tconvert your build to use the env.Decider() method instead."
|
| - SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
|
| - _warn_target_signatures_deprecated = False
|
| - type = self.subst(type)
|
| - self.tgt_sig_type = type
|
| - if type in ('MD5', 'content'):
|
| - if not SCons.Util.md5:
|
| - raise UserError, "MD5 signatures are not available in this version of Python."
|
| - self.decide_target = self._changed_content
|
| - elif type == 'timestamp':
|
| - self.decide_target = self._changed_timestamp_match
|
| - elif type == 'build':
|
| - self.decide_target = self._changed_build
|
| - elif type == 'source':
|
| - self.decide_target = self._changed_source
|
| - else:
|
| - raise UserError, "Unknown target signature type '%s'"%type
|
| -
|
| - def Value(self, value, built_value=None):
|
| - """
|
| - """
|
| - return SCons.Node.Python.Value(value, built_value)
|
| -
|
| - def VariantDir(self, variant_dir, src_dir, duplicate=1):
|
| - variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
|
| - src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
|
| - self.fs.VariantDir(variant_dir, src_dir, duplicate)
|
| -
|
| - def FindSourceFiles(self, node='.'):
|
| - """ returns a list of all source files.
|
| - """
|
| - node = self.arg2nodes(node, self.fs.Entry)[0]
|
| -
|
| - sources = []
|
| - # Uncomment this and get rid of the global definition when we
|
| - # drop support for pre-2.2 Python versions.
|
| - #def build_source(ss, result):
|
| - # for s in ss:
|
| - # if isinstance(s, SCons.Node.FS.Dir):
|
| - # build_source(s.all_children(), result)
|
| - # elif s.has_builder():
|
| - # build_source(s.sources, result)
|
| - # elif isinstance(s.disambiguate(), SCons.Node.FS.File):
|
| - # result.append(s)
|
| - build_source(node.all_children(), sources)
|
| -
|
| - # now strip the build_node from the sources by calling the srcnode
|
| - # function
|
| - def get_final_srcnode(file):
|
| - srcnode = file.srcnode()
|
| - while srcnode != file.srcnode():
|
| - srcnode = file.srcnode()
|
| - return srcnode
|
| -
|
| - # get the final srcnode for all nodes, this means stripping any
|
| - # attached build node.
|
| - map( get_final_srcnode, sources )
|
| -
|
| - # remove duplicates
|
| - return list(set(sources))
|
| -
|
| - def FindInstalledFiles(self):
|
| - """ returns the list of all targets of the Install and InstallAs Builder.
|
| - """
|
| - from SCons.Tool import install
|
| - if install._UNIQUE_INSTALLED_FILES is None:
|
| - install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
|
| - return install._UNIQUE_INSTALLED_FILES
|
| -
|
| -class OverrideEnvironment(Base):
|
| - """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. 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
|
| - values from the overrides dictionary.
|
| - """
|
| -
|
| - if SCons.Memoize.use_memoizer:
|
| - __metaclass__ = SCons.Memoize.Memoized_Metaclass
|
| -
|
| - def __init__(self, subject, overrides={}):
|
| - if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
|
| - self.__dict__['__subject'] = subject
|
| - self.__dict__['overrides'] = overrides
|
| -
|
| - # Methods that make this class act like a proxy.
|
| - def __getattr__(self, name):
|
| - return getattr(self.__dict__['__subject'], name)
|
| - def __setattr__(self, name, value):
|
| - setattr(self.__dict__['__subject'], name, value)
|
| -
|
| - # Methods that make this class act like a dictionary.
|
| - def __getitem__(self, key):
|
| - try:
|
| - return self.__dict__['overrides'][key]
|
| - except KeyError:
|
| - return self.__dict__['__subject'].__getitem__(key)
|
| - def __setitem__(self, key, value):
|
| - if not is_valid_construction_var(key):
|
| - raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
|
| - self.__dict__['overrides'][key] = value
|
| - def __delitem__(self, key):
|
| - try:
|
| - del self.__dict__['overrides'][key]
|
| - except KeyError:
|
| - deleted = 0
|
| - else:
|
| - deleted = 1
|
| - try:
|
| - result = self.__dict__['__subject'].__delitem__(key)
|
| - except KeyError:
|
| - if not deleted:
|
| - raise
|
| - result = None
|
| - return result
|
| - def get(self, key, default=None):
|
| - """Emulates the get() method of dictionaries."""
|
| - try:
|
| - return self.__dict__['overrides'][key]
|
| - except KeyError:
|
| - return self.__dict__['__subject'].get(key, default)
|
| - def has_key(self, key):
|
| - try:
|
| - self.__dict__['overrides'][key]
|
| - return 1
|
| - except KeyError:
|
| - return self.__dict__['__subject'].has_key(key)
|
| - def Dictionary(self):
|
| - """Emulates the items() method of dictionaries."""
|
| - d = self.__dict__['__subject'].Dictionary().copy()
|
| - d.update(self.__dict__['overrides'])
|
| - return d
|
| - def items(self):
|
| - """Emulates the items() method of dictionaries."""
|
| - return self.Dictionary().items()
|
| -
|
| - # Overridden private construction environment methods.
|
| - def _update(self, dict):
|
| - """Update an environment's values directly, bypassing the normal
|
| - checks that occur when users try to set items.
|
| - """
|
| - 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 Replace(self, **kw):
|
| - kw = copy_non_reserved_keywords(kw)
|
| - self.__dict__['overrides'].update(semi_deepcopy(kw))
|
| -
|
| -# The entry point that will be used by the external world
|
| -# to refer to a construction environment. This allows the wrapper
|
| -# interface to extend a construction environment for its own purposes
|
| -# by subclassing SCons.Environment.Base and then assigning the
|
| -# class to SCons.Environment.Environment.
|
| -
|
| -Environment = Base
|
| -
|
| -# An entry point for returning a proxy subclass instance that overrides
|
| -# the subst*() methods so they don't actually perform construction
|
| -# variable substitution. This is specifically intended to be the shim
|
| -# layer in between global function calls (which don't want construction
|
| -# variable substitution) and the DefaultEnvironment() (which would
|
| -# substitute variables if left to its own devices)."""
|
| -#
|
| -# We have to wrap this in a function that allows us to delay definition of
|
| -# the class until it's necessary, so that when it subclasses Environment
|
| -# it will pick up whatever Environment subclass the wrapper interface
|
| -# might have assigned to SCons.Environment.Environment.
|
| -
|
| -def NoSubstitutionProxy(subject):
|
| - class _NoSubstitutionProxy(Environment):
|
| - def __init__(self, subject):
|
| - self.__dict__['__subject'] = subject
|
| - def __getattr__(self, name):
|
| - return getattr(self.__dict__['__subject'], name)
|
| - def __setattr__(self, name, value):
|
| - return setattr(self.__dict__['__subject'], name, value)
|
| - def raw_to_mode(self, dict):
|
| - try:
|
| - raw = dict['raw']
|
| - except KeyError:
|
| - pass
|
| - else:
|
| - del dict['raw']
|
| - dict['mode'] = raw
|
| - def subst(self, string, *args, **kwargs):
|
| - return string
|
| - def subst_kw(self, kw, *args, **kwargs):
|
| - return kw
|
| - def subst_list(self, string, *args, **kwargs):
|
| - nargs = (string, self,) + args
|
| - nkw = kwargs.copy()
|
| - nkw['gvars'] = {}
|
| - self.raw_to_mode(nkw)
|
| - return apply(SCons.Subst.scons_subst_list, nargs, nkw)
|
| - def subst_target_source(self, string, *args, **kwargs):
|
| - nargs = (string, self,) + args
|
| - nkw = kwargs.copy()
|
| - nkw['gvars'] = {}
|
| - self.raw_to_mode(nkw)
|
| - return apply(SCons.Subst.scons_subst, nargs, nkw)
|
| - return _NoSubstitutionProxy(subject)
|
| +"""SCons.Environment
|
| +
|
| +Base class for construction Environments. These are
|
| +the primary objects used to communicate dependency and
|
| +construction information to the build engine.
|
| +
|
| +Keyword arguments supplied when the construction Environment
|
| +is created are construction variables used to initialize the
|
| +Environment
|
| +"""
|
| +
|
| +#
|
| +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
|
| +#
|
| +# Permission is hereby granted, free of charge, to any person obtaining
|
| +# a copy of this software and associated documentation files (the
|
| +# "Software"), to deal in the Software without restriction, including
|
| +# without limitation the rights to use, copy, modify, merge, publish,
|
| +# distribute, sublicense, and/or sell copies of the Software, and to
|
| +# permit persons to whom the Software is furnished to do so, subject to
|
| +# the following conditions:
|
| +#
|
| +# The above copyright notice and this permission notice shall be included
|
| +# in all copies or substantial portions of the Software.
|
| +#
|
| +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
| +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
| +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
| +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
| +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
| +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
| +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| +#
|
| +
|
| +__revision__ = "src/engine/SCons/Environment.py 3603 2008/10/10 05:46:45 scons"
|
| +
|
| +
|
| +import copy
|
| +import os
|
| +import sys
|
| +import re
|
| +import shlex
|
| +import string
|
| +from UserDict import UserDict
|
| +
|
| +import SCons.Action
|
| +import SCons.Builder
|
| +from SCons.Debug import logInstanceCreation
|
| +import SCons.Defaults
|
| +import SCons.Errors
|
| +import SCons.Memoize
|
| +import SCons.Node
|
| +import SCons.Node.Alias
|
| +import SCons.Node.FS
|
| +import SCons.Node.Python
|
| +import SCons.Platform
|
| +import SCons.SConsign
|
| +import SCons.Subst
|
| +import SCons.Tool
|
| +import SCons.Util
|
| +import SCons.Warnings
|
| +
|
| +class _Null:
|
| + pass
|
| +
|
| +_null = _Null
|
| +
|
| +_warn_copy_deprecated = True
|
| +_warn_source_signatures_deprecated = True
|
| +_warn_target_signatures_deprecated = True
|
| +
|
| +CleanTargets = {}
|
| +CalculatorArgs = {}
|
| +
|
| +semi_deepcopy = SCons.Util.semi_deepcopy
|
| +
|
| +# Pull UserError into the global name space for the benefit of
|
| +# Environment().SourceSignatures(), which has some import statements
|
| +# which seem to mess up its ability to reference SCons directly.
|
| +UserError = SCons.Errors.UserError
|
| +
|
| +def alias_builder(env, target, source):
|
| + pass
|
| +
|
| +AliasBuilder = SCons.Builder.Builder(action = alias_builder,
|
| + target_factory = SCons.Node.Alias.default_ans.Alias,
|
| + source_factory = SCons.Node.FS.Entry,
|
| + multi = 1,
|
| + is_explicit = None,
|
| + name='AliasBuilder')
|
| +
|
| +def apply_tools(env, tools, toolpath):
|
| + # Store the toolpath in the Environment.
|
| + if toolpath is not None:
|
| + env['toolpath'] = toolpath
|
| +
|
| + if not tools:
|
| + return
|
| + # Filter out null tools from the list.
|
| + for tool in filter(None, tools):
|
| + if SCons.Util.is_List(tool) or type(tool)==type(()):
|
| + toolname = tool[0]
|
| + toolargs = tool[1] # should be a dict of kw args
|
| + tool = apply(env.Tool, [toolname], toolargs)
|
| + else:
|
| + env.Tool(tool)
|
| +
|
| +# These names are controlled by SCons; users should never set or override
|
| +# them. This warning can optionally be turned off, but scons will still
|
| +# ignore the illegal variable names even if it's off.
|
| +reserved_construction_var_names = \
|
| + ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
|
| +
|
| +def copy_non_reserved_keywords(dict):
|
| + result = semi_deepcopy(dict)
|
| + for k in result.keys():
|
| + if k in reserved_construction_var_names:
|
| + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
|
| + "Ignoring attempt to set reserved variable `%s'" % k)
|
| + del result[k]
|
| + return result
|
| +
|
| +def _set_reserved(env, key, value):
|
| + msg = "Ignoring attempt to set reserved variable `%s'" % key
|
| + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg)
|
| +
|
| +def _set_BUILDERS(env, key, value):
|
| + try:
|
| + bd = env._dict[key]
|
| + for k in bd.keys():
|
| + del bd[k]
|
| + except KeyError:
|
| + bd = BuilderDict(kwbd, env)
|
| + env._dict[key] = bd
|
| + bd.update(value)
|
| +
|
| +def _del_SCANNERS(env, key):
|
| + del env._dict[key]
|
| + env.scanner_map_delete()
|
| +
|
| +def _set_SCANNERS(env, key, value):
|
| + env._dict[key] = value
|
| + env.scanner_map_delete()
|
| +
|
| +
|
| +
|
| +# The following is partly based on code in a comment added by Peter
|
| +# Shannon at the following page (there called the "transplant" class):
|
| +#
|
| +# ASPN : Python Cookbook : Dynamically added methods to a class
|
| +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
|
| +#
|
| +# We had independently been using the idiom as BuilderWrapper, but
|
| +# factoring out the common parts into this base class, and making
|
| +# BuilderWrapper a subclass that overrides __call__() to enforce specific
|
| +# Builder calling conventions, simplified some of our higher-layer code.
|
| +
|
| +class MethodWrapper:
|
| + """
|
| + A generic Wrapper class that associates a method (which can
|
| + actually be any callable) with an object. As part of creating this
|
| + MethodWrapper object an attribute with the specified (by default,
|
| + the name of the supplied method) is added to the underlying object.
|
| + When that new "method" is called, our __call__() method adds the
|
| + object as the first argument, simulating the Python behavior of
|
| + supplying "self" on method calls.
|
| +
|
| + We hang on to the name by which the method was added to the underlying
|
| + base class so that we can provide a method to "clone" ourselves onto
|
| + a new underlying object being copied (without which we wouldn't need
|
| + to save that info).
|
| + """
|
| + def __init__(self, object, method, name=None):
|
| + if name is None:
|
| + name = method.__name__
|
| + self.object = object
|
| + self.method = method
|
| + self.name = name
|
| + setattr(self.object, name, self)
|
| +
|
| + def __call__(self, *args, **kwargs):
|
| + nargs = (self.object,) + args
|
| + return apply(self.method, nargs, kwargs)
|
| +
|
| + def clone(self, new_object):
|
| + """
|
| + Returns an object that re-binds the underlying "method" to
|
| + the specified new object.
|
| + """
|
| + return self.__class__(new_object, self.method, self.name)
|
| +
|
| +class BuilderWrapper(MethodWrapper):
|
| + """
|
| + A MethodWrapper subclass that that associates an environment with
|
| + a Builder.
|
| +
|
| + This mainly exists to wrap the __call__() function so that all calls
|
| + to Builders can have their argument lists massaged in the same way
|
| + (treat a lone argument as the source, treat two arguments as target
|
| + then source, make sure both target and source are lists) without
|
| + having to have cut-and-paste code to do it.
|
| +
|
| + As a bit of obsessive backwards compatibility, we also intercept
|
| + attempts to get or set the "env" or "builder" attributes, which were
|
| + the names we used before we put the common functionality into the
|
| + MethodWrapper base class. We'll keep this around for a while in case
|
| + people shipped Tool modules that reached into the wrapper (like the
|
| + Tool/qt.py module does, or did). There shouldn't be a lot attribute
|
| + fetching or setting on these, so a little extra work shouldn't hurt.
|
| + """
|
| + def __call__(self, target=None, source=_null, *args, **kw):
|
| + if source is _null:
|
| + source = target
|
| + target = None
|
| + if not target is None and not SCons.Util.is_List(target):
|
| + target = [target]
|
| + if not source is None and not SCons.Util.is_List(source):
|
| + source = [source]
|
| + return apply(MethodWrapper.__call__, (self, target, source) + args, kw)
|
| +
|
| + def __repr__(self):
|
| + fmt = '<BuilderWrapper %s instance at 0x%08X>'
|
| + return fmt % (repr(self.name), id(self))
|
| +
|
| + def __str__(self):
|
| + return self.__repr__()
|
| +
|
| + def __getattr__(self, name):
|
| + if name == 'env':
|
| + return self.object
|
| + elif name == 'builder':
|
| + return self.method
|
| + else:
|
| + raise AttributeError, name
|
| +
|
| + def __setattr__(self, name, value):
|
| + if name == 'env':
|
| + self.object = value
|
| + elif name == 'builder':
|
| + self.method = value
|
| + else:
|
| + self.__dict__[name] = value
|
| +
|
| + # This allows a Builder to be executed directly
|
| + # through the Environment to which it's attached.
|
| + # In practice, we shouldn't need this, because
|
| + # builders actually get executed through a Node.
|
| + # But we do have a unit test for this, and can't
|
| + # yet rule out that it would be useful in the
|
| + # future, so leave it for now.
|
| + #def execute(self, **kw):
|
| + # kw['env'] = self.env
|
| + # apply(self.builder.execute, (), kw)
|
| +
|
| +class BuilderDict(UserDict):
|
| + """This is a dictionary-like class used by an Environment to hold
|
| + the Builders. We need to do this because every time someone changes
|
| + the Builders in the Environment's BUILDERS dictionary, we must
|
| + update the Environment's attributes."""
|
| + def __init__(self, dict, env):
|
| + # Set self.env before calling the superclass initialization,
|
| + # because it will end up calling our other methods, which will
|
| + # need to point the values in this dictionary to self.env.
|
| + self.env = env
|
| + UserDict.__init__(self, dict)
|
| +
|
| + def __semi_deepcopy__(self):
|
| + return self.__class__(self.data, self.env)
|
| +
|
| + def __setitem__(self, item, val):
|
| + try:
|
| + method = getattr(self.env, item).method
|
| + except AttributeError:
|
| + pass
|
| + else:
|
| + self.env.RemoveMethod(method)
|
| + UserDict.__setitem__(self, item, val)
|
| + BuilderWrapper(self.env, val, item)
|
| +
|
| + def __delitem__(self, item):
|
| + UserDict.__delitem__(self, item)
|
| + delattr(self.env, item)
|
| +
|
| + def update(self, dict):
|
| + for i, v in dict.items():
|
| + self.__setitem__(i, v)
|
| +
|
| +
|
| +
|
| +_is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
|
| +
|
| +def is_valid_construction_var(varstr):
|
| + """Return if the specified string is a legitimate construction
|
| + variable.
|
| + """
|
| + return _is_valid_var.match(varstr)
|
| +
|
| +
|
| +
|
| +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.)
|
| + """
|
| +
|
| + if SCons.Memoize.use_memoizer:
|
| + __metaclass__ = SCons.Memoize.Memoized_Metaclass
|
| +
|
| + def __init__(self, **kw):
|
| + """Initialization of an underlying SubstitutionEnvironment class.
|
| + """
|
| + if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
|
| + self.fs = SCons.Node.FS.get_default_fs()
|
| + self.ans = SCons.Node.Alias.default_ans
|
| + self.lookup_list = SCons.Node.arg2nodes_lookups
|
| + self._dict = kw.copy()
|
| + self._init_special()
|
| + self.added_methods = []
|
| + #self._memo = {}
|
| +
|
| + def _init_special(self):
|
| + """Initial the dispatch tables for special handling of
|
| + special construction variables."""
|
| + self._special_del = {}
|
| + self._special_del['SCANNERS'] = _del_SCANNERS
|
| +
|
| + self._special_set = {}
|
| + for key in reserved_construction_var_names:
|
| + self._special_set[key] = _set_reserved
|
| + self._special_set['BUILDERS'] = _set_BUILDERS
|
| + self._special_set['SCANNERS'] = _set_SCANNERS
|
| +
|
| + # Freeze the keys of self._special_set in a list for use by
|
| + # methods that need to check. (Empirically, list scanning has
|
| + # gotten better than dict.has_key() in Python 2.5.)
|
| + self._special_set_keys = self._special_set.keys()
|
| +
|
| + def __cmp__(self, other):
|
| + return cmp(self._dict, other._dict)
|
| +
|
| + def __delitem__(self, key):
|
| + special = self._special_del.get(key)
|
| + if special:
|
| + special(self, key)
|
| + else:
|
| + del self._dict[key]
|
| +
|
| + def __getitem__(self, key):
|
| + return self._dict[key]
|
| +
|
| + def __setitem__(self, key, value):
|
| + # This is heavily used. This implementation is the best we have
|
| + # according to the timings in bench/env.__setitem__.py.
|
| + #
|
| + # The "key in self._special_set_keys" test here seems to perform
|
| + # pretty well for the number of keys we have. A hard-coded
|
| + # list works a little better in Python 2.5, but that has the
|
| + # disadvantage of maybe getting out of sync if we ever add more
|
| + # variable names. Using self._special_set.has_key() works a
|
| + # little better in Python 2.4, but is worse then this test.
|
| + # So right now it seems like a good trade-off, but feel free to
|
| + # revisit this with bench/env.__setitem__.py as needed (and
|
| + # as newer versions of Python come out).
|
| + if key in self._special_set_keys:
|
| + self._special_set[key](self, key, value)
|
| + else:
|
| + # If we already have the entry, then it's obviously a valid
|
| + # key and we don't need to check. If we do check, using a
|
| + # global, pre-compiled regular expression directly is more
|
| + # efficient than calling another function or a method.
|
| + if not self._dict.has_key(key) \
|
| + and not _is_valid_var.match(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):
|
| + return self._dict.items()
|
| +
|
| + def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
|
| + if node_factory is _null:
|
| + node_factory = self.fs.File
|
| + if lookup_list is _null:
|
| + lookup_list = self.lookup_list
|
| +
|
| + if not args:
|
| + return []
|
| +
|
| + args = SCons.Util.flatten(args)
|
| +
|
| + nodes = []
|
| + for v in args:
|
| + if SCons.Util.is_String(v):
|
| + n = None
|
| + for l in lookup_list:
|
| + n = l(v)
|
| + if not n is None:
|
| + break
|
| + if not n is None:
|
| + if SCons.Util.is_String(n):
|
| + # n = self.subst(n, raw=1, **kw)
|
| + kw['raw'] = 1
|
| + n = apply(self.subst, (n,), kw)
|
| + if node_factory:
|
| + n = node_factory(n)
|
| + if SCons.Util.is_List(n):
|
| + nodes.extend(n)
|
| + else:
|
| + nodes.append(n)
|
| + elif node_factory:
|
| + # v = node_factory(self.subst(v, raw=1, **kw))
|
| + kw['raw'] = 1
|
| + v = node_factory(apply(self.subst, (v,), kw))
|
| + if SCons.Util.is_List(v):
|
| + nodes.extend(v)
|
| + else:
|
| + nodes.append(v)
|
| + else:
|
| + nodes.append(v)
|
| +
|
| + return nodes
|
| +
|
| + 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
|
| + in the string and begin with an initial underscore or
|
| + alphabetic character followed by any number of underscores
|
| + or alphanumeric characters. The construction variable names
|
| + may be surrounded by curly braces to separate the name from
|
| + trailing characters.
|
| + """
|
| + gvars = self.gvars()
|
| + lvars = self.lvars()
|
| + lvars['__env__'] = self
|
| + return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
|
| +
|
| + 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)
|
| + if SCons.Util.is_String(v):
|
| + v = self.subst(v, raw, target, source)
|
| + nkw[k] = v
|
| + return nkw
|
| +
|
| + def subst_list(self, string, raw=0, target=None, source=None, conv=None):
|
| + """Calls through to SCons.Subst.scons_subst_list(). See
|
| + the documentation for that function."""
|
| + gvars = self.gvars()
|
| + lvars = self.lvars()
|
| + lvars['__env__'] = self
|
| + return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
|
| +
|
| + def subst_path(self, path, target=None, source=None):
|
| + """Substitute a path list, turning EntryProxies into Nodes
|
| + and leaving Nodes (and other objects) as-is."""
|
| +
|
| + if not SCons.Util.is_List(path):
|
| + path = [path]
|
| +
|
| + def s(obj):
|
| + """This is the "string conversion" routine that we have our
|
| + substitutions use to return Nodes, not strings. This relies
|
| + on the fact that an EntryProxy object has a get() method that
|
| + returns the underlying Node that it wraps, which is a bit of
|
| + architectural dependence that we might need to break or modify
|
| + in the future in response to additional requirements."""
|
| + try:
|
| + get = obj.get
|
| + except AttributeError:
|
| + obj = SCons.Util.to_String_for_subst(obj)
|
| + else:
|
| + obj = get()
|
| + return obj
|
| +
|
| + r = []
|
| + for p in path:
|
| + if SCons.Util.is_String(p):
|
| + p = self.subst(p, target=target, source=source, conv=s)
|
| + if SCons.Util.is_List(p):
|
| + if len(p) == 1:
|
| + p = p[0]
|
| + else:
|
| + # We have an object plus a string, or multiple
|
| + # objects that we need to smush together. No choice
|
| + # but to make them into a string.
|
| + p = string.join(map(SCons.Util.to_String_for_subst, p), '')
|
| + else:
|
| + p = s(p)
|
| + r.append(p)
|
| + return r
|
| +
|
| + subst_target_source = subst
|
| +
|
| + def backtick(self, command):
|
| + import subprocess
|
| + # common arguments
|
| + kw = { 'stdout' : subprocess.PIPE,
|
| + 'stderr' : subprocess.PIPE,
|
| + 'universal_newlines' : True,
|
| + }
|
| + # if the command is a list, assume it's been quoted
|
| + # othewise force a shell
|
| + if not SCons.Util.is_List(command): kw['shell'] = True
|
| + # run constructed command
|
| + #TODO(1.5) p = SCons.Action._subproc(self, command, **kw)
|
| + p = apply(SCons.Action._subproc, (self, command), kw)
|
| + out,err = p.communicate()
|
| + status = p.wait()
|
| + if err:
|
| + sys.stderr.write(err)
|
| + if status:
|
| + raise OSError("'%s' exited %d" % (command, status))
|
| + return out
|
| +
|
| + def AddMethod(self, function, name=None):
|
| + """
|
| + Adds the specified function as a method of this construction
|
| + environment with the specified name. If the name is omitted,
|
| + the default name is the name of the function itself.
|
| + """
|
| + method = MethodWrapper(self, function, name)
|
| + self.added_methods.append(method)
|
| +
|
| + def RemoveMethod(self, function):
|
| + """
|
| + Removes the specified function's MethodWrapper from the
|
| + added_methods list, so we don't re-bind it when making a clone.
|
| + """
|
| + is_not_func = lambda dm, f=function: not dm.method is f
|
| + self.added_methods = filter(is_not_func, self.added_methods)
|
| +
|
| + 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 Clone() 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 not overrides: return self
|
| + o = copy_non_reserved_keywords(overrides)
|
| + if not o: return self
|
| + overrides = {}
|
| + merges = None
|
| + for key, value in o.items():
|
| + if key == 'parse_flags':
|
| + merges = value
|
| + else:
|
| + overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
|
| + env = OverrideEnvironment(self, overrides)
|
| + if merges: env.MergeFlags(merges)
|
| + return env
|
| +
|
| + def ParseFlags(self, *flags):
|
| + """
|
| + Parse the set of flags and return a dict with the flags placed
|
| + in the appropriate entry. The flags are treated as a typical
|
| + set of command-line flags for a GNU-like toolchain and used to
|
| + populate the entries in the dict immediately below. If one of
|
| + the flag strings begins with a bang (exclamation mark), it is
|
| + assumed to be a command and the rest of the string is executed;
|
| + the result of that evaluation is then added to the dict.
|
| + """
|
| + dict = {
|
| + 'ASFLAGS' : SCons.Util.CLVar(''),
|
| + 'CFLAGS' : SCons.Util.CLVar(''),
|
| + 'CCFLAGS' : SCons.Util.CLVar(''),
|
| + 'CPPDEFINES' : [],
|
| + 'CPPFLAGS' : SCons.Util.CLVar(''),
|
| + 'CPPPATH' : [],
|
| + 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
|
| + 'FRAMEWORKS' : SCons.Util.CLVar(''),
|
| + 'LIBPATH' : [],
|
| + 'LIBS' : [],
|
| + 'LINKFLAGS' : SCons.Util.CLVar(''),
|
| + 'RPATH' : [],
|
| + }
|
| +
|
| + # The use of the "me" parameter to provide our own name for
|
| + # recursion is an egregious hack to support Python 2.1 and before.
|
| + def do_parse(arg, me, self = self, dict = dict):
|
| + # if arg is a sequence, recurse with each element
|
| + if not arg:
|
| + return
|
| +
|
| + if not SCons.Util.is_String(arg):
|
| + for t in arg: me(t, me)
|
| + return
|
| +
|
| + # if arg is a command, execute it
|
| + if arg[0] == '!':
|
| + arg = self.backtick(arg[1:])
|
| +
|
| + # utility function to deal with -D option
|
| + def append_define(name, dict = dict):
|
| + t = string.split(name, '=')
|
| + if len(t) == 1:
|
| + dict['CPPDEFINES'].append(name)
|
| + else:
|
| + dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
|
| +
|
| + # Loop through the flags and add them to the appropriate option.
|
| + # This tries to strike a balance between checking for all possible
|
| + # flags and keeping the logic to a finite size, so it doesn't
|
| + # check for some that don't occur often. It particular, if the
|
| + # flag is not known to occur in a config script and there's a way
|
| + # of passing the flag to the right place (by wrapping it in a -W
|
| + # flag, for example) we don't check for it. Note that most
|
| + # preprocessor options are not handled, since unhandled options
|
| + # are placed in CCFLAGS, so unless the preprocessor is invoked
|
| + # separately, these flags will still get to the preprocessor.
|
| + # Other options not currently handled:
|
| + # -iqoutedir (preprocessor search path)
|
| + # -u symbol (linker undefined symbol)
|
| + # -s (linker strip files)
|
| + # -static* (linker static binding)
|
| + # -shared* (linker dynamic binding)
|
| + # -symbolic (linker global binding)
|
| + # -R dir (deprecated linker rpath)
|
| + # IBM compilers may also accept -qframeworkdir=foo
|
| +
|
| + params = shlex.split(arg)
|
| + append_next_arg_to = None # for multi-word args
|
| + for arg in params:
|
| + if append_next_arg_to:
|
| + if append_next_arg_to == 'CPPDEFINES':
|
| + append_define(arg)
|
| + elif append_next_arg_to == '-include':
|
| + t = ('-include', self.fs.File(arg))
|
| + dict['CCFLAGS'].append(t)
|
| + elif append_next_arg_to == '-isysroot':
|
| + t = ('-isysroot', arg)
|
| + dict['CCFLAGS'].append(t)
|
| + dict['LINKFLAGS'].append(t)
|
| + elif append_next_arg_to == '-arch':
|
| + t = ('-arch', arg)
|
| + dict['CCFLAGS'].append(t)
|
| + dict['LINKFLAGS'].append(t)
|
| + else:
|
| + dict[append_next_arg_to].append(arg)
|
| + append_next_arg_to = None
|
| + elif not arg[0] in ['-', '+']:
|
| + dict['LIBS'].append(self.fs.File(arg))
|
| + elif arg[:2] == '-L':
|
| + if arg[2:]:
|
| + dict['LIBPATH'].append(arg[2:])
|
| + else:
|
| + append_next_arg_to = 'LIBPATH'
|
| + elif arg[:2] == '-l':
|
| + if arg[2:]:
|
| + dict['LIBS'].append(arg[2:])
|
| + else:
|
| + append_next_arg_to = 'LIBS'
|
| + elif arg[:2] == '-I':
|
| + if arg[2:]:
|
| + dict['CPPPATH'].append(arg[2:])
|
| + else:
|
| + append_next_arg_to = 'CPPPATH'
|
| + elif arg[:4] == '-Wa,':
|
| + dict['ASFLAGS'].append(arg[4:])
|
| + dict['CCFLAGS'].append(arg)
|
| + elif arg[:4] == '-Wl,':
|
| + if arg[:11] == '-Wl,-rpath=':
|
| + dict['RPATH'].append(arg[11:])
|
| + elif arg[:7] == '-Wl,-R,':
|
| + dict['RPATH'].append(arg[7:])
|
| + elif arg[:6] == '-Wl,-R':
|
| + dict['RPATH'].append(arg[6:])
|
| + else:
|
| + dict['LINKFLAGS'].append(arg)
|
| + elif arg[:4] == '-Wp,':
|
| + dict['CPPFLAGS'].append(arg)
|
| + elif arg[:2] == '-D':
|
| + if arg[2:]:
|
| + append_define(arg[2:])
|
| + else:
|
| + append_next_arg_to = 'CPPDEFINES'
|
| + elif arg == '-framework':
|
| + append_next_arg_to = 'FRAMEWORKS'
|
| + elif arg[:14] == '-frameworkdir=':
|
| + dict['FRAMEWORKPATH'].append(arg[14:])
|
| + elif arg[:2] == '-F':
|
| + if arg[2:]:
|
| + dict['FRAMEWORKPATH'].append(arg[2:])
|
| + else:
|
| + append_next_arg_to = 'FRAMEWORKPATH'
|
| + elif arg == '-mno-cygwin':
|
| + dict['CCFLAGS'].append(arg)
|
| + dict['LINKFLAGS'].append(arg)
|
| + elif arg == '-mwindows':
|
| + dict['LINKFLAGS'].append(arg)
|
| + elif arg == '-pthread':
|
| + dict['CCFLAGS'].append(arg)
|
| + dict['LINKFLAGS'].append(arg)
|
| + elif arg[:5] == '-std=':
|
| + dict['CFLAGS'].append(arg) # C only
|
| + elif arg[0] == '+':
|
| + dict['CCFLAGS'].append(arg)
|
| + dict['LINKFLAGS'].append(arg)
|
| + elif arg in ['-include', '-isysroot', '-arch']:
|
| + append_next_arg_to = arg
|
| + else:
|
| + dict['CCFLAGS'].append(arg)
|
| +
|
| + for arg in flags:
|
| + do_parse(arg, do_parse)
|
| + return dict
|
| +
|
| + def MergeFlags(self, args, unique=1, dict=None):
|
| + """
|
| + Merge the dict in args into the construction variables of this
|
| + env, or the passed-in dict. If args is not a dict, it is
|
| + converted into a dict using ParseFlags. If unique is not set,
|
| + the flags are appended rather than merged.
|
| + """
|
| +
|
| + if dict is None:
|
| + dict = self
|
| + if not SCons.Util.is_Dict(args):
|
| + args = self.ParseFlags(args)
|
| + if not unique:
|
| + apply(self.Append, (), args)
|
| + return self
|
| + for key, value in args.items():
|
| + if not value:
|
| + continue
|
| + try:
|
| + orig = self[key]
|
| + except KeyError:
|
| + orig = value
|
| + else:
|
| + if not orig:
|
| + orig = value
|
| + elif value:
|
| + # Add orig and value. The logic here was lifted from
|
| + # part of env.Append() (see there for a lot of comments
|
| + # about the order in which things are tried) and is
|
| + # used mainly to handle coercion of strings to CLVar to
|
| + # "do the right thing" given (e.g.) an original CCFLAGS
|
| + # string variable like '-pipe -Wall'.
|
| + try:
|
| + orig = orig + value
|
| + except (KeyError, TypeError):
|
| + try:
|
| + add_to_orig = orig.append
|
| + except AttributeError:
|
| + value.insert(0, orig)
|
| + orig = value
|
| + else:
|
| + add_to_orig(value)
|
| + t = []
|
| + if key[-4:] == 'PATH':
|
| + ### keep left-most occurence
|
| + for v in orig:
|
| + if v not in t:
|
| + t.append(v)
|
| + else:
|
| + ### keep right-most occurence
|
| + orig.reverse()
|
| + for v in orig:
|
| + if v not in t:
|
| + t.insert(0, v)
|
| + self[key] = t
|
| + return self
|
| +
|
| +# def MergeShellPaths(self, args, prepend=1):
|
| +# """
|
| +# Merge the dict in args into the shell environment in env['ENV'].
|
| +# Shell path elements are appended or prepended according to prepend.
|
| +
|
| +# Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
|
| +
|
| +# Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'})
|
| +# prepends /usr/local/lib to env['ENV']['LIBPATH'].
|
| +# """
|
| +
|
| +# for pathname, pathval in args.items():
|
| +# if not pathval:
|
| +# continue
|
| +# if prepend:
|
| +# apply(self.PrependENVPath, (pathname, pathval))
|
| +# else:
|
| +# apply(self.AppendENVPath, (pathname, pathval))
|
| +
|
| +
|
| +# Used by the FindSourceFiles() method, below.
|
| +# Stuck here for support of pre-2.2 Python versions.
|
| +def build_source(ss, result):
|
| + for s in ss:
|
| + if isinstance(s, SCons.Node.FS.Dir):
|
| + build_source(s.all_children(), result)
|
| + elif s.has_builder():
|
| + build_source(s.sources, result)
|
| + elif isinstance(s.disambiguate(), SCons.Node.FS.File):
|
| + result.append(s)
|
| +
|
| +def default_decide_source(dependency, target, prev_ni):
|
| + f = SCons.Defaults.DefaultEnvironment().decide_source
|
| + return f(dependency, target, prev_ni)
|
| +
|
| +def default_decide_target(dependency, target, prev_ni):
|
| + f = SCons.Defaults.DefaultEnvironment().decide_target
|
| + return f(dependency, target, prev_ni)
|
| +
|
| +def default_copy_from_cache(src, dst):
|
| + f = SCons.Defaults.DefaultEnvironment().copy_from_cache
|
| + return f(src, dst)
|
| +
|
| +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.
|
| +
|
| + Keyword arguments supplied when the construction Environment
|
| + is created are construction variables used to initialize the
|
| + Environment.
|
| + """
|
| +
|
| + if SCons.Memoize.use_memoizer:
|
| + __metaclass__ = SCons.Memoize.Memoized_Metaclass
|
| +
|
| + memoizer_counters = []
|
| +
|
| + #######################################################################
|
| + # This is THE class for interacting with the SCons build engine,
|
| + # and it contains a lot of stuff, so we're going to try to keep this
|
| + # a little organized by grouping the methods.
|
| + #######################################################################
|
| +
|
| + #######################################################################
|
| + # Methods that make an Environment act like a dictionary. These have
|
| + # the expected standard names for Python mapping objects. Note that
|
| + # we don't actually make an Environment a subclass of UserDict for
|
| + # performance reasons. Note also that we only supply methods for
|
| + # dictionary functionality that we actually need and use.
|
| + #######################################################################
|
| +
|
| + def __init__(self,
|
| + platform=None,
|
| + tools=None,
|
| + toolpath=None,
|
| + variables=None,
|
| + parse_flags = 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, 'Environment.Base')
|
| + self._memo = {}
|
| + self.fs = SCons.Node.FS.get_default_fs()
|
| + self.ans = SCons.Node.Alias.default_ans
|
| + self.lookup_list = SCons.Node.arg2nodes_lookups
|
| + self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
|
| + self._init_special()
|
| + self.added_methods = []
|
| +
|
| + # We don't use AddMethod, or define these as methods in this
|
| + # class, because we *don't* want these functions to be bound
|
| + # methods. They need to operate independently so that the
|
| + # settings will work properly regardless of whether a given
|
| + # target ends up being built with a Base environment or an
|
| + # OverrideEnvironment or what have you.
|
| + self.decide_target = default_decide_target
|
| + self.decide_source = default_decide_source
|
| +
|
| + self.copy_from_cache = default_copy_from_cache
|
| +
|
| + self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
|
| +
|
| + if platform is None:
|
| + platform = self._dict.get('PLATFORM', None)
|
| + if platform is None:
|
| + platform = SCons.Platform.Platform()
|
| + if SCons.Util.is_String(platform):
|
| + platform = SCons.Platform.Platform(platform)
|
| + self._dict['PLATFORM'] = str(platform)
|
| + platform(self)
|
| +
|
| + # Apply the passed-in and customizable variables to the
|
| + # environment before calling the tools, because they may use
|
| + # some of them during initialization.
|
| + if kw.has_key('options'):
|
| + # Backwards compatibility: they may stll be using the
|
| + # old "options" keyword.
|
| + variables = kw['options']
|
| + del kw['options']
|
| + apply(self.Replace, (), kw)
|
| + keys = kw.keys()
|
| + if variables:
|
| + keys = keys + variables.keys()
|
| + variables.Update(self)
|
| +
|
| + save = {}
|
| + for k in keys:
|
| + try:
|
| + save[k] = self._dict[k]
|
| + except KeyError:
|
| + # No value may have been set if they tried to pass in a
|
| + # reserved variable name like TARGETS.
|
| + pass
|
| +
|
| + SCons.Tool.Initializers(self)
|
| +
|
| + if tools is None:
|
| + tools = self._dict.get('TOOLS', None)
|
| + if tools is None:
|
| + tools = ['default']
|
| + apply_tools(self, tools, toolpath)
|
| +
|
| + # Now restore the passed-in and customized variables
|
| + # to the environment, since the values the user set explicitly
|
| + # should override any values set by the tools.
|
| + for key, val in save.items():
|
| + self._dict[key] = val
|
| +
|
| + # Finally, apply any flags to be merged in
|
| + if parse_flags: self.MergeFlags(parse_flags)
|
| +
|
| + #######################################################################
|
| + # Utility methods that are primarily for internal use by SCons.
|
| + # These begin with lower-case letters.
|
| + #######################################################################
|
| +
|
| + def get_builder(self, name):
|
| + """Fetch the builder with the specified name from the environment.
|
| + """
|
| + try:
|
| + return self._dict['BUILDERS'][name]
|
| + except KeyError:
|
| + return None
|
| +
|
| + def get_CacheDir(self):
|
| + try:
|
| + path = self._CacheDir_path
|
| + except AttributeError:
|
| + path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
|
| + try:
|
| + if path == self._last_CacheDir_path:
|
| + return self._last_CacheDir
|
| + except AttributeError:
|
| + pass
|
| + cd = SCons.CacheDir.CacheDir(path)
|
| + self._last_CacheDir_path = path
|
| + self._last_CacheDir = cd
|
| + return cd
|
| +
|
| + def get_factory(self, factory, default='File'):
|
| + """Return a factory function for creating Nodes for this
|
| + construction environment.
|
| + """
|
| + name = default
|
| + try:
|
| + is_node = issubclass(factory, SCons.Node.Node)
|
| + except TypeError:
|
| + # The specified factory isn't a Node itself--it's
|
| + # most likely None, or possibly a callable.
|
| + pass
|
| + else:
|
| + if is_node:
|
| + # The specified factory is a Node (sub)class. Try to
|
| + # return the FS method that corresponds to the Node's
|
| + # name--that is, we return self.fs.Dir if they want a Dir,
|
| + # self.fs.File for a File, etc.
|
| + try: name = factory.__name__
|
| + except AttributeError: pass
|
| + else: factory = None
|
| + if not factory:
|
| + # They passed us None, or we picked up a name from a specified
|
| + # class, so return the FS method. (Note that we *don't*
|
| + # use our own self.{Dir,File} methods because that would
|
| + # cause env.subst() to be called twice on the file name,
|
| + # interfering with files that have $$ in them.)
|
| + factory = getattr(self.fs, name)
|
| + return factory
|
| +
|
| + memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
|
| +
|
| + def _gsm(self):
|
| + try:
|
| + return self._memo['_gsm']
|
| + except KeyError:
|
| + pass
|
| +
|
| + result = {}
|
| +
|
| + try:
|
| + scanners = self._dict['SCANNERS']
|
| + except KeyError:
|
| + pass
|
| + else:
|
| + # Reverse the scanner list so that, if multiple scanners
|
| + # claim they can scan the same suffix, earlier scanners
|
| + # in the list will overwrite later scanners, so that
|
| + # the result looks like a "first match" to the user.
|
| + if not SCons.Util.is_List(scanners):
|
| + scanners = [scanners]
|
| + else:
|
| + scanners = scanners[:] # copy so reverse() doesn't mod original
|
| + scanners.reverse()
|
| + for scanner in scanners:
|
| + for k in scanner.get_skeys(self):
|
| + result[k] = scanner
|
| +
|
| + self._memo['_gsm'] = result
|
| +
|
| + return result
|
| +
|
| + def get_scanner(self, skey):
|
| + """Find the appropriate scanner given a key (usually a file suffix).
|
| + """
|
| + return self._gsm().get(skey)
|
| +
|
| + def scanner_map_delete(self, kw=None):
|
| + """Delete the cached scanner map (if we need to).
|
| + """
|
| + try:
|
| + del self._memo['_gsm']
|
| + except KeyError:
|
| + pass
|
| +
|
| + def _update(self, dict):
|
| + """Update an environment's values directly, bypassing the normal
|
| + checks that occur when users try to set items.
|
| + """
|
| + self._dict.update(dict)
|
| +
|
| + def get_src_sig_type(self):
|
| + try:
|
| + return self.src_sig_type
|
| + except AttributeError:
|
| + t = SCons.Defaults.DefaultEnvironment().src_sig_type
|
| + self.src_sig_type = t
|
| + return t
|
| +
|
| + def get_tgt_sig_type(self):
|
| + try:
|
| + return self.tgt_sig_type
|
| + except AttributeError:
|
| + t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
|
| + self.tgt_sig_type = t
|
| + return t
|
| +
|
| + #######################################################################
|
| + # Public methods for manipulating an Environment. These begin with
|
| + # upper-case letters. The essential characteristic of methods in
|
| + # this section is that they do *not* have corresponding same-named
|
| + # global functions. For example, a stand-alone Append() function
|
| + # makes no sense, because Append() is all about appending values to
|
| + # an Environment's construction variables.
|
| + #######################################################################
|
| +
|
| + def Append(self, **kw):
|
| + """Append values to existing construction variables
|
| + in an Environment.
|
| + """
|
| + kw = copy_non_reserved_keywords(kw)
|
| + for key, val in kw.items():
|
| + # It would be easier on the eyes to write this using
|
| + # "continue" statements whenever we finish processing an item,
|
| + # but Python 1.5.2 apparently doesn't let you use "continue"
|
| + # within try:-except: blocks, so we have to nest our code.
|
| + try:
|
| + orig = self._dict[key]
|
| + except KeyError:
|
| + # No existing variable in the environment, so just set
|
| + # it to the new value.
|
| + self._dict[key] = val
|
| + else:
|
| + try:
|
| + # Check if the original looks like a dictionary.
|
| + # If it is, we can't just try adding the value because
|
| + # dictionaries don't have __add__() methods, and
|
| + # things like UserList will incorrectly coerce the
|
| + # original dict to a list (which we don't want).
|
| + update_dict = orig.update
|
| + except AttributeError:
|
| + try:
|
| + # Most straightforward: just try to add them
|
| + # together. This will work in most cases, when the
|
| + # original and new values are of compatible types.
|
| + self._dict[key] = orig + val
|
| + except (KeyError, TypeError):
|
| + try:
|
| + # Check if the original is a list.
|
| + add_to_orig = orig.append
|
| + except AttributeError:
|
| + # The original isn't a list, but the new
|
| + # value is (by process of elimination),
|
| + # so insert the original in the new value
|
| + # (if there's one to insert) and replace
|
| + # the variable with it.
|
| + if orig:
|
| + val.insert(0, orig)
|
| + self._dict[key] = val
|
| + else:
|
| + # The original is a list, so append the new
|
| + # value to it (if there's a value to append).
|
| + if val:
|
| + add_to_orig(val)
|
| + else:
|
| + # The original looks like a dictionary, so update it
|
| + # based on what we think the value looks like.
|
| + if SCons.Util.is_List(val):
|
| + for v in val:
|
| + orig[v] = None
|
| + else:
|
| + try:
|
| + update_dict(val)
|
| + except (AttributeError, TypeError, ValueError):
|
| + if SCons.Util.is_Dict(val):
|
| + for k, v in val.items():
|
| + orig[k] = v
|
| + else:
|
| + orig[val] = None
|
| + self.scanner_map_delete(kw)
|
| +
|
| + def AppendENVPath(self, name, newpath, envname = 'ENV',
|
| + sep = os.pathsep, delete_existing=1):
|
| + """Append path elements to the path 'name' in the 'ENV'
|
| + dictionary for this environment. Will only add any particular
|
| + path once, and will normpath and normcase all paths to help
|
| + assure this. This can also handle the case where the env
|
| + variable is a list instead of a string.
|
| +
|
| + If delete_existing is 0, a newpath which is already in the path
|
| + will not be moved to the end (it will be left where it is).
|
| + """
|
| +
|
| + orig = ''
|
| + if self._dict.has_key(envname) and self._dict[envname].has_key(name):
|
| + orig = self._dict[envname][name]
|
| +
|
| + nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing)
|
| +
|
| + if not self._dict.has_key(envname):
|
| + self._dict[envname] = {}
|
| +
|
| + self._dict[envname][name] = nv
|
| +
|
| + def AppendUnique(self, delete_existing=0, **kw):
|
| + """Append values to existing construction variables
|
| + in an Environment, if they're not already there.
|
| + If delete_existing is 1, removes existing values first, so
|
| + values move to end.
|
| + """
|
| + kw = copy_non_reserved_keywords(kw)
|
| + for key, val in kw.items():
|
| + if not self._dict.has_key(key) or self._dict[key] in ('', None):
|
| + self._dict[key] = val
|
| + elif SCons.Util.is_Dict(self._dict[key]) and \
|
| + SCons.Util.is_Dict(val):
|
| + self._dict[key].update(val)
|
| + elif SCons.Util.is_List(val):
|
| + dk = self._dict[key]
|
| + if not SCons.Util.is_List(dk):
|
| + dk = [dk]
|
| + if delete_existing:
|
| + dk = filter(lambda x, val=val: x not in val, dk)
|
| + else:
|
| + val = filter(lambda x, dk=dk: x not in dk, val)
|
| + self._dict[key] = dk + val
|
| + else:
|
| + dk = self._dict[key]
|
| + if SCons.Util.is_List(dk):
|
| + # By elimination, val is not a list. Since dk is a
|
| + # list, wrap val in a list first.
|
| + if delete_existing:
|
| + dk = filter(lambda x, val=val: x not in val, dk)
|
| + self._dict[key] = dk + [val]
|
| + else:
|
| + if not val in dk:
|
| + self._dict[key] = dk + [val]
|
| + else:
|
| + if delete_existing:
|
| + dk = filter(lambda x, val=val: x not in val, dk)
|
| + self._dict[key] = dk + val
|
| + self.scanner_map_delete(kw)
|
| +
|
| + def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
|
| + """Return a copy of a construction Environment. The
|
| + copy is like a Python "deep copy"--that is, independent
|
| + copies are made recursively of each objects--except that
|
| + a reference is copied when an object is not deep-copyable
|
| + (like a function). There are no references to any mutable
|
| + objects in the original Environment.
|
| + """
|
| + clone = copy.copy(self)
|
| + clone._dict = semi_deepcopy(self._dict)
|
| +
|
| + try:
|
| + cbd = clone._dict['BUILDERS']
|
| + except KeyError:
|
| + pass
|
| + else:
|
| + clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
|
| +
|
| + # Check the methods added via AddMethod() and re-bind them to
|
| + # the cloned environment. Only do this if the attribute hasn't
|
| + # been overwritten by the user explicitly and still points to
|
| + # the added method.
|
| + clone.added_methods = []
|
| + for mw in self.added_methods:
|
| + if mw == getattr(self, mw.name):
|
| + clone.added_methods.append(mw.clone(clone))
|
| +
|
| + clone._memo = {}
|
| +
|
| + # Apply passed-in variables before the tools
|
| + # so the tools can use the new variables
|
| + kw = copy_non_reserved_keywords(kw)
|
| + new = {}
|
| + for key, value in kw.items():
|
| + new[key] = SCons.Subst.scons_subst_once(value, self, key)
|
| + apply(clone.Replace, (), new)
|
| +
|
| + apply_tools(clone, tools, toolpath)
|
| +
|
| + # apply them again in case the tools overwrote them
|
| + apply(clone.Replace, (), new)
|
| +
|
| + # Finally, apply any flags to be merged in
|
| + if parse_flags: clone.MergeFlags(parse_flags)
|
| +
|
| + if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
|
| + return clone
|
| +
|
| + def Copy(self, *args, **kw):
|
| + global _warn_copy_deprecated
|
| + if _warn_copy_deprecated:
|
| + msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
|
| + SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
|
| + _warn_copy_deprecated = False
|
| + return apply(self.Clone, args, kw)
|
| +
|
| + def _changed_build(self, dependency, target, prev_ni):
|
| + if dependency.changed_state(target, prev_ni):
|
| + return 1
|
| + return self.decide_source(dependency, target, prev_ni)
|
| +
|
| + def _changed_content(self, dependency, target, prev_ni):
|
| + return dependency.changed_content(target, prev_ni)
|
| +
|
| + def _changed_source(self, dependency, target, prev_ni):
|
| + target_env = dependency.get_build_env()
|
| + type = target_env.get_tgt_sig_type()
|
| + if type == 'source':
|
| + return target_env.decide_source(dependency, target, prev_ni)
|
| + else:
|
| + return target_env.decide_target(dependency, target, prev_ni)
|
| +
|
| + def _changed_timestamp_then_content(self, dependency, target, prev_ni):
|
| + return dependency.changed_timestamp_then_content(target, prev_ni)
|
| +
|
| + def _changed_timestamp_newer(self, dependency, target, prev_ni):
|
| + return dependency.changed_timestamp_newer(target, prev_ni)
|
| +
|
| + def _changed_timestamp_match(self, dependency, target, prev_ni):
|
| + return dependency.changed_timestamp_match(target, prev_ni)
|
| +
|
| + def _copy_from_cache(self, src, dst):
|
| + return self.fs.copy(src, dst)
|
| +
|
| + def _copy2_from_cache(self, src, dst):
|
| + return self.fs.copy2(src, dst)
|
| +
|
| + def Decider(self, function):
|
| + copy_function = self._copy2_from_cache
|
| + if function in ('MD5', 'content'):
|
| + if not SCons.Util.md5:
|
| + raise UserError, "MD5 signatures are not available in this version of Python."
|
| + function = self._changed_content
|
| + elif function == 'MD5-timestamp':
|
| + function = self._changed_timestamp_then_content
|
| + elif function in ('timestamp-newer', 'make'):
|
| + function = self._changed_timestamp_newer
|
| + copy_function = self._copy_from_cache
|
| + elif function == 'timestamp-match':
|
| + function = self._changed_timestamp_match
|
| + elif not callable(function):
|
| + raise UserError, "Unknown Decider value %s" % repr(function)
|
| +
|
| + # We don't use AddMethod because we don't want to turn the
|
| + # function, which only expects three arguments, into a bound
|
| + # method, which would add self as an initial, fourth argument.
|
| + self.decide_target = function
|
| + self.decide_source = function
|
| +
|
| + self.copy_from_cache = copy_function
|
| +
|
| + def Detect(self, progs):
|
| + """Return the first available program in progs.
|
| + """
|
| + if not SCons.Util.is_List(progs):
|
| + progs = [ progs ]
|
| + for prog in progs:
|
| + path = self.WhereIs(prog)
|
| + if path: return prog
|
| + return None
|
| +
|
| + def Dictionary(self, *args):
|
| + if not args:
|
| + return self._dict
|
| + dlist = map(lambda x, s=self: s._dict[x], args)
|
| + if len(dlist) == 1:
|
| + dlist = dlist[0]
|
| + return dlist
|
| +
|
| + def Dump(self, key = None):
|
| + """
|
| + Using the standard Python pretty printer, dump the contents of the
|
| + scons build environment to stdout.
|
| +
|
| + If the key passed in is anything other than None, then that will
|
| + be used as an index into the build environment dictionary and
|
| + whatever is found there will be fed into the pretty printer. Note
|
| + that this key is case sensitive.
|
| + """
|
| + import pprint
|
| + pp = pprint.PrettyPrinter(indent=2)
|
| + if key:
|
| + dict = self.Dictionary(key)
|
| + else:
|
| + dict = self.Dictionary()
|
| + return pp.pformat(dict)
|
| +
|
| + def FindIxes(self, paths, prefix, suffix):
|
| + """
|
| + Search a list of paths for something that matches the prefix and suffix.
|
| +
|
| + paths - the list of paths or nodes.
|
| + prefix - construction variable for the prefix.
|
| + suffix - construction variable for the suffix.
|
| + """
|
| +
|
| + suffix = self.subst('$'+suffix)
|
| + prefix = self.subst('$'+prefix)
|
| +
|
| + for path in paths:
|
| + dir,name = os.path.split(str(path))
|
| + if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
|
| + return path
|
| +
|
| + def ParseConfig(self, command, function=None, unique=1):
|
| + """
|
| + Use the specified function to parse the output of the command
|
| + in order to modify the current environment. The 'command' can
|
| + be a string or a list of strings representing a command and
|
| + its arguments. 'Function' is an optional argument that takes
|
| + the environment, the output of the command, and the unique flag.
|
| + If no function is specified, MergeFlags, which treats the output
|
| + as the result of a typical 'X-config' command (i.e. gtk-config),
|
| + will merge the output into the appropriate variables.
|
| + """
|
| + if function is None:
|
| + def parse_conf(env, cmd, unique=unique):
|
| + return env.MergeFlags(cmd, unique)
|
| + function = parse_conf
|
| + if SCons.Util.is_List(command):
|
| + command = string.join(command)
|
| + command = self.subst(command)
|
| + return function(self, self.backtick(command))
|
| +
|
| + def ParseDepends(self, filename, must_exist=None, only_one=0):
|
| + """
|
| + Parse a mkdep-style file for explicit dependencies. This is
|
| + completely abusable, and should be unnecessary in the "normal"
|
| + case of proper SCons configuration, but it may help make
|
| + the transition from a Make hierarchy easier for some people
|
| + to swallow. It can also be genuinely useful when using a tool
|
| + that can write a .d file, but for which writing a scanner would
|
| + be too complicated.
|
| + """
|
| + filename = self.subst(filename)
|
| + try:
|
| + fp = open(filename, 'r')
|
| + except IOError:
|
| + if must_exist:
|
| + raise
|
| + return
|
| + lines = SCons.Util.LogicalLines(fp).readlines()
|
| + lines = filter(lambda l: l[0] != '#', lines)
|
| + tdlist = []
|
| + for line in lines:
|
| + try:
|
| + target, depends = string.split(line, ':', 1)
|
| + except (AttributeError, TypeError, ValueError):
|
| + # Python 1.5.2 throws TypeError if line isn't a string,
|
| + # Python 2.x throws AttributeError because it tries
|
| + # to call line.split(). Either can throw ValueError
|
| + # if the line doesn't split into two or more elements.
|
| + pass
|
| + else:
|
| + tdlist.append((string.split(target), string.split(depends)))
|
| + if only_one:
|
| + targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
|
| + if len(targets) > 1:
|
| + raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
|
| + for target, depends in tdlist:
|
| + self.Depends(target, depends)
|
| +
|
| + def Platform(self, platform):
|
| + platform = self.subst(platform)
|
| + return SCons.Platform.Platform(platform)(self)
|
| +
|
| + def Prepend(self, **kw):
|
| + """Prepend values to existing construction variables
|
| + in an Environment.
|
| + """
|
| + kw = copy_non_reserved_keywords(kw)
|
| + for key, val in kw.items():
|
| + # It would be easier on the eyes to write this using
|
| + # "continue" statements whenever we finish processing an item,
|
| + # but Python 1.5.2 apparently doesn't let you use "continue"
|
| + # within try:-except: blocks, so we have to nest our code.
|
| + try:
|
| + orig = self._dict[key]
|
| + except KeyError:
|
| + # No existing variable in the environment, so just set
|
| + # it to the new value.
|
| + self._dict[key] = val
|
| + else:
|
| + try:
|
| + # Check if the original looks like a dictionary.
|
| + # If it is, we can't just try adding the value because
|
| + # dictionaries don't have __add__() methods, and
|
| + # things like UserList will incorrectly coerce the
|
| + # original dict to a list (which we don't want).
|
| + update_dict = orig.update
|
| + except AttributeError:
|
| + try:
|
| + # Most straightforward: just try to add them
|
| + # together. This will work in most cases, when the
|
| + # original and new values are of compatible types.
|
| + self._dict[key] = val + orig
|
| + except (KeyError, TypeError):
|
| + try:
|
| + # Check if the added value is a list.
|
| + add_to_val = val.append
|
| + except AttributeError:
|
| + # The added value isn't a list, but the
|
| + # original is (by process of elimination),
|
| + # so insert the the new value in the original
|
| + # (if there's one to insert).
|
| + if val:
|
| + orig.insert(0, val)
|
| + else:
|
| + # The added value is a list, so append
|
| + # the original to it (if there's a value
|
| + # to append).
|
| + if orig:
|
| + add_to_val(orig)
|
| + self._dict[key] = val
|
| + else:
|
| + # The original looks like a dictionary, so update it
|
| + # based on what we think the value looks like.
|
| + if SCons.Util.is_List(val):
|
| + for v in val:
|
| + orig[v] = None
|
| + else:
|
| + try:
|
| + update_dict(val)
|
| + except (AttributeError, TypeError, ValueError):
|
| + if SCons.Util.is_Dict(val):
|
| + for k, v in val.items():
|
| + orig[k] = v
|
| + else:
|
| + orig[val] = None
|
| + self.scanner_map_delete(kw)
|
| +
|
| + def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep,
|
| + delete_existing=1):
|
| + """Prepend path elements to the path 'name' in the 'ENV'
|
| + dictionary for this environment. Will only add any particular
|
| + path once, and will normpath and normcase all paths to help
|
| + assure this. This can also handle the case where the env
|
| + variable is a list instead of a string.
|
| +
|
| + If delete_existing is 0, a newpath which is already in the path
|
| + will not be moved to the front (it will be left where it is).
|
| + """
|
| +
|
| + orig = ''
|
| + if self._dict.has_key(envname) and self._dict[envname].has_key(name):
|
| + orig = self._dict[envname][name]
|
| +
|
| + nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing)
|
| +
|
| + if not self._dict.has_key(envname):
|
| + self._dict[envname] = {}
|
| +
|
| + self._dict[envname][name] = nv
|
| +
|
| + def PrependUnique(self, delete_existing=0, **kw):
|
| + """Prepend values to existing construction variables
|
| + in an Environment, if they're not already there.
|
| + If delete_existing is 1, removes existing values first, so
|
| + values move to front.
|
| + """
|
| + kw = copy_non_reserved_keywords(kw)
|
| + for key, val in kw.items():
|
| + if not self._dict.has_key(key) or self._dict[key] in ('', None):
|
| + self._dict[key] = val
|
| + elif SCons.Util.is_Dict(self._dict[key]) and \
|
| + SCons.Util.is_Dict(val):
|
| + self._dict[key].update(val)
|
| + elif SCons.Util.is_List(val):
|
| + dk = self._dict[key]
|
| + if not SCons.Util.is_List(dk):
|
| + dk = [dk]
|
| + if delete_existing:
|
| + dk = filter(lambda x, val=val: x not in val, dk)
|
| + else:
|
| + val = filter(lambda x, dk=dk: x not in dk, val)
|
| + self._dict[key] = val + dk
|
| + else:
|
| + dk = self._dict[key]
|
| + if SCons.Util.is_List(dk):
|
| + # By elimination, val is not a list. Since dk is a
|
| + # list, wrap val in a list first.
|
| + if delete_existing:
|
| + dk = filter(lambda x, val=val: x not in val, dk)
|
| + self._dict[key] = [val] + dk
|
| + else:
|
| + if not val in dk:
|
| + self._dict[key] = [val] + dk
|
| + else:
|
| + if delete_existing:
|
| + dk = filter(lambda x, val=val: x not in val, dk)
|
| + self._dict[key] = val + dk
|
| + self.scanner_map_delete(kw)
|
| +
|
| + def Replace(self, **kw):
|
| + """Replace existing construction variables in an Environment
|
| + with new construction variables and/or values.
|
| + """
|
| + try:
|
| + kwbd = kw['BUILDERS']
|
| + except KeyError:
|
| + pass
|
| + else:
|
| + kwbd = semi_deepcopy(kwbd)
|
| + del kw['BUILDERS']
|
| + self.__setitem__('BUILDERS', kwbd)
|
| + kw = copy_non_reserved_keywords(kw)
|
| + self._update(semi_deepcopy(kw))
|
| + self.scanner_map_delete(kw)
|
| +
|
| + def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
|
| + """
|
| + Replace old_prefix with new_prefix and old_suffix with new_suffix.
|
| +
|
| + env - Environment used to interpolate variables.
|
| + path - the path that will be modified.
|
| + old_prefix - construction variable for the old prefix.
|
| + old_suffix - construction variable for the old suffix.
|
| + new_prefix - construction variable for the new prefix.
|
| + new_suffix - construction variable for the new suffix.
|
| + """
|
| + old_prefix = self.subst('$'+old_prefix)
|
| + old_suffix = self.subst('$'+old_suffix)
|
| +
|
| + new_prefix = self.subst('$'+new_prefix)
|
| + new_suffix = self.subst('$'+new_suffix)
|
| +
|
| + dir,name = os.path.split(str(path))
|
| + if name[:len(old_prefix)] == old_prefix:
|
| + name = name[len(old_prefix):]
|
| + if name[-len(old_suffix):] == old_suffix:
|
| + name = name[:-len(old_suffix)]
|
| + return os.path.join(dir, new_prefix+name+new_suffix)
|
| +
|
| + def SetDefault(self, **kw):
|
| + for k in kw.keys():
|
| + if self._dict.has_key(k):
|
| + del kw[k]
|
| + apply(self.Replace, (), kw)
|
| +
|
| + def _find_toolpath_dir(self, tp):
|
| + return self.fs.Dir(self.subst(tp)).srcnode().abspath
|
| +
|
| + def Tool(self, tool, toolpath=None, **kw):
|
| + if SCons.Util.is_String(tool):
|
| + tool = self.subst(tool)
|
| + if toolpath is None:
|
| + toolpath = self.get('toolpath', [])
|
| + toolpath = map(self._find_toolpath_dir, toolpath)
|
| + tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
|
| + tool(self)
|
| +
|
| + def WhereIs(self, prog, path=None, pathext=None, reject=[]):
|
| + """Find prog in the path.
|
| + """
|
| + if path is None:
|
| + try:
|
| + path = self['ENV']['PATH']
|
| + except KeyError:
|
| + pass
|
| + elif SCons.Util.is_String(path):
|
| + path = self.subst(path)
|
| + if pathext is None:
|
| + try:
|
| + pathext = self['ENV']['PATHEXT']
|
| + except KeyError:
|
| + pass
|
| + elif SCons.Util.is_String(pathext):
|
| + pathext = self.subst(pathext)
|
| + prog = self.subst(prog)
|
| + path = SCons.Util.WhereIs(prog, path, pathext, reject)
|
| + if path: return path
|
| + return None
|
| +
|
| + #######################################################################
|
| + # Public methods for doing real "SCons stuff" (manipulating
|
| + # dependencies, setting attributes on targets, etc.). These begin
|
| + # with upper-case letters. The essential characteristic of methods
|
| + # in this section is that they all *should* have corresponding
|
| + # same-named global functions.
|
| + #######################################################################
|
| +
|
| + def Action(self, *args, **kw):
|
| + def subst_string(a, self=self):
|
| + if SCons.Util.is_String(a):
|
| + a = self.subst(a)
|
| + return a
|
| + nargs = map(subst_string, args)
|
| + nkw = self.subst_kw(kw)
|
| + return apply(SCons.Action.Action, nargs, nkw)
|
| +
|
| + def AddPreAction(self, files, action):
|
| + nodes = self.arg2nodes(files, self.fs.Entry)
|
| + action = SCons.Action.Action(action)
|
| + uniq = {}
|
| + for executor in map(lambda n: n.get_executor(), nodes):
|
| + uniq[executor] = 1
|
| + for executor in uniq.keys():
|
| + executor.add_pre_action(action)
|
| + return nodes
|
| +
|
| + def AddPostAction(self, files, action):
|
| + nodes = self.arg2nodes(files, self.fs.Entry)
|
| + action = SCons.Action.Action(action)
|
| + uniq = {}
|
| + for executor in map(lambda n: n.get_executor(), nodes):
|
| + uniq[executor] = 1
|
| + for executor in uniq.keys():
|
| + executor.add_post_action(action)
|
| + return nodes
|
| +
|
| + def Alias(self, target, source=[], action=None, **kw):
|
| + tlist = self.arg2nodes(target, self.ans.Alias)
|
| + if not SCons.Util.is_List(source):
|
| + source = [source]
|
| + source = filter(None, source)
|
| +
|
| + if not action:
|
| + if not source:
|
| + # There are no source files and no action, so just
|
| + # return a target list of classic Alias Nodes, without
|
| + # any builder. The externally visible effect is that
|
| + # this will make the wrapping Script.BuildTask class
|
| + # say that there's "Nothing to be done" for this Alias,
|
| + # instead of that it's "up to date."
|
| + return tlist
|
| +
|
| + # No action, but there are sources. Re-call all the target
|
| + # builders to add the sources to each target.
|
| + result = []
|
| + for t in tlist:
|
| + bld = t.get_builder(AliasBuilder)
|
| + result.extend(bld(self, t, source))
|
| + return result
|
| +
|
| + nkw = self.subst_kw(kw)
|
| + nkw.update({
|
| + 'action' : SCons.Action.Action(action),
|
| + 'source_factory' : self.fs.Entry,
|
| + 'multi' : 1,
|
| + 'is_explicit' : None,
|
| + })
|
| + bld = apply(SCons.Builder.Builder, (), nkw)
|
| +
|
| + # Apply the Builder separately to each target so that the Aliases
|
| + # stay separate. If we did one "normal" Builder call with the
|
| + # whole target list, then all of the target Aliases would be
|
| + # associated under a single Executor.
|
| + result = []
|
| + for t in tlist:
|
| + # Calling the convert() method will cause a new Executor to be
|
| + # created from scratch, so we have to explicitly initialize
|
| + # it with the target's existing sources, plus our new ones,
|
| + # so nothing gets lost.
|
| + b = t.get_builder()
|
| + if b is None or b is AliasBuilder:
|
| + b = bld
|
| + else:
|
| + nkw['action'] = b.action + action
|
| + b = apply(SCons.Builder.Builder, (), nkw)
|
| + t.convert()
|
| + result.extend(b(self, t, t.sources + source))
|
| + return result
|
| +
|
| + def AlwaysBuild(self, *targets):
|
| + tlist = []
|
| + for t in targets:
|
| + tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
| + for t in tlist:
|
| + t.set_always_build()
|
| + return tlist
|
| +
|
| + def BuildDir(self, *args, **kw):
|
| + if kw.has_key('build_dir'):
|
| + kw['variant_dir'] = kw['build_dir']
|
| + del kw['build_dir']
|
| + return apply(self.VariantDir, args, kw)
|
| +
|
| + def Builder(self, **kw):
|
| + nkw = self.subst_kw(kw)
|
| + return apply(SCons.Builder.Builder, [], nkw)
|
| +
|
| + def CacheDir(self, path):
|
| + import SCons.CacheDir
|
| + if not path is None:
|
| + path = self.subst(path)
|
| + self._CacheDir_path = path
|
| +
|
| + def Clean(self, targets, files):
|
| + global CleanTargets
|
| + tlist = self.arg2nodes(targets, self.fs.Entry)
|
| + flist = self.arg2nodes(files, self.fs.Entry)
|
| + for t in tlist:
|
| + try:
|
| + CleanTargets[t].extend(flist)
|
| + except KeyError:
|
| + CleanTargets[t] = flist
|
| +
|
| + def Configure(self, *args, **kw):
|
| + nargs = [self]
|
| + if args:
|
| + nargs = nargs + self.subst_list(args)[0]
|
| + nkw = self.subst_kw(kw)
|
| + nkw['_depth'] = kw.get('_depth', 0) + 1
|
| + try:
|
| + nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
|
| + except KeyError:
|
| + pass
|
| + return apply(SCons.SConf.SConf, nargs, nkw)
|
| +
|
| + def Command(self, target, source, action, **kw):
|
| + """Builds the supplied target files from the supplied
|
| + source files using the supplied action. Action may
|
| + be any type that the Builder constructor will accept
|
| + for an action."""
|
| + bkw = {
|
| + 'action' : action,
|
| + 'target_factory' : self.fs.Entry,
|
| + 'source_factory' : self.fs.Entry,
|
| + }
|
| + try: bkw['source_scanner'] = kw['source_scanner']
|
| + except KeyError: pass
|
| + else: del kw['source_scanner']
|
| + bld = apply(SCons.Builder.Builder, (), bkw)
|
| + return apply(bld, (self, target, source), kw)
|
| +
|
| + def Depends(self, target, dependency):
|
| + """Explicity specify that 'target's depend on 'dependency'."""
|
| + tlist = self.arg2nodes(target, self.fs.Entry)
|
| + dlist = self.arg2nodes(dependency, self.fs.Entry)
|
| + for t in tlist:
|
| + t.add_dependency(dlist)
|
| + return tlist
|
| +
|
| + def Dir(self, name, *args, **kw):
|
| + """
|
| + """
|
| + s = self.subst(name)
|
| + if SCons.Util.is_Sequence(s):
|
| + result=[]
|
| + for e in s:
|
| + result.append(apply(self.fs.Dir, (e,) + args, kw))
|
| + return result
|
| + return apply(self.fs.Dir, (s,) + args, kw)
|
| +
|
| + def NoClean(self, *targets):
|
| + """Tags a target so that it will not be cleaned by -c"""
|
| + tlist = []
|
| + for t in targets:
|
| + tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
| + for t in tlist:
|
| + t.set_noclean()
|
| + return tlist
|
| +
|
| + def NoCache(self, *targets):
|
| + """Tags a target so that it will not be cached"""
|
| + tlist = []
|
| + for t in targets:
|
| + tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
| + for t in tlist:
|
| + t.set_nocache()
|
| + return tlist
|
| +
|
| + def Entry(self, name, *args, **kw):
|
| + """
|
| + """
|
| + s = self.subst(name)
|
| + if SCons.Util.is_Sequence(s):
|
| + result=[]
|
| + for e in s:
|
| + result.append(apply(self.fs.Entry, (e,) + args, kw))
|
| + return result
|
| + return apply(self.fs.Entry, (s,) + args, kw)
|
| +
|
| + def Environment(self, **kw):
|
| + return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
|
| +
|
| + def Execute(self, action, *args, **kw):
|
| + """Directly execute an action through an Environment
|
| + """
|
| + action = apply(self.Action, (action,) + args, kw)
|
| + result = action([], [], self)
|
| + if isinstance(result, SCons.Errors.BuildError):
|
| + errstr = result.errstr
|
| + if result.filename:
|
| + errstr = result.filename + ': ' + errstr
|
| + sys.stderr.write("scons: *** %s\n" % errstr)
|
| + return result.status
|
| + else:
|
| + return result
|
| +
|
| + def File(self, name, *args, **kw):
|
| + """
|
| + """
|
| + s = self.subst(name)
|
| + if SCons.Util.is_Sequence(s):
|
| + result=[]
|
| + for e in s:
|
| + result.append(apply(self.fs.File, (e,) + args, kw))
|
| + return result
|
| + return apply(self.fs.File, (s,) + args, kw)
|
| +
|
| + def FindFile(self, file, dirs):
|
| + file = self.subst(file)
|
| + nodes = self.arg2nodes(dirs, self.fs.Dir)
|
| + return SCons.Node.FS.find_file(file, tuple(nodes))
|
| +
|
| + def Flatten(self, sequence):
|
| + return SCons.Util.flatten(sequence)
|
| +
|
| + def GetBuildPath(self, files):
|
| + result = map(str, self.arg2nodes(files, self.fs.Entry))
|
| + if SCons.Util.is_List(files):
|
| + return result
|
| + else:
|
| + return result[0]
|
| +
|
| + def Glob(self, pattern, ondisk=True, source=False, strings=False):
|
| + return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
|
| +
|
| + def Ignore(self, target, dependency):
|
| + """Ignore a dependency."""
|
| + tlist = self.arg2nodes(target, self.fs.Entry)
|
| + dlist = self.arg2nodes(dependency, self.fs.Entry)
|
| + for t in tlist:
|
| + t.add_ignore(dlist)
|
| + return tlist
|
| +
|
| + def Literal(self, string):
|
| + return SCons.Subst.Literal(string)
|
| +
|
| + def Local(self, *targets):
|
| + ret = []
|
| + for targ in targets:
|
| + if isinstance(targ, SCons.Node.Node):
|
| + targ.set_local()
|
| + ret.append(targ)
|
| + else:
|
| + for t in self.arg2nodes(targ, self.fs.Entry):
|
| + t.set_local()
|
| + ret.append(t)
|
| + return ret
|
| +
|
| + def Precious(self, *targets):
|
| + tlist = []
|
| + for t in targets:
|
| + tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
| + for t in tlist:
|
| + t.set_precious()
|
| + return tlist
|
| +
|
| + def Repository(self, *dirs, **kw):
|
| + dirs = self.arg2nodes(list(dirs), self.fs.Dir)
|
| + apply(self.fs.Repository, dirs, kw)
|
| +
|
| + def Requires(self, target, prerequisite):
|
| + """Specify that 'prerequisite' must be built before 'target',
|
| + (but 'target' does not actually depend on 'prerequisite'
|
| + and need not be rebuilt if it changes)."""
|
| + tlist = self.arg2nodes(target, self.fs.Entry)
|
| + plist = self.arg2nodes(prerequisite, self.fs.Entry)
|
| + for t in tlist:
|
| + t.add_prerequisite(plist)
|
| + return tlist
|
| +
|
| + def Scanner(self, *args, **kw):
|
| + nargs = []
|
| + for arg in args:
|
| + if SCons.Util.is_String(arg):
|
| + arg = self.subst(arg)
|
| + nargs.append(arg)
|
| + nkw = self.subst_kw(kw)
|
| + return apply(SCons.Scanner.Base, nargs, nkw)
|
| +
|
| + def SConsignFile(self, name=".sconsign", dbm_module=None):
|
| + if not name is None:
|
| + name = self.subst(name)
|
| + if not os.path.isabs(name):
|
| + name = os.path.join(str(self.fs.SConstruct_dir), name)
|
| + if name:
|
| + name = os.path.normpath(name)
|
| + sconsign_dir = os.path.dirname(name)
|
| + if sconsign_dir and not os.path.exists(sconsign_dir):
|
| + self.Execute(SCons.Defaults.Mkdir(sconsign_dir))
|
| + SCons.SConsign.File(name, dbm_module)
|
| +
|
| + def SideEffect(self, side_effect, target):
|
| + """Tell scons that side_effects are built as side
|
| + effects of building targets."""
|
| + side_effects = self.arg2nodes(side_effect, self.fs.Entry)
|
| + targets = self.arg2nodes(target, self.fs.Entry)
|
| +
|
| + for side_effect in side_effects:
|
| + if side_effect.multiple_side_effect_has_builder():
|
| + raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
|
| + side_effect.add_source(targets)
|
| + side_effect.side_effect = 1
|
| + self.Precious(side_effect)
|
| + for target in targets:
|
| + target.side_effects.append(side_effect)
|
| + return side_effects
|
| +
|
| + def SourceCode(self, entry, builder):
|
| + """Arrange for a source code builder for (part of) a tree."""
|
| + entries = self.arg2nodes(entry, self.fs.Entry)
|
| + for entry in entries:
|
| + entry.set_src_builder(builder)
|
| + return entries
|
| +
|
| + def SourceSignatures(self, type):
|
| + global _warn_source_signatures_deprecated
|
| + if _warn_source_signatures_deprecated:
|
| + msg = "The env.SourceSignatures() method is deprecated;\n" + \
|
| + "\tconvert your build to use the env.Decider() method instead."
|
| + SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
|
| + _warn_source_signatures_deprecated = False
|
| + type = self.subst(type)
|
| + self.src_sig_type = type
|
| + if type == 'MD5':
|
| + if not SCons.Util.md5:
|
| + raise UserError, "MD5 signatures are not available in this version of Python."
|
| + self.decide_source = self._changed_content
|
| + elif type == 'timestamp':
|
| + self.decide_source = self._changed_timestamp_match
|
| + else:
|
| + raise UserError, "Unknown source signature type '%s'" % type
|
| +
|
| + def Split(self, arg):
|
| + """This function converts a string or list into a list of strings
|
| + or Nodes. This makes things easier for users by allowing files to
|
| + be specified as a white-space separated list to be split.
|
| + The input rules are:
|
| + - A single string containing names separated by spaces. These will be
|
| + split apart at the spaces.
|
| + - A single Node instance
|
| + - A list containing either strings or Node instances. Any strings
|
| + in the list are not split at spaces.
|
| + In all cases, the function returns a list of Nodes and strings."""
|
| + if SCons.Util.is_List(arg):
|
| + return map(self.subst, arg)
|
| + elif SCons.Util.is_String(arg):
|
| + return string.split(self.subst(arg))
|
| + else:
|
| + return [self.subst(arg)]
|
| +
|
| + def TargetSignatures(self, type):
|
| + global _warn_target_signatures_deprecated
|
| + if _warn_target_signatures_deprecated:
|
| + msg = "The env.TargetSignatures() method is deprecated;\n" + \
|
| + "\tconvert your build to use the env.Decider() method instead."
|
| + SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
|
| + _warn_target_signatures_deprecated = False
|
| + type = self.subst(type)
|
| + self.tgt_sig_type = type
|
| + if type in ('MD5', 'content'):
|
| + if not SCons.Util.md5:
|
| + raise UserError, "MD5 signatures are not available in this version of Python."
|
| + self.decide_target = self._changed_content
|
| + elif type == 'timestamp':
|
| + self.decide_target = self._changed_timestamp_match
|
| + elif type == 'build':
|
| + self.decide_target = self._changed_build
|
| + elif type == 'source':
|
| + self.decide_target = self._changed_source
|
| + else:
|
| + raise UserError, "Unknown target signature type '%s'"%type
|
| +
|
| + def Value(self, value, built_value=None):
|
| + """
|
| + """
|
| + return SCons.Node.Python.Value(value, built_value)
|
| +
|
| + def VariantDir(self, variant_dir, src_dir, duplicate=1):
|
| + variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
|
| + src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
|
| + self.fs.VariantDir(variant_dir, src_dir, duplicate)
|
| +
|
| + def FindSourceFiles(self, node='.'):
|
| + """ returns a list of all source files.
|
| + """
|
| + node = self.arg2nodes(node, self.fs.Entry)[0]
|
| +
|
| + sources = []
|
| + # Uncomment this and get rid of the global definition when we
|
| + # drop support for pre-2.2 Python versions.
|
| + #def build_source(ss, result):
|
| + # for s in ss:
|
| + # if isinstance(s, SCons.Node.FS.Dir):
|
| + # build_source(s.all_children(), result)
|
| + # elif s.has_builder():
|
| + # build_source(s.sources, result)
|
| + # elif isinstance(s.disambiguate(), SCons.Node.FS.File):
|
| + # result.append(s)
|
| + build_source(node.all_children(), sources)
|
| +
|
| + # now strip the build_node from the sources by calling the srcnode
|
| + # function
|
| + def get_final_srcnode(file):
|
| + srcnode = file.srcnode()
|
| + while srcnode != file.srcnode():
|
| + srcnode = file.srcnode()
|
| + return srcnode
|
| +
|
| + # get the final srcnode for all nodes, this means stripping any
|
| + # attached build node.
|
| + map( get_final_srcnode, sources )
|
| +
|
| + # remove duplicates
|
| + return list(set(sources))
|
| +
|
| + def FindInstalledFiles(self):
|
| + """ returns the list of all targets of the Install and InstallAs Builder.
|
| + """
|
| + from SCons.Tool import install
|
| + if install._UNIQUE_INSTALLED_FILES is None:
|
| + install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
|
| + return install._UNIQUE_INSTALLED_FILES
|
| +
|
| +class OverrideEnvironment(Base):
|
| + """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. 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
|
| + values from the overrides dictionary.
|
| + """
|
| +
|
| + if SCons.Memoize.use_memoizer:
|
| + __metaclass__ = SCons.Memoize.Memoized_Metaclass
|
| +
|
| + def __init__(self, subject, overrides={}):
|
| + if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
|
| + self.__dict__['__subject'] = subject
|
| + self.__dict__['overrides'] = overrides
|
| +
|
| + # Methods that make this class act like a proxy.
|
| + def __getattr__(self, name):
|
| + return getattr(self.__dict__['__subject'], name)
|
| + def __setattr__(self, name, value):
|
| + setattr(self.__dict__['__subject'], name, value)
|
| +
|
| + # Methods that make this class act like a dictionary.
|
| + def __getitem__(self, key):
|
| + try:
|
| + return self.__dict__['overrides'][key]
|
| + except KeyError:
|
| + return self.__dict__['__subject'].__getitem__(key)
|
| + def __setitem__(self, key, value):
|
| + if not is_valid_construction_var(key):
|
| + raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
|
| + self.__dict__['overrides'][key] = value
|
| + def __delitem__(self, key):
|
| + try:
|
| + del self.__dict__['overrides'][key]
|
| + except KeyError:
|
| + deleted = 0
|
| + else:
|
| + deleted = 1
|
| + try:
|
| + result = self.__dict__['__subject'].__delitem__(key)
|
| + except KeyError:
|
| + if not deleted:
|
| + raise
|
| + result = None
|
| + return result
|
| + def get(self, key, default=None):
|
| + """Emulates the get() method of dictionaries."""
|
| + try:
|
| + return self.__dict__['overrides'][key]
|
| + except KeyError:
|
| + return self.__dict__['__subject'].get(key, default)
|
| + def has_key(self, key):
|
| + try:
|
| + self.__dict__['overrides'][key]
|
| + return 1
|
| + except KeyError:
|
| + return self.__dict__['__subject'].has_key(key)
|
| + def Dictionary(self):
|
| + """Emulates the items() method of dictionaries."""
|
| + d = self.__dict__['__subject'].Dictionary().copy()
|
| + d.update(self.__dict__['overrides'])
|
| + return d
|
| + def items(self):
|
| + """Emulates the items() method of dictionaries."""
|
| + return self.Dictionary().items()
|
| +
|
| + # Overridden private construction environment methods.
|
| + def _update(self, dict):
|
| + """Update an environment's values directly, bypassing the normal
|
| + checks that occur when users try to set items.
|
| + """
|
| + 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 Replace(self, **kw):
|
| + kw = copy_non_reserved_keywords(kw)
|
| + self.__dict__['overrides'].update(semi_deepcopy(kw))
|
| +
|
| +# The entry point that will be used by the external world
|
| +# to refer to a construction environment. This allows the wrapper
|
| +# interface to extend a construction environment for its own purposes
|
| +# by subclassing SCons.Environment.Base and then assigning the
|
| +# class to SCons.Environment.Environment.
|
| +
|
| +Environment = Base
|
| +
|
| +# An entry point for returning a proxy subclass instance that overrides
|
| +# the subst*() methods so they don't actually perform construction
|
| +# variable substitution. This is specifically intended to be the shim
|
| +# layer in between global function calls (which don't want construction
|
| +# variable substitution) and the DefaultEnvironment() (which would
|
| +# substitute variables if left to its own devices)."""
|
| +#
|
| +# We have to wrap this in a function that allows us to delay definition of
|
| +# the class until it's necessary, so that when it subclasses Environment
|
| +# it will pick up whatever Environment subclass the wrapper interface
|
| +# might have assigned to SCons.Environment.Environment.
|
| +
|
| +def NoSubstitutionProxy(subject):
|
| + class _NoSubstitutionProxy(Environment):
|
| + def __init__(self, subject):
|
| + self.__dict__['__subject'] = subject
|
| + def __getattr__(self, name):
|
| + return getattr(self.__dict__['__subject'], name)
|
| + def __setattr__(self, name, value):
|
| + return setattr(self.__dict__['__subject'], name, value)
|
| + def raw_to_mode(self, dict):
|
| + try:
|
| + raw = dict['raw']
|
| + except KeyError:
|
| + pass
|
| + else:
|
| + del dict['raw']
|
| + dict['mode'] = raw
|
| + def subst(self, string, *args, **kwargs):
|
| + return string
|
| + def subst_kw(self, kw, *args, **kwargs):
|
| + return kw
|
| + def subst_list(self, string, *args, **kwargs):
|
| + nargs = (string, self,) + args
|
| + nkw = kwargs.copy()
|
| + nkw['gvars'] = {}
|
| + self.raw_to_mode(nkw)
|
| + return apply(SCons.Subst.scons_subst_list, nargs, nkw)
|
| + def subst_target_source(self, string, *args, **kwargs):
|
| + nargs = (string, self,) + args
|
| + nkw = kwargs.copy()
|
| + nkw['gvars'] = {}
|
| + self.raw_to_mode(nkw)
|
| + return apply(SCons.Subst.scons_subst, nargs, nkw)
|
| + return _NoSubstitutionProxy(subject)
|
|
|