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) |