| Index: scons-2.0.1/engine/SCons/Environment.py
|
| ===================================================================
|
| --- scons-2.0.1/engine/SCons/Environment.py (revision 0)
|
| +++ scons-2.0.1/engine/SCons/Environment.py (revision 0)
|
| @@ -0,0 +1,2318 @@
|
| +"""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, 2009, 2010 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 5134 2010/08/16 23:02:40 bdeegan"
|
| +
|
| +
|
| +import copy
|
| +import os
|
| +import sys
|
| +import re
|
| +import shlex
|
| +from collections 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.SConf
|
| +import SCons.SConsign
|
| +import SCons.Subst
|
| +import SCons.Tool
|
| +import SCons.Util
|
| +import SCons.Warnings
|
| +
|
| +class _Null(object):
|
| + 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 [_f for _f in tools if _f]:
|
| + if SCons.Util.is_List(tool) or isinstance(tool, tuple):
|
| + toolname = tool[0]
|
| + toolargs = tool[1] # should be a dict of kw args
|
| + tool = env.Tool(toolname, **toolargs)
|
| + else:
|
| + env.Tool(tool)
|
| +
|
| +# These names are (or will be) 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 = [
|
| + 'CHANGED_SOURCES',
|
| + 'CHANGED_TARGETS',
|
| + 'SOURCE',
|
| + 'SOURCES',
|
| + 'TARGET',
|
| + 'TARGETS',
|
| + 'UNCHANGED_SOURCES',
|
| + 'UNCHANGED_TARGETS',
|
| +]
|
| +
|
| +future_reserved_construction_var_names = [
|
| + #'HOST_OS',
|
| + #'HOST_ARCH',
|
| + #'HOST_CPU',
|
| + ]
|
| +
|
| +def copy_non_reserved_keywords(dict):
|
| + result = semi_deepcopy(dict)
|
| + for k in result.keys():
|
| + if k in reserved_construction_var_names:
|
| + msg = "Ignoring attempt to set reserved variable `$%s'"
|
| + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k)
|
| + del result[k]
|
| + return result
|
| +
|
| +def _set_reserved(env, key, value):
|
| + msg = "Ignoring attempt to set reserved variable `$%s'"
|
| + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key)
|
| +
|
| +def _set_future_reserved(env, key, value):
|
| + env._dict[key] = value
|
| + msg = "`$%s' will be reserved in a future release and setting it will become ignored"
|
| + SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key)
|
| +
|
| +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
|
| + for k, v in value.items():
|
| + if not SCons.Builder.is_a_Builder(v):
|
| + raise SCons.Errors.UserError('%s is not a Builder.' % repr(v))
|
| + 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()
|
| +
|
| +def _delete_duplicates(l, keep_last):
|
| + """Delete duplicates from a sequence, keeping the first or last."""
|
| + seen={}
|
| + result=[]
|
| + if keep_last: # reverse in & out, then keep first
|
| + l.reverse()
|
| + for i in l:
|
| + try:
|
| + if i not in seen:
|
| + result.append(i)
|
| + seen[i]=1
|
| + except TypeError:
|
| + # probably unhashable. Just keep it.
|
| + result.append(i)
|
| + if keep_last:
|
| + result.reverse()
|
| + return result
|
| +
|
| +
|
| +
|
| +# 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(object):
|
| + """
|
| + 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 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 target is not None and not SCons.Util.is_List(target):
|
| + target = [target]
|
| + if source is not None and not SCons.Util.is_List(source):
|
| + source = [source]
|
| + return 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
|
| + # 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(object):
|
| + """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
|
| + for key in future_reserved_construction_var_names:
|
| + self._special_set[key] = _set_future_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 = list(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 than 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 key not in self._dict \
|
| + 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 key in self._dict
|
| +
|
| + def __contains__(self, key):
|
| + return self._dict.__contains__(key)
|
| +
|
| + def items(self):
|
| + return list(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 n is not None:
|
| + break
|
| + if n is not None:
|
| + if SCons.Util.is_String(n):
|
| + # n = self.subst(n, raw=1, **kw)
|
| + kw['raw'] = 1
|
| + n = 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(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, executor=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
|
| + if executor:
|
| + lvars.update(executor.get_lvars())
|
| + 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, executor=None):
|
| + """Calls through to SCons.Subst.scons_subst_list(). See
|
| + the documentation for that function."""
|
| + gvars = self.gvars()
|
| + lvars = self.lvars()
|
| + lvars['__env__'] = self
|
| + if executor:
|
| + lvars.update(executor.get_lvars())
|
| + 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 = ''.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 = { 'stdin' : 'devnull',
|
| + '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
|
| + p = SCons.Action._subproc(self, command, **kw)
|
| + out,err = p.communicate()
|
| + status = p.wait()
|
| + if err:
|
| + sys.stderr.write(unicode(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.
|
| + """
|
| + self.added_methods = [dm for dm in self.added_methods if not dm.method is function]
|
| +
|
| + 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' : [],
|
| + }
|
| +
|
| + def do_parse(arg):
|
| + # if arg is a sequence, recurse with each element
|
| + if not arg:
|
| + return
|
| +
|
| + if not SCons.Util.is_String(arg):
|
| + for t in arg: do_parse(t)
|
| + 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 = name.split('=')
|
| + if len(t) == 1:
|
| + dict['CPPDEFINES'].append(name)
|
| + else:
|
| + dict['CPPDEFINES'].append([t[0], '='.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)
|
| + 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:
|
| + 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:
|
| +# self.PrependENVPath(pathname, pathval)
|
| +# else:
|
| +# self.AppendENVPath(pathname, pathval)
|
| +
|
| +
|
| +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.
|
| + """
|
| +
|
| + 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)
|
| +
|
| + self._dict['HOST_OS'] = self._dict.get('HOST_OS',None)
|
| + self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None)
|
| +
|
| + # Now set defaults for TARGET_{OS|ARCH}
|
| + self._dict['TARGET_OS'] = self._dict.get('HOST_OS',None)
|
| + self._dict['TARGET_ARCH'] = self._dict.get('HOST_ARCH',None)
|
| +
|
| +
|
| + # Apply the passed-in and customizable variables to the
|
| + # environment before calling the tools, because they may use
|
| + # some of them during initialization.
|
| + if 'options' in kw:
|
| + # Backwards compatibility: they may stll be using the
|
| + # old "options" keyword.
|
| + variables = kw['options']
|
| + del kw['options']
|
| + self.Replace(**kw)
|
| + keys = list(kw.keys())
|
| + if variables:
|
| + keys = keys + list(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.FS.Base)
|
| + 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):
|
| + if k and self['PLATFORM'] == 'win32':
|
| + k = k.lower()
|
| + 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).
|
| + """
|
| + if skey and self['PLATFORM'] == 'win32':
|
| + skey = skey.lower()
|
| + 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)
|
| +
|
| + # allow Dirs and strings beginning with # for top-relative
|
| + # Note this uses the current env's fs (in self).
|
| + def _canonicalize(self, path):
|
| + if not SCons.Util.is_String(path): # typically a Dir
|
| + path = str(path)
|
| + if path and path[0] == '#':
|
| + path = str(self.fs.Dir(path))
|
| + return path
|
| +
|
| + 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 envname in self._dict and name in self._dict[envname]:
|
| + orig = self._dict[envname][name]
|
| +
|
| + nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing,
|
| + canonicalize=self._canonicalize)
|
| +
|
| + if envname not in self._dict:
|
| + 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 SCons.Util.is_List(val):
|
| + val = _delete_duplicates(val, delete_existing)
|
| + if key not in self._dict 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 = [x for x in dk if x not in val]
|
| + else:
|
| + val = [x for x in val if x not in dk]
|
| + 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 = [x for x in dk if x not in val]
|
| + self._dict[key] = dk + [val]
|
| + else:
|
| + if not val in dk:
|
| + self._dict[key] = dk + [val]
|
| + else:
|
| + if delete_existing:
|
| + dk = [x for x in dk if x not in val]
|
| + 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)
|
| + clone.Replace(**new)
|
| +
|
| + apply_tools(clone, tools, toolpath)
|
| +
|
| + # apply them again in case the tools overwrote them
|
| + 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 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 = [self._dict[x] for x in 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 = ' '.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 = [l for l in lines if l[0] != '#']
|
| + tdlist = []
|
| + for line in lines:
|
| + try:
|
| + target, depends = line.split(':', 1)
|
| + except (AttributeError, ValueError):
|
| + # Throws AttributeError if line isn't a string. Can throw
|
| + # ValueError if line doesn't split into two or more elements.
|
| + pass
|
| + else:
|
| + tdlist.append((target.split(), depends.split()))
|
| + if only_one:
|
| + targets = []
|
| + for td in tdlist:
|
| + targets.extend(td[0])
|
| + 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 envname in self._dict and name in self._dict[envname]:
|
| + orig = self._dict[envname][name]
|
| +
|
| + nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing,
|
| + canonicalize=self._canonicalize)
|
| +
|
| + if envname not in self._dict:
|
| + 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 SCons.Util.is_List(val):
|
| + val = _delete_duplicates(val, not delete_existing)
|
| + if key not in self._dict 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 = [x for x in dk if x not in val]
|
| + else:
|
| + val = [x for x in val if x not in dk]
|
| + 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 = [x for x in dk if x not in val]
|
| + self._dict[key] = [val] + dk
|
| + else:
|
| + if not val in dk:
|
| + self._dict[key] = [val] + dk
|
| + else:
|
| + if delete_existing:
|
| + dk = [x for x in dk if x not in val]
|
| + 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 k in self._dict:
|
| + del kw[k]
|
| + 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 = list(map(self._find_toolpath_dir, toolpath))
|
| + tool = 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 = list(map(subst_string, args))
|
| + nkw = self.subst_kw(kw)
|
| + return 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 [n.get_executor() for n in 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 [n.get_executor() for n in 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 = [_f for _f in source if _f]
|
| +
|
| + 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 = 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 = 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):
|
| + msg = """BuildDir() and the build_dir keyword have been deprecated;\n\tuse VariantDir() and the variant_dir keyword instead."""
|
| + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg)
|
| + if 'build_dir' in kw:
|
| + kw['variant_dir'] = kw['build_dir']
|
| + del kw['build_dir']
|
| + return self.VariantDir(*args, **kw)
|
| +
|
| + def Builder(self, **kw):
|
| + nkw = self.subst_kw(kw)
|
| + return SCons.Builder.Builder(**nkw)
|
| +
|
| + def CacheDir(self, path):
|
| + import SCons.CacheDir
|
| + if path is not 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 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 = SCons.Builder.Builder(**bkw)
|
| + return 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(self.fs.Dir(e, *args, **kw))
|
| + return result
|
| + return 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(self.fs.Entry(e, *args, **kw))
|
| + return result
|
| + return self.fs.Entry(s, *args, **kw)
|
| +
|
| + def Environment(self, **kw):
|
| + return SCons.Environment.Environment(**self.subst_kw(kw))
|
| +
|
| + def Execute(self, action, *args, **kw):
|
| + """Directly execute an action through an Environment
|
| + """
|
| + action = 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(self.fs.File(e, *args, **kw))
|
| + return result
|
| + return 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 = list(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)
|
| + 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 SCons.Scanner.Base(*nargs, **nkw)
|
| +
|
| + def SConsignFile(self, name=".sconsign", dbm_module=None):
|
| + if name is not 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."""
|
| + msg = """SourceCode() has been deprecated and there is no replacement.
|
| +\tIf you need this function, please contact dev@scons.tigris.org."""
|
| + SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceCodeWarning, msg)
|
| + 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 list(map(self.subst, arg))
|
| + elif SCons.Util.is_String(arg):
|
| + return self.subst(arg).split()
|
| + 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 = []
|
| + def build_source(ss):
|
| + for s in ss:
|
| + if isinstance(s, SCons.Node.FS.Dir):
|
| + build_source(s.all_children())
|
| + elif s.has_builder():
|
| + build_source(s.sources)
|
| + elif isinstance(s.disambiguate(), SCons.Node.FS.File):
|
| + sources.append(s)
|
| + build_source(node.all_children())
|
| +
|
| + # THIS CODE APPEARS TO HAVE NO EFFECT
|
| + # # get the final srcnode for all nodes, this means stripping any
|
| + # # attached build node by calling the srcnode function
|
| + # for file in sources:
|
| + # srcnode = file.srcnode()
|
| + # while srcnode != file.srcnode():
|
| + # srcnode = file.srcnode()
|
| +
|
| + # 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.
|
| + """
|
| +
|
| + 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 key in self.__dict__['__subject']
|
| + def __contains__(self, key):
|
| + if self.__dict__['overrides'].__contains__(key):
|
| + return 1
|
| + return self.__dict__['__subject'].__contains__(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 list(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 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 SCons.Subst.scons_subst(*nargs, **nkw)
|
| + return _NoSubstitutionProxy(subject)
|
| +
|
| +# Local Variables:
|
| +# tab-width:4
|
| +# indent-tabs-mode:nil
|
| +# End:
|
| +# vim: set expandtab tabstop=4 shiftwidth=4:
|
|
|
| Property changes on: scons-2.0.1/engine/SCons/Environment.py
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|