Skip to content
Browse files

+ upgrade scons to 2.1.0 series

  • Loading branch information...
1 parent 959516e commit d3bb1a2a267483f51149b05e436c104922581378 @artemp artemp committed
Showing with 14,786 additions and 0 deletions.
  1. +25 −0 scons/scons-LICENSE
  2. +204 −0 scons/scons-README
  3. +1,257 −0 scons/scons-local-2.1.0/SCons/Action.py
  4. +877 −0 scons/scons-local-2.1.0/SCons/Builder.py
  5. +216 −0 scons/scons-local-2.1.0/SCons/CacheDir.py
  6. +793 −0 scons/scons-local-2.1.0/SCons/Conftest.py
  7. +220 −0 scons/scons-local-2.1.0/SCons/Debug.py
  8. +494 −0 scons/scons-local-2.1.0/SCons/Defaults.py
  9. +2,417 −0 scons/scons-local-2.1.0/SCons/Environment.py
  10. +205 −0 scons/scons-local-2.1.0/SCons/Errors.py
  11. +633 −0 scons/scons-local-2.1.0/SCons/Executor.py
  12. +435 −0 scons/scons-local-2.1.0/SCons/Job.py
  13. +244 −0 scons/scons-local-2.1.0/SCons/Memoize.py
  14. +152 −0 scons/scons-local-2.1.0/SCons/Node/Alias.py
  15. +3,302 −0 scons/scons-local-2.1.0/SCons/Node/FS.py
  16. +128 −0 scons/scons-local-2.1.0/SCons/Node/Python.py
  17. +1,329 −0 scons/scons-local-2.1.0/SCons/Node/__init__.py
  18. +50 −0 scons/scons-local-2.1.0/SCons/Options/BoolOption.py
  19. +50 −0 scons/scons-local-2.1.0/SCons/Options/EnumOption.py
  20. +50 −0 scons/scons-local-2.1.0/SCons/Options/ListOption.py
  21. +50 −0 scons/scons-local-2.1.0/SCons/Options/PackageOption.py
  22. +76 −0 scons/scons-local-2.1.0/SCons/Options/PathOption.py
  23. +67 −0 scons/scons-local-2.1.0/SCons/Options/__init__.py
  24. +231 −0 scons/scons-local-2.1.0/SCons/PathList.py
  25. +241 −0 scons/scons-local-2.1.0/SCons/Platform/__init__.py
  26. +69 −0 scons/scons-local-2.1.0/SCons/Platform/aix.py
  27. +55 −0 scons/scons-local-2.1.0/SCons/Platform/cygwin.py
  28. +70 −0 scons/scons-local-2.1.0/SCons/Platform/darwin.py
  29. +46 −0 scons/scons-local-2.1.0/SCons/Platform/hpux.py
  30. +44 −0 scons/scons-local-2.1.0/SCons/Platform/irix.py
  31. +58 −0 scons/scons-local-2.1.0/SCons/Platform/os2.py
  32. +263 −0 scons/scons-local-2.1.0/SCons/Platform/posix.py
  33. +50 −0 scons/scons-local-2.1.0/SCons/Platform/sunos.py
  34. +385 −0 scons/scons-local-2.1.0/SCons/Platform/win32.py
Sorry, we could not display the entire diff because it was too big.
View
25 scons/scons-LICENSE
@@ -0,0 +1,25 @@
+ Copyright and license for SCons - a software construction tool
+
+ This copyright and license do not apply to any other software
+ with which this software may have been included.
+
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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.
View
204 scons/scons-README
@@ -0,0 +1,204 @@
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation
+
+ SCons - a software construction tool
+
+This is the scons-README file for a version of SCons packaged for local
+execution--that is, execution out of a specific local directory, without
+having to install SCons as a system-wide utility.
+
+You are likely reading this file in one of the following two situations:
+
+ 1) You have unpacked an scons-local-{version} package and are
+ examining the contents.
+
+ In this case, you are presumably interested in using this
+ package to include a local copy of SCons with some other
+ software that you package, so that you can use SCons to build
+ your software without forcing all of your users to have it fully
+ installed. Instructions for this can be found below.
+
+ If you are not looking to use SCons in this way, then please
+ use either the scons-{version} package to install SCons on your
+ system, or the scons-src-{version} package if you want the full
+ source to SCons, including its packaging code and underlying
+ tests and testing infrastructure.
+
+ 2) This file was included in some other software package so that
+ the package could be built using SCons.
+
+ In this case, follow the instructions provided with the
+ rest of the software package for how to use SCons to build
+ and/or install the software. The file containing build and
+ installation instructions will typically be named README or
+ INSTALL.
+
+LATEST VERSION
+==============
+
+Before going further, you can check for the latest version of the
+scons-local package, or any SCons package, at the SCons download page:
+
+ http://www.scons.org/download.html
+
+
+EXECUTION REQUIREMENTS
+======================
+
+Running SCons requires Python version 2.4 or later. There should be
+no other dependencies or requirements to run SCons.
+
+The default SCons configuration assumes use of the Microsoft Visual C++
+compiler suite on WIN32 systems, and assumes a C compiler named 'cc',
+a C++ compiler named 'c++', and a Fortran compiler named 'g77' (such
+as found in the GNU C compiler suite) on any other type of system.
+You may, of course, override these default values by appropriate
+configuration of Environment construction variables.
+
+
+INSTALLATION
+============
+
+Installation of this package should be as simple as unpacking the
+archive (either .tar.gz or .zip) in any directory (top-level or a
+subdirectory) within the software package with which you want to ship
+SCons.
+
+Once you have installed this package, you should write an SConstruct
+file at the top level of your source tree to build your software as you
+see fit.
+
+Then modify the build/install instructions for your package to instruct
+your users to execute SCons as follows (if you installed this package in
+your top-level directory):
+
+ $ python scons.py
+
+Or (if, for example, you installed this package in a subdirectory named
+"scons"):
+
+ $ python scons/scons.py
+
+That should be all you have to do. (If it isn't that simple, please let
+us know!)
+
+
+CONTENTS OF THIS PACKAGE
+========================
+
+This scons-local package consists of the following:
+
+scons-LICENSE
+ A copy of the copyright and terms under which SCons is
+ distributed (the Open Source Initiative-approved MIT license).
+
+ A disclaimer has been added to the beginning to make clear that
+ this license applies only to SCons, and not to any separate
+ software you've written with which you're planning to package
+ SCons.
+
+scons-README
+ What you're looking at right now.
+
+scons-local-{version}/
+ The SCons build engine. This is structured as a Python
+ library.
+
+scons.py
+ The SCons script itself. The script sets up the Python
+ sys.path variable to use the build engine found in the
+ scons-local-{version}/ directory in preference to any other
+ SCons build engine installed on your system.
+
+
+DOCUMENTATION
+=============
+
+Because this package is intended to be included with other software by
+experienced users, we have not included any SCons documentation in this
+package (other than this scons-README file you're reading right now).
+
+If, however, you need documentation about SCons, then consult any of the
+following from the corresponding scons-{version} or scons-src-{version}
+package:
+
+ The RELEASE.txt file (src/RELEASE.txt file in the
+ scons-src-{version} package), which contains notes about this
+ specific release, including known problems.
+
+ The CHANGES.txt file (src/CHANGES.txt file in the
+ scons-src-{version} package), which contains a list of changes
+ since the previous release.
+
+ The scons.1 man page (doc/man/scons.1 in the scons-src-{version}
+ package), which contains a section of small examples for getting
+ started using SCons.
+
+Additional documentation for SCons is available at:
+
+ http://www.scons.org/doc.html
+
+
+LICENSING
+=========
+
+SCons is distributed under the MIT license, a full copy of which is
+available in the scons-LICENSE file in this package. The MIT license is
+an approved Open Source license, which means:
+
+ This software is OSI Certified Open Source Software. OSI
+ Certified is a certification mark of the Open Source Initiative.
+
+More information about OSI certifications and Open Source software is
+available at:
+
+ http://www.opensource.org/
+
+
+REPORTING BUGS
+==============
+
+You can report bugs either by following the "Tracker - Bugs" link
+on the SCons project page:
+
+ http://sourceforge.net/projects/scons/
+
+or by sending mail to the SCons developers mailing list:
+
+ scons-devel@lists.sourceforge.net
+
+
+MAILING LISTS
+=============
+
+A mailing list for users of SCons is available. You may send questions
+or comments to the list at:
+
+ scons-users@lists.sourceforge.net
+
+You may subscribe to the scons-users mailing list at:
+
+ http://lists.sourceforge.net/lists/listinfo/scons-users
+
+
+FOR MORE INFORMATION
+====================
+
+Check the SCons web site at:
+
+ http://www.scons.org/
+
+
+AUTHOR INFO
+===========
+
+Steven Knight
+knight at baldmt dot com
+http://www.baldmt.com/~knight/
+
+With plenty of help from the SCons Development team:
+ Chad Austin
+ Charles Crain
+ Steve Leblanc
+ Anthony Roach
+ Terrel Shumway
+
View
1,257 scons/scons-local-2.1.0/SCons/Action.py
@@ -0,0 +1,1257 @@
+"""SCons.Action
+
+This encapsulates information about executing any sort of action that
+can build one or more target Nodes (typically files) from one or more
+source Nodes (also typically files) given a specific Environment.
+
+The base class here is ActionBase. The base class supplies just a few
+OO utility methods and some generic methods for displaying information
+about an Action in response to the various commands that control printing.
+
+A second-level base class is _ActionAction. This extends ActionBase
+by providing the methods that can be used to show and perform an
+action. True Action objects will subclass _ActionAction; Action
+factory class objects will subclass ActionBase.
+
+The heavy lifting is handled by subclasses for the different types of
+actions we might execute:
+
+ CommandAction
+ CommandGeneratorAction
+ FunctionAction
+ ListAction
+
+The subclasses supply the following public interface methods used by
+other modules:
+
+ __call__()
+ THE public interface, "calling" an Action object executes the
+ command or Python function. This also takes care of printing
+ a pre-substitution command for debugging purposes.
+
+ get_contents()
+ Fetches the "contents" of an Action for signature calculation
+ plus the varlist. This is what gets MD5 checksummed to decide
+ if a target needs to be rebuilt because its action changed.
+
+ genstring()
+ Returns a string representation of the Action *without*
+ command substitution, but allows a CommandGeneratorAction to
+ generate the right action based on the specified target,
+ source and env. This is used by the Signature subsystem
+ (through the Executor) to obtain an (imprecise) representation
+ of the Action operation for informative purposes.
+
+
+Subclasses also supply the following methods for internal use within
+this module:
+
+ __str__()
+ Returns a string approximation of the Action; no variable
+ substitution is performed.
+
+ execute()
+ The internal method that really, truly, actually handles the
+ execution of a command or Python function. This is used so
+ that the __call__() methods can take care of displaying any
+ pre-substitution representations, and *then* execute an action
+ without worrying about the specific Actions involved.
+
+ get_presig()
+ Fetches the "contents" of a subclass for signature calculation.
+ The varlist is added to this to produce the Action's contents.
+
+ strfunction()
+ Returns a substituted string representation of the Action.
+ This is used by the _ActionAction.show() command to display the
+ command/function that will be executed to generate the target(s).
+
+There is a related independent ActionCaller class that looks like a
+regular Action, and which serves as a wrapper for arbitrary functions
+that we want to let the user specify the arguments to now, but actually
+execute later (when an out-of-date check determines that it's needed to
+be executed, for example). Objects of this class are returned by an
+ActionFactory class that provides a __call__() method as a convenient
+way for wrapping up the functions.
+
+"""
+
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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/Action.py 5357 2011/09/09 21:31:03 bdeegan"
+
+import SCons.compat
+
+import dis
+import os
+# compat layer imports "cPickle" for us if it's available.
+import pickle
+import re
+import sys
+import subprocess
+
+from SCons.Debug import logInstanceCreation
+import SCons.Errors
+import SCons.Executor
+import SCons.Util
+import SCons.Subst
+
+# we use these a lot, so try to optimize them
+is_String = SCons.Util.is_String
+is_List = SCons.Util.is_List
+
+class _null(object):
+ pass
+
+print_actions = 1
+execute_actions = 1
+print_actions_presub = 0
+
+def rfile(n):
+ try:
+ return n.rfile()
+ except AttributeError:
+ return n
+
+def default_exitstatfunc(s):
+ return s
+
+try:
+ SET_LINENO = dis.SET_LINENO
+ HAVE_ARGUMENT = dis.HAVE_ARGUMENT
+except AttributeError:
+ remove_set_lineno_codes = lambda x: x
+else:
+ def remove_set_lineno_codes(code):
+ result = []
+ n = len(code)
+ i = 0
+ while i < n:
+ c = code[i]
+ op = ord(c)
+ if op >= HAVE_ARGUMENT:
+ if op != SET_LINENO:
+ result.append(code[i:i+3])
+ i = i+3
+ else:
+ result.append(c)
+ i = i+1
+ return ''.join(result)
+
+strip_quotes = re.compile('^[\'"](.*)[\'"]$')
+
+
+def _callable_contents(obj):
+ """Return the signature contents of a callable Python object.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+
+def _object_contents(obj):
+ """Return the signature contents of any Python object.
+
+ We have to handle the case where object contains a code object
+ since it can be pickled directly.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ try:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+ except AttributeError:
+ # Should be a pickable Python object.
+ try:
+ return pickle.dumps(obj)
+ except (pickle.PicklingError, TypeError):
+ # This is weird, but it seems that nested classes
+ # are unpickable. The Python docs say it should
+ # always be a PicklingError, but some Python
+ # versions seem to return TypeError. Just do
+ # the best we can.
+ return str(obj)
+
+
+def _code_contents(code):
+ """Return the signature contents of a code object.
+
+ By providing direct access to the code object of the
+ function, Python makes this extremely easy. Hooray!
+
+ Unfortunately, older versions of Python include line
+ number indications in the compiled byte code. Boo!
+ So we remove the line number byte codes to prevent
+ recompilations from moving a Python function.
+ """
+
+ contents = []
+
+ # The code contents depends on the number of local variables
+ # but not their actual names.
+ contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
+ try:
+ contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ contents.append(",0,0")
+
+ # The code contents depends on any constants accessed by the
+ # function. Note that we have to call _object_contents on each
+ # constants because the code object of nested functions can
+ # show-up among the constants.
+ #
+ # Note that we also always ignore the first entry of co_consts
+ # which contains the function doc string. We assume that the
+ # function does not access its doc string.
+ contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')')
+
+ # The code contents depends on the variable names used to
+ # accessed global variable, as changing the variable name changes
+ # the variable actually accessed and therefore changes the
+ # function result.
+ contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')')
+
+
+ # The code contents depends on its actual code!!!
+ contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
+
+ return ''.join(contents)
+
+
+def _function_contents(func):
+ """Return the signature contents of a function."""
+
+ contents = [_code_contents(func.func_code)]
+
+ # The function contents depends on the value of defaults arguments
+ if func.func_defaults:
+ contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')')
+ else:
+ contents.append(',()')
+
+ # The function contents depends on the closure captured cell values.
+ try:
+ closure = func.func_closure or []
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ closure = []
+
+ #xxx = [_object_contents(x.cell_contents) for x in closure]
+ try:
+ xxx = [_object_contents(x.cell_contents) for x in closure]
+ except AttributeError:
+ xxx = []
+ contents.append(',(' + ','.join(xxx) + ')')
+
+ return ''.join(contents)
+
+
+def _actionAppend(act1, act2):
+ # This function knows how to slap two actions together.
+ # Mainly, it handles ListActions by concatenating into
+ # a single ListAction.
+ a1 = Action(act1)
+ a2 = Action(act2)
+ if a1 is None:
+ return a2
+ if a2 is None:
+ return a1
+ if isinstance(a1, ListAction):
+ if isinstance(a2, ListAction):
+ return ListAction(a1.list + a2.list)
+ else:
+ return ListAction(a1.list + [ a2 ])
+ else:
+ if isinstance(a2, ListAction):
+ return ListAction([ a1 ] + a2.list)
+ else:
+ return ListAction([ a1, a2 ])
+
+def _do_create_keywords(args, kw):
+ """This converts any arguments after the action argument into
+ their equivalent keywords and adds them to the kw argument.
+ """
+ v = kw.get('varlist', ())
+ # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O']
+ if is_String(v): v = (v,)
+ kw['varlist'] = tuple(v)
+ if args:
+ # turn positional args into equivalent keywords
+ cmdstrfunc = args[0]
+ if cmdstrfunc is None or is_String(cmdstrfunc):
+ kw['cmdstr'] = cmdstrfunc
+ elif callable(cmdstrfunc):
+ kw['strfunction'] = cmdstrfunc
+ else:
+ raise SCons.Errors.UserError(
+ 'Invalid command display variable type. '
+ 'You must either pass a string or a callback which '
+ 'accepts (target, source, env) as parameters.')
+ if len(args) > 1:
+ kw['varlist'] = args[1:] + kw['varlist']
+ if kw.get('strfunction', _null) is not _null \
+ and kw.get('cmdstr', _null) is not _null:
+ raise SCons.Errors.UserError(
+ 'Cannot have both strfunction and cmdstr args to Action()')
+
+def _do_create_action(act, kw):
+ """This is the actual "implementation" for the
+ Action factory method, below. This handles the
+ fact that passing lists to Action() itself has
+ different semantics than passing lists as elements
+ of lists.
+
+ The former will create a ListAction, the latter
+ will create a CommandAction by converting the inner
+ list elements to strings."""
+
+ if isinstance(act, ActionBase):
+ return act
+
+ if is_List(act):
+ return CommandAction(act, **kw)
+
+ if callable(act):
+ try:
+ gen = kw['generator']
+ del kw['generator']
+ except KeyError:
+ gen = 0
+ if gen:
+ action_type = CommandGeneratorAction
+ else:
+ action_type = FunctionAction
+ return action_type(act, kw)
+
+ if is_String(act):
+ var=SCons.Util.get_environment_var(act)
+ if var:
+ # This looks like a string that is purely an Environment
+ # variable reference, like "$FOO" or "${FOO}". We do
+ # something special here...we lazily evaluate the contents
+ # of that Environment variable, so a user could put something
+ # like a function or a CommandGenerator in that variable
+ # instead of a string.
+ return LazyAction(var, kw)
+ commands = str(act).split('\n')
+ if len(commands) == 1:
+ return CommandAction(commands[0], **kw)
+ # The list of string commands may include a LazyAction, so we
+ # reprocess them via _do_create_list_action.
+ return _do_create_list_action(commands, kw)
+ # Catch a common error case with a nice message:
+ if isinstance(act, int) or isinstance(act, float):
+ raise TypeError("Don't know how to create an Action from a number (%s)"%act)
+ # Else fail silently (???)
+ return None
+
+def _do_create_list_action(act, kw):
+ """A factory for list actions. Convert the input list into Actions
+ and then wrap them in a ListAction."""
+ acts = []
+ for a in act:
+ aa = _do_create_action(a, kw)
+ if aa is not None: acts.append(aa)
+ if not acts:
+ return ListAction([])
+ elif len(acts) == 1:
+ return acts[0]
+ else:
+ return ListAction(acts)
+
+def Action(act, *args, **kw):
+ """A factory for action objects."""
+ # Really simple: the _do_create_* routines do the heavy lifting.
+ _do_create_keywords(args, kw)
+ if is_List(act):
+ return _do_create_list_action(act, kw)
+ return _do_create_action(act, kw)
+
+class ActionBase(object):
+ """Base class for all types of action objects that can be held by
+ other objects (Builders, Executors, etc.) This provides the
+ common methods for manipulating and combining those actions."""
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other)
+
+ def no_batch_key(self, env, target, source):
+ return None
+
+ batch_key = no_batch_key
+
+ def genstring(self, target, source, env):
+ return str(self)
+
+ def get_contents(self, target, source, env):
+ result = [ self.get_presig(target, source, env) ]
+ # This should never happen, as the Action() factory should wrap
+ # the varlist, but just in case an action is created directly,
+ # we duplicate this check here.
+ vl = self.get_varlist(target, source, env)
+ if is_String(vl): vl = (vl,)
+ for v in vl:
+ result.append(env.subst('${'+v+'}'))
+ return ''.join(result)
+
+ def __add__(self, other):
+ return _actionAppend(self, other)
+
+ def __radd__(self, other):
+ return _actionAppend(other, self)
+
+ def presub_lines(self, env):
+ # CommandGeneratorAction needs a real environment
+ # in order to return the proper string here, since
+ # it may call LazyAction, which looks up a key
+ # in that env. So we temporarily remember the env here,
+ # and CommandGeneratorAction will use this env
+ # when it calls its _generate method.
+ self.presub_env = env
+ lines = str(self).split('\n')
+ self.presub_env = None # don't need this any more
+ return lines
+
+ def get_varlist(self, target, source, env, executor=None):
+ return self.varlist
+
+ def get_targets(self, env, executor):
+ """
+ Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
+ by this action.
+ """
+ return self.targets
+
+class _ActionAction(ActionBase):
+ """Base class for actions that create output objects."""
+ def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
+ presub=_null, chdir=None, exitstatfunc=None,
+ batch_key=None, targets='$TARGETS',
+ **kw):
+ self.cmdstr = cmdstr
+ if strfunction is not _null:
+ if strfunction is None:
+ self.cmdstr = None
+ else:
+ self.strfunction = strfunction
+ self.varlist = varlist
+ self.presub = presub
+ self.chdir = chdir
+ if not exitstatfunc:
+ exitstatfunc = default_exitstatfunc
+ self.exitstatfunc = exitstatfunc
+
+ self.targets = targets
+
+ if batch_key:
+ if not callable(batch_key):
+ # They have set batch_key, but not to their own
+ # callable. The default behavior here will batch
+ # *all* targets+sources using this action, separated
+ # for each construction environment.
+ def default_batch_key(self, env, target, source):
+ return (id(self), id(env))
+ batch_key = default_batch_key
+ SCons.Util.AddMethod(self, batch_key, 'batch_key')
+
+ def print_cmd_line(self, s, target, source, env):
+ # In python 3, and in some of our tests, sys.stdout is
+ # a String io object, and it takes unicode strings only
+ # In other cases it's a regular Python 2.x file object
+ # which takes strings (bytes), and if you pass those a
+ # unicode object they try to decode with 'ascii' codec
+ # which fails if the cmd line has any hi-bit-set chars.
+ # This code assumes s is a regular string, but should
+ # work if it's unicode too.
+ try:
+ sys.stdout.write(unicode(s + "\n"))
+ except UnicodeDecodeError:
+ sys.stdout.write(s + "\n")
+
+ def __call__(self, target, source, env,
+ exitstatfunc=_null,
+ presub=_null,
+ show=_null,
+ execute=_null,
+ chdir=_null,
+ executor=None):
+ if not is_List(target):
+ target = [target]
+ if not is_List(source):
+ source = [source]
+
+ if presub is _null:
+ presub = self.presub
+ if presub is _null:
+ presub = print_actions_presub
+ if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
+ if show is _null: show = print_actions
+ if execute is _null: execute = execute_actions
+ if chdir is _null: chdir = self.chdir
+ save_cwd = None
+ if chdir:
+ save_cwd = os.getcwd()
+ try:
+ chdir = str(chdir.abspath)
+ except AttributeError:
+ if not is_String(chdir):
+ if executor:
+ chdir = str(executor.batches[0].targets[0].dir)
+ else:
+ chdir = str(target[0].dir)
+ if presub:
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ t = ' and '.join(map(str, target))
+ l = '\n '.join(self.presub_lines(env))
+ out = u"Building %s with action:\n %s\n" % (t, l)
+ sys.stdout.write(out)
+ cmd = None
+ if show and self.strfunction:
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ try:
+ cmd = self.strfunction(target, source, env, executor)
+ except TypeError:
+ cmd = self.strfunction(target, source, env)
+ if cmd:
+ if chdir:
+ cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
+ try:
+ get = env.get
+ except AttributeError:
+ print_func = self.print_cmd_line
+ else:
+ print_func = get('PRINT_CMD_LINE_FUNC')
+ if not print_func:
+ print_func = self.print_cmd_line
+ print_func(cmd, target, source, env)
+ stat = 0
+ if execute:
+ if chdir:
+ os.chdir(chdir)
+ try:
+ stat = self.execute(target, source, env, executor=executor)
+ if isinstance(stat, SCons.Errors.BuildError):
+ s = exitstatfunc(stat.status)
+ if s:
+ stat.status = s
+ else:
+ stat = s
+ else:
+ stat = exitstatfunc(stat)
+ finally:
+ if save_cwd:
+ os.chdir(save_cwd)
+ if cmd and save_cwd:
+ print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
+
+ return stat
+
+
+def _string_from_cmd_list(cmd_list):
+ """Takes a list of command line arguments and returns a pretty
+ representation for printing."""
+ cl = []
+ for arg in map(str, cmd_list):
+ if ' ' in arg or '\t' in arg:
+ arg = '"' + arg + '"'
+ cl.append(arg)
+ return ' '.join(cl)
+
+# A fiddlin' little function that has an 'import SCons.Environment' which
+# can't be moved to the top level without creating an import loop. Since
+# this import creates a local variable named 'SCons', it blocks access to
+# the global variable, so we move it here to prevent complaints about local
+# variables being used uninitialized.
+default_ENV = None
+def get_default_ENV(env):
+ global default_ENV
+ try:
+ return env['ENV']
+ except KeyError:
+ if not default_ENV:
+ import SCons.Environment
+ # This is a hideously expensive way to get a default shell
+ # environment. What it really should do is run the platform
+ # setup to get the default ENV. Fortunately, it's incredibly
+ # rare for an Environment not to have a shell environment, so
+ # we're not going to worry about it overmuch.
+ default_ENV = SCons.Environment.Environment()['ENV']
+ return default_ENV
+
+# This function is still in draft mode. We're going to need something like
+# it in the long run as more and more places use subprocess, but I'm sure
+# it'll have to be tweaked to get the full desired functionality.
+# one special arg (so far?), 'error', to tell what to do with exceptions.
+def _subproc(scons_env, cmd, error = 'ignore', **kw):
+ """Do common setup for a subprocess.Popen() call"""
+ # allow std{in,out,err} to be "'devnull'"
+ io = kw.get('stdin')
+ if is_String(io) and io == 'devnull':
+ kw['stdin'] = open(os.devnull)
+ io = kw.get('stdout')
+ if is_String(io) and io == 'devnull':
+ kw['stdout'] = open(os.devnull, 'w')
+ io = kw.get('stderr')
+ if is_String(io) and io == 'devnull':
+ kw['stderr'] = open(os.devnull, 'w')
+
+ # Figure out what shell environment to use
+ ENV = kw.get('env', None)
+ if ENV is None: ENV = get_default_ENV(scons_env)
+
+ # Ensure that the ENV values are all strings:
+ new_env = {}
+ for key, value in ENV.items():
+ if is_List(value):
+ # If the value is a list, then we assume it is a path list,
+ # because that's a pretty common list-like value to stick
+ # in an environment variable:
+ value = SCons.Util.flatten_sequence(value)
+ new_env[key] = os.pathsep.join(map(str, value))
+ else:
+ # It's either a string or something else. If it's a string,
+ # we still want to call str() because it might be a *Unicode*
+ # string, which makes subprocess.Popen() gag. If it isn't a
+ # string or a list, then we just coerce it to a string, which
+ # is the proper way to handle Dir and File instances and will
+ # produce something reasonable for just about everything else:
+ new_env[key] = str(value)
+ kw['env'] = new_env
+
+ try:
+ return subprocess.Popen(cmd, **kw)
+ except EnvironmentError, e:
+ if error == 'raise': raise
+ # return a dummy Popen instance that only returns error
+ class dummyPopen(object):
+ def __init__(self, e): self.exception = e
+ def communicate(self): return ('','')
+ def wait(self): return -self.exception.errno
+ stdin = None
+ class f(object):
+ def read(self): return ''
+ def readline(self): return ''
+ stdout = stderr = f()
+ return dummyPopen(e)
+
+class CommandAction(_ActionAction):
+ """Class for command-execution actions."""
+ def __init__(self, cmd, **kw):
+ # Cmd can actually be a list or a single item; if it's a
+ # single item it should be the command string to execute; if a
+ # list then it should be the words of the command string to
+ # execute. Only a single command should be executed by this
+ # object; lists of commands should be handled by embedding
+ # these objects in a ListAction object (which the Action()
+ # factory above does). cmd will be passed to
+ # Environment.subst_list() for substituting environment
+ # variables.
+ if __debug__: logInstanceCreation(self, 'Action.CommandAction')
+
+ _ActionAction.__init__(self, **kw)
+ if is_List(cmd):
+ if list(filter(is_List, cmd)):
+ raise TypeError("CommandAction should be given only " \
+ "a single command")
+ self.cmd_list = cmd
+
+ def __str__(self):
+ if is_List(self.cmd_list):
+ return ' '.join(map(str, self.cmd_list))
+ return str(self.cmd_list)
+
+ def process(self, target, source, env, executor=None):
+ if executor:
+ result = env.subst_list(self.cmd_list, 0, executor=executor)
+ else:
+ result = env.subst_list(self.cmd_list, 0, target, source)
+ silent = None
+ ignore = None
+ while True:
+ try: c = result[0][0][0]
+ except IndexError: c = None
+ if c == '@': silent = 1
+ elif c == '-': ignore = 1
+ else: break
+ result[0][0] = result[0][0][1:]
+ try:
+ if not result[0][0]:
+ result[0] = result[0][1:]
+ except IndexError:
+ pass
+ return result, ignore, silent
+
+ def strfunction(self, target, source, env, executor=None):
+ if self.cmdstr is None:
+ return None
+ if self.cmdstr is not _null:
+ from SCons.Subst import SUBST_RAW
+ if executor:
+ c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
+ else:
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+ if c:
+ return c
+ cmd_list, ignore, silent = self.process(target, source, env, executor)
+ if silent:
+ return ''
+ return _string_from_cmd_list(cmd_list[0])
+
+ def execute(self, target, source, env, executor=None):
+ """Execute a command action.
+
+ This will handle lists of commands as well as individual commands,
+ because construction variable substitution may turn a single
+ "command" into a list. This means that this class can actually
+ handle lists of commands, even though that's not how we use it
+ externally.
+ """
+ escape_list = SCons.Subst.escape_list
+ flatten_sequence = SCons.Util.flatten_sequence
+
+ try:
+ shell = env['SHELL']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing SHELL construction variable.')
+
+ try:
+ spawn = env['SPAWN']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing SPAWN construction variable.')
+ else:
+ if is_String(spawn):
+ spawn = env.subst(spawn, raw=1, conv=lambda x: x)
+
+ escape = env.get('ESCAPE', lambda x: x)
+
+ ENV = get_default_ENV(env)
+
+ # Ensure that the ENV values are all strings:
+ for key, value in ENV.items():
+ if not is_String(value):
+ if is_List(value):
+ # If the value is a list, then we assume it is a
+ # path list, because that's a pretty common list-like
+ # value to stick in an environment variable:
+ value = flatten_sequence(value)
+ ENV[key] = os.pathsep.join(map(str, value))
+ else:
+ # If it isn't a string or a list, then we just coerce
+ # it to a string, which is the proper way to handle
+ # Dir and File instances and will produce something
+ # reasonable for just about everything else:
+ ENV[key] = str(value)
+
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor)
+
+ # Use len() to filter out any "command" that's zero-length.
+ for cmd_line in filter(len, cmd_list):
+ # Escape the command line for the interpreter we are using.
+ cmd_line = escape_list(cmd_line, escape)
+ result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
+ if not ignore and result:
+ msg = "Error %s" % result
+ return SCons.Errors.BuildError(errstr=msg,
+ status=result,
+ action=self,
+ command=cmd_line)
+ return 0
+
+ def get_presig(self, target, source, env, executor=None):
+ """Return the signature contents of this action's command line.
+
+ This strips $(-$) and everything in between the string,
+ since those parts don't affect signatures.
+ """
+ from SCons.Subst import SUBST_SIG
+ cmd = self.cmd_list
+ if is_List(cmd):
+ cmd = ' '.join(map(str, cmd))
+ else:
+ cmd = str(cmd)
+ if executor:
+ return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
+ else:
+ return env.subst_target_source(cmd, SUBST_SIG, target, source)
+
+ def get_implicit_deps(self, target, source, env, executor=None):
+ icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
+ if is_String(icd) and icd[:1] == '$':
+ icd = env.subst(icd)
+ if not icd or icd in ('0', 'None'):
+ return []
+ from SCons.Subst import SUBST_SIG
+ if executor:
+ cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor)
+ else:
+ cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
+ res = []
+ for cmd_line in cmd_list:
+ if cmd_line:
+ d = str(cmd_line[0])
+ m = strip_quotes.match(d)
+ if m:
+ d = m.group(1)
+ d = env.WhereIs(d)
+ if d:
+ res.append(env.fs.File(d))
+ return res
+
+class CommandGeneratorAction(ActionBase):
+ """Class for command-generator actions."""
+ def __init__(self, generator, kw):
+ if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
+ self.generator = generator
+ self.gen_kw = kw
+ self.varlist = kw.get('varlist', ())
+ self.targets = kw.get('targets', '$TARGETS')
+
+ def _generate(self, target, source, env, for_signature, executor=None):
+ # ensure that target is a list, to make it easier to write
+ # generator functions:
+ if not is_List(target):
+ target = [target]
+
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ ret = self.generator(target=target,
+ source=source,
+ env=env,
+ for_signature=for_signature)
+ gen_cmd = Action(ret, **self.gen_kw)
+ if not gen_cmd:
+ raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
+ return gen_cmd
+
+ def __str__(self):
+ try:
+ env = self.presub_env
+ except AttributeError:
+ env = None
+ if env is None:
+ env = SCons.Defaults.DefaultEnvironment()
+ act = self._generate([], [], env, 1)
+ return str(act)
+
+ def batch_key(self, env, target, source):
+ return self._generate(target, source, env, 1).batch_key(env, target, source)
+
+ def genstring(self, target, source, env, executor=None):
+ return self._generate(target, source, env, 1, executor).genstring(target, source, env)
+
+ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
+ show=_null, execute=_null, chdir=_null, executor=None):
+ act = self._generate(target, source, env, 0, executor)
+ if act is None:
+ raise UserError("While building `%s': "
+ "Cannot deduce file extension from source files: %s"
+ % (repr(list(map(str, target))), repr(list(map(str, source)))))
+ return act(target, source, env, exitstatfunc, presub,
+ show, execute, chdir, executor)
+
+ def get_presig(self, target, source, env, executor=None):
+ """Return the signature contents of this action's command line.
+
+ This strips $(-$) and everything in between the string,
+ since those parts don't affect signatures.
+ """
+ return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
+
+ def get_implicit_deps(self, target, source, env, executor=None):
+ return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)
+
+ def get_varlist(self, target, source, env, executor=None):
+ return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor)
+
+ def get_targets(self, env, executor):
+ return self._generate(None, None, env, 1, executor).get_targets(env, executor)
+
+
+
+# A LazyAction is a kind of hybrid generator and command action for
+# strings of the form "$VAR". These strings normally expand to other
+# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
+# want to be able to replace them with functions in the construction
+# environment. Consequently, we want lazy evaluation and creation of
+# an Action in the case of the function, but that's overkill in the more
+# normal case of expansion to other strings.
+#
+# So we do this with a subclass that's both a generator *and*
+# a command action. The overridden methods all do a quick check
+# of the construction variable, and if it's a string we just call
+# the corresponding CommandAction method to do the heavy lifting.
+# If not, then we call the same-named CommandGeneratorAction method.
+# The CommandGeneratorAction methods work by using the overridden
+# _generate() method, that is, our own way of handling "generation" of
+# an action based on what's in the construction variable.
+
+class LazyAction(CommandGeneratorAction, CommandAction):
+
+ def __init__(self, var, kw):
+ if __debug__: logInstanceCreation(self, 'Action.LazyAction')
+ #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
+ CommandAction.__init__(self, '${'+var+'}', **kw)
+ self.var = SCons.Util.to_String(var)
+ self.gen_kw = kw
+
+ def get_parent_class(self, env):
+ c = env.get(self.var)
+ if is_String(c) and not '\n' in c:
+ return CommandAction
+ return CommandGeneratorAction
+
+ def _generate_cache(self, env):
+ if env:
+ c = env.get(self.var, '')
+ else:
+ c = ''
+ gen_cmd = Action(c, **self.gen_kw)
+ if not gen_cmd:
+ raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
+ return gen_cmd
+
+ def _generate(self, target, source, env, for_signature, executor=None):
+ return self._generate_cache(env)
+
+ def __call__(self, target, source, env, *args, **kw):
+ c = self.get_parent_class(env)
+ return c.__call__(self, target, source, env, *args, **kw)
+
+ def get_presig(self, target, source, env):
+ c = self.get_parent_class(env)
+ return c.get_presig(self, target, source, env)
+
+ def get_varlist(self, target, source, env, executor=None):
+ c = self.get_parent_class(env)
+ return c.get_varlist(self, target, source, env, executor)
+
+
+class FunctionAction(_ActionAction):
+ """Class for Python function actions."""
+
+ def __init__(self, execfunction, kw):
+ if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
+
+ self.execfunction = execfunction
+ try:
+ self.funccontents = _callable_contents(execfunction)
+ except AttributeError:
+ try:
+ # See if execfunction will do the heavy lifting for us.
+ self.gc = execfunction.get_contents
+ except AttributeError:
+ # This is weird, just do the best we can.
+ self.funccontents = _object_contents(execfunction)
+
+ _ActionAction.__init__(self, **kw)
+
+ def function_name(self):
+ try:
+ return self.execfunction.__name__
+ except AttributeError:
+ try:
+ return self.execfunction.__class__.__name__
+ except AttributeError:
+ return "unknown_python_function"
+
+ def strfunction(self, target, source, env, executor=None):
+ if self.cmdstr is None:
+ return None
+ if self.cmdstr is not _null:
+ from SCons.Subst import SUBST_RAW
+ if executor:
+ c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
+ else:
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+ if c:
+ return c
+ def array(a):
+ def quote(s):
+ try:
+ str_for_display = s.str_for_display
+ except AttributeError:
+ s = repr(s)
+ else:
+ s = str_for_display()
+ return s
+ return '[' + ", ".join(map(quote, a)) + ']'
+ try:
+ strfunc = self.execfunction.strfunction
+ except AttributeError:
+ pass
+ else:
+ if strfunc is None:
+ return None
+ if callable(strfunc):
+ return strfunc(target, source, env)
+ name = self.function_name()
+ tstr = array(target)
+ sstr = array(source)
+ return "%s(%s, %s)" % (name, tstr, sstr)
+
+ def __str__(self):
+ name = self.function_name()
+ if name == 'ActionCaller':
+ return str(self.execfunction)
+ return "%s(target, source, env)" % name
+
+ def execute(self, target, source, env, executor=None):
+ exc_info = (None,None,None)
+ try:
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ rsources = list(map(rfile, source))
+ try:
+ result = self.execfunction(target=target, source=rsources, env=env)
+ except KeyboardInterrupt, e:
+ raise
+ except SystemExit, e:
+ raise
+ except Exception, e:
+ result = e
+ exc_info = sys.exc_info()
+
+ if result:
+ result = SCons.Errors.convert_to_BuildError(result, exc_info)
+ result.node=target
+ result.action=self
+ try:
+ result.command=self.strfunction(target, source, env, executor)
+ except TypeError:
+ result.command=self.strfunction(target, source, env)
+
+ # FIXME: This maintains backward compatibility with respect to
+ # which type of exceptions were returned by raising an
+ # exception and which ones were returned by value. It would
+ # probably be best to always return them by value here, but
+ # some codes do not check the return value of Actions and I do
+ # not have the time to modify them at this point.
+ if (exc_info[1] and
+ not isinstance(exc_info[1],EnvironmentError)):
+ raise result
+
+ return result
+ finally:
+ # Break the cycle between the traceback object and this
+ # function stack frame. See the sys.exc_info() doc info for
+ # more information about this issue.
+ del exc_info
+
+
+ def get_presig(self, target, source, env):
+ """Return the signature contents of this callable action."""
+ try:
+ return self.gc(target, source, env)
+ except AttributeError:
+ return self.funccontents
+
+ def get_implicit_deps(self, target, source, env):
+ return []
+
+class ListAction(ActionBase):
+ """Class for lists of other actions."""
+ def __init__(self, actionlist):
+ if __debug__: logInstanceCreation(self, 'Action.ListAction')
+ def list_of_actions(x):
+ if isinstance(x, ActionBase):
+ return x
+ return Action(x)
+ self.list = list(map(list_of_actions, actionlist))
+ # our children will have had any varlist
+ # applied; we don't need to do it again
+ self.varlist = ()
+ self.targets = '$TARGETS'
+
+ def genstring(self, target, source, env):
+ return '\n'.join([a.genstring(target, source, env) for a in self.list])
+
+ def __str__(self):
+ return '\n'.join(map(str, self.list))
+
+ def presub_lines(self, env):
+ return SCons.Util.flatten_sequence(
+ [a.presub_lines(env) for a in self.list])
+
+ def get_presig(self, target, source, env):
+ """Return the signature contents of this action list.
+
+ Simple concatenation of the signatures of the elements.
+ """
+ return "".join([x.get_contents(target, source, env) for x in self.list])
+
+ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
+ show=_null, execute=_null, chdir=_null, executor=None):
+ if executor:
+ target = executor.get_all_targets()
+ source = executor.get_all_sources()
+ for act in self.list:
+ stat = act(target, source, env, exitstatfunc, presub,
+ show, execute, chdir, executor)
+ if stat:
+ return stat
+ return 0
+
+ def get_implicit_deps(self, target, source, env):
+ result = []
+ for act in self.list:
+ result.extend(act.get_implicit_deps(target, source, env))
+ return result
+
+ def get_varlist(self, target, source, env, executor=None):
+ result = SCons.Util.OrderedDict()
+ for act in self.list:
+ for var in act.get_varlist(target, source, env, executor):
+ result[var] = True
+ return list(result.keys())
+
+class ActionCaller(object):
+ """A class for delaying calling an Action function with specific
+ (positional and keyword) arguments until the Action is actually
+ executed.
+
+ This class looks to the rest of the world like a normal Action object,
+ but what it's really doing is hanging on to the arguments until we
+ have a target, source and env to use for the expansion.
+ """
+ def __init__(self, parent, args, kw):
+ self.parent = parent
+ self.args = args
+ self.kw = kw
+
+ def get_contents(self, target, source, env):
+ actfunc = self.parent.actfunc
+ try:
+ # "self.actfunc" is a function.
+ contents = str(actfunc.func_code.co_code)
+ except AttributeError:
+ # "self.actfunc" is a callable object.
+ try:
+ contents = str(actfunc.__call__.im_func.func_code.co_code)
+ except AttributeError:
+ # No __call__() method, so it might be a builtin
+ # or something like that. Do the best we can.
+ contents = str(actfunc)
+ contents = remove_set_lineno_codes(contents)
+ return contents
+
+ def subst(self, s, target, source, env):
+ # If s is a list, recursively apply subst()
+ # to every element in the list
+ if is_List(s):
+ result = []
+ for elem in s:
+ result.append(self.subst(elem, target, source, env))
+ return self.parent.convert(result)
+
+ # Special-case hack: Let a custom function wrapped in an
+ # ActionCaller get at the environment through which the action
+ # was called by using this hard-coded value as a special return.
+ if s == '$__env__':
+ return env
+ elif is_String(s):
+ return env.subst(s, 1, target, source)
+ return self.parent.convert(s)
+
+ def subst_args(self, target, source, env):
+ return [self.subst(x, target, source, env) for x in self.args]
+
+ def subst_kw(self, target, source, env):
+ kw = {}
+ for key in self.kw.keys():
+ kw[key] = self.subst(self.kw[key], target, source, env)
+ return kw
+
+ def __call__(self, target, source, env, executor=None):
+ args = self.subst_args(target, source, env)
+ kw = self.subst_kw(target, source, env)
+ return self.parent.actfunc(*args, **kw)
+
+ def strfunction(self, target, source, env):
+ args = self.subst_args(target, source, env)
+ kw = self.subst_kw(target, source, env)
+ return self.parent.strfunc(*args, **kw)
+
+ def __str__(self):
+ return self.parent.strfunc(*self.args, **self.kw)
+
+class ActionFactory(object):
+ """A factory class that will wrap up an arbitrary function
+ as an SCons-executable Action object.
+
+ The real heavy lifting here is done by the ActionCaller class.
+ We just collect the (positional and keyword) arguments that we're
+ called with and give them to the ActionCaller object we create,
+ so it can hang onto them until it needs them.
+ """
+ def __init__(self, actfunc, strfunc, convert=lambda x: x):
+ self.actfunc = actfunc
+ self.strfunc = strfunc
+ self.convert = convert
+
+ def __call__(self, *args, **kw):
+ ac = ActionCaller(self, args, kw)
+ action = Action(ac, strfunction=ac.strfunction)
+ return action
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
View
877 scons/scons-local-2.1.0/SCons/Builder.py
@@ -0,0 +1,877 @@
+"""SCons.Builder
+
+Builder object subsystem.
+
+A Builder object is a callable that encapsulates information about how
+to execute actions to create a target Node (file) from source Nodes
+(files), and how to create those dependencies for tracking.
+
+The main entry point here is the Builder() factory method. This provides
+a procedural interface that creates the right underlying Builder object
+based on the keyword arguments supplied and the types of the arguments.
+
+The goal is for this external interface to be simple enough that the
+vast majority of users can create new Builders as necessary to support
+building new types of files in their configurations, without having to
+dive any deeper into this subsystem.
+
+The base class here is BuilderBase. This is a concrete base class which
+does, in fact, represent the Builder objects that we (or users) create.
+
+There is also a proxy that looks like a Builder:
+
+ CompositeBuilder
+
+ This proxies for a Builder with an action that is actually a
+ dictionary that knows how to map file suffixes to a specific
+ action. This is so that we can invoke different actions
+ (compilers, compile options) for different flavors of source
+ files.
+
+Builders and their proxies have the following public interface methods
+used by other modules:
+
+ __call__()
+ THE public interface. Calling a Builder object (with the
+ use of internal helper methods) sets up the target and source
+ dependencies, appropriate mapping to a specific action, and the
+ environment manipulation necessary for overridden construction
+ variable. This also takes care of warning about possible mistakes
+ in keyword arguments.
+
+ add_emitter()
+ Adds an emitter for a specific file suffix, used by some Tool
+ modules to specify that (for example) a yacc invocation on a .y
+ can create a .h *and* a .c file.
+
+ add_action()
+ Adds an action for a specific file suffix, heavily used by
+ Tool modules to add their specific action(s) for turning
+ a source file into an object file to the global static
+ and shared object file Builders.
+
+There are the following methods for internal use within this module:
+
+ _execute()
+ The internal method that handles the heavily lifting when a
+ Builder is called. This is used so that the __call__() methods
+ can set up warning about possible mistakes in keyword-argument
+ overrides, and *then* execute all of the steps necessary so that
+ the warnings only occur once.
+
+ get_name()
+ Returns the Builder's name within a specific Environment,
+ primarily used to try to return helpful information in error
+ messages.
+
+ adjust_suffix()
+ get_prefix()
+ get_suffix()
+ get_src_suffix()
+ set_src_suffix()
+ Miscellaneous stuff for handling the prefix and suffix
+ manipulation we use in turning source file names into target
+ file names.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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/Builder.py 5357 2011/09/09 21:31:03 bdeegan"
+
+import collections
+
+import SCons.Action
+from SCons.Debug import logInstanceCreation
+from SCons.Errors import InternalError, UserError
+import SCons.Executor
+import SCons.Memoize
+import SCons.Node
+import SCons.Node.FS
+import SCons.Util
+import SCons.Warnings
+
+class _Null(object):
+ pass
+
+_null = _Null
+
+def match_splitext(path, suffixes = []):
+ if suffixes:
+ matchsuf = [S for S in suffixes if path[-len(S):] == S]
+ if matchsuf:
+ suf = max([(len(_f),_f) for _f in matchsuf])[1]
+ return [path[:-len(suf)], path[-len(suf):]]
+ return SCons.Util.splitext(path)
+
+class DictCmdGenerator(SCons.Util.Selector):
+ """This is a callable class that can be used as a
+ command generator function. It holds on to a dictionary
+ mapping file suffixes to Actions. It uses that dictionary
+ to return the proper action based on the file suffix of
+ the source file."""
+
+ def __init__(self, dict=None, source_ext_match=1):
+ SCons.Util.Selector.__init__(self, dict)
+ self.source_ext_match = source_ext_match
+
+ def src_suffixes(self):
+ return list(self.keys())
+
+ def add_action(self, suffix, action):
+ """Add a suffix-action pair to the mapping.
+ """
+ self[suffix] = action
+
+ def __call__(self, target, source, env, for_signature):
+ if not source:
+ return []
+
+ if self.source_ext_match:
+ suffixes = self.src_suffixes()
+ ext = None
+ for src in map(str, source):
+ my_ext = match_splitext(src, suffixes)[1]
+ if ext and my_ext != ext:
+ raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s"
+ % (repr(list(map(str, target))), src, ext, my_ext))
+ ext = my_ext
+ else:
+ ext = match_splitext(str(source[0]), self.src_suffixes())[1]
+
+ if not ext:
+ #return ext
+ raise UserError("While building `%s': "
+ "Cannot deduce file extension from source files: %s"
+ % (repr(list(map(str, target))), repr(list(map(str, source)))))
+
+ try:
+ ret = SCons.Util.Selector.__call__(self, env, source, ext)
+ except KeyError, e:
+ raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2]))
+ if ret is None:
+ raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \
+ (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys()))))
+ return ret
+
+class CallableSelector(SCons.Util.Selector):
+ """A callable dictionary that will, in turn, call the value it
+ finds if it can."""
+ def __call__(self, env, source):
+ value = SCons.Util.Selector.__call__(self, env, source)
+ if callable(value):
+ value = value(env, source)
+ return value
+
+class DictEmitter(SCons.Util.Selector):
+ """A callable dictionary that maps file suffixes to emitters.
+ When called, it finds the right emitter in its dictionary for the
+ suffix of the first source file, and calls that emitter to get the
+ right lists of targets and sources to return. If there's no emitter
+ for the suffix in its dictionary, the original target and source are
+ returned.
+ """
+ def __call__(self, target, source, env):
+ emitter = SCons.Util.Selector.__call__(self, env, source)
+ if emitter:
+ target, source = emitter(target, source, env)
+ return (target, source)
+
+class ListEmitter(collections.UserList):
+ """A callable list of emitters that calls each in sequence,
+ returning the result.
+ """
+ def __call__(self, target, source, env):
+ for e in self.data:
+ target, source = e(target, source, env)
+ return (target, source)
+
+# These are a common errors when calling a Builder;
+# they are similar to the 'target' and 'source' keyword args to builders,
+# so we issue warnings when we see them. The warnings can, of course,
+# be disabled.
+misleading_keywords = {
+ 'targets' : 'target',
+ 'sources' : 'source',
+}
+
+class OverrideWarner(collections.UserDict):
+ """A class for warning about keyword arguments that we use as
+ overrides in a Builder call.
+
+ This class exists to handle the fact that a single Builder call
+ can actually invoke multiple builders. This class only emits the
+ warnings once, no matter how many Builders are invoked.
+ """
+ def __init__(self, dict):
+ collections.UserDict.__init__(self, dict)
+ if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
+ self.already_warned = None
+ def warn(self):
+ if self.already_warned:
+ return
+ for k in self.keys():
+ if k in misleading_keywords:
+ alt = misleading_keywords[k]
+ msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
+ SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
+ self.already_warned = 1
+
+def Builder(**kw):
+ """A factory for builder objects."""
+ composite = None
+ if 'generator' in kw:
+ if 'action' in kw:
+ raise UserError("You must not specify both an action and a generator.")
+ kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {})
+ del kw['generator']
+ elif 'action' in kw:
+ source_ext_match = kw.get('source_ext_match', 1)
+ if 'source_ext_match' in kw:
+ del kw['source_ext_match']
+ if SCons.Util.is_Dict(kw['action']):
+ composite = DictCmdGenerator(kw['action'], source_ext_match)
+ kw['action'] = SCons.Action.CommandGeneratorAction(composite, {})
+ kw['src_suffix'] = composite.src_suffixes()
+ else:
+ kw['action'] = SCons.Action.Action(kw['action'])
+
+ if 'emitter' in kw:
+ emitter = kw['emitter']
+ if SCons.Util.is_String(emitter):
+ # This allows users to pass in an Environment
+ # variable reference (like "$FOO") as an emitter.
+ # We will look in that Environment variable for
+ # a callable to use as the actual emitter.
+ var = SCons.Util.get_environment_var(emitter)
+ if not var:
+ raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter)
+ kw['emitter'] = EmitterProxy(var)
+ elif SCons.Util.is_Dict(emitter):
+ kw['emitter'] = DictEmitter(emitter)
+ elif SCons.Util.is_List(emitter):
+ kw['emitter'] = ListEmitter(emitter)
+
+ result = BuilderBase(**kw)
+
+ if not composite is None:
+ result = CompositeBuilder(result, composite)
+
+ return result
+
+def _node_errors(builder, env, tlist, slist):
+ """Validate that the lists of target and source nodes are
+ legal for this builder and environment. Raise errors or
+ issue warnings as appropriate.
+ """
+
+ # First, figure out if there are any errors in the way the targets
+ # were specified.
+ for t in tlist:
+ if t.side_effect:
+ raise UserError("Multiple ways to build the same target were specified for: %s" % t)
+ if t.has_explicit_builder():
+ if not t.env is None and not t.env is env:
+ action = t.builder.action
+ t_contents = action.get_contents(tlist, slist, t.env)
+ contents = action.get_contents(tlist, slist, env)
+
+ if t_contents == contents:
+ msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env))
+ SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
+ else:
+ msg = "Two environments with different actions were specified for the same target: %s" % t
+ raise UserError(msg)
+ if builder.multi:
+ if t.builder != builder:
+ msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
+ raise UserError(msg)
+ # TODO(batch): list constructed each time!
+ if t.get_executor().get_all_targets() != tlist:
+ msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist)))
+ raise UserError(msg)
+ elif t.sources != slist:
+ msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist)))
+ raise UserError(msg)
+
+ if builder.single_source:
+ if len(slist) > 1:
+ raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist))))
+
+class EmitterProxy(object):
+ """This is a callable class that can act as a
+ Builder emitter. It holds on to a string that
+ is a key into an Environment dictionary, and will
+ look there at actual build time to see if it holds
+ a callable. If so, we will call that as the actual
+ emitter."""
+ def __init__(self, var):
+ self.var = SCons.Util.to_String(var)
+
+ def __call__(self, target, source, env):
+ emitter = self.var
+
+ # Recursively substitute the variable.
+ # We can't use env.subst() because it deals only
+ # in strings. Maybe we should change that?
+ while SCons.Util.is_String(emitter) and emitter in env:
+ emitter = env[emitter]
+ if callable(emitter):
+ target, source = emitter(target, source, env)
+ elif SCons.Util.is_List(emitter):
+ for e in emitter:
+ target, source = e(target, source, env)
+
+ return (target, source)
+
+
+ def __cmp__(self, other):
+ return cmp(self.var, other.var)
+
+class BuilderBase(object):
+ """Base class for Builders, objects that create output
+ nodes (files) from input nodes (files).
+ """
+
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+ memoizer_counters = []
+
+ def __init__(self, action = None,
+ prefix = '',
+ suffix = '',
+ src_suffix = '',
+ target_factory = None,
+ source_factory = None,
+ target_scanner = None,
+ source_scanner = None,
+ emitter = None,
+ multi = 0,
+ env = None,
+ single_source = 0,
+ name = None,
+ chdir = _null,
+ is_explicit = 1,
+ src_builder = None,
+ ensure_suffix = False,
+ **overrides):
+ if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
+ self._memo = {}
+ self.action = action
+ self.multi = multi
+ if SCons.Util.is_Dict(prefix):
+ prefix = CallableSelector(prefix)
+ self.prefix = prefix
+ if SCons.Util.is_Dict(suffix):
+ suffix = CallableSelector(suffix)
+ self.env = env
+ self.single_source = single_source
+ if 'overrides' in overrides:
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning,
+ "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
+ "\tspecify the items as keyword arguments to the Builder() call instead.")
+ overrides.update(overrides['overrides'])
+ del overrides['overrides']
+ if 'scanner' in overrides:
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning,
+ "The \"scanner\" keyword to Builder() creation has been deprecated;\n"
+ "\tuse: source_scanner or target_scanner as appropriate.")
+ del overrides['scanner']
+ self.overrides = overrides
+
+ self.set_suffix(suffix)
+ self.set_src_suffix(src_suffix)
+ self.ensure_suffix = ensure_suffix
+
+ self.target_factory = target_factory
+ self.source_factory = source_factory
+ self.target_scanner = target_scanner
+ self.source_scanner = source_scanner
+
+ self.emitter = emitter
+
+ # Optional Builder name should only be used for Builders
+ # that don't get attached to construction environments.
+ if name:
+ self.name = name
+ self.executor_kw = {}
+ if not chdir is _null:
+ self.executor_kw['chdir'] = chdir
+ self.is_explicit = is_explicit
+
+ if src_builder is None:
+ src_builder = []
+ elif not SCons.Util.is_List(src_builder):
+ src_builder = [ src_builder ]
+ self.src_builder = src_builder
+
+ def __nonzero__(self):
+ raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead")
+
+ def get_name(self, env):
+ """Attempts to get the name of the Builder.
+
+ Look at the BUILDERS variable of env, expecting it to be a
+ dictionary containing this Builder, and return the key of the
+ dictionary. If there's no key, then return a directly-configured
+ name (if there is one) or the name of the class (by default)."""
+
+ try:
+ index = list(env['BUILDERS'].values()).index(self)
+ return list(env['BUILDERS'].keys())[index]
+ except (AttributeError, KeyError, TypeError, ValueError):
+ try:
+ return self.name
+ except AttributeError:
+ return str(self.__class__)
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other.__dict__)
+
+ def splitext(self, path, env=None):
+ if not env:
+ env = self.env
+ if env:
+ suffixes = self.src_suffixes(env)
+ else:
+ suffixes = []
+ return match_splitext(path, suffixes)
+
+ def _adjustixes(self, files, pre, suf, ensure_suffix=False):
+ if not files:
+ return []
+ result = []
+ if not SCons.Util.is_List(files):
+ files = [files]
+
+ for f in files:
+ if SCons.Util.is_String(f):
+ f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
+ result.append(f)
+ return result
+
+ def _create_nodes(self, env, target = None, source = None):
+ """Create and return lists of target and source nodes.
+ """
+ src_suf = self.get_src_suffix(env)
+
+ target_factory = env.get_factory(self.target_factory)
+ source_factory = env.get_factory(self.source_factory)
+
+ source = self._adjustixes(source, None, src_suf)
+ slist = env.arg2nodes(source, source_factory)
+
+ pre = self.get_prefix(env, slist)
+ suf = self.get_suffix(env, slist)
+
+ if target is None:
+ try:
+ t_from_s = slist[0].target_from_source
+ except AttributeError:
+ raise UserError("Do not know how to create a target from source `%s'" % slist[0])
+ except IndexError:
+ tlist = []
+ else:
+ splitext = lambda S: self.splitext(S,env)
+ tlist = [ t_from_s(pre, suf, splitext) ]
+ else:
+ target = self._adjustixes(target, pre, suf, self.ensure_suffix)
+ tlist = env.arg2nodes(target, target_factory, target=target, source=source)
+
+ if self.emitter:
+ # The emitter is going to do str(node), but because we're
+ # being called *from* a builder invocation, the new targets
+ # don't yet have a builder set on them and will look like
+ # source files. Fool the emitter's str() calls by setting
+ # up a temporary builder on the new targets.
+ new_targets = []
+ for t in tlist:
+ if not t.is_derived():
+ t.builder_set(self)
+ new_targets.append(t)
+
+ orig_tlist = tlist[:]
+ orig_slist = slist[:]
+
+ target, source = self.emitter(target=tlist, source=slist, env=env)
+
+ # Now delete the temporary builders that we attached to any
+ # new targets, so that _node_errors() doesn't do weird stuff
+ # to them because it thinks they already have builders.
+ for t in new_targets:
+ if t.builder is self:
+ # Only delete the temporary builder if the emitter
+ # didn't change it on us.
+ t.builder_set(None)
+
+ # Have to call arg2nodes yet again, since it is legal for
+ # emitters to spit out strings as well as Node instances.
+ tlist = env.arg2nodes(target, target_factory,
+ target=orig_tlist, source=orig_slist)
+ slist = env.arg2nodes(source, source_factory,
+ target=orig_tlist, source=orig_slist)
+
+ return tlist, slist
+
+ def _execute(self, env, target, source, overwarn={}, executor_kw={}):
+ # We now assume that target and source are lists or None.
+ if self.src_builder:
+ source = self.src_builder_sources(env, source, overwarn)
+
+ if self.single_source and len(source) > 1 and target is None:
+ result = []
+ if target is None: target = [None]*len(source)
+ for tgt, src in zip(target, source):
+ if not tgt is None: tgt = [tgt]
+ if not src is None: src = [src]
+ result.extend(self._execute(env, tgt, src, overwarn))
+ return SCons.Node.NodeList(result)
+
+ overwarn.warn()
+
+ tlist, slist = self._create_nodes(env, target, source)
+
+ # Check for errors with the specified target/source lists.
+ _node_errors(self, env, tlist, slist)
+
+ # The targets are fine, so find or make the appropriate Executor to
+ # build this particular list of targets from this particular list of
+ # sources.
+
+ executor = None
+ key = None
+
+ if self.multi:
+ try:
+ executor = tlist[0].get_executor(create = 0)
+ except (AttributeError, IndexError):
+ pass
+ else:
+ executor.add_sources(slist)
+
+ if executor is None:
+ if not self.action:
+ fmt = "Builder %s must have an action to build %s."
+ raise UserError(fmt % (self.get_name(env or self.env),
+ list(map(str,tlist))))
+ key = self.action.batch_key(env or self.env, tlist, slist)
+ if key:
+ try:
+ executor = SCons.Executor.GetBatchExecutor(key)
+ except KeyError:
+ pass
+ else:
+ executor.add_batch(tlist, slist)
+
+ if executor is None:
+ executor = SCons.Executor.Executor(self.action, env, [],
+ tlist, slist, executor_kw)
+ if key:
+ SCons.Executor.AddBatchExecutor(key, executor)
+
+ # Now set up the relevant information in the target Nodes themselves.
+ for t in tlist:
+ t.cwd = env.fs.getcwd()
+ t.builder_set(self)
+ t.env_set(env)
+ t.add_source(slist)
+ t.set_executor(executor)
+ t.set_explicit(self.is_explicit)
+
+ return SCons.Node.NodeList(tlist)
+
+ def __call__(self, env, target=None, source=None, chdir=_null, **kw):
+ # We now assume that target and source are lists or None.
+ # The caller (typically Environment.BuilderWrapper) is
+ # responsible for converting any scalar values to lists.
+ if chdir is _null:
+ ekw = self.executor_kw
+ else:
+ ekw = self.executor_kw.copy()
+ ekw['chdir'] = chdir
+ if kw:
+ if 'srcdir' in kw:
+ def prependDirIfRelative(f, srcdir=kw['srcdir']):
+ import os.path
+ if SCons.Util.is_String(f) and not os.path.isabs(f):
+ f = os.path.join(srcdir, f)
+ return f
+ if not SCons.Util.is_List(source):
+ source = [source]
+ source = list(map(prependDirIfRelative, source))
+ del kw['srcdir']
+ if self.overrides:
+ env_kw = self.overrides.copy()
+ env_kw.update(kw)
+ else:
+ env_kw = kw
+ else:
+ env_kw = self.overrides
+ env = env.Override(env_kw)
+ return self._execute(env, target, source, OverrideWarner(kw), ekw)
+
+ def adjust_suffix(self, suff):
+ if suff and not suff[0] in [ '.', '_', '$' ]:
+ return '.' + suff
+ return suff
+
+ def get_prefix(self, env, sources=[]):
+ prefix = self.prefix
+ if callable(prefix):
+ prefix = prefix(env, sources)
+ return env.subst(prefix)
+
+ def set_suffix(self, suffix):
+ if not callable(suffix):
+ suffix = self.adjust_suffix(suffix)
+ self.suffix = suffix
+
+ def get_suffix(self, env, sources=[]):
+ suffix = self.suffix
+ if callable(suffix):
+ suffix = suffix(env, sources)
+ return env.subst(suffix)
+
+ def set_src_suffix(self, src_suffix):
+ if not src_suffix:
+ src_suffix = []
+ elif not SCons.Util.is_List(src_suffix):
+ src_suffix = [ src_suffix ]
+ self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix]
+
+ def get_src_suffix(self, env):
+ """Get the first src_suffix in the list of src_suffixes."""
+ ret = self.src_suffixes(env)
+ if not ret:
+ return ''
+ return ret[0]
+
+ def add_emitter(self, suffix, emitter):
+ """Add a suffix-emitter mapping to this Builder.
+
+ This assumes that emitter has been initialized with an
+ appropriate dictionary type, and will throw a TypeError if
+ not, so the caller is responsible for knowing that this is an
+ appropriate method to call for the Builder in question.
+ """
+ self.emitter[suffix] = emitter
+
+ def add_src_builder(self, builder):
+ """
+ Add a new Builder to the list of src_builders.
+
+ This requires wiping out cached values so that the computed
+ lists of source suffixes get re-calculated.
+ """
+ self._memo = {}
+ self.src_builder.append(builder)
+
+ def _get_sdict(self, env):
+ """
+ Returns a dictionary mapping all of the source suffixes of all
+ src_builders of this Builder to the underlying Builder that
+ should be called first.
+
+ This dictionary is used for each target specified, so we save a
+ lot of extra computation by memoizing it for each construction
+ environment.
+
+ Note that this is re-computed each time, not cached, because there
+ might be changes to one of our source Builders (or one of their
+ source Builders, and so on, and so on...) that we can't "see."
+
+ The underlying methods we call cache their computed values,
+ though, so we hope repeatedly aggregating them into a dictionary
+ like this won't be too big a hit. We may need to look for a
+ better way to do this if performance data show this has turned
+ into a significant bottleneck.
+ """
+ sdict = {}
+ for bld in self.get_src_builders(env):
+ for suf in bld.src_suffixes(env):
+ sdict[suf] = bld
+ return sdict
+
+ def src_builder_sources(self, env, source, overwarn={}):
+ sdict = self._get_sdict(env)
+
+ src_suffixes = self.src_suffixes(env)
+
+ lengths = list(set(map(len, src_suffixes)))
+
+ def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths):
+ node_suffixes = [name[-l:] for l in lengths]
+ for suf in src_suffixes:
+ if suf in node_suffixes:
+ return suf
+ return None
+
+ result = []
+ for s in SCons.Util.flatten(source):
+ if SCons.Util.is_String(s):
+ match_suffix = match_src_suffix(env.subst(s))
+ if not match_suffix and not '.' in s:
+ src_suf = self.get_src_suffix(env)
+ s = self._adjustixes(s, None, src_suf)[0]
+ else:
+ match_suffix = match_src_suffix(s.name)
+ if match_suffix:
+ try:
+ bld = sdict[match_suffix]
+ except KeyError:
+ result.append(s)
+ else:
+ tlist = bld._execute(env, None, [s], overwarn)
+ # If the subsidiary Builder returned more than one
+ # target, then filter out any sources that this
+ # Builder isn't capable of building.
+ if len(tlist) > 1:
+ tlist = [t for t in tlist if match_src_suffix(t.name)]
+ result.extend(tlist)
+ else:
+ result.append(s)
+
+ source_factory = env.get_factory(self.source_factory)
+
+ return env.arg2nodes(result, source_factory)
+
+ def _get_src_builders_key(self, env):
+ return id(env)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
+
+ def get_src_builders(self, env):
+ """
+ Returns the list of source Builders for this Builder.
+
+ This exists mainly to look up Builders referenced as
+ strings in the 'BUILDER' variable of the construction
+ environment and cache the result.
+ """
+ memo_key = id(env)
+ try:
+ memo_dict = self._memo['get_src_builders']
+ except KeyError:
+ memo_dict = {}
+ self._memo['get_src_builders'] = memo_dict
+ else:
+ try:
+ return memo_dict[memo_key]
+ except KeyError:
+ pass
+
+ builders = []
+ for bld in self.src_builder:
+ if SCons.Util.is_String(bld):
+ try:
+ bld = env['BUILDERS'][bld]
+ except KeyError:
+ continue
+ builders.append(bld)
+
+ memo_dict[memo_key] = builders
+ return builders
+
+ def _subst_src_suffixes_key(self, env):
+ return id(env)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
+
+ def subst_src_suffixes(self, env):
+ """
+ The suffix list may contain construction variable expansions,
+ so we have to evaluate the individual strings. To avoid doing
+ this over and over, we memoize the results for each construction
+ environment.
+ """
+ memo_key = id(env)
+ try:
+ memo_dict = self._memo['subst_src_suffixes']
+ except KeyError:
+ memo_dict = {}
+ self._memo['subst_src_suffixes'] = memo_dict
+ else:
+ try:
+ return memo_dict[memo_key]
+ except KeyError:
+ pass
+ suffixes = [env.subst(x) for x in self.src_suffix]
+ memo_dict[memo_key] = suffixes
+ return suffixes
+
+ def src_suffixes(self, env):
+ """
+ Returns the list of source suffixes for all src_builders of this
+ Builder.
+
+ This is essentially a recursive descent of the src_builder "tree."
+ (This value isn't cached because there may be changes in a
+ src_builder many levels deep that we can't see.)
+ """
+ sdict = {}
+ suffixes = self.subst_src_suffixes(env)
+ for s in suffixes:
+ sdict[s] = 1
+ for builder in self.get_src_builders(env):
+ for s in builder.src_suffixes(env):
+ if s not in sdict:
+ sdict[s] = 1
+ suffixes.append(s)
+ return suffixes
+
+class CompositeBuilder(SCons.Util.Proxy):
+ """A Builder Proxy whose main purpose is to always have
+ a DictCmdGenerator as its action, and to provide access
+ to the DictCmdGenerator's add_action() method.
+ """
+
+ def __init__(self, builder, cmdgen):
+ if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
+ SCons.Util.Proxy.__init__(self, builder)
+
+ # cmdgen should always be an instance of DictCmdGenerator.
+ self.cmdgen = cmdgen
+ self.builder = builder
+
+ __call__ = SCons.Util.Delegate('__call__')
+
+ def add_action(self, suffix, action):
+ self.cmdgen.add_action(suffix, action)
+ self.set_src_suffix(self.cmdgen.src_suffixes())
+
+def is_a_Builder(obj):
+ """"Returns True iff the specified obj is one of our Builder classes.
+
+ The test is complicated a bit by the fact that CompositeBuilder
+ is a proxy, not a subclass of BuilderBase.
+ """
+ return (isinstance(obj, BuilderBase)
+ or isinstance(obj, CompositeBuilder)
+ or callable(obj))
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
View
216 scons/scons-local-2.1.0/SCons/CacheDir.py
@@ -0,0 +1,216 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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: