initial dump of scons 2.3
authorCampbell Barton <ideasman42@gmail.com>
Wed, 13 Nov 2013 09:13:33 +0000 (20:13 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 13 Nov 2013 09:13:33 +0000 (20:13 +1100)
197 files changed:
scons-LICENSE [new file with mode: 0644]
scons-README [new file with mode: 0644]
scons-local/SCons/Action.py [new file with mode: 0644]
scons-local/SCons/Builder.py [new file with mode: 0644]
scons-local/SCons/CacheDir.py [new file with mode: 0644]
scons-local/SCons/Conftest.py [new file with mode: 0644]
scons-local/SCons/Debug.py [new file with mode: 0644]
scons-local/SCons/Defaults.py [new file with mode: 0644]
scons-local/SCons/Environment.py [new file with mode: 0644]
scons-local/SCons/Errors.py [new file with mode: 0644]
scons-local/SCons/Executor.py [new file with mode: 0644]
scons-local/SCons/Job.py [new file with mode: 0644]
scons-local/SCons/Memoize.py [new file with mode: 0644]
scons-local/SCons/Node/Alias.py [new file with mode: 0644]
scons-local/SCons/Node/FS.py [new file with mode: 0644]
scons-local/SCons/Node/Python.py [new file with mode: 0644]
scons-local/SCons/Node/__init__.py [new file with mode: 0644]
scons-local/SCons/Options/BoolOption.py [new file with mode: 0644]
scons-local/SCons/Options/EnumOption.py [new file with mode: 0644]
scons-local/SCons/Options/ListOption.py [new file with mode: 0644]
scons-local/SCons/Options/PackageOption.py [new file with mode: 0644]
scons-local/SCons/Options/PathOption.py [new file with mode: 0644]
scons-local/SCons/Options/__init__.py [new file with mode: 0644]
scons-local/SCons/PathList.py [new file with mode: 0644]
scons-local/SCons/Platform/__init__.py [new file with mode: 0644]
scons-local/SCons/Platform/aix.py [new file with mode: 0644]
scons-local/SCons/Platform/cygwin.py [new file with mode: 0644]
scons-local/SCons/Platform/darwin.py [new file with mode: 0644]
scons-local/SCons/Platform/hpux.py [new file with mode: 0644]
scons-local/SCons/Platform/irix.py [new file with mode: 0644]
scons-local/SCons/Platform/os2.py [new file with mode: 0644]
scons-local/SCons/Platform/posix.py [new file with mode: 0644]
scons-local/SCons/Platform/sunos.py [new file with mode: 0644]
scons-local/SCons/Platform/win32.py [new file with mode: 0644]
scons-local/SCons/SConf.py [new file with mode: 0644]
scons-local/SCons/SConsign.py [new file with mode: 0644]
scons-local/SCons/Scanner/C.py [new file with mode: 0644]
scons-local/SCons/Scanner/D.py [new file with mode: 0644]
scons-local/SCons/Scanner/Dir.py [new file with mode: 0644]
scons-local/SCons/Scanner/Fortran.py [new file with mode: 0644]
scons-local/SCons/Scanner/IDL.py [new file with mode: 0644]
scons-local/SCons/Scanner/LaTeX.py [new file with mode: 0644]
scons-local/SCons/Scanner/Prog.py [new file with mode: 0644]
scons-local/SCons/Scanner/RC.py [new file with mode: 0644]
scons-local/SCons/Scanner/__init__.py [new file with mode: 0644]
scons-local/SCons/Script/Interactive.py [new file with mode: 0644]
scons-local/SCons/Script/Main.py [new file with mode: 0644]
scons-local/SCons/Script/SConsOptions.py [new file with mode: 0644]
scons-local/SCons/Script/SConscript.py [new file with mode: 0644]
scons-local/SCons/Script/__init__.py [new file with mode: 0644]
scons-local/SCons/Sig.py [new file with mode: 0644]
scons-local/SCons/Subst.py [new file with mode: 0644]
scons-local/SCons/Taskmaster.py [new file with mode: 0644]
scons-local/SCons/Tool/386asm.py [new file with mode: 0644]
scons-local/SCons/Tool/BitKeeper.py [new file with mode: 0644]
scons-local/SCons/Tool/CVS.py [new file with mode: 0644]
scons-local/SCons/Tool/FortranCommon.py [new file with mode: 0644]
scons-local/SCons/Tool/GettextCommon.py [new file with mode: 0644]
scons-local/SCons/Tool/JavaCommon.py [new file with mode: 0644]
scons-local/SCons/Tool/MSCommon/__init__.py [new file with mode: 0644]
scons-local/SCons/Tool/MSCommon/arch.py [new file with mode: 0644]
scons-local/SCons/Tool/MSCommon/common.py [new file with mode: 0644]
scons-local/SCons/Tool/MSCommon/netframework.py [new file with mode: 0644]
scons-local/SCons/Tool/MSCommon/sdk.py [new file with mode: 0644]
scons-local/SCons/Tool/MSCommon/vc.py [new file with mode: 0644]
scons-local/SCons/Tool/MSCommon/vs.py [new file with mode: 0644]
scons-local/SCons/Tool/Perforce.py [new file with mode: 0644]
scons-local/SCons/Tool/PharLapCommon.py [new file with mode: 0644]
scons-local/SCons/Tool/RCS.py [new file with mode: 0644]
scons-local/SCons/Tool/SCCS.py [new file with mode: 0644]
scons-local/SCons/Tool/Subversion.py [new file with mode: 0644]
scons-local/SCons/Tool/__init__.py [new file with mode: 0644]
scons-local/SCons/Tool/aixc++.py [new file with mode: 0644]
scons-local/SCons/Tool/aixcc.py [new file with mode: 0644]
scons-local/SCons/Tool/aixf77.py [new file with mode: 0644]
scons-local/SCons/Tool/aixlink.py [new file with mode: 0644]
scons-local/SCons/Tool/applelink.py [new file with mode: 0644]
scons-local/SCons/Tool/ar.py [new file with mode: 0644]
scons-local/SCons/Tool/as.py [new file with mode: 0644]
scons-local/SCons/Tool/bcc32.py [new file with mode: 0644]
scons-local/SCons/Tool/c++.py [new file with mode: 0644]
scons-local/SCons/Tool/cc.py [new file with mode: 0644]
scons-local/SCons/Tool/cvf.py [new file with mode: 0644]
scons-local/SCons/Tool/default.py [new file with mode: 0644]
scons-local/SCons/Tool/dmd.py [new file with mode: 0644]
scons-local/SCons/Tool/dvi.py [new file with mode: 0644]
scons-local/SCons/Tool/dvipdf.py [new file with mode: 0644]
scons-local/SCons/Tool/dvips.py [new file with mode: 0644]
scons-local/SCons/Tool/f03.py [new file with mode: 0644]
scons-local/SCons/Tool/f77.py [new file with mode: 0644]
scons-local/SCons/Tool/f90.py [new file with mode: 0644]
scons-local/SCons/Tool/f95.py [new file with mode: 0644]
scons-local/SCons/Tool/filesystem.py [new file with mode: 0644]
scons-local/SCons/Tool/fortran.py [new file with mode: 0644]
scons-local/SCons/Tool/g++.py [new file with mode: 0644]
scons-local/SCons/Tool/g77.py [new file with mode: 0644]
scons-local/SCons/Tool/gas.py [new file with mode: 0644]
scons-local/SCons/Tool/gcc.py [new file with mode: 0644]
scons-local/SCons/Tool/gettext.py [new file with mode: 0644]
scons-local/SCons/Tool/gfortran.py [new file with mode: 0644]
scons-local/SCons/Tool/gnulink.py [new file with mode: 0644]
scons-local/SCons/Tool/gs.py [new file with mode: 0644]
scons-local/SCons/Tool/hpc++.py [new file with mode: 0644]
scons-local/SCons/Tool/hpcc.py [new file with mode: 0644]
scons-local/SCons/Tool/hplink.py [new file with mode: 0644]
scons-local/SCons/Tool/icc.py [new file with mode: 0644]
scons-local/SCons/Tool/icl.py [new file with mode: 0644]
scons-local/SCons/Tool/ifl.py [new file with mode: 0644]
scons-local/SCons/Tool/ifort.py [new file with mode: 0644]
scons-local/SCons/Tool/ilink.py [new file with mode: 0644]
scons-local/SCons/Tool/ilink32.py [new file with mode: 0644]
scons-local/SCons/Tool/install.py [new file with mode: 0644]
scons-local/SCons/Tool/intelc.py [new file with mode: 0644]
scons-local/SCons/Tool/ipkg.py [new file with mode: 0644]
scons-local/SCons/Tool/jar.py [new file with mode: 0644]
scons-local/SCons/Tool/javac.py [new file with mode: 0644]
scons-local/SCons/Tool/javah.py [new file with mode: 0644]
scons-local/SCons/Tool/latex.py [new file with mode: 0644]
scons-local/SCons/Tool/lex.py [new file with mode: 0644]
scons-local/SCons/Tool/link.py [new file with mode: 0644]
scons-local/SCons/Tool/linkloc.py [new file with mode: 0644]
scons-local/SCons/Tool/m4.py [new file with mode: 0644]
scons-local/SCons/Tool/masm.py [new file with mode: 0644]
scons-local/SCons/Tool/midl.py [new file with mode: 0644]
scons-local/SCons/Tool/mingw.py [new file with mode: 0644]
scons-local/SCons/Tool/msgfmt.py [new file with mode: 0644]
scons-local/SCons/Tool/msginit.py [new file with mode: 0644]
scons-local/SCons/Tool/msgmerge.py [new file with mode: 0644]
scons-local/SCons/Tool/mslib.py [new file with mode: 0644]
scons-local/SCons/Tool/mslink.py [new file with mode: 0644]
scons-local/SCons/Tool/mssdk.py [new file with mode: 0644]
scons-local/SCons/Tool/msvc.py [new file with mode: 0644]
scons-local/SCons/Tool/msvs.py [new file with mode: 0644]
scons-local/SCons/Tool/mwcc.py [new file with mode: 0644]
scons-local/SCons/Tool/mwld.py [new file with mode: 0644]
scons-local/SCons/Tool/nasm.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/__init__.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/ipk.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/msi.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/rpm.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/src_tarbz2.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/src_targz.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/src_zip.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/tarbz2.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/targz.py [new file with mode: 0644]
scons-local/SCons/Tool/packaging/zip.py [new file with mode: 0644]
scons-local/SCons/Tool/pdf.py [new file with mode: 0644]
scons-local/SCons/Tool/pdflatex.py [new file with mode: 0644]
scons-local/SCons/Tool/pdftex.py [new file with mode: 0644]
scons-local/SCons/Tool/qt.py [new file with mode: 0644]
scons-local/SCons/Tool/rmic.py [new file with mode: 0644]
scons-local/SCons/Tool/rpcgen.py [new file with mode: 0644]
scons-local/SCons/Tool/rpm.py [new file with mode: 0644]
scons-local/SCons/Tool/rpmutils.py [new file with mode: 0644]
scons-local/SCons/Tool/sgiar.py [new file with mode: 0644]
scons-local/SCons/Tool/sgic++.py [new file with mode: 0644]
scons-local/SCons/Tool/sgicc.py [new file with mode: 0644]
scons-local/SCons/Tool/sgilink.py [new file with mode: 0644]
scons-local/SCons/Tool/sunar.py [new file with mode: 0644]
scons-local/SCons/Tool/sunc++.py [new file with mode: 0644]
scons-local/SCons/Tool/suncc.py [new file with mode: 0644]
scons-local/SCons/Tool/sunf77.py [new file with mode: 0644]
scons-local/SCons/Tool/sunf90.py [new file with mode: 0644]
scons-local/SCons/Tool/sunf95.py [new file with mode: 0644]
scons-local/SCons/Tool/sunlink.py [new file with mode: 0644]
scons-local/SCons/Tool/swig.py [new file with mode: 0644]
scons-local/SCons/Tool/tar.py [new file with mode: 0644]
scons-local/SCons/Tool/tex.py [new file with mode: 0644]
scons-local/SCons/Tool/textfile.py [new file with mode: 0644]
scons-local/SCons/Tool/tlib.py [new file with mode: 0644]
scons-local/SCons/Tool/wix.py [new file with mode: 0644]
scons-local/SCons/Tool/xgettext.py [new file with mode: 0644]
scons-local/SCons/Tool/yacc.py [new file with mode: 0644]
scons-local/SCons/Tool/zip.py [new file with mode: 0644]
scons-local/SCons/Util.py [new file with mode: 0644]
scons-local/SCons/Variables/BoolVariable.py [new file with mode: 0644]
scons-local/SCons/Variables/EnumVariable.py [new file with mode: 0644]
scons-local/SCons/Variables/ListVariable.py [new file with mode: 0644]
scons-local/SCons/Variables/PackageVariable.py [new file with mode: 0644]
scons-local/SCons/Variables/PathVariable.py [new file with mode: 0644]
scons-local/SCons/Variables/__init__.py [new file with mode: 0644]
scons-local/SCons/Warnings.py [new file with mode: 0644]
scons-local/SCons/__init__.py [new file with mode: 0644]
scons-local/SCons/compat/__init__.py [new file with mode: 0644]
scons-local/SCons/compat/_scons_builtins.py [new file with mode: 0644]
scons-local/SCons/compat/_scons_collections.py [new file with mode: 0644]
scons-local/SCons/compat/_scons_dbm.py [new file with mode: 0644]
scons-local/SCons/compat/_scons_hashlib.py [new file with mode: 0644]
scons-local/SCons/compat/_scons_io.py [new file with mode: 0644]
scons-local/SCons/compat/_scons_sets.py [new file with mode: 0644]
scons-local/SCons/compat/_scons_subprocess.py [new file with mode: 0644]
scons-local/SCons/cpp.py [new file with mode: 0644]
scons-local/SCons/dblite.py [new file with mode: 0644]
scons-local/SCons/exitfuncs.py [new file with mode: 0644]
scons-time.py [new file with mode: 0644]
scons.py [new file with mode: 0644]
sconsign.py [new file with mode: 0644]

diff --git a/scons-LICENSE b/scons-LICENSE
new file mode 100644 (file)
index 0000000..4ac2352
--- /dev/null
@@ -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 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.
diff --git a/scons-README b/scons-README
new file mode 100644 (file)
index 0000000..89bc634
--- /dev/null
@@ -0,0 +1,204 @@
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 1.5.2 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
+
diff --git a/scons-local/SCons/Action.py b/scons-local/SCons/Action.py
new file mode 100644 (file)
index 0000000..0021df6
--- /dev/null
@@ -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, 2012, 2013 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  2013/03/03 09:48:35 garyo"
+
+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 SCons.Errors.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:
diff --git a/scons-local/SCons/Builder.py b/scons-local/SCons/Builder.py
new file mode 100644 (file)
index 0000000..2c9958f
--- /dev/null
@@ -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, 2012, 2013 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  2013/03/03 09:48:35 garyo"
+
+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:
diff --git a/scons-local/SCons/CacheDir.py b/scons-local/SCons/CacheDir.py
new file mode 100644 (file)
index 0000000..aef03b4
--- /dev/null
@@ -0,0 +1,216 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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/CacheDir.py  2013/03/03 09:48:35 garyo"
+
+__doc__ = """
+CacheDir support
+"""
+
+import os.path
+import stat
+import sys
+
+import SCons.Action
+
+cache_enabled = True
+cache_debug = False
+cache_force = False
+cache_show = False
+
+def CacheRetrieveFunc(target, source, env):
+    t = target[0]
+    fs = t.fs
+    cd = env.get_CacheDir()
+    cachedir, cachefile = cd.cachepath(t)
+    if not fs.exists(cachefile):
+        cd.CacheDebug('CacheRetrieve(%s):  %s not in cache\n', t, cachefile)
+        return 1
+    cd.CacheDebug('CacheRetrieve(%s):  retrieving from %s\n', t, cachefile)
+    if SCons.Action.execute_actions:
+        if fs.islink(cachefile):
+            fs.symlink(fs.readlink(cachefile), t.path)
+        else:
+            env.copy_from_cache(cachefile, t.path)
+        st = fs.stat(cachefile)
+        fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+    return 0
+
+def CacheRetrieveString(target, source, env):
+    t = target[0]
+    fs = t.fs
+    cd = env.get_CacheDir()
+    cachedir, cachefile = cd.cachepath(t)
+    if t.fs.exists(cachefile):
+        return "Retrieved `%s' from cache" % t.path
+    return None
+
+CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString)
+
+CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None)
+
+def CachePushFunc(target, source, env):
+    t = target[0]
+    if t.nocache:
+        return
+    fs = t.fs
+    cd = env.get_CacheDir()
+    cachedir, cachefile = cd.cachepath(t)
+    if fs.exists(cachefile):
+        # Don't bother copying it if it's already there.  Note that
+        # usually this "shouldn't happen" because if the file already
+        # existed in cache, we'd have retrieved the file from there,
+        # not built it.  This can happen, though, in a race, if some
+        # other person running the same build pushes their copy to
+        # the cache after we decide we need to build it but before our
+        # build completes.
+        cd.CacheDebug('CachePush(%s):  %s already exists in cache\n', t, cachefile)
+        return
+
+    cd.CacheDebug('CachePush(%s):  pushing to %s\n', t, cachefile)
+
+    tempfile = cachefile+'.tmp'+str(os.getpid())
+    errfmt = "Unable to copy %s to cache. Cache file is %s"
+
+    if not fs.isdir(cachedir):
+        try:
+            fs.makedirs(cachedir)
+        except EnvironmentError:
+            # We may have received an exception because another process
+            # has beaten us creating the directory.
+            if not fs.isdir(cachedir):
+                msg = errfmt % (str(target), cachefile)
+                raise SCons.Errors.EnvironmentError(msg)
+
+    try:
+        if fs.islink(t.path):
+            fs.symlink(fs.readlink(t.path), tempfile)
+        else:
+            fs.copy2(t.path, tempfile)
+        fs.rename(tempfile, cachefile)
+        st = fs.stat(t.path)
+        fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+    except EnvironmentError:
+        # It's possible someone else tried writing the file at the
+        # same time we did, or else that there was some problem like
+        # the CacheDir being on a separate file system that's full.
+        # In any case, inability to push a file to cache doesn't affect
+        # the correctness of the build, so just print a warning.
+        msg = errfmt % (str(target), cachefile)
+        SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, msg)
+
+CachePush = SCons.Action.Action(CachePushFunc, None)
+
+class CacheDir(object):
+
+    def __init__(self, path):
+        try:
+            import hashlib
+        except ImportError:
+            msg = "No hashlib or MD5 module available, CacheDir() not supported"
+            SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
+            self.path = None
+        else:
+            self.path = path
+        self.current_cache_debug = None
+        self.debugFP = None
+
+    def CacheDebug(self, fmt, target, cachefile):
+        if cache_debug != self.current_cache_debug:
+            if cache_debug == '-':
+                self.debugFP = sys.stdout
+            elif cache_debug:
+                self.debugFP = open(cache_debug, 'w')
+            else:
+                self.debugFP = None
+            self.current_cache_debug = cache_debug
+        if self.debugFP:
+            self.debugFP.write(fmt % (target, os.path.split(cachefile)[1]))
+
+    def is_enabled(self):
+        return (cache_enabled and not self.path is None)
+
+    def cachepath(self, node):
+        """
+        """
+        if not self.is_enabled():
+            return None, None
+
+        sig = node.get_cachedir_bsig()
+        subdir = sig[0].upper()
+        dir = os.path.join(self.path, subdir)
+        return dir, os.path.join(dir, sig)
+
+    def retrieve(self, node):
+        """
+        This method is called from multiple threads in a parallel build,
+        so only do thread safe stuff here. Do thread unsafe stuff in
+        built().
+
+        Note that there's a special trick here with the execute flag
+        (one that's not normally done for other actions).  Basically
+        if the user requested a no_exec (-n) build, then
+        SCons.Action.execute_actions is set to 0 and when any action
+        is called, it does its showing but then just returns zero
+        instead of actually calling the action execution operation.
+        The problem for caching is that if the file does NOT exist in
+        cache then the CacheRetrieveString won't return anything to
+        show for the task, but the Action.__call__ won't call
+        CacheRetrieveFunc; instead it just returns zero, which makes
+        the code below think that the file *was* successfully
+        retrieved from the cache, therefore it doesn't do any
+        subsequent building.  However, the CacheRetrieveString didn't
+        print anything because it didn't actually exist in the cache,
+        and no more build actions will be performed, so the user just
+        sees nothing.  The fix is to tell Action.__call__ to always
+        execute the CacheRetrieveFunc and then have the latter
+        explicitly check SCons.Action.execute_actions itself.
+        """
+        if not self.is_enabled():
+            return False
+
+        env = node.get_build_env()
+        if cache_show:
+            if CacheRetrieveSilent(node, [], env, execute=1) == 0:
+                node.build(presub=0, execute=0)
+                return True
+        else:
+            if CacheRetrieve(node, [], env, execute=1) == 0:
+                return True
+
+        return False
+
+    def push(self, node):
+        if not self.is_enabled():
+            return
+        return CachePush(node, [], node.get_build_env())
+
+    def push_if_forced(self, node):
+        if cache_force:
+            return self.push(node)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/scons-local/SCons/Conftest.py b/scons-local/SCons/Conftest.py
new file mode 100644 (file)
index 0000000..d466278
--- /dev/null
@@ -0,0 +1,793 @@
+"""SCons.Conftest
+
+Autoconf-like configuration support; low level implementation of tests.
+"""
+
+#
+# Copyright (c) 2003 Stichting NLnet Labs
+# Copyright (c) 2001, 2002, 2003 Steven Knight
+#
+# 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.
+#
+
+#
+# The purpose of this module is to define how a check is to be performed.
+# Use one of the Check...() functions below.
+#
+
+#
+# A context class is used that defines functions for carrying out the tests,
+# logging and messages.  The following methods and members must be present:
+#
+# context.Display(msg)  Function called to print messages that are normally
+#                       displayed for the user.  Newlines are explicitly used.
+#                       The text should also be written to the logfile!
+#
+# context.Log(msg)      Function called to write to a log file.
+#
+# context.BuildProg(text, ext)
+#                       Function called to build a program, using "ext" for the
+#                       file extention.  Must return an empty string for
+#                       success, an error message for failure.
+#                       For reliable test results building should be done just
+#                       like an actual program would be build, using the same
+#                       command and arguments (including configure results so
+#                       far).
+#
+# context.CompileProg(text, ext)
+#                       Function called to compile a program, using "ext" for
+#                       the file extention.  Must return an empty string for
+#                       success, an error message for failure.
+#                       For reliable test results compiling should be done just
+#                       like an actual source file would be compiled, using the
+#                       same command and arguments (including configure results
+#                       so far).
+#
+# context.AppendLIBS(lib_name_list)
+#                       Append "lib_name_list" to the value of LIBS.
+#                       "lib_namelist" is a list of strings.
+#                       Return the value of LIBS before changing it (any type
+#                       can be used, it is passed to SetLIBS() later.)
+#
+# context.PrependLIBS(lib_name_list)
+#                       Prepend "lib_name_list" to the value of LIBS.
+#                       "lib_namelist" is a list of strings.
+#                       Return the value of LIBS before changing it (any type
+#                       can be used, it is passed to SetLIBS() later.)
+#
+# context.SetLIBS(value)
+#                       Set LIBS to "value".  The type of "value" is what
+#                       AppendLIBS() returned.
+#                       Return the value of LIBS before changing it (any type
+#                       can be used, it is passed to SetLIBS() later.)
+#
+# context.headerfilename
+#                       Name of file to append configure results to, usually
+#                       "confdefs.h".
+#                       The file must not exist or be empty when starting.
+#                       Empty or None to skip this (some tests will not work!).
+#
+# context.config_h      (may be missing). If present, must be a string, which
+#                       will be filled with the contents of a config_h file.
+#
+# context.vardict       Dictionary holding variables used for the tests and
+#                       stores results from the tests, used for the build
+#                       commands.
+#                       Normally contains "CC", "LIBS", "CPPFLAGS", etc.
+#
+# context.havedict      Dictionary holding results from the tests that are to
+#                       be used inside a program.
+#                       Names often start with "HAVE_".  These are zero
+#                       (feature not present) or one (feature present).  Other
+#                       variables may have any value, e.g., "PERLVERSION" can
+#                       be a number and "SYSTEMNAME" a string.
+#
+
+import re
+from types import IntType
+
+#
+# PUBLIC VARIABLES
+#
+
+LogInputFiles = 1    # Set that to log the input files in case of a failed test
+LogErrorMessages = 1 # Set that to log Conftest-generated error messages
+
+#
+# PUBLIC FUNCTIONS
+#
+
+# Generic remarks:
+# - When a language is specified which is not supported the test fails.  The
+#   message is a bit different, because not all the arguments for the normal
+#   message are available yet (chicken-egg problem).
+
+
+def CheckBuilder(context, text = None, language = None):
+    """
+    Configure check to see if the compiler works.
+    Note that this uses the current value of compiler and linker flags, make
+    sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
+    "language" should be "C" or "C++" and is used to select the compiler.
+    Default is "C".
+    "text" may be used to specify the code to be build.
+    Returns an empty string for success, an error message for failure.
+    """
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        context.Display("%s\n" % msg)
+        return msg
+
+    if not text:
+        text = """
+int main() {
+    return 0;
+}
+"""
+
+    context.Display("Checking if building a %s file works... " % lang)
+    ret = context.BuildProg(text, suffix)
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def CheckCC(context):
+    """
+    Configure check for a working C compiler.
+
+    This checks whether the C compiler, as defined in the $CC construction
+    variable, can compile a C source file. It uses the current $CCCOM value
+    too, so that it can test against non working flags.
+
+    """
+    context.Display("Checking whether the C compiler works")
+    text = """
+int main()
+{
+    return 0;
+}
+"""
+    ret = _check_empty_program(context, 'CC', text, 'C')
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def CheckSHCC(context):
+    """
+    Configure check for a working shared C compiler.
+
+    This checks whether the C compiler, as defined in the $SHCC construction
+    variable, can compile a C source file. It uses the current $SHCCCOM value
+    too, so that it can test against non working flags.
+
+    """
+    context.Display("Checking whether the (shared) C compiler works")
+    text = """
+int foo()
+{
+    return 0;
+}
+"""
+    ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True)
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def CheckCXX(context):
+    """
+    Configure check for a working CXX compiler.
+
+    This checks whether the CXX compiler, as defined in the $CXX construction
+    variable, can compile a CXX source file. It uses the current $CXXCOM value
+    too, so that it can test against non working flags.
+
+    """
+    context.Display("Checking whether the C++ compiler works")
+    text = """
+int main()
+{
+    return 0;
+}
+"""
+    ret = _check_empty_program(context, 'CXX', text, 'C++')
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def CheckSHCXX(context):
+    """
+    Configure check for a working shared CXX compiler.
+
+    This checks whether the CXX compiler, as defined in the $SHCXX construction
+    variable, can compile a CXX source file. It uses the current $SHCXXCOM value
+    too, so that it can test against non working flags.
+
+    """
+    context.Display("Checking whether the (shared) C++ compiler works")
+    text = """
+int main()
+{
+    return 0;
+}
+"""
+    ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True)
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def _check_empty_program(context, comp, text, language, use_shared = False):
+    """Return 0 on success, 1 otherwise."""
+    if comp not in context.env or not context.env[comp]:
+        # The compiler construction variable is not set or empty
+        return 1
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        return 1
+
+    if use_shared:
+        return context.CompileSharedObject(text, suffix)
+    else:
+        return context.CompileProg(text, suffix)
+
+
+def CheckFunc(context, function_name, header = None, language = None):
+    """
+    Configure check for a function "function_name".
+    "language" should be "C" or "C++" and is used to select the compiler.
+    Default is "C".
+    Optional "header" can be defined to define a function prototype, include a
+    header file or anything else that comes before main().
+    Sets HAVE_function_name in context.havedict according to the result.
+    Note that this uses the current value of compiler and linker flags, make
+    sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
+    Returns an empty string for success, an error message for failure.
+    """
+
+    # Remarks from autoconf:
+    # - Don't include <ctype.h> because on OSF/1 3.0 it includes <sys/types.h>
+    #   which includes <sys/select.h> which contains a prototype for select.
+    #   Similarly for bzero.
+    # - assert.h is included to define __stub macros and hopefully few
+    #   prototypes, which can conflict with char $1(); below.
+    # - Override any gcc2 internal prototype to avoid an error.
+    # - We use char for the function declaration because int might match the
+    #   return type of a gcc2 builtin and then its argument prototype would
+    #   still apply.
+    # - The GNU C library defines this for functions which it implements to
+    #   always fail with ENOSYS.  Some functions are actually named something
+    #   starting with __ and the normal name is an alias.
+
+    if context.headerfilename:
+        includetext = '#include "%s"' % context.headerfilename
+    else:
+        includetext = ''
+    if not header:
+        header = """
+#ifdef __cplusplus
+extern "C"
+#endif
+char %s();""" % function_name
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        context.Display("Cannot check for %s(): %s\n" % (function_name, msg))
+        return msg
+
+    text = """
+%(include)s
+#include <assert.h>
+%(hdr)s
+
+int main() {
+#if defined (__stub_%(name)s) || defined (__stub___%(name)s)
+  fail fail fail
+#else
+  %(name)s();
+#endif
+
+  return 0;
+}
+""" % { 'name': function_name,
+        'include': includetext,
+        'hdr': header }
+
+    context.Display("Checking for %s function %s()... " % (lang, function_name))
+    ret = context.BuildProg(text, suffix)
+    _YesNoResult(context, ret, "HAVE_" + function_name, text,
+                 "Define to 1 if the system has the function `%s'." %\
+                 function_name)
+    return ret
+
+
+def CheckHeader(context, header_name, header = None, language = None,
+                                                        include_quotes = None):
+    """
+    Configure check for a C or C++ header file "header_name".
+    Optional "header" can be defined to do something before including the
+    header file (unusual, supported for consistency).
+    "language" should be "C" or "C++" and is used to select the compiler.
+    Default is "C".
+    Sets HAVE_header_name in context.havedict according to the result.
+    Note that this uses the current value of compiler and linker flags, make
+    sure $CFLAGS and $CPPFLAGS are set correctly.
+    Returns an empty string for success, an error message for failure.
+    """
+    # Why compile the program instead of just running the preprocessor?
+    # It is possible that the header file exists, but actually using it may
+    # fail (e.g., because it depends on other header files).  Thus this test is
+    # more strict.  It may require using the "header" argument.
+    #
+    # Use <> by default, because the check is normally used for system header
+    # files.  SCons passes '""' to overrule this.
+
+    # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+    if context.headerfilename:
+        includetext = '#include "%s"\n' % context.headerfilename
+    else:
+        includetext = ''
+    if not header:
+        header = ""
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        context.Display("Cannot check for header file %s: %s\n"
+                                                          % (header_name, msg))
+        return msg
+
+    if not include_quotes:
+        include_quotes = "<>"
+
+    text = "%s%s\n#include %s%s%s\n\n" % (includetext, header,
+                             include_quotes[0], header_name, include_quotes[1])
+
+    context.Display("Checking for %s header file %s... " % (lang, header_name))
+    ret = context.CompileProg(text, suffix)
+    _YesNoResult(context, ret, "HAVE_" + header_name, text, 
+                 "Define to 1 if you have the <%s> header file." % header_name)
+    return ret
+
+
+def CheckType(context, type_name, fallback = None,
+                                               header = None, language = None):
+    """
+    Configure check for a C or C++ type "type_name".
+    Optional "header" can be defined to include a header file.
+    "language" should be "C" or "C++" and is used to select the compiler.
+    Default is "C".
+    Sets HAVE_type_name in context.havedict according to the result.
+    Note that this uses the current value of compiler and linker flags, make
+    sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
+    Returns an empty string for success, an error message for failure.
+    """
+
+    # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+    if context.headerfilename:
+        includetext = '#include "%s"' % context.headerfilename
+    else:
+        includetext = ''
+    if not header:
+        header = ""
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        context.Display("Cannot check for %s type: %s\n" % (type_name, msg))
+        return msg
+
+    # Remarks from autoconf about this test:
+    # - Grepping for the type in include files is not reliable (grep isn't
+    #   portable anyway).
+    # - Using "TYPE my_var;" doesn't work for const qualified types in C++.
+    #   Adding an initializer is not valid for some C++ classes.
+    # - Using the type as parameter to a function either fails for K&$ C or for
+    #   C++.
+    # - Using "TYPE *my_var;" is valid in C for some types that are not
+    #   declared (struct something).
+    # - Using "sizeof(TYPE)" is valid when TYPE is actually a variable.
+    # - Using the previous two together works reliably.
+    text = """
+%(include)s
+%(header)s
+
+int main() {
+  if ((%(name)s *) 0)
+    return 0;
+  if (sizeof (%(name)s))
+    return 0;
+}
+""" % { 'include': includetext,
+        'header': header,
+        'name': type_name }
+
+    context.Display("Checking for %s type %s... " % (lang, type_name))
+    ret = context.BuildProg(text, suffix)
+    _YesNoResult(context, ret, "HAVE_" + type_name, text,
+                 "Define to 1 if the system has the type `%s'." % type_name)
+    if ret and fallback and context.headerfilename:
+        f = open(context.headerfilename, "a")
+        f.write("typedef %s %s;\n" % (fallback, type_name))
+        f.close()
+
+    return ret
+
+def CheckTypeSize(context, type_name, header = None, language = None, expect = None):
+    """This check can be used to get the size of a given type, or to check whether
+    the type is of expected size.
+
+    Arguments:
+        - type : str
+            the type to check
+        - includes : sequence
+            list of headers to include in the test code before testing the type
+        - language : str
+            'C' or 'C++'
+        - expect : int
+            if given, will test wether the type has the given number of bytes.
+            If not given, will automatically find the size.
+
+        Returns:
+            status : int
+                0 if the check failed, or the found size of the type if the check succeeded."""
+    
+    # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+    if context.headerfilename:
+        includetext = '#include "%s"' % context.headerfilename
+    else:
+        includetext = ''
+
+    if not header:
+        header = ""
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        context.Display("Cannot check for %s type: %s\n" % (type_name, msg))
+        return msg
+
+    src = includetext + header 
+    if not expect is None:
+        # Only check if the given size is the right one
+        context.Display('Checking %s is %d bytes... ' % (type_name, expect))
+
+        # test code taken from autoconf: this is a pretty clever hack to find that
+        # a type is of a given size using only compilation. This speeds things up
+        # quite a bit compared to straightforward code using TryRun
+        src = src + r"""
+typedef %s scons_check_type;
+
+int main()
+{
+    static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)];
+    test_array[0] = 0;
+
+    return 0;
+}
+"""
+
+        st = context.CompileProg(src % (type_name, expect), suffix)
+        if not st:
+            context.Display("yes\n")
+            _Have(context, "SIZEOF_%s" % type_name, expect, 
+                  "The size of `%s', as computed by sizeof." % type_name)
+            return expect
+        else:
+            context.Display("no\n")
+            _LogFailed(context, src, st)
+            return 0
+    else:
+        # Only check if the given size is the right one
+        context.Message('Checking size of %s ... ' % type_name)
+
+        # We have to be careful with the program we wish to test here since
+        # compilation will be attempted using the current environment's flags.
+        # So make sure that the program will compile without any warning. For
+        # example using: 'int main(int argc, char** argv)' will fail with the
+        # '-Wall -Werror' flags since the variables argc and argv would not be
+        # used in the program...
+        #
+        src = src + """
+#include <stdlib.h>
+#include <stdio.h>
+int main() {
+    printf("%d", (int)sizeof(""" + type_name + """));
+    return 0;
+}
+    """
+        st, out = context.RunProg(src, suffix)
+        try:
+            size = int(out)
+        except ValueError:
+            # If cannot convert output of test prog to an integer (the size),
+            # something went wront, so just fail
+            st = 1
+            size = 0
+
+        if not st:
+            context.Display("yes\n")
+            _Have(context, "SIZEOF_%s" % type_name, size,
+                  "The size of `%s', as computed by sizeof." % type_name)
+            return size
+        else:
+            context.Display("no\n")
+            _LogFailed(context, src, st)
+            return 0
+
+    return 0
+
+def CheckDeclaration(context, symbol, includes = None, language = None):
+    """Checks whether symbol is declared.
+
+    Use the same test as autoconf, that is test whether the symbol is defined
+    as a macro or can be used as an r-value.
+
+    Arguments:
+        symbol : str
+            the symbol to check
+        includes : str
+            Optional "header" can be defined to include a header file.
+        language : str
+            only C and C++ supported.
+
+    Returns:
+        status : bool
+            True if the check failed, False if succeeded."""
+    
+    # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+    if context.headerfilename:
+        includetext = '#include "%s"' % context.headerfilename
+    else:
+        includetext = ''
+
+    if not includes:
+        includes = ""
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        context.Display("Cannot check for declaration %s: %s\n" % (symbol, msg))
+        return msg
+
+    src = includetext + includes 
+    context.Display('Checking whether %s is declared... ' % symbol)
+
+    src = src + r"""
+int main()
+{
+#ifndef %s
+    (void) %s;
+#endif
+    ;
+    return 0;
+}
+""" % (symbol, symbol)
+
+    st = context.CompileProg(src, suffix)
+    _YesNoResult(context, st, "HAVE_DECL_" + symbol, src,
+                 "Set to 1 if %s is defined." % symbol)
+    return st
+
+def CheckLib(context, libs, func_name = None, header = None,
+             extra_libs = None, call = None, language = None, autoadd = 1,
+             append = True):
+    """
+    Configure check for a C or C++ libraries "libs".  Searches through
+    the list of libraries, until one is found where the test succeeds.
+    Tests if "func_name" or "call" exists in the library.  Note: if it exists
+    in another library the test succeeds anyway!
+    Optional "header" can be defined to include a header file.  If not given a
+    default prototype for "func_name" is added.
+    Optional "extra_libs" is a list of library names to be added after
+    "lib_name" in the build command.  To be used for libraries that "lib_name"
+    depends on.
+    Optional "call" replaces the call to "func_name" in the test code.  It must
+    consist of complete C statements, including a trailing ";".
+    Both "func_name" and "call" arguments are optional, and in that case, just
+    linking against the libs is tested.
+    "language" should be "C" or "C++" and is used to select the compiler.
+    Default is "C".
+    Note that this uses the current value of compiler and linker flags, make
+    sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
+    Returns an empty string for success, an error message for failure.
+    """
+    # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+    if context.headerfilename:
+        includetext = '#include "%s"' % context.headerfilename
+    else:
+        includetext = ''
+    if not header:
+        header = ""
+
+    text = """
+%s
+%s""" % (includetext, header)
+
+    # Add a function declaration if needed.
+    if func_name and func_name != "main":
+        if not header:
+            text = text + """
+#ifdef __cplusplus
+extern "C"
+#endif
+char %s();
+""" % func_name
+
+        # The actual test code.
+        if not call:
+            call = "%s();" % func_name
+
+    # if no function to test, leave main() blank
+    text = text + """
+int
+main() {
+  %s
+return 0;
+}
+""" % (call or "")
+
+    if call:
+        i = call.find("\n")
+        if i > 0:
+            calltext = call[:i] + ".."
+        elif call[-1] == ';':
+            calltext = call[:-1]
+        else:
+            calltext = call
+
+    for lib_name in libs:
+
+        lang, suffix, msg = _lang2suffix(language)
+        if msg:
+            context.Display("Cannot check for library %s: %s\n" % (lib_name, msg))
+            return msg
+
+        # if a function was specified to run in main(), say it
+        if call:
+                context.Display("Checking for %s in %s library %s... "
+                                % (calltext, lang, lib_name))
+        # otherwise, just say the name of library and language
+        else:
+                context.Display("Checking for %s library %s... "
+                                % (lang, lib_name))
+
+        if lib_name:
+            l = [ lib_name ]
+            if extra_libs:
+                l.extend(extra_libs)
+            if append:
+                oldLIBS = context.AppendLIBS(l)
+            else:
+                oldLIBS = context.PrependLIBS(l)
+            sym = "HAVE_LIB" + lib_name
+        else:
+            oldLIBS = -1
+            sym = None
+
+        ret = context.BuildProg(text, suffix)
+
+        _YesNoResult(context, ret, sym, text,
+                     "Define to 1 if you have the `%s' library." % lib_name)
+        if oldLIBS != -1 and (ret or not autoadd):
+            context.SetLIBS(oldLIBS)
+            
+        if not ret:
+            return ret
+
+    return ret
+
+#
+# END OF PUBLIC FUNCTIONS
+#
+
+def _YesNoResult(context, ret, key, text, comment = None):
+    """
+    Handle the result of a test with a "yes" or "no" result.
+    "ret" is the return value: empty if OK, error message when not.
+    "key" is the name of the symbol to be defined (HAVE_foo).
+    "text" is the source code of the program used for testing.
+    "comment" is the C comment to add above the line defining the symbol (the
+    comment is automatically put inside a /* */). If None, no comment is added.
+    """
+    if key:
+        _Have(context, key, not ret, comment)
+    if ret:
+        context.Display("no\n")
+        _LogFailed(context, text, ret)
+    else:
+        context.Display("yes\n")
+
+
+def _Have(context, key, have, comment = None):
+    """
+    Store result of a test in context.havedict and context.headerfilename.
+    "key" is a "HAVE_abc" name.  It is turned into all CAPITALS and non-
+    alphanumerics are replaced by an underscore.
+    The value of "have" can be:
+    1      - Feature is defined, add "#define key".
+    0      - Feature is not defined, add "/* #undef key */".
+             Adding "undef" is what autoconf does.  Not useful for the
+             compiler, but it shows that the test was done.
+    number - Feature is defined to this number "#define key have".
+             Doesn't work for 0 or 1, use a string then.
+    string - Feature is defined to this string "#define key have".
+             Give "have" as is should appear in the header file, include quotes
+             when desired and escape special characters!
+    """
+    key_up = key.upper()
+    key_up = re.sub('[^A-Z0-9_]', '_', key_up)
+    context.havedict[key_up] = have
+    if have == 1:
+        line = "#define %s 1\n" % key_up
+    elif have == 0:
+        line = "/* #undef %s */\n" % key_up
+    elif isinstance(have, IntType):
+        line = "#define %s %d\n" % (key_up, have)
+    else:
+        line = "#define %s %s\n" % (key_up, str(have))
+    
+    if comment is not None:
+        lines = "\n/* %s */\n" % comment + line
+    else:
+        lines = "\n" + line
+
+    if context.headerfilename:
+        f = open(context.headerfilename, "a")
+        f.write(lines)
+        f.close()
+    elif hasattr(context,'config_h'):
+        context.config_h = context.config_h + lines
+
+
+def _LogFailed(context, text, msg):
+    """
+    Write to the log about a failed program.
+    Add line numbers, so that error messages can be understood.
+    """
+    if LogInputFiles:
+        context.Log("Failed program was:\n")
+        lines = text.split('\n')
+        if len(lines) and lines[-1] == '':
+            lines = lines[:-1]              # remove trailing empty line
+        n = 1
+        for line in lines:
+            context.Log("%d: %s\n" % (n, line))
+            n = n + 1
+    if LogErrorMessages:
+        context.Log("Error message: %s\n" % msg)
+
+
+def _lang2suffix(lang):
+    """
+    Convert a language name to a suffix.
+    When "lang" is empty or None C is assumed.
+    Returns a tuple (lang, suffix, None) when it works.
+    For an unrecognized language returns (None, None, msg).
+    Where:
+        lang   = the unified language name
+        suffix = the suffix, including the leading dot
+        msg    = an error message
+    """
+    if not lang or lang in ["C", "c"]:
+        return ("C", ".c", None)
+    if lang in ["c++", "C++", "cpp", "CXX", "cxx"]:
+        return ("C++", ".cpp", None)
+
+    return None, None, "Unsupported language: %s" % lang
+
+
+# vim: set sw=4 et sts=4 tw=79 fo+=l:
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/scons-local/SCons/Debug.py b/scons-local/SCons/Debug.py
new file mode 100644 (file)
index 0000000..41b0ab6
--- /dev/null
@@ -0,0 +1,220 @@
+"""SCons.Debug
+
+Code for debugging SCons internal things.  Shouldn't be
+needed by most users.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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/Debug.py  2013/03/03 09:48:35 garyo"
+
+import os
+import sys
+import time
+import weakref
+
+tracked_classes = {}
+
+def logInstanceCreation(instance, name=None):
+    if name is None:
+        name = instance.__class__.__name__
+    if name not in tracked_classes:
+        tracked_classes[name] = []
+    tracked_classes[name].append(weakref.ref(instance))
+
+def string_to_classes(s):
+    if s == '*':
+        return sorted(tracked_classes.keys())
+    else:
+        return s.split()
+
+def fetchLoggedInstances(classes="*"):
+    classnames = string_to_classes(classes)
+    return [(cn, len(tracked_classes[cn])) for cn in classnames]
+  
+def countLoggedInstances(classes, file=sys.stdout):
+    for classname in string_to_classes(classes):
+        file.write("%s: %d\n" % (classname, len(tracked_classes[classname])))
+
+def listLoggedInstances(classes, file=sys.stdout):
+    for classname in string_to_classes(classes):
+        file.write('\n%s:\n' % classname)
+        for ref in tracked_classes[classname]:
+            obj = ref()
+            if obj is not None:
+                file.write('    %s\n' % repr(obj))
+
+def dumpLoggedInstances(classes, file=sys.stdout):
+    for classname in string_to_classes(classes):
+        file.write('\n%s:\n' % classname)
+        for ref in tracked_classes[classname]:
+            obj = ref()
+            if obj is not None:
+                file.write('    %s:\n' % obj)
+                for key, value in obj.__dict__.items():
+                    file.write('        %20s : %s\n' % (key, value))
+
+
+
+if sys.platform[:5] == "linux":
+    # Linux doesn't actually support memory usage stats from getrusage().
+    def memory():
+        mstr = open('/proc/self/stat').read()
+        mstr = mstr.split()[22]
+        return int(mstr)
+elif sys.platform[:6] == 'darwin':
+    #TODO really get memory stats for OS X
+    def memory():
+        return 0
+else:
+    try:
+        import resource
+    except ImportError:
+        try:
+            import win32process
+            import win32api
+        except ImportError:
+            def memory():
+                return 0
+        else:
+            def memory():
+                process_handle = win32api.GetCurrentProcess()
+                memory_info = win32process.GetProcessMemoryInfo( process_handle )
+                return memory_info['PeakWorkingSetSize']
+    else:
+        def memory():
+            res = resource.getrusage(resource.RUSAGE_SELF)
+            return res[4]
+
+# returns caller's stack
+def caller_stack(*backlist):
+    import traceback
+    if not backlist:
+        backlist = [0]
+    result = []
+    for back in backlist:
+        tb = traceback.extract_stack(limit=3+back)
+        key = tb[0][:3]
+        result.append('%s:%d(%s)' % func_shorten(key))
+    return result
+
+caller_bases = {}
+caller_dicts = {}
+
+# trace a caller's stack
+def caller_trace(back=0):
+    import traceback
+    tb = traceback.extract_stack(limit=3+back)
+    tb.reverse()
+    callee = tb[1][:3]
+    caller_bases[callee] = caller_bases.get(callee, 0) + 1
+    for caller in tb[2:]:
+        caller = callee + caller[:3]
+        try:
+            entry = caller_dicts[callee]
+        except KeyError:
+            caller_dicts[callee] = entry = {}
+        entry[caller] = entry.get(caller, 0) + 1
+        callee = caller
+
+# print a single caller and its callers, if any
+def _dump_one_caller(key, file, level=0):
+    leader = '      '*level
+    for v,c in sorted([(-v,c) for c,v in caller_dicts[key].items()]):
+        file.write("%s  %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:])))
+        if c in caller_dicts:
+            _dump_one_caller(c, file, level+1)
+
+# print each call tree
+def dump_caller_counts(file=sys.stdout):
+    for k in sorted(caller_bases.keys()):
+        file.write("Callers of %s:%d(%s), %d calls:\n"
+                    % (func_shorten(k) + (caller_bases[k],)))
+        _dump_one_caller(k, file)
+
+shorten_list = [
+    ( '/scons/SCons/',          1),
+    ( '/src/engine/SCons/',     1),
+    ( '/usr/lib/python',        0),
+]
+
+if os.sep != '/':
+    shorten_list = [(t[0].replace('/', os.sep), t[1]) for t in shorten_list]
+
+def func_shorten(func_tuple):
+    f = func_tuple[0]
+    for t in shorten_list:
+        i = f.find(t[0])
+        if i >= 0:
+            if t[1]:
+                i = i + len(t[0])
+            return (f[i:],)+func_tuple[1:]
+    return func_tuple
+
+
+TraceFP = {}
+if sys.platform == 'win32':
+    TraceDefault = 'con'
+else:
+    TraceDefault = '/dev/tty'
+
+TimeStampDefault = None
+StartTime = time.time()
+PreviousTime = StartTime
+
+def Trace(msg, file=None, mode='w', tstamp=None):
+    """Write a trace message to a file.  Whenever a file is specified,
+    it becomes the default for the next call to Trace()."""
+    global TraceDefault
+    global TimeStampDefault
+    global PreviousTime
+    if file is None:
+        file = TraceDefault
+    else:
+        TraceDefault = file
+    if tstamp is None:
+        tstamp = TimeStampDefault
+    else:
+        TimeStampDefault = tstamp
+    try:
+        fp = TraceFP[file]
+    except KeyError:
+        try:
+            fp = TraceFP[file] = open(file, mode)
+        except TypeError:
+            # Assume we were passed an open file pointer.
+            fp = file
+    if tstamp:
+        now = time.time()
+        fp.write('%8.4f %8.4f:  ' % (now - StartTime, now - PreviousTime))
+        PreviousTime = now
+    fp.write(msg)
+    fp.flush()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/scons-local/SCons/Defaults.py b/scons-local/SCons/Defaults.py
new file mode 100644 (file)
index 0000000..219190a
--- /dev/null
@@ -0,0 +1,494 @@
+"""SCons.Defaults
+
+Builders and other things for the local site.  Here's where we'll
+duplicate the functionality of autoconf until we move it into the
+installation procedure or use something like qmconf.
+
+The code that reads the registry to find MSVC components was borrowed
+from distutils.msvccompiler.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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.
+#
+from __future__ import division
+
+__revision__ = "src/engine/SCons/Defaults.py  2013/03/03 09:48:35 garyo"
+
+
+import os
+import errno
+import shutil
+import stat
+import time
+import sys
+
+import SCons.Action
+import SCons.Builder
+import SCons.CacheDir
+import SCons.Environment
+import SCons.PathList
+import SCons.Subst
+import SCons.Tool
+
+# A placeholder for a default Environment (for fetching source files
+# from source code management systems and the like).  This must be
+# initialized later, after the top-level directory is set by the calling
+# interface.
+_default_env = None
+
+# Lazily instantiate the default environment so the overhead of creating
+# it doesn't apply when it's not needed.
+def _fetch_DefaultEnvironment(*args, **kw):
+    """
+    Returns the already-created default construction environment.
+    """
+    global _default_env
+    return _default_env
+
+def DefaultEnvironment(*args, **kw):
+    """
+    Initial public entry point for creating the default construction
+    Environment.
+
+    After creating the environment, we overwrite our name
+    (DefaultEnvironment) with the _fetch_DefaultEnvironment() function,
+    which more efficiently returns the initialized default construction
+    environment without checking for its existence.
+
+    (This function still exists with its _default_check because someone
+    else (*cough* Script/__init__.py *cough*) may keep a reference
+    to this function.  So we can't use the fully functional idiom of
+    having the name originally be a something that *only* creates the
+    construction environment and then overwrites the name.)
+    """
+    global _default_env
+    if not _default_env:
+        import SCons.Util
+        _default_env = SCons.Environment.Environment(*args, **kw)
+        if SCons.Util.md5:
+            _default_env.Decider('MD5')
+        else:
+            _default_env.Decider('timestamp-match')
+        global DefaultEnvironment
+        DefaultEnvironment = _fetch_DefaultEnvironment
+        _default_env._CacheDir_path = None
+    return _default_env
+
+# Emitters for setting the shared attribute on object files,
+# and an action for checking that all of the source files
+# going into a shared library are, in fact, shared.
+def StaticObjectEmitter(target, source, env):
+    for tgt in target:
+        tgt.attributes.shared = None
+    return (target, source)
+
+def SharedObjectEmitter(target, source, env):
+    for tgt in target:
+        tgt.attributes.shared = 1
+    return (target, source)
+
+def SharedFlagChecker(source, target, env):
+    same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME')
+    if same == '0' or same == '' or same == 'False':
+        for src in source:
+            try:
+                shared = src.attributes.shared
+            except AttributeError:
+                shared = None
+            if not shared:
+                raise SCons.Errors.UserError("Source file: %s is static and is not compatible with shared target: %s" % (src, target[0]))
+
+SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
+
+# Some people were using these variable name before we made
+# SourceFileScanner part of the public interface.  Don't break their
+# SConscript files until we've given them some fair warning and a
+# transition period.
+CScan = SCons.Tool.CScanner
+DScan = SCons.Tool.DScanner
+LaTeXScan = SCons.Tool.LaTeXScanner
+ObjSourceScan = SCons.Tool.SourceFileScanner
+ProgScan = SCons.Tool.ProgramScanner
+
+# These aren't really tool scanners, so they don't quite belong with
+# the rest of those in Tool/__init__.py, but I'm not sure where else
+# they should go.  Leave them here for now.
+import SCons.Scanner.Dir
+DirScanner = SCons.Scanner.Dir.DirScanner()
+DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner()
+
+# Actions for common languages.
+CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR")
+ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR")
+CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR")
+ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR")
+
+ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR")
+ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR")
+
+LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR")
+ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR")
+
+LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
+
+# Common tasks that we allow users to perform in platform-independent
+# ways by creating ActionFactory instances.
+ActionFactory = SCons.Action.ActionFactory
+
+def get_paths_str(dest):
+    # If dest is a list, we need to manually call str() on each element
+    if SCons.Util.is_List(dest):
+        elem_strs = []
+        for element in dest:
+            elem_strs.append('"' + str(element) + '"')
+        return '[' + ', '.join(elem_strs) + ']'
+    else:
+        return '"' + str(dest) + '"'
+
+def chmod_func(dest, mode):
+    SCons.Node.FS.invalidate_node_memos(dest)
+    if not SCons.Util.is_List(dest):
+        dest = [dest]
+    for element in dest:
+        os.chmod(str(element), mode)
+
+def chmod_strfunc(dest, mode):
+    return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode)
+
+Chmod = ActionFactory(chmod_func, chmod_strfunc)
+
+def copy_func(dest, src):
+    SCons.Node.FS.invalidate_node_memos(dest)
+    if SCons.Util.is_List(src) and os.path.isdir(dest):
+        for file in src:
+            shutil.copy2(file, dest)
+        return 0
+    elif os.path.isfile(src):
+        return shutil.copy2(src, dest)
+    else:
+        return shutil.copytree(src, dest, 1)
+
+Copy = ActionFactory(copy_func,
+                     lambda dest, src: 'Copy("%s", "%s")' % (dest, src),
+                     convert=str)
+
+def delete_func(dest, must_exist=0):
+    SCons.Node.FS.invalidate_node_memos(dest)
+    if not SCons.Util.is_List(dest):
+        dest = [dest]
+    for entry in dest:
+        entry = str(entry)
+        # os.path.exists returns False with broken links that exist
+        entry_exists = os.path.exists(entry) or os.path.islink(entry)
+        if not entry_exists and not must_exist:
+            continue
+        # os.path.isdir returns True when entry is a link to a dir
+        if os.path.isdir(entry) and not os.path.islink(entry):
+            shutil.rmtree(entry, 1)
+            continue
+        os.unlink(entry)
+
+def delete_strfunc(dest, must_exist=0):
+    return 'Delete(%s)' % get_paths_str(dest)
+
+Delete = ActionFactory(delete_func, delete_strfunc)
+
+def mkdir_func(dest):
+    SCons.Node.FS.invalidate_node_memos(dest)
+    if not SCons.Util.is_List(dest):
+        dest = [dest]
+    for entry in dest:
+        try:
+            os.makedirs(str(entry))
+        except os.error, e:
+            p = str(entry)
+            if (e.args[0] == errno.EEXIST or
+                    (sys.platform=='win32' and e.args[0]==183)) \
+                    and os.path.isdir(str(entry)):
+                pass            # not an error if already exists
+            else:
+                raise
+
+Mkdir = ActionFactory(mkdir_func,
+                      lambda dir: 'Mkdir(%s)' % get_paths_str(dir))
+
+def move_func(dest, src):
+    SCons.Node.FS.invalidate_node_memos(dest)
+    SCons.Node.FS.invalidate_node_memos(src)
+    shutil.move(src, dest)
+
+Move = ActionFactory(move_func,
+                     lambda dest, src: 'Move("%s", "%s")' % (dest, src),
+                     convert=str)
+
+def touch_func(dest):
+    SCons.Node.FS.invalidate_node_memos(dest)
+    if not SCons.Util.is_List(dest):
+        dest = [dest]
+    for file in dest:
+        file = str(file)
+        mtime = int(time.time())
+        if os.path.exists(file):
+            atime = os.path.getatime(file)
+        else:
+            open(file, 'w')
+            atime = mtime
+        os.utime(file, (atime, mtime))
+
+Touch = ActionFactory(touch_func,
+                      lambda file: 'Touch(%s)' % get_paths_str(file))
+
+# Internal utility functions
+
+def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
+    """
+    Creates a new list from 'list' by first interpolating each element
+    in the list using the 'env' dictionary and then calling f on the
+    list, and finally calling _concat_ixes to concatenate 'prefix' and
+    'suffix' onto each element of the list.
+    """
+    if not list:
+        return list
+
+    l = f(SCons.PathList.PathList(list).subst_path(env, target, source))
+    if l is not None:
+        list = l
+
+    return _concat_ixes(prefix, list, suffix, env)
+
+def _concat_ixes(prefix, list, suffix, env):
+    """
+    Creates a new list from 'list' by concatenating the 'prefix' and
+    'suffix' arguments onto each element of the list.  A trailing space
+    on 'prefix' or leading space on 'suffix' will cause them to be put
+    into separate list elements rather than being concatenated.
+    """
+
+    result = []
+
+    # ensure that prefix and suffix are strings
+    prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW))
+    suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW))
+
+    for x in list:
+        if isinstance(x, SCons.Node.FS.File):
+            result.append(x)
+            continue
+        x = str(x)
+        if x:
+
+            if prefix:
+                if prefix[-1] == ' ':
+                    result.append(prefix[:-1])
+                elif x[:len(prefix)] != prefix:
+                    x = prefix + x
+
+            result.append(x)
+
+            if suffix:
+                if suffix[0] == ' ':
+                    result.append(suffix[1:])
+                elif x[-len(suffix):] != suffix:
+                    result[-1] = result[-1]+suffix
+
+    return result
+
+def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None):
+    """
+    This is a wrapper around _concat()/_concat_ixes() that checks for
+    the existence of prefixes or suffixes on list items and strips them
+    where it finds them.  This is used by tools (like the GNU linker)
+    that need to turn something like 'libfoo.a' into '-lfoo'.
+    """
+    
+    if not itms:
+        return itms
+
+    if not callable(c):
+        env_c = env['_concat']
+        if env_c != _concat and callable(env_c):
+            # There's a custom _concat() method in the construction
+            # environment, and we've allowed people to set that in
+            # the past (see test/custom-concat.py), so preserve the
+            # backwards compatibility.
+            c = env_c
+        else:
+            c = _concat_ixes
+    
+    stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes)))
+    stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes)))
+
+    stripped = []
+    for l in SCons.PathList.PathList(itms).subst_path(env, None, None):
+        if isinstance(l, SCons.Node.FS.File):
+            stripped.append(l)
+            continue
+
+        if not SCons.Util.is_String(l):
+            l = str(l)
+
+        for stripprefix in stripprefixes:
+            lsp = len(stripprefix)
+            if l[:lsp] == stripprefix:
+                l = l[lsp:]
+                # Do not strip more than one prefix
+                break
+
+        for stripsuffix in stripsuffixes:
+            lss = len(stripsuffix)
+            if l[-lss:] == stripsuffix:
+                l = l[:-lss]
+                # Do not strip more than one suffix
+                break
+
+        stripped.append(l)
+
+    return c(prefix, stripped, suffix, env)
+
+def processDefines(defs):
+    """process defines, resolving strings, lists, dictionaries, into a list of
+    strings
+    """
+    if SCons.Util.is_List(defs):
+        l = []
+        for d in defs:
+            if d is None:
+                continue
+            elif SCons.Util.is_List(d) or isinstance(d, tuple):
+                if len(d) >= 2:
+                    l.append(str(d[0]) + '=' + str(d[1]))
+                else:
+                    l.append(str(d[0]))
+            elif SCons.Util.is_Dict(d):
+                for macro,value in d.iteritems():
+                    if value is not None:
+                        l.append(str(macro) + '=' + str(value))
+                    else:
+                        l.append(str(macro))
+            elif SCons.Util.is_String(d):
+                l.append(str(d))
+            else:
+                raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d))
+    elif SCons.Util.is_Dict(defs):
+        # The items in a dictionary are stored in random order, but
+        # if the order of the command-line options changes from
+        # invocation to invocation, then the signature of the command
+        # line will change and we'll get random unnecessary rebuilds.
+        # Consequently, we have to sort the keys to ensure a
+        # consistent order...
+        l = []
+        for k,v in sorted(defs.items()):
+            if v is None:
+                l.append(str(k))
+            else:
+                l.append(str(k) + '=' + str(v))
+    else:
+        l = [str(defs)]
+    return l
+
+def _defines(prefix, defs, suffix, env, c=_concat_ixes):
+    """A wrapper around _concat_ixes that turns a list or string
+    into a list of C preprocessor command-line definitions.
+    """
+
+    return c(prefix, env.subst_path(processDefines(defs)), suffix, env)
+    
+class NullCmdGenerator(object):
+    """This is a callable class that can be used in place of other
+    command generators if you don't want them to do anything.
+
+    The __call__ method for this class simply returns the thing
+    you instantiated it with.
+
+    Example usage:
+    env["DO_NOTHING"] = NullCmdGenerator
+    env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
+    """
+
+    def __init__(self, cmd):
+        self.cmd = cmd
+
+    def __call__(self, target, source, env, for_signature=None):
+        return self.cmd
+
+class Variable_Method_Caller(object):
+    """A class for finding a construction variable on the stack and
+    calling one of its methods.
+
+    We use this to support "construction variables" in our string
+    eval()s that actually stand in for methods--specifically, use
+    of "RDirs" in call to _concat that should actually execute the
+    "TARGET.RDirs" method.  (We used to support this by creating a little
+    "build dictionary" that mapped RDirs to the method, but this got in
+    the way of Memoizing construction environments, because we had to
+    create new environment objects to hold the variables.)
+    """
+    def __init__(self, variable, method):
+        self.variable = variable
+        self.method = method
+    def __call__(self, *args, **kw):
+        try: 1//0
+        except ZeroDivisionError: 
+            # Don't start iterating with the current stack-frame to
+            # prevent creating reference cycles (f_back is safe).
+            frame = sys.exc_info()[2].tb_frame.f_back
+        variable = self.variable
+        while frame:
+            if variable in frame.f_locals:
+                v = frame.f_locals[variable]
+                if v:
+                    method = getattr(v, self.method)
+                    return method(*args, **kw)
+            frame = frame.f_back
+        return None
+
+ConstructionEnvironment = {
+    'BUILDERS'      : {},
+    'SCANNERS'      : [],
+    'CONFIGUREDIR'  : '#/.sconf_temp',
+    'CONFIGURELOG'  : '#/config.log',
+    'CPPSUFFIXES'   : SCons.Tool.CSuffixes,
+    'DSUFFIXES'     : SCons.Tool.DSuffixes,
+    'ENV'           : {},
+    'IDLSUFFIXES'   : SCons.Tool.IDLSuffixes,
+#    'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions
+    '_concat'       : _concat,
+    '_defines'      : _defines,
+    '_stripixes'    : _stripixes,
+    '_LIBFLAGS'     : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
+    '_LIBDIRFLAGS'  : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
+    '_CPPINCFLAGS'  : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
+    '_CPPDEFFLAGS'  : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
+    'TEMPFILE'      : NullCmdGenerator,
+    'Dir'           : Variable_Method_Caller('TARGET', 'Dir'),
+    'Dirs'          : Variable_Method_Caller('TARGET', 'Dirs'),
+    'File'          : Variable_Method_Caller('TARGET', 'File'),
+    'RDirs'         : Variable_Method_Caller('TARGET', 'RDirs'),
+}
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/scons-local/SCons/Environment.py b/scons-local/SCons/Environment.py
new file mode 100644 (file)
index 0000000..833f3cc
--- /dev/null
@@ -0,0 +1,2418 @@
+"""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, 2011, 2012, 2013 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  2013/03/03 09:48:35 garyo"
+
+
+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
+semi_deepcopy_dict = SCons.Util.semi_deepcopy_dict
+
+# 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):
+        # These cannot be copied since they would both modify the same builder object, and indeed
+        # just copying would modify the original builder
+        raise TypeError( 'cannot semi_deepcopy a BuilderDict' )
+
+    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(''),
+            'CXXFLAGS'      : 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 == '-dylib_file':
+                    dict['LINKFLAGS'].append(arg)
+                    append_next_arg_to = 'LINKFLAGS'
+                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 in ['-mno-cygwin',
+                             '-pthread',
+                             '-openmp',
+                             '-fopenmp']:
+                    dict['CCFLAGS'].append(arg)
+                    dict['LINKFLAGS'].append(arg)
+                elif arg == '-mwindows':
+                    dict['LINKFLAGS'].append(arg)
+                elif arg[:5] == '-std=':
+                    if arg[5:].find('++')!=-1:
+                        key='CXXFLAGS'
+                    else:
+                        key='CFLAGS'
+                    dict[key].append(arg)
+                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:                
+                if key == 'CPPDEFINES' and SCons.Util.is_String(self._dict[key]):
+                    self._dict[key] = [self._dict[key]]
+                orig = self._dict[key]
+            except KeyError:
+                # No existing variable in the environment, so just set
+                # it to the new value.
+                if key == 'CPPDEFINES' and SCons.Util.is_String(val):
+                    self._dict[key] = [val]
+                else:
+                    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):
+                        if key == 'CPPDEFINES':
+                            orig = orig.items()
+                            orig += val
+                            self._dict[key] = orig
+                        else:    
+                            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 key == 'CPPDEFINES':
+                    tmp = []
+                    for i in val:
+                        if SCons.Util.is_List(i):
+                            if len(i) >= 2:
+                                tmp.append((i[0], i[1]))
+                            else:
+                                tmp.append((i[0],))
+                        elif SCons.Util.is_Tuple(i):
+                            tmp.append(i)
+                        else:
+                            tmp.append((i,))
+                    val = tmp
+                    if SCons.Util.is_Dict(dk):
+                        dk = dk.items()
+                    elif SCons.Util.is_String(dk):
+                        dk = [(dk,)]
+                    else:                    
+                        tmp = []
+                        for i in dk:
+                            if SCons.Util.is_List(i):
+                                if len(i) >= 2:
+                                    tmp.append((i[0], i[1]))
+                                else:
+                                    tmp.append((i[0],))
+                            elif SCons.Util.is_Tuple(i):
+                                tmp.append(i)
+                            else:
+                                tmp.append((i,))
+                        dk = tmp
+                else:
+                    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):
+                    if key == 'CPPDEFINES':
+                        tmp = []
+                        for i in dk:
+                            if SCons.Util.is_List(i):
+                                if len(i) >= 2:
+                                    tmp.append((i[0], i[1]))
+                                else:
+                                    tmp.append((i[0],))
+                            elif SCons.Util.is_Tuple(i):
+                                tmp.append(i)
+                            else:
+                                tmp.append((i,))
+                        dk = tmp
+                        if SCons.Util.is_Dict(val):
+                            val = val.items()
+                        elif SCons.Util.is_String(val):
+                            val = [(val,)]
+                        if delete_existing:
+                            dk = filter(lambda x, val=val: x not in val, dk)
+                            self._dict[key] = dk + val
+                        else:
+                            dk = [x for x in dk if x not in val]                
+                            self._dict[key] = dk + val
+                    else:
+                        # By elimination, val is not a list.  Since dk is a
+                        # list, wrap val in a list first.
+                        if delete_existing:
+                            dk = filter(lambda x, val=val: x not in val, dk)
+                            self._dict[key] = dk + [val]
+                        else:
+                            if not val in dk:
+                                self._dict[key] = dk + [val]
+                else:
+                    if key == 'CPPDEFINES':
+                        if SCons.Util.is_String(dk):
+                            dk = [dk]
+                        elif SCons.Util.is_Dict(dk):
+                            dk = dk.items()
+                        if SCons.Util.is_String(val):
+                            if val in dk:
+                                val = []
+                            else:
+                                val = [val]
+                        elif SCons.Util.is_Dict(val):
+                            tmp = []
+                            for i,j in val.iteritems():
+                                if j is not None:
+                                    tmp.append((i,j))
+                                else:
+                                    tmp.append(i)
+                            val = tmp
+                    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.
+        """
+        try:
+            builders = self._dict['BUILDERS']
+        except KeyError:
+            pass
+            
+        clone = copy.copy(self)
+        # BUILDERS is not safe to do a simple copy
+        clone._dict = semi_deepcopy_dict(self._dict, ['BUILDERS'])
+        clone._dict['BUILDERS'] = BuilderDict(builders, 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 = BuilderDict(kwbd,self)
+            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())
+
+        def final_source(node):
+            while (node != node.srcnode()):
+              node = node.srcnode()
+            return node
+        sources = map( final_source, sources );
+        # remove duplicates
+        return list(set(sources))
+
+    def FindInstalledFiles(self):
+        """ returns the list of all targets of the Install and InstallAs Builder.
+        """
+        from SCons.Tool import install
+        if install._UNIQUE_INSTALLED_FILES is None:
+            install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
+        return install._UNIQUE_INSTALLED_FILES
+
+
+class OverrideEnvironment(Base):
+    """A proxy that overrides variables in a wrapped construction
+    environment by returning values from an overrides dictionary in
+    preference to values from the underlying subject environment.
+
+    This is a lightweight (I hope) proxy that passes through most use of
+    attributes to the underlying Environment.Base class, but has just
+    enough additional methods defined to act like a real construction
+    environment with overridden values.  It can wrap either a Base
+    construction environment, or another OverrideEnvironment, which
+    can in turn nest arbitrary OverrideEnvironments...
+
+    Note that we do *not* call the underlying base class
+    (SubsitutionEnvironment) initialization, because we get most of those
+    from proxying the attributes of the subject construction environment.
+    But because we subclass SubstitutionEnvironment, this class also
+    has inherited arg2nodes() and subst*() methods; those methods can't
+    be proxied because they need *this* object's methods to fetch the
+    values from the overrides dictionary.
+    """
+
+    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 executor_to_lvars(self, kwdict):
+            if kwdict.has_key('executor'):
+                kwdict['lvars'] = kwdict['executor'].get_lvars()
+                del kwdict['executor']
+            else:
+                kwdict['lvars'] = {}
+        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.executor_to_lvars(nkw)
+            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.executor_to_lvars(nkw)
+            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:
diff --git a/scons-local/SCons/Errors.py b/scons-local/SCons/Errors.py
new file mode 100644 (file)
index 0000000..41dac19
--- /dev/null
@@ -0,0 +1,205 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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.
+#
+
+"""SCons.Errors
+
+This file contains the exception classes used to handle internal
+and user errors in SCons.
+
+"""
+
+__revision__ = "src/engine/SCons/Errors.py  2013/03/03 09:48:35 garyo"
+
+import SCons.Util
+
+import exceptions
+
+class BuildError(Exception):
+    """ Errors occuring while building.
+
+    BuildError have the following attributes:
+
+        Information about the cause of the build error:
+        -----------------------------------------------
+
+        errstr : a description of the error message
+
+        status : the return code of the action that caused the build
+                 error. Must be set to a non-zero value even if the
+                 build error is not due to an action returning a
+                 non-zero returned code.
+
+        exitstatus : SCons exit status due to this build error.
+                     Must be nonzero unless due to an explicit Exit()
+                     call.  Not always the same as status, since
+                     actions return a status code that should be
+                     respected, but SCons typically exits with 2
+                     irrespective of the return value of the failed
+                     action.
+
+        filename : The name of the file or directory that caused the
+                   build error. Set to None if no files are associated with
+                   this error. This might be different from the target
+                   being built. For example, failure to create the
+                   directory in which the target file will appear. It
+                   can be None if the error is not due to a particular
+                   filename.
+
+        exc_info : Info about exception that caused the build
+                   error. Set to (None, None, None) if this build
+                   error is not due to an exception.
+
+
+        Information about the cause of the location of the error:
+        ---------------------------------------------------------
+
+        node : the error occured while building this target node(s)
+        
+        executor : the executor that caused the build to fail (might
+                   be None if the build failures is not due to the
+                   executor failing)
+        
+        action : the action that caused the build to fail (might be
+                 None if the build failures is not due to the an
+                 action failure)
+
+        command : the command line for the action that caused the
+                  build to fail (might be None if the build failures
+                  is not due to the an action failure)
+        """
+
+    def __init__(self, 
+                 node=None, errstr="Unknown error", status=2, exitstatus=2,
+                 filename=None, executor=None, action=None, command=None,
+                 exc_info=(None, None, None)):
+        
+        self.errstr = errstr
+        self.status = status
+        self.exitstatus = exitstatus
+        self.filename = filename
+        self.exc_info = exc_info
+
+        self.node = node
+        self.executor = executor
+        self.action = action
+        self.command = command
+
+        Exception.__init__(self, node, errstr, status, exitstatus, filename, 
+                           executor, action, command, exc_info)
+
+    def __str__(self):
+        if self.filename:
+            return self.filename + ': ' + self.errstr
+        else:
+            return self.errstr
+
+class InternalError(Exception):
+    pass
+
+class UserError(Exception):
+    pass
+
+class StopError(Exception):
+    pass
+
+class EnvironmentError(Exception):
+    pass
+
+class MSVCError(IOError):
+    pass
+
+class ExplicitExit(Exception):
+    def __init__(self, node=None, status=None, *args):
+        self.node = node
+        self.status = status
+        self.exitstatus = status
+        Exception.__init__(self, *args)
+
+def convert_to_BuildError(status, exc_info=None):
+    """
+    Convert any return code a BuildError Exception.
+
+    `status' can either be a return code or an Exception.
+    The buildError.status we set here will normally be
+    used as the exit status of the "scons" process.
+    """
+    if not exc_info and isinstance(status, Exception):
+        exc_info = (status.__class__, status, None)
+
+    if isinstance(status, BuildError):
+        buildError = status
+        buildError.exitstatus = 2   # always exit with 2 on build errors
+    elif isinstance(status, ExplicitExit):
+        status = status.status
+        errstr = 'Explicit exit, status %s' % status
+        buildError = BuildError(
+            errstr=errstr,
+            status=status,      # might be 0, OK here
+            exitstatus=status,      # might be 0, OK here
+            exc_info=exc_info)
+    elif isinstance(status, (StopError, UserError)):
+        buildError = BuildError(
+            errstr=str(status),
+            status=2,
+            exitstatus=2,
+            exc_info=exc_info)
+    elif isinstance(status, exceptions.EnvironmentError):
+        # If an IOError/OSError happens, raise a BuildError.
+        # Report the name of the file or directory that caused the
+        # error, which might be different from the target being built
+        # (for example, failure to create the directory in which the
+        # target file will appear).
+        try: filename = status.filename
+        except AttributeError: filename = None
+        buildError = BuildError( 
+            errstr=status.strerror,
+            status=status.errno,
+            exitstatus=2,
+            filename=filename,
+            exc_info=exc_info)
+    elif isinstance(status, Exception):
+        buildError = BuildError(
+            errstr='%s : %s' % (status.__class__.__name__, status),
+            status=2,
+            exitstatus=2,
+            exc_info=exc_info)
+    elif SCons.Util.is_String(status):
+        buildError = BuildError(
+            errstr=status,
+            status=2,
+            exitstatus=2)
+    else:
+        buildError = BuildError(
+            errstr="Error %s" % status,
+            status=status,
+            exitstatus=2)
+    
+    #import sys
+    #sys.stderr.write("convert_to_BuildError: status %s => (errstr %s, status %s)"%(status,buildError.errstr, buildError.status))
+    return buildError
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/scons-local/SCons/Executor.py b/scons-local/SCons/Executor.py
new file mode 100644 (file)
index 0000000..0bea6fb
--- /dev/null
@@ -0,0 +1,633 @@
+"""SCons.Executor
+
+A module for executing actions with specific lists of target and source
+Nodes.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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/Executor.py  2013/03/03 09:48:35 garyo"
+
+import collections
+
+from SCons.Debug import logInstanceCreation
+import SCons.Errors
+import SCons.Memoize
+
+
+class Batch(object):
+    """Remembers exact association between targets
+    and sources of executor."""
+    def __init__(self, targets=[], sources=[]):
+        self.targets = targets
+        self.sources = sources
+
+
+
+class TSList(collections.UserList):
+    """A class that implements $TARGETS or $SOURCES expansions by wrapping
+    an executor Method.  This class is used in the Executor.lvars()
+    to delay creation of NodeList objects until they're needed.
+
+    Note that we subclass collections.UserList purely so that the
+    is_Sequence() function will identify an object of this class as
+    a list during variable expansion.  We're not really using any
+    collections.UserList methods in practice.
+    """
+    def __init__(self, func):
+        self.func = func
+    def __getattr__(self, attr):
+        nl = self.func()
+        return getattr(nl, attr)
+    def __getitem__(self, i):
+        nl = self.func()
+        return nl[i]
+    def __getslice__(self, i, j):
+        nl = self.func()
+        i = max(i, 0); j = max(j, 0)
+        return nl[i:j]
+    def __str__(self):
+        nl = self.func()
+        return str(nl)
+    def __repr__(self):
+        nl = self.func()
+        return repr(nl)
+
+class TSObject(object):
+    """A class that implements $TARGET or $SOURCE expansions by wrapping
+    an Executor method.
+    """
+    def __init__(self, func):
+        self.func = func
+    def __getattr__(self, attr):
+        n = self.func()
+        return getattr(n, attr)
+    def __str__(self):
+        n = self.func()
+        if n:
+            return str(n)
+        return ''
+    def __repr__(self):
+        n = self.func()
+        if n:
+            return repr(n)
+        return ''
+
+def rfile(node):
+    """
+    A function to return the results of a Node's rfile() method,
+    if it exists, and the Node itself otherwise (if it's a Value
+    Node, e.g.).
+    """
+    try:
+        rfile = node.rfile
+    except AttributeError:
+        return node
+    else:
+        return rfile()
+
+
+class Executor(object):
+    """A class for controlling instances of executing an action.
+
+    This largely exists to hold a single association of an action,
+    environment, list of environment override dictionaries, targets
+    and sources for later processing as needed.
+    """
+
+    if SCons.Memoize.use_memoizer:
+        __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
+    memoizer_counters = []
+
+    def __init__(self, action, env=None, overridelist=[{}],
+                 targets=[], sources=[], builder_kw={}):
+        if __debug__: logInstanceCreation(self, 'Executor.Executor')
+        self.set_action_list(action)
+        self.pre_actions = []
+        self.post_actions = []
+        self.env = env
+        self.overridelist = overridelist
+        if targets or sources:
+            self.batches = [Batch(targets[:], sources[:])]
+        else:
+            self.batches = []
+        self.builder_kw = builder_kw
+        self._memo = {}
+
+    def get_lvars(self):
+        try:
+            return self.lvars
+        except AttributeError:
+            self.lvars = {
+                'CHANGED_SOURCES' : TSList(self._get_changed_sources),
+                'CHANGED_TARGETS' : TSList(self._get_changed_targets),
+                'SOURCE' : TSObject(self._get_source),
+                'SOURCES' : TSList(self._get_sources),
+                'TARGET' : TSObject(self._get_target),
+                'TARGETS' : TSList(self._get_targets),
+                'UNCHANGED_SOURCES' : TSList(self._get_unchanged_sources),
+                'UNCHANGED_TARGETS' : TSList(self._get_unchanged_targets),
+            }
+            return self.lvars
+
+    def _get_changes(self):
+        cs = []
+        ct = []
+        us = []
+        ut = []
+        for b in self.batches:
+            if b.targets[0].is_up_to_date():
+                us.extend(list(map(rfile, b.sources)))
+                ut.extend(b.targets)
+            else:
+                cs.extend(list(map(rfile, b.sources)))
+                ct.extend(b.targets)
+        self._changed_sources_list = SCons.Util.NodeList(cs)
+        self._changed_targets_list = SCons.Util.NodeList(ct)
+        self._unchanged_sources_list = SCons.Util.NodeList(us)
+        self._unchanged_targets_list = SCons.Util.NodeList(ut)
+
+    def _get_changed_sources(self, *args, **kw):
+        try:
+            return self._changed_sources_list
+        except AttributeError:
+            self._get_changes()
+            return self._changed_sources_list
+
+    def _get_changed_targets(self, *args, **kw):
+        try:
+            return self._changed_targets_list
+        except AttributeError:
+            self._get_changes()
+            return self._changed_targets_list
+
+    def _get_source(self, *args, **kw):
+        #return SCons.Util.NodeList([rfile(self.batches[0].sources[0]).get_subst_proxy()])
+        return rfile(self.batches[0].sources[0]).get_subst_proxy()
+
+    def _get_sources(self, *args, **kw):
+        return SCons.Util.NodeList([rfile(n).get_subst_proxy() for n in self.get_all_sources()])
+
+    def _get_target(self, *args, **kw):
+        #return SCons.Util.NodeList([self.batches[0].targets[0].get_subst_proxy()])
+        return self.batches[0].targets[0].get_subst_proxy()
+
+    def _get_targets(self, *args, **kw):
+        return SCons.Util.NodeList([n.get_subst_proxy() for n in self.get_all_targets()])
+
+    def _get_unchanged_sources(self, *args, **kw):
+        try:
+            return self._unchanged_sources_list
+        except AttributeError:
+            self._get_changes()
+            return self._unchanged_sources_list
+
+    def _get_unchanged_targets(self, *args, **kw):
+        try:
+            return self._unchanged_targets_list
+        except AttributeError:
+            self._get_changes()
+            return self._unchanged_targets_list
+
+    def get_action_targets(self):
+        if not self.action_list:
+            return []
+        targets_string = self.action_list[0].get_targets(self.env, self)
+        if targets_string[0] == '$':
+            targets_string = targets_string[1:]
+        return self.get_lvars()[targets_string]
+
+    def set_action_list(self, action):
+        import SCons.Util
+        if not SCons.Util.is_List(action):
+            if not action:
+                import SCons.Errors
+                raise SCons.Errors.UserError("Executor must have an action.")
+            action = [action]
+        self.action_list = action
+
+    def get_action_list(self):
+        return self.pre_actions + self.action_list + self.post_actions
+
+    def get_all_targets(self):
+        """Returns all targets for all batches of this Executor."""
+        result = []
+        for batch in self.batches:
+            result.extend(batch.targets)
+        return result
+
+    def get_all_sources(self):
+        """Returns all sources for all batches of this Executor."""
+        result = []
+        for batch in self.batches:
+            result.extend(batch.sources)
+        return result
+
+    def get_all_children(self):
+        """Returns all unique children (dependencies) for all batches
+        of this Executor.
+
+        The Taskmaster can recognize when it's already evaluated a
+        Node, so we don't have to make this list unique for its intended
+        canonical use case, but we expect there to be a lot of redundancy
+        (long lists of batched .cc files #including the same .h files
+        over and over), so removing the duplicates once up front should
+        save the Taskmaster a lot of work.
+        """
+        result = SCons.Util.UniqueList([])
+        for target in self.get_all_targets():
+            result.extend(target.children())
+        return result
+
+    def get_all_prerequisites(self):
+        """Returns all unique (order-only) prerequisites for all batches
+        of this Executor.
+        """
+        result = SCons.Util.UniqueList([])
+        for target in self.get_all_targets():
+            result.extend(target.prerequisites)
+        return result
+
+    def get_action_side_effects(self):
+
+        """Returns all side effects for all batches of this
+        Executor used by the underlying Action.
+        """
+        result = SCons.Util.UniqueList([])
+        for target in self.get_action_targets():
+            result.extend(target.side_effects)
+        return result
+
+    memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
+
+    def get_build_env(self):
+        """Fetch or create the appropriate build Environment
+        for this Executor.
+        """
+        try:
+            return self._memo['get_build_env']
+        except KeyError:
+            pass
+
+        # Create the build environment instance with appropriate
+        # overrides.  These get evaluated against the current
+        # environment's construction variables so that users can
+        # add to existing values by referencing the variable in
+        # the expansion.
+        overrides = {}
+        for odict in self.overridelist:
+            overrides.update(odict)
+
+        import SCons.Defaults
+        env = self.env or SCons.Defaults.DefaultEnvironment()
+        build_env = env.Override(overrides)
+
+        self._memo['get_build_env'] = build_env
+
+        return build_env
+
+    def get_build_scanner_path(self, scanner):
+        """Fetch the scanner path for this executor's targets and sources.
+        """
+        env = self.get_build_env()
+        try:
+            cwd = self.batches[0].targets[0].cwd
+        except (IndexError, AttributeError):
+            cwd = None
+        return scanner.path(env, cwd,
+                            self.get_all_targets(),
+                            self.get_all_sources())
+
+    def get_kw(self, kw={}):
+        result = self.builder_kw.copy()
+        result.update(kw)
+        result['executor'] = self
+        return result
+
+    def do_nothing(self, target, kw):
+        return 0
+
+    def do_execute(self, target, kw):
+        """Actually execute the action list."""
+        env = self.get_build_env()
+        kw = self.get_kw(kw)
+        status = 0
+        for act in self.get_action_list():
+            #args = (self.get_all_targets(), self.get_all_sources(), env)
+            args = ([], [], env)
+            status = act(*args, **kw)
+            if isinstance(status, SCons.Errors.BuildError):
+                status.executor = self
+                raise status
+            elif status:
+                msg = "Error %s" % status
+                raise SCons.Errors.BuildError(
+                    errstr=msg, 
+                    node=self.batches[0].targets,
+                    executor=self, 
+                    action=act)
+        return status
+
+    # use extra indirection because with new-style objects (Python 2.2
+    # and above) we can't override special methods, and nullify() needs
+    # to be able to do this.
+
+    def __call__(self, target, **kw):
+        return self.do_execute(target, kw)
+
+    def cleanup(self):
+        self._memo = {}
+
+    def add_sources(self, sources):
+        """Add source files to this Executor's list.  This is necessary
+        for "multi" Builders that can be called repeatedly to build up
+        a source file list for a given target."""
+        # TODO(batch):  extend to multiple batches
+        assert (len(self.batches) == 1)
+        # TODO(batch):  remove duplicates?
+        sources = [x for x in sources if x not in self.batches[0].sources]
+        self.batches[0].sources.extend(sources)
+
+    def get_sources(self):
+        return self.batches[0].sources
+
+    def add_batch(self, targets, sources):
+        """Add pair of associated target and source to this Executor's list.
+        This is necessary for "batch" Builders that can be called repeatedly
+        to build up a list of matching target and source files that will be
+        used in order to update multiple target files at once from multiple
+        corresponding source files, for tools like MSVC that support it."""
+        self.batches.append(Batch(targets, sources))
+
+    def prepare(self):
+        """
+        Preparatory checks for whether this Executor can go ahead
+        and (try to) build its targets.
+        """
+        for s in self.get_all_sources():
+            if s.missing():
+                msg = "Source `%s' not found, needed by target `%s'."
+                raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0]))
+
+    def add_pre_action(self, action):
+        self.pre_actions.append(action)
+
+    def add_post_action(self, action):
+        self.post_actions.append(action)
+
+    # another extra indirection for new-style objects and nullify...
+
+    def my_str(self):
+        env = self.get_build_env()
+        return "\n".join([action.genstring(self.get_all_targets(),
+                                           self.get_all_sources(),
+                                           env)
+                          for action in self.get_action_list()])
+
+
+    def __str__(self):
+        return self.my_str()
+
+    def nullify(self):
+        self.cleanup()
+        self.do_execute = self.do_nothing
+        self.my_str     = lambda: ''
+
+    memoizer_counters.append(SCons.Memoize.CountValue('get_contents'))
+
+    def get_contents(self):
+        """Fetch the signature contents.  This is the main reason this
+        class exists, so we can compute this once and cache it regardless
+        of how many target or source Nodes there are.
+        """
+        try:
+            return self._memo['get_contents']
+        except KeyError:
+            pass
+        env = self.get_build_env()
+        result = "".join([action.get_contents(self.get_all_targets(),
+                                              self.get_all_sources(),
+                                              env)
+                          for action in self.get_action_list()])
+        self._memo['get_contents'] = result
+        return result
+
+    def get_timestamp(self):
+        """Fetch a time stamp for this Executor.  We don't have one, of
+        course (only files do), but this is the interface used by the
+        timestamp module.
+        """
+        return 0
+
+    def scan_targets(self, scanner):
+        # TODO(batch):  scan by batches
+        self.scan(scanner, self.get_all_targets())
+
+    def scan_sources(self, scanner):
+        # TODO(batch):  scan by batches
+        if self.batches[0].sources:
+            self.scan(scanner, self.get_all_sources())
+
+    def scan(self, scanner, node_list):
+        """Scan a list of this Executor's files (targets or sources) for
+        implicit dependencies and update all of the targets with them.
+        This essentially short-circuits an N*M scan of the sources for
+        each individual target, which is a hell of a lot more efficient.
+        """
+        env = self.get_build_env()
+
+        # TODO(batch):  scan by batches)
+        deps = []
+        if scanner:
+            for node in node_list:
+                node.disambiguate()
+                s = scanner.select(node)
+                if not s:
+                    continue
+                path = self.get_build_scanner_path(s)
+                deps.extend(node.get_implicit_deps(env, s, path))
+        else:
+            kw = self.get_kw()
+            for node in node_list:
+                node.disambiguate()
+                scanner = node.get_env_scanner(env, kw)
+                if not scanner:
+                    continue
+                scanner = scanner.select(node)
+                if not scanner:
+                    continue
+                path = self.get_build_scanner_path(scanner)
+                deps.extend(node.get_implicit_deps(env, scanner, path))
+
+        deps.extend(self.get_implicit_deps())
+
+        for tgt in self.get_all_targets():
+            tgt.add_to_implicit(deps)
+
+    def _get_unignored_sources_key(self, node, ignore=()):
+        return (node,) + tuple(ignore)
+
+    memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key))
+
+    def get_unignored_sources(self, node, ignore=()):
+        key = (node,) + tuple(ignore)
+        try:
+            memo_dict = self._memo['get_unignored_sources']
+        except KeyError:
+            memo_dict = {}
+            self._memo['get_unignored_sources'] = memo_dict
+        else:
+            try:
+                return memo_dict[key]
+            except KeyError:
+                pass
+
+        if node:
+            # TODO:  better way to do this (it's a linear search,
+            # but it may not be critical path)?
+            sourcelist = []
+            for b in self.batches:
+                if node in b.targets:
+                    sourcelist = b.sources
+                    break
+        else:
+            sourcelist = self.get_all_sources()
+        if ignore:
+            idict = {}
+            for i in ignore:
+                idict[i] = 1
+            sourcelist = [s for s in sourcelist if s not in idict]
+
+        memo_dict[key] = sourcelist
+
+        return sourcelist
+
+    def get_implicit_deps(self):
+        """Return the executor's implicit dependencies, i.e. the nodes of
+        the commands to be executed."""
+        result = []
+        build_env = self.get_build_env()
+        for act in self.get_action_list():
+            deps = act.get_implicit_deps(self.get_all_targets(),
+                                         self.get_all_sources(),
+                                         build_env)
+            result.extend(deps)
+        return result
+
+
+
+_batch_executors = {}
+
+def GetBatchExecutor(key):
+    return _batch_executors[key]
+
+def AddBatchExecutor(key, executor):
+    assert key not in _batch_executors
+    _batch_executors[key] = executor
+
+nullenv = None
+
+
+def get_NullEnvironment():
+    """Use singleton pattern for Null Environments."""
+    global nullenv
+
+    import SCons.Util
+    class NullEnvironment(SCons.Util.Null):
+        import SCons.CacheDir
+        _CacheDir_path = None
+        _CacheDir = SCons.CacheDir.CacheDir(None)
+        def get_CacheDir(self):
+            return self._CacheDir
+
+    if not nullenv:
+        nullenv = NullEnvironment()
+    return nullenv
+
+class Null(object):
+    """A null Executor, with a null build Environment, that does
+    nothing when the rest of the methods call it.
+
+    This might be able to disapper when we refactor things to
+    disassociate Builders from Nodes entirely, so we're not
+    going to worry about unit tests for this--at least for now.
+    """
+    def __init__(self, *args, **kw):
+        if __debug__: logInstanceCreation(self, 'Executor.Null')
+        self.batches = [Batch(kw['targets'][:], [])]
+    def get_build_env(self):
+        return get_NullEnvironment()
+    def get_build_scanner_path(self):
+        return None
+    def cleanup(self):
+        pass
+    def prepare(self):
+        pass
+    def get_unignored_sources(self, *args, **kw):
+        return tuple(())
+    def get_action_targets(self):
+        return []
+    def get_action_list(self):
+        return []
+    def get_all_targets(self):
+        return self.batches[0].targets
+    def get_all_sources(self):
+        return self.batches[0].targets[0].sources
+    def get_all_children(self):
+        return self.batches[0].targets[0].children()
+    def get_all_prerequisites(self):
+        return []
+    def get_action_side_effects(self):
+        return []
+    def __call__(self, *args, **kw):
+        return 0
+    def get_contents(self):
+        return ''
+    def _morph(self):
+        """Morph this Null executor to a real Executor object."""
+        batches = self.batches
+        self.__class__ = Executor
+        self.__init__([])            
+        self.batches = batches
+
+    # The following methods require morphing this Null Executor to a
+    # real Executor object.
+
+    def add_pre_action(self, action):
+        self._morph()
+        self.add_pre_action(action)
+    def add_post_action(self, action):
+        self._morph()
+        self.add_post_action(action)
+    def set_action_list(self, action):
+        self._morph()
+        self.set_action_list(action)
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/scons-local/SCons/Job.py b/scons-local/SCons/Job.py
new file mode 100644 (file)
index 0000000..4e51b99
--- /dev/null
@@ -0,0 +1,435 @@
+"""SCons.Job
+
+This module defines the Serial and Parallel classes that execute tasks to
+complete a build. The Jobs class provides a higher level interface to start,
+stop, and wait on jobs.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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/Job.py  2013/03/03 09:48:35 garyo"
+
+import SCons.compat
+
+import os
+import signal
+
+import SCons.Errors
+
+# The default stack size (in kilobytes) of the threads used to execute
+# jobs in parallel.
+#
+# We use a stack size of 256 kilobytes. The default on some platforms
+# is too large and prevents us from creating enough threads to fully
+# parallelized the build. For example, the default stack size on linux
+# is 8 MBytes.
+
+explicit_stack_size = None
+default_stack_size = 256
+
+interrupt_msg = 'Build interrupted.'
+
+
+class InterruptState(object):
+   def __init__(self):
+       self.interrupted = False
+
+   def set(self):
+       self.interrupted = True
+
+   def __call__(self):
+       return self.interrupted
+
+
+class Jobs(object):
+    """An instance of this class initializes N jobs, and provides
+    methods for starting, stopping, and waiting on all N jobs.
+    """
+
+    def __init__(self, num, taskmaster):
+        """
+        create 'num' jobs using the given taskmaster.
+
+        If 'num' is 1 or less, then a serial job will be used,
+        otherwise a parallel job with 'num' worker threads will
+        be used.
+
+        The 'num_jobs' attribute will be set to the actual number of jobs
+        allocated.  If more than one job is requested but the Parallel
+        class can't do it, it gets reset to 1.  Wrapping interfaces that
+        care should check the value of 'num_jobs' after initialization.
+        """
+
+        self.job = None
+        if num > 1:
+            stack_size = explicit_stack_size
+            if stack_size is None:
+                stack_size = default_stack_size
+                
+            try:
+                self.job = Parallel(taskmaster, num, stack_size)
+                self.num_jobs = num
+            except NameError:
+                pass
+        if self.job is None:
+            self.job = Serial(taskmaster)
+            self.num_jobs = 1
+
+    def run(self, postfunc=lambda: None):
+        """Run the jobs.
+
+        postfunc() will be invoked after the jobs has run. It will be
+        invoked even if the jobs are interrupted by a keyboard
+        interrupt (well, in fact by a signal such as either SIGINT,
+        SIGTERM or SIGHUP). The execution of postfunc() is protected
+        against keyboard interrupts and is guaranteed to run to
+        completion."""
+        self._setup_sig_handler()
+        try:
+            self.job.start()
+        finally:
+            postfunc()
+            self._reset_sig_handler()
+
+    def were_interrupted(self):
+        """Returns whether the jobs were interrupted by a signal."""
+        return self.job.interrupted()
+
+    def _setup_sig_handler(self):
+        """Setup an interrupt handler so that SCons can shutdown cleanly in
+        various conditions:
+
+          a) SIGINT: Keyboard interrupt
+          b) SIGTERM: kill or system shutdown
+          c) SIGHUP: Controlling shell exiting
+
+        We handle all of these cases by stopping the taskmaster. It
+        turns out that it very difficult to stop the build process
+        by throwing asynchronously an exception such as
+        KeyboardInterrupt. For example, the python Condition
+        variables (threading.Condition) and queue's do not seem to
+        asynchronous-exception-safe. It would&nb