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