diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..04a59b2 --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,5541 @@ + + + SCons - a software construction tool + + Change Log + + +RELEASE 2.1.0 - Mon, 09 Sep 2011 20:54:57 -0700 + + From Anton Lazarev: + - Fix Windows resource compiler scanner to accept DOS line endings. + + From Matthias: + - Update MSVS documents to remove note indicating that only one + project is currently supported per solution file. + + From Grzegorz Bizoń: + - Fix long compile lines in batch mode by using TEMPFILE + - Fix MSVC_BATCH=False (was treating it as true) + + From Justin Gullingsrud: + - support -std=c++0x and related CXXFLAGS in pkgconfig (ParseFlags) + + From Vincent Beffara: + - Support -dylib_file in pkgconfig (ParseFlags) + + From Gary Oberbrunner and Sohail Somani: + - new construction variable WINDOWS_EMBED_MANIFEST to automatically + embed manifests in Windows EXEs and DLLs. + + From Gary Oberbrunner: + - Fix Visual Studio project generation when CPPPATH contains Dir nodes + - Ensure Visual Studio project is regenerated when CPPPATH or CPPDEFINES change + - Fix unicode error when using non-ASCII filenames with Copy or Install + - Put RPATH in LINKCOM rather than LINKFLAGS so resetting + LINKFLAGS doesn't kill RPATH + - Fix precompiled headers on Windows when variant dir name has spaces. + - Adding None to an Action no longer fails (just returns original action) + - New --debug=prepare option to show each target as it's being + prepared, whether or not anything needs to be done for it. + - New debug option --debug=duplicate to print a line for each + unlink/relink (or copy) of a variant file from its source file. + - Improve error message for EnumVariables to show legal values. + - Fix Intel compiler to sort versions >9 correctly (esp. on Linux) + - Fix Install() when the source and target are directories and the + target directory exists. + + From David Garcia Garzon: + - Fix Delete to be able to delete broken symlinks and dir + symlinks. + + From Robert Lehr: + - Handle .output file generated by bison/yacc properly. Cleaning it + when necessary. + + From Antoine Dechaume: + - Handle SWIG file where there is whitespace after the module name + properly. Previously the generated files would include + the whitespace. + + From Dmitry R.: + - Handle Environment in case __semi_deepcopy is None + + From Benoit Belley: + + - Much improved support for Windows UNC paths (\\SERVERNAME). + + From Jean-Baptiste Lab: + + - Fix problems with appending CPPDEFINES that contain + dictionaries, and related issues with Parse/MergeFlags and + CPPDEFINES. + + From Allen Weeks: + + - Fix for an issue with implicit-cache with multiple targets + when dependencies are removed on disk. + + From Evgeny Podjachev and Alexey Petruchick: + + - Support generation of Microsoft Visual Studio 2008 (9.0) + and 2010 (10.0) project and solution files. + + From Ken Deeter: + + - Fix a problem when FS Entries which are actually Dirs have builders. + + From Luca Falavigna: + + - Support Fortran 03 + + From Gary Oberbrunner: + + - Print the path to the SCons package in scons --version + + From Jean-Fran�ois Colson: + + - Improve Microsoft Visual Studio Solution generation, and fix + various errors in the generated solutions especially when using + MSVS_SCC_PROVIDER, and when generating multiple projects. The + construction variable MSVS_SCC_PROJECT_BASE_PATH, which never + worked properly, is removed. Users can use the new variable + MSVS_SCC_CONNECTION_ROOT instead if desired. + + From Anatoly Techtonik: + + - Use subprocess in bootstrap.py instead of os.execve to avoid + losing output control on Windows (http://bugs.python.org/issue9148) + + - Revert patch for adding SCons to App Paths, because standard cmd + shell doesn't search there. This is confusing, because `scons` can + be executed from explorer, but fail to start from console. + + - Fix broken installation with easy_install on Windows (issue #2051) + SCons traditionally installed in a way that allowed to run multiple + versions side by side. This custom logic was incompatible with + easy_install way of doing things. + + - Use epydoc module for generating API docs in HTML if command line + utility is not found in PATH. Actual for Windows. + + From Alexander Goomenyuk: + + - Add .sx to assembly source scanner list so .sx files + get their header file dependencies detected. + + From Arve Knudsen: + + - Set module metadata when loading site_scons/site_init.py + so it is treated as a proper module; __doc__, __file__ and + __name__ now refer to the site_init.py file. + + From Russel Winder: + + - Users Guide updates explaining that Tools can be packages as + well as python modules. + + From Gary Oberbrunner: + + - New systemwide and per-user site_scons dirs. + + From Dirk Baechle: + + - XML fixes in User's Guide. + - Fixed the detection of 'jar' and 'rmic' during + the initialization of the respective Tools (#2730). + - Improved docs for custom Decider functions and + custom Scanner objects (#2711, #2713). + - Corrected SWIG module names for generated *.i files (#2707). + + From Joe Zuntz: + + - Fixed a case-sensitivity problem with Fortran modules. + + From Bauke Conijn: + + - Added Users Guide example for auto-generated source code + + From Steven Knight: + + - Fix explicit dependencies (Depends()) on Nodes that don't have + attached Builders. + + - Fix use of the global Alias() function with command actions. + + From Matt Hughes: + + - Fix the ability to append to default $*FLAGS values (which are + implemented as CLVar instances) in a copied construction environment + without affecting the original construction environment's value. + + From Rob Managan: + + - Updated the TeX command strings to include a /D on Windows in + case the new directory is on a different drive letter. + + - Fixed the LaTeX scanner so dependencies are found in commands that + are broken across lines with a comment or have embedded spaces. + + - The TeX builders should now work with tex files that are generated + by another program. Thanks to Hans-Martin von Gaudecker for + isolating the cause of this bug. + + - Added support for INDEXSTYLE environment variable so makeindex can + find style files. + + - Added support for the bibunits package so we call bibtex on all + the bu*.aux files. + + - Add support of finding path information on OSX for TeX applications + MacPorts and Fink paths need to be added by the user + + From Russel Winder: + + - Add support for DMD version 2 (the phobos2 library). + + From William Deegan: + + - Add initial support for VS/VC 2010 (express and non-express versions) + - Remove warning for not finding MS VC/VS install. + "scons: warning: No version of Visual Studio compiler found + - C/C++ compilers most likely not set correctly" + - Add support for Linux 3.0 + + +RELEASE 2.0.1 - Mon, 15 Aug 2010 15:46:32 -0700 + + From Dirk Baechle: + + - Fix XML in documentation. + + From Joe Zuntz: + + - Fixed a case-sensitivity problem with Fortran modules. + + From Bauke Conijn: + + - Added Users Guide example for auto-generated source code + + From Steven Knight: + + - Fix explicit dependencies (Depends()) on Nodes that don't have + attached Builders. + + From Matt Hughes: + + - Fix the ability to append to default $*FLAGS values (which are + implemented as CLVar instances) in a copied construction environment + without affecting the original construction environment's value. + + From Rob Managan: + + - Updated the TeX command strings to include a /D on Windows in + case the new directory is on a different drive letter. + + - Fixed the LaTeX scanner so dependencies are found in commands that + are broken across lines with a comment or have embedded spaces. + + +RELEASE 2.0.0.final.0 - Mon, 14 Jun 2010 22:01:37 -0700 + + From Dirk Baechle: + + - Fix XML in documentation. + + From Steven Knight: + + - Provide forward compatibility for the 'profile' module. + + - Provide forward compatibility for the 'pickle' module. + + - Provide forward compatibility for the 'io' module. + + - Provide forward compatibility for the 'queue' module. + + - Provide forward compatibility for the 'collections' module. + + - Provide forward compatibility for the 'builtins' module. + + - Provide forward compatibility for 'sys.intern()'. + + - Convert to os.walk() from of os.path.walk(). + + - Remove compatibility logic no longer needed. + + - Add a '-3' option to runtest to print 3.x incompatibility warnings. + + - Convert old-style classes into new-style classes. + + - Fix "Ignoring corrupt sconsign entry" warnings when building + in a tree with a pre-2.0 .sconsign file. + + - Fix propagation from environment of VS*COMNTOOLS to resolve issues + initializing MSVC/MSVS/SDK issues. + + - Handle detecting Visual C++ on Python verions with upper-case + platform architectures like 'AMD64'. + + From W. Trevor King: + + - Revisions to README. + + From Greg Noel: + + - Apply numerous Python fixers to update code to more modern idioms. + Find where fixers should be applied to code in test strings and + apply the fixers there, too. + + - Write a fixer to convert string functions to string methods. + + - Modify the 'dict' fixer to be less conservative. + + - Modify the 'apply' fixer to handle more cases. + + - Create a modified 'types' fixer that converts types to 2.x + equivalents rather than 3.x equivalents. + + - Write a 'division' fixer to highlight uses of the old-style + division operator. Correct usage where needed. + + - Add forward compatibility for the new 'memoryview' function + (which replaces the 'buffer' function). + + - Add forward compatibility for the 'winreg' module. + + - Remove no-longer-needed 'platform' module. + + - Run tests with the '-3' option to Python 2.6 and clear up + various reported incompatibilities. + + - Comb out code paths specialized to Pythons older than 2.4. + + - Update deprecation warnings; most now become mandatory. + + - Start deprecation cycle for BuildDir() and build_dir. + + - Start deprecation cycle for SourceCode() and related factories + + - Fixed a problem with is_Dict() not identifying some objects derived + from UserDict. + + From Jim Randall: + + - Document the AllowSubstExceptions() function in the User's Guide. + + From William Deegan: + + - Migrate MSVC/MSVS/SDK improvements from 1.3 branch. + + +RELEASE 1.3.0 - Tue, 23 Mar 2010 21:44:19 -0400 + + From Steven Knight: + + - Update man page and documentation. + + From William Deegan (plus minor patch from Gary Oberbrunner): + + - Support Visual Studio 8.0 Express + +RELEASE 1.2.0.d20100306 - Sat, 06 Mar 2010 16:18:33 -0800 + + From Luca Falavigna: + + - Fix typos in the man page. + + From Gottfried Ganssauge: + + - Support execution when SCons is installed via easy_install. + + From Steven Knight: + + - Make the messages for Configure checks of compilers consistent. + + - Issue an error message if a BUILDERS entry is not a Builder + object or a callable wrapper. + + From Rob Managan: + + - Update tex builder to handle the case where a \input{foo} + command tries to work with a directory named foo instead of the + file foo.tex. The builder now ignores a directory and continues + searching to find the correct file. Thanks to Lennart Sauerbeck + for the test case and initial patch + + Also allow the \include of files in subdirectories when variantDir + is used with duplicate=0. Previously latex would crash since + the directory in which the .aux file is written was not created. + Thanks to Stefan Hepp for finding this and part of the solution. + + From James Teh: + - Patches to fix some issues using MS SDK V7.0 + + From William Deegan: + - Lots of testing and minor patches to handle mixed MS VC and SDK + installations, as well as having only the SDK installed. + + +RELEASE 1.2.0.d20100117 - Sun, 17 Jan 2010 14:26:59 -0800 + + From Jim Randall: + - Fixed temp filename race condition on Windows with long cmd lines. + + From David Cournapeau: + - Fixed tryRun when sconf directory is in a variant dir. + - Do not add -fPIC for ifort tool on non-posix platforms (darwin and + windows). + - Fix bug 2294 (spurious CheckCC failures). + - Fix scons bootstrap process on windows 64 (wrong wininst name) + + From William Deegan: + - Final merge from vs_revamp branch to main + + - Added definition and usage of HOST_OS, HOST_ARCH, TARGET_OS, + TARGET_ARCH, currently only defined/used by Visual Studio + Compilers. This will be rolled out to other platforms/tools + in the future. + + - Add check for python >= 3.0.0 and exit gracefully. + For 1.3 python >= 1.5.2 and < 3.0.0 are supported + + - Fix bug 1944 - Handle non-existent .i file in swig emitter, previously + it would crash with an IOError exception. Now it will try to make an + educated guess on the module name based on the filename. + + From Lukas Erlinghagen: + + - Have AddOption() remove variables from the list of + seen-but-unknown variables (which are reported later). + + - An option name and aliases can now be specified as a tuple. + + From Hartmut Goebel: + + - Textfile builder. + + From Jared Grubb: + + - use "is/is not" in comparisons with None instead of "==" or "!=". + + From Jim Hunziker: + + - Avoid adding -gphobos to a command line multiple times + when initializing use of the DMD compiler. + + From Jason Kenney: + + - Sugguested HOST/TARGET OS/ARCH separation. + + From Steven Knight: + + - Fix the -n option when used with VariantDir(duplicate=1) + and the variant directory doesn't already exist. + + - Fix scanning of Unicode files for both UTF-16 endian flavors. + + - Fix a TypeError on #include of file names with Unicode characters. + + - Fix an exception if a null command-line argument is passed in. + + - Evaluate Requires() prerequisites before a Node's direct children + (sources and dependencies). + + From Greg Noel: + + - Remove redundant __metaclass__ initializations in Environment.py. + + - Correct the documentation of text returned by sconf.Result(). + + - Document that filenames with '.' as the first character are + ignored by Glob() by default (matching UNIX glob semantics). + + - Fix SWIG testing infrastructure to work on Mac OS X. + + - Restructure a test that occasionally hung so that the test would + detect when it was stuck and fail instead. + + - Substfile builder. + + From Gary Oberbrunner: + + - When reporting a target that SCons doesn't know how to make, + specify whether it's a File, Dir, etc. + + From Ben Webb: + + - Fix use of $SWIGOUTDIR when generating Python wrappers. + + - Add $SWIGDIRECTORSUFFIX and $SWIGVERSION construction variables. + + From Rob Managan: + + - Add -recorder flag to Latex commands and updated internals to + use the output to find files TeX creates. This allows the MiKTeX + installations to find the created files + + - Notify user of Latex errors that would get buried in the + Latex output + + - Remove LATEXSUFFIXES from environments that don't initialize Tex. + + - Add support for the glossaries package for glossaries and acronyms + + - Fix problem that pdftex, latex, and pdflatex tools by themselves did + not create the actions for bibtex, makeindex,... by creating them + and other environment settings in one routine called by all four + tex tools. + + - Fix problem with filenames of sideeffects when the user changes + the name of the output file from the latex default + + - Add scanning of files included in Latex by means of \lstinputlisting{} + Patch from Stefan Hepp. + + - Change command line for epstopdf to use --outfile= instead of -o + since this works on all platforms. + Patch from Stefan Hepp. + + - Change scanner to properly search for included file from the + directory of the main file instead of the file it is included from. + Also update the emitter to add the .aux file associated with + \include{filename} commands. This makes sure the required directories + if any are created for variantdir cases. + Half of the patch from Stefan Hepp. + +RELEASE 1.2.0.d20090223 - Mon, 23 Feb 2009 08:41:06 -0800 + + From Stanislav Baranov: + + - Make suffix-matching for scanners case-insensitive on Windows. + + From David Cournapeau: + + - Change the way SCons finds versions of Visual C/C++ and Visual + Studio to find and use the Microsoft v*vars.bat files. + + From Robert P. J. Day: + + - User's Guide updates. + + From Dan Eaton: + + - Fix generation of Visual Studio 8 project files on x64 platforms. + + From Allan Erskine: + + - Set IncludeSearchPath and PreprocessorDefinitions in generated + Visual Studio 8 project files, to help IntelliSense work. + + From Mateusz Gruca: + + - Fix deletion of broken symlinks by the --clean option. + + From Steven Knight: + + - Fix the error message when use of a non-existent drive on Windows + is detected. + + - Add sources for files whose targets don't exist in $CHANGED_SOURCES. + + - Detect implicit dependencies on commands even when the command is + quoted. + + - Fix interaction of $CHANGED_SOURCES with the --config=force option. + + - Fix finding #include files when the string contains escaped + backslashes like "C:\\some\\include.h". + + - Pass $CCFLAGS to Visual C/C++ precompiled header compilation. + + - Remove unnecessary nested $( $) around $_LIBDIRFLAGS on link lines + for the Microsoft linker, the OS/2 ilink linker and the Phar Lap + linkloc linker. + + - Spell the Windows environment variables consistently "SystemDrive" + and "SystemRoot" instead of "SYSTEMDRIVE" and "SYSTEMROOT". + + + +RELEASE 1.2.0.d20090113 - Tue, 13 Jan 2009 02:50:30 -0800 + + From Stanislav Baranov, Ted Johnson and Steven Knight: + + - Add support for batch compilation of Visual Studio C/C++ source + files, controlled by a new $MSVC_BATCH construction variable. + + From Steven Knight: + + - Print the message, "scons: Build interrupted." on error output, + not standard output. + + - Add a --warn=future-deprecated option for advance warnings about + deprecated features that still have warnings hidden by default. + + - Fix use of $SOURCE and $SOURCES attributes when there are no + sources specified in the Builder call. + + - Add support for new $CHANGED_SOURCES, $CHANGED_TARGETS, + $UNCHANGED_SOURCES and $UNCHANGED_TARGETS variables. + + - Add general support for batch builds through new batch_key= and + targets= keywords to Action object creation. + + From Arve Knudsen: + + - Make linker tools differentiate properly between SharedLibrary + and LoadableModule. + + - Document TestCommon.shobj_prefix variable. + + - Support $SWIGOUTDIR values with spaces. + + From Rob Managan: + + - Don't automatically try to build .pdf graphics files for + .eps files in \includegraphics{} calls in TeX/LaTeX files + when building with the PDF builder (and thus using pdflatex). + + From Gary Oberbrunner: + + - Allow AppendENVPath() and PrependENVPath() to interpret '#' + for paths relative to the top-level SConstruct directory. + + - Use the Borland ilink -e option to specify the output file name. + + - Document that the msvc Tool module uses $PCH, $PCHSTOP and $PDB. + + - Allow WINDOWS_INSERT_DEF=0 to disable --output-def when linking + under MinGW. + + From Zia Sobhani: + + - Fix typos in the User's Guide. + + From Greg Spencer: + + - Support implicit dependency scanning of files encoded in utf-8 + and utf-16. + + From Roberto de Vecchi: + + - Remove $CCFLAGS from the the default definitions of $CXXFLAGS for + Visual C/C++ and MIPSpro C++ on SGI so, they match other tools + and avoid flag duplication on C++ command lines. + + From Ben Webb: + + - Handle quoted module names in SWIG source files. + + - Emit *_wrap.h when SWIG generates header file for directors + + From Matthew Wesley: + + - Copy file attributes so we identify, and can link a shared library + from, shared object files in a Repository. + + + +RELEASE 1.2.0 - Sat, 20 Dec 2008 22:47:29 -0800 + + From Steven Knight: + + - Don't fail if can't import a _subprocess module on Windows. + + - Add warnings for use of the deprecated Options object. + + + +RELEASE 1.1.0.d20081207 - Sun, 07 Dec 2008 19:17:23 -0800 + + From Benoit Belley: + + - Improve the robustness of GetBuildFailures() by refactoring + SCons exception handling (especially BuildError exceptions). + + - Have the --taskmastertrace= option print information about + individual Task methods, not just the Taskmaster control flow. + + - Eliminate some spurious dependency cycles by being more aggressive + about pruning pending children from the Taskmaster walk. + + - Suppress mistaken reports of a dependency cycle when a child + left on the pending list is a single Node in EXECUTED state. + + From David Cournapeau: + + - Fix $FORTRANMODDIRPREFIX for the ifort (Intel Fortran) tool. + + From Brad Fitzpatrick: + + - Don't pre-generate an exception message (which will likely be + ignored anyway) when an EntryProxy re-raises an AttributeError. + + From Jared Grubb: + + - Clean up coding style and white space in Node/FS.py. + + - Fix a typo in the documentation for $_CPPDEFFLAGS. + + - Issue 2401: Fix usage of comparisons with None. + + From Ludwig H�hne: + + - Handle Java inner classes declared within a method. + + From Steven Knight: + + - Fix label placement by the "scons-time.py func" subcommand + when a profile value was close to (or equal to) 0.0. + + - Fix env.Append() and env.Prepend()'s ability to add a string to + list-like variables like $CCFLAGS under Python 2.6. + + - Other Python2.6 portability: don't use "as" (a Python 2.6 keyword). + Don't use the deprecated Exception.message attribute. + + - Support using the -f option to search for a different top-level + file name when walking up with the -D, -U or -u options. + + - Fix use of VariantDir when the -n option is used and doesn't, + therefore, actually create the variant directory. + + - Fix a stack trace from the --debug=includes option when passed a + static or shared library as an argument. + + - Speed up the internal find_file() function (used for searching + CPPPATH, LIBPATH, etc.). + + - Add support for using the Python "in" keyword on construction + environments (for example, if "CPPPATH" in env: ...). + + - Fix use of Glob() when a repository or source directory contains + an in-memory Node without a corresponding on-disk file or directory. + + - Add a warning about future reservation of $CHANGED_SOURCES, + $CHANGED_TARGETS, $UNCHANGED_SOURCES and $UNCHANGED_TARGETS. + + - Enable by default the existing warnings about setting the resource + $SOURCE, $SOURCES, $TARGET and $TARGETS variable. + + From Rob Managan: + + - Scan for TeX files in the paths specified in the $TEXINPUTS + construction variable and the $TEXINPUTS environment variable. + + - Configure the PDF() and PostScript() Builders as single_source so + they know each source file generates a separate target file. + + - Add $EPSTOPDF, $EPSTOPDFFLAGS and $EPSTOPDFCOM + + - Add .tex as a valid extension for the PDF() builder. + + - Add regular expressions to find \input, \include and + \includegraphics. + + - Support generating a .pdf file from a .eps source. + + - Recursive scan included input TeX files. + + - Handle requiring searched-for TeX input graphics files to have + extensions (to avoid trying to build a .eps from itself, e.g.). + + From Greg Noel: + + - Make the Action() function handle positional parameters consistently. + + - Clarify use of Configure.CheckType(). + + - Make the File.{Dir,Entry,File}() methods create their entries + relative to the calling File's directory, not the SConscript + directory. + + - Use the Python os.devnull variable to discard error output when + looking for the $CC or $CXX version. + + - Mention LoadableModule() in the SharedLibrary() documentation. + + From Gary Oberbrunner: + + - Update the User's Guide to clarify use of the site_scons/ + directory and the site_init.py module. + + - Make env.AppendUnique() and env.PrependUnique remove duplicates + within a passed-in list being added, too. + + From Randall Spangler: + + - Fix Glob() so an on-disk file or directory beginning with '#' + doesn't throw an exception. + + + +RELEASE 1.1.0 - Thu, 09 Oct 2008 08:33:47 -0700 + + From Chris AtLee + + - Use the specified environment when checking for the GCC compiler + version. + + From Ian P. Cardenas: + + - Fix Glob() polluting LIBPATH by returning copy of list + + From David Cournapeau: + + - Add CheckCC, CheckCXX, CheckSHCC and CheckSHCXX tests to + configuration contexts. + + - Have the --profile= argument use the much faster cProfile module + (if it's available in the running Python version). + + - Reorder MSVC compilation arguments so the /Fo is first. + + From Bill Deegan: + + - Add scanning Windows resource (.rc) files for implicit dependencies. + + From John Gozde: + + - When scanning for a #include file, don't use a directory that + has the same name as the file. + + From Ralf W. Grosse-Kunstleve + + - Suppress error output when checking for the GCC compiler version. + + From Jared Grubb: + + - Fix VariantDir duplication of #included files in subdirectories. + + From Ludwig H�hne: + + - Reduce memory usage when a directory is used as a dependency of + another Node (such as an Alias) by returning a concatenation + of the children's signatures + names, not the children's contents, + as the directory contents. + + - Raise AttributeError, not KeyError, when a Builder can't be found. + + - Invalidate cached Node information (such as the contenst returned + by the get_contents() method) when calling actions with Execute(). + + - Avoid object reference cycles from frame objects. + + - Reduce memory usage from Null Executor objects. + + - Compute MD5 checksums of large files without reading the entire + file contents into memory. Add a new --md5-chunksize option to + control the size of each chunk read into memory. + + From Steven Knight: + + - Fix the ability of the add_src_builder() method to add a new + source builder to any other builder. + + - Avoid an infinite loop on non-Windows systems trying to find the + SCons library directory if the Python library directory does not + begin with the string "python". + + - Search for the SCons library directory in "scons-local" (with + no version number) after "scons-local-{VERSION}". + + From Rob Managan: + + - Fix the user's ability to interrupt the TeX build chain. + + - Fix the TeX builder's allowing the user to specify the target name, + instead of always using its default output name based on the source. + + - Iterate building TeX output files until all warning are gone + and the auxiliary files stop changing, or until we reach the + (configurable) maximum number of retries. + + - Add TeX scanner support for: glossaries, nomenclatures, lists of + figures, lists of tables, hyperref and beamer. + + - Use the $BIBINPUTS, $BSTINPUTS, $TEXINPUTS and $TEXPICTS construction + variables as search paths for the relevant types of input file. + + - Fix building TeX with VariantDir(duplicate=0) in effect. + + - Fix the LaTeX scanner to search for graphics on the TEXINPUTS path. + + - Have the PDFLaTeX scanner search for .gif files as well. + + From Greg Noel: + + - Fix typos and format bugs in the man page. + + - Add a first draft of a wrapper module for Python's subprocess + module. + + - Refactor use of the SCons.compat module so other modules don't + have to import it individually. + + - Add .sx as a suffix for assembly language files that use the + C preprocessor. + + From Gary Oberbrunner: + + - Make Glob() sort the returned list of Files or Nodes + to prevent spurious rebuilds. + + - Add a delete_existing keyword argument to the AppendENVPath() + and PrependENVPath() Environment methods. + + - Add ability to use "$SOURCE" when specifying a target to a builder + + From Damyan Pepper: + + - Add a test case to verify that SConsignFile() files can be + created in previously non-existent subdirectories. + + From Jim Randall: + + - Make the subdirectory in which the SConsignFile() file will + live, if the subdirectory doesn't already exist. + + From Ali Tofigh: + + - Add a test to verify duplication of files in VariantDir subdirectories. + + + +RELEASE 1.0.1 - Sat, 06 Sep 2008 07:29:34 -0700 + + From Greg Noel: + + - Add a FindFile() section to the User's Guide. + + - Fix the FindFile() documentation in the man page. + + - Fix formatting errors in the Package() description in the man page. + + - Escape parentheses that appear within variable names when spawning + command lines using os.system(). + + + +RELEASE 1.0.0 - XXX + + From Jared Grubb: + + - Clear the Node state when turning a generic Entry into a Dir. + + From Ludwig H�hne: + + - Fix sporadic output-order failures in test/GetBuildFailures/parallel.py. + + - Document the ParseDepends() function in the User's Guide. + + From khomenko: + + - Create a separate description and long_description for RPM packages. + + From Steven Knight: + + - Document the GetLaunchDir() function in the User's Guide. + + - Have the env.Execute() method print an error message if the + executed command fails. + + - Add a script for creating a standard SCons development system on + Ubuntu Hardy. Rewrite subsidiary scripts for install Python and + SCons versions in Python (from shell). + + From Greg Noel: + + - Handle yacc/bison on newer Mac OS X versions creating file.hpp, + not file.cpp.h. + + - In RPCGEN tests, ignore stderr messages from older versions of + rpcgen on some versions of Mac OS X. + + - Fix typos in man page descriptions of Tag() and Package(), and in + the scons-time man page. + + - Fix documentation of SConf.CheckLibWithHeader and other SConf methods. + + - Update documentation of SConscript(variant_dir) usage. + + - Fix SWIG tests for (some versions of) Mac OS X. + + From Jonas Olsson: + + - Print the warning about -j on Windows being potentially unreliable if + the pywin32 extensions are unavailable or lack file handle operations. + + From Jim Randall: + + - Fix the env.WhereIs() method to expand construction variables. + + From Rogier Schouten: + + - Enable building of shared libraries with the Bordand ilink32 linker. + + + +RELEASE 1.0.0 - Sat, 09 Aug 2008 12:19:44 -0700 + + From Luca Falavigna: + + - Fix SCons man page indentation under Debian's man page macros. + + From Steven Knight: + + - Clarify the man page description of the SConscript(src_dir) argument. + + - User's Guide updates: + + - Document the BUILD_TARGETS, COMMAND_LINE_TARGETS and + DEFAULT_TARGETS variables. + + - Document the AddOption(), GetOption() and SetOption() functions. + + - Document the Requires() function; convert to the Variables + object, its UnknownOptions() method, and its associated + BoolVariable(), EnumVariable(), ListVariable(), PackageVariable() + and PathVariable() functions. + + - Document the Progress() function. + + - Reorganize the chapter and sections describing the different + types of environments and how they interact. Document the + SetDefault() method. Document the PrependENVPath() and + AppendENVPath() functions. + + - Reorganize the command-line arguments chapter. Document the + ARGLIST variable. + + - Collect some miscellaneous sections into a chapter about + configuring build output. + + - Man page updates: + + - Document suggested use of the Visual C/C++ /FC option to fix + the ability to double-click on file names in compilation error + messages. + + - Document the need to use Clean() for any SideEffect() files that + must be explicitly removed when their targets are removed. + + - Explicitly document use of Node lists as input to Dependency(). + + From Greg Noel: + + - Document MergeFlags(), ParseConfig(), ParseFlags() and SideEffect() + in the User's Guide. + + From Gary Oberbrunner: + + - Document use of the GetBuildFailures() function in the User's Guide. + + From Adam Simpkins: + + - Add man page text clarifying the behavior of AddPreAction() and + AddPostAction() when called with multiple targets. + + From Alexey Zezukin: + + - Fix incorrectly swapped man page descriptions of the --warn= options + for duplicate-environment and missing-sconscript. + + + +RELEASE 0.98.5 - Sat, 07 Jun 2008 08:20:35 -0700 + + From Benoit Belley: + + - Fix the Intel C++ compiler ABI specification for EMT64 processors. + + From David Cournapeau: + + - Issue a (suppressable) warning, not an error, when trying to link + C++ and Fortran object files into the same executable. + + From Steven Knight: + + - Update the scons.bat file so that it returns the real exit status + from SCons, even though it uses setlocal + endlocal. + + - Fix the --interactive post-build messages so it doesn't get stuck + mistakenly reporting failures after any individual build fails. + + - Fix calling File() as a File object method in some circumstances. + + - Fix setup.py installation on Mac OS X so SCons gets installed + under /usr/lcoal by default, not in the Mac OS X Python framework. + + + +RELEASE 0.98.4 - Sat, 17 May 2008 22:14:46 -0700 + + From Benoit Belley: + + - Fix calculation of signatures for Python function actions with + closures in Python versions before 2.5. + + From David Cournapeau: + + - Fix the initialization of $SHF77FLAGS so it includes $F77FLAGS. + + From Jonas Olsson: + + - Fix a syntax error in the Intel C compiler support on Windows. + + From Steven Knight: + + - Change how we represent Python Value Nodes when printing and when + stored in .sconsign files (to avoid blowing out memory by storing + huge strings in .sconsign files after multiple runs using Configure + contexts cause the Value strings to be re-escaped each time). + + - Fix a regression in not executing configuration checks after failure + of any configuration check that used the same compiler or other tool. + + - Handle multiple destinations in Visual Studio 8 settings for the + analogues to the INCLUDE, LIBRARY and PATH variables. + + From Greg Noel: + + - Update man page text for VariantDir(). + + + +RELEASE 0.98.3 - Tue, 29 Apr 2008 22:40:12 -0700 + + From Greg Noel: + + - Fix use of $CXXFLAGS when building C++ shared object files. + + From Steven Knight: + + - Fix a regression when a Builder's source_scanner doesn't select + a more specific scanner for the suffix of a specified source file. + + - Fix the Options object backwards compatibility so people can still + "import SCons.Options.{Bool,Enum,List,Package,Path}Option" submodules. + + - Fix searching for implicit dependencies when an Entry Node shows up + in the search path list. + + From Stefano: + + - Fix expansion of $FORTRANMODDIR in the default Fortran command line(s) + when it's set to something like ${TARGET.dir}. + + + +RELEASE 0.98.2 - Sun, 20 Apr 2008 23:38:56 -0700 + + From Steven Knight: + + - Fix a bug in Fortran suffix computation that would cause SCons to + run out of memory on Windows systems. + + - Fix being able to specify --interactive mode command lines with + \ (backslash) path name separators on Windows. + + From Gary Oberbrunner: + + - Document Glob() in the User's Guide. + + + +RELEASE 0.98.1 - Fri, 18 Apr 2008 19:11:58 -0700 + + From Benoit Belley: + + - Speed up the SCons.Util.to_string*() functions. + + - Optimize various Node intialization and calculations. + + - Optimize Executor scanning code. + + - Optimize Taskmaster execution, including dependency-cycle checking. + + - Fix the --debug=stree option so it prints its tree once, not twice. + + From Johan Boul�: + + - Fix the ability to use LoadableModule() under MinGW. + + From David Cournapeau: + + - Various missing Fortran-related construction variables have been added. + + - SCons now uses the program specified in the $FORTRAN construction + variable to link Fortran object files. + + - Fortran compilers on Linux (Intel, g77 and gfortran) now add the -fPIC + option by default when compilling shared objects. + + - New 'sunf77', 'sunf90' and 'sunf95' Tool modules have been added to + support Sun Fortran compilers. On Solaris, the Sun Fortran compilers + are used in preference to other compilers by default. + + - Fortran support now uses gfortran in preference to g77. + + - Fortran file suffixes are now configurable through the + $F77FILESUFFIXES, $F90FILESUFFIXES, $F95FILESUFFIXES and + $FORTRANFILESUFFIXES variables. + + From Steven Knight: + + - Make the -d, -e, -w and --no-print-directory options "Ignored for + compatibility." (We're not going to implement them.) + + - Fix a serious inefficiency in how SCons checks for whether any source + files are missing when a Builder call creates many targets from many + input source files. + + - In Java projects, make the target .class files depend only on the + specific source .java files where the individual classes are defined. + + - Don't store duplicate source file entries in the .sconsign file so + we don't endlessly rebuild the target(s) for no reason. + + - Add a Variables object as the first step towards deprecating the + Options object name. Similarly, add BoolVariable(), EnumVariable(), + ListVariable(), PackageVariable() and PathVariable() functions + as first steps towards replacing BoolOption(), EnumOption(), + ListOption(), PackageOption() and PathOption(). + + - Change the options= keyword argument to the Environment() function + to variables=, to avoid confusion with SCons command-line options. + Continue supporting the options= keyword for backwards compatibility. + + - When $SWIGFLAGS contains the -python flag, expect the generated .py + file to be in the same (sub)directory as the target. + + - When compiling C++ files, allow $CCFLAGS settings to show up on the + command line even when $CXXFLAGS has been redefined. + + - Fix --interactive with -u/-U/-D when a VariantDir() is used. + + From Anatoly Techtonik: + + - Have the scons.bat file add the script execution directory to its + local %PATH% on Windows, so the Python executable can be found. + + From Mike Wake: + + - Fix passing variable names as a list to the Return() function. + + From Matthew Wesley: + + - Add support for the GDC 'D' language compiler. + + + +RELEASE 0.98 - Sun, 30 Mar 2008 23:33:05 -0700 + + From Benoit Belley: + + - Fix the --keep-going flag so it builds all possible targets even when + a later top-level target depends on a child that failed its build. + + - Fix being able to use $PDB and $WINDWOWS_INSERT_MANIFEST together. + + - Don't crash if un-installing the Intel C compiler leaves left-over, + dangling entries in the Windows registry. + + - Improve support for non-standard library prefixes and suffixes by + stripping all prefixes/suffixes from file name string as appropriate. + + - Reduce the default stack size for -j worker threads to 256 Kbytes. + Provide user control over this value by adding --stack-size and + --warn=stack-size options, and a SetOption('stack_size') function. + + - Fix a crash on Linux systems when trying to use the Intel C compiler + and no /opt/intel_cc_* directories are found. + + - Improve using Python functions as actions by incorporating into + a FunctionAction's signature: + - literal values referenced by the byte code. + - values of default arguments + - code of nested functions + - values of variables captured by closures + - names of referenced global variables and functions + + - Fix the closing message when --clean and --keep-going are both + used and no errors occur. + + - Add support for the Intel C compiler on Mac OS X. + + - Speed up reading SConscript files by about 20% (for some + configurations) by: 1) optimizing the SCons.Util.is_*() and + SCons.Util.flatten() functions; 2) avoiding unnecessary os.stat() + calls by using a File's .suffix attribute directly instead of + stringifying it. + + From Jérôme Berger: + + - Have the D language scanner search for .di files as well as .d files. + + - Add a find_include_names() method to the Scanner.Classic class to + abstract out how included names can be generated by subclasses. + + - Allow the D language scanner to detect multiple modules imported by + a single statement. + + From Konstantin Bozhikov: + + - Support expansion of construction variables that contain or refer + to lists of other variables or Nodes within expansions like $CPPPATH. + + - Change variable substitution (the env.subst() method) so that an + input sequence (list or tuple) is preserved as a list in the output. + + From David Cournapeau: + + - Add a CheckDeclaration() call to configure contexts. + + - Improve the CheckTypeSize() code. + + - Add a Define() call to configure contexts, to add arbitrary #define + lines to a generated configure header file. + + - Add a "gfortran" Tool module for the GNU F95/F2003 compiler. + + - Avoid use of -rpath with the Mac OS X linker. + + - Add comment lines to the generated config.h file to describe what + the various #define/#undef lines are doing. + + From Steven Knight: + + - Support the ability to subclass the new-style "str" class as input + to Builders. + + - Improve the performance of our type-checking by using isinstance() + with new-style classes. + + - Fix #include (and other $*PATH variables searches) of files with + absolute path names. Don't die if they don't exist (due to being + #ifdef'ed out or the like). + + - Fix --interactive mode when Default(None) is used. + + - Fix --debug=memoizer to work around a bug in base Python 2.2 metaclass + initialization (by just not allowing Memoization in Python versions + that have the bug). + + - Have the "scons-time time" subcommand handle empty log files, and + log files that contain no results specified by the --which option. + + - Fix the max Y of vertical bars drawn by "scons-time --fmt=gnuplot". + + - On Mac OS X, account for the fact that the header file generated + from a C++ file will be named (e.g.) file.cpp.h, not file.hpp. + + - Fix floating-point numbers confusing the Java parser about + generated .class file names in some configurations. + + - Document (nearly) all the values you can now fetch with GetOption(). + + - Fix use of file names containing strings of multiple spaces when + using ActionFactory instances like the Copy() or Move() function. + + - Fix a 0.97 regression when using a variable expansion (like + $OBJSUFFIX) in a source file name to a builder with attached source + builders that match suffix (like Program()+Object()). + + - Have the Java parser recognize generics (surrounded by angle brackets) + so they don't interfere with identifying anonymous inner classes. + + - Avoid an infinite loop when trying to use saved copies of the + env.Install() or env.InstallAs() after replacing the method + attributes. + + - Improve the performance of setting construction variables. + + - When cloning a construction environment, avoid over-writing an + attribute for an added method if the user explicitly replaced it. + + - Add a warning about deprecated support for Python 1.5, 2.0 and 2.1. + + - Fix being able to SetOption('warn', ...) in SConscript files. + + - Add a warning about env.Copy() being deprecated. + + - Add warnings about the --debug={dtree,stree,tree} options + being deprecated. + + - Add VariantDir() as the first step towards deprecating BuildDir(). + Add the keyword argument "variant_dir" as the replacement for + "build_dir". + + - Add warnings about the {Target,Source}Signatures() methods and + functions being deprecated. + + From Rob Managan: + + - Enhance TeX and LaTeX support to work with BuildDir(duplicate=0). + + - Re-run LaTeX when it issues a package warning that it must be re-run. + + From Leanid Nazdrynau: + + - Have the Copy() action factory preserve file modes and times + when copying individual files. + + From Jan Nijtmans: + + - If $JARCHDIR isn't set explicitly, use the .java_classdir attribute + that was set when the Java() Builder built the .class files. + + From Greg Noel: + + - Document the Dir(), File() and Entry() methods of Dir and File Nodes. + + - Add the parse_flags option when creating Environments + + From Gary Oberbrunner: + + - Make File(), Dir() and Entry() return a list of Nodes when passed + a list of names, instead of trying to make a string from the name + list and making a Node from that string. + + - Fix the ability to build an Alias in --interactive mode. + + - Fix the ability to hash the contents of actions for nested Python + functions on Python versions where the inability to pickle them + returns a TypeError (instead of the documented PicklingError). + + From Jonas Olsson: + + - Fix use of the Intel C compiler when the top compiler directory, + but not the compiler version, is specified. + + - Handle Intel C compiler network license files (port@system). + + From Jim Randall: + + - Fix how Python Value Nodes are printed in --debug=explain output. + + From Adam Simpkins: + + - Add a --interactive option that starts a session for building (or + cleaning) targets without re-reading the SConscript files every time. + + - Fix use of readline command-line editing in --interactive mode. + + - Have the --interactive mode "build" command with no arguments + build the specified Default() targets. + + - Fix the Chmod(), Delete(), Mkdir() and Touch() Action factories to + take a list (of Nodes or strings) as arguments. + + From Vaclav Smilauer: + + - Fix saving and restoring an Options value of 'all' on Python + versions where all() is a builtin function. + + From Daniel Svensson: + + - Code correction in SCons.Util.is_List(). + + From Ben Webb: + + - Support the SWIG %module statement with following modifiers in + parenthese (e.g., '%module(directors="1")'). + + + +RELEASE 0.97.0d20071212 - Wed, 12 Dec 2007 09:29:32 -0600 + + From Benoit Belley: + + - Fix occasional spurious rebuilds and inefficiency when using + --implicit-cache and Builders that produce multiple targets. + + - Allow SCons to not have to know about the builders of generated + files when BuildDir(duplicate=0) is used, potentially allowing some + SConscript files to be ignored for smaller builds. + + From David Cournapeau: + + - Add a CheckTypeSize() call to configure contexts. + + From Ken Deeter: + + - Make the "contents" of Alias Nodes a concatenation of the children's + content signatures (MD5 checksums), not a concatenation of the + children's contents, to avoid using large amounts of memory during + signature calculation. + + From Malte Helmert: + + - Fix a lot of typos in the man page and User's Guide. + + From Geoffrey Irving: + + - Speed up conversion of paths in .sconsign files to File or Dir Nodes. + + From Steven Knight: + + - Add an Options.UnknownOptions() method that returns any settings + (from the command line, or whatever dictionary was passed in) + that aren't known to the Options object. + + - Add a Glob() function. + + - When removing targets with the -c option, use the absolute path (to + avoid problems interpreting BuildDir() when the top-level directory + is the source directory). + + - Fix problems with Install() and InstallAs() when called through a + clone (of a clone, ...) of a cloned construction environment. + + - When executing a file containing Options() settings, add the file's + directory to sys.path (so modules can be imported from there) and + explicity set __name__ to the name of the file so the statement's + in the file can deduce the location if they need to. + + - Fix an O(n^2) performance problem when adding sources to a target + through calls to a multi Builder (including Aliases). + + - Redefine the $WINDOWSPROGMANIFESTSUFFIX and + $WINDOWSSHLIBMANIFESTSUFFIX variables so they pick up changes to + the underlying $SHLIBSUFFIX and $PROGSUFFIX variables. + + - Add a GetBuildFailures() function that can be called from functions + registered with the Python atexit module to print summary information + about any failures encountered while building. + + - Return a NodeList object, not a Python list, when a single_source + Builder like Object() is called with more than one file. + + - When searching for implicit dependency files in the directories + in a $*PATH list, don't create Dir Nodes for directories that + don't actually exist on-disk. + + - Add a Requires() function to allow the specification of order-only + prerequisites, which will be updated before specified "downstream" + targets but which don't actually cause the target to be rebuilt. + + - Restore the FS.{Dir,File,Entry}.rel_path() method. + + - Make the default behavior of {Source,Target}Signatures('timestamp') + be equivalent to 'timestamp-match', not 'timestamp-newer'. + + - Fix use of CacheDir with Decider('timestamp-newer') by updating + the modification time when copying files from the cache. + + - Fix random issues with parallel (-j) builds on Windows when Python + holds open file handles (especially for SCons temporary files, + or targets built by Python function actions) across process creation. + + From Maxim Kartashev: + + - Fix test scripts when run on Solaris. + + From Gary Oberbrunner: + + - Fix Glob() when a pattern is in an explicitly-named subdirectory. + + From Philipp Scholl: + + - Fix setting up targets if multiple Package builders are specified + at once. + + + +RELEASE 0.97.0d20070918 - Tue, 18 Sep 2007 10:51:27 -0500 + + From Steven Knight: + + - Fix the wix Tool module to handle null entries in $PATH variables. + + - Move the documentation of Install() and InstallAs() from the list + of functions to the list of Builders (now that they're implemented + as such). + + - Allow env.CacheDir() to be set per construction environment. The + global CacheDir() function now sets an overridable global default. + + - Add an env.Decider() method and a Node.Decider() method that allow + flexible specification of an arbitrary function to decide if a given + dependency has changed since the last time a target was built. + + - Don't execute Configure actions (while reading SConscript files) + when cleaning (-c) or getting help (-h or -H). + + - Add to each target an implicit dependency on the external command(s) + used to build the target, as found by searching env['ENV']['PATH'] + for the first argument on each executed command line. + + - Add support for a $IMPLICIT_COMMAND_DEPENDENCIES construction + variabe that can be used to disable the automatic implicit + dependency on executed commands. + + - Add an "ensure_suffix" keyword to Builder() definitions that, when + true, will add the configured suffix to the targets even if it looks + like they already have a different suffix. + + - Add a Progress() function that allows for calling a function or string + (or list of strings) to display progress while walking the DAG. + + - Allow ParseConfig(), MergeFlags() and ParseFlags() to handle output + from a *config command with quoted path names that contain spaces. + + - Make the Return() function stop processing the SConscript file and + return immediately. Add a "stop=" keyword argument that can be set + to False to preserve the old behavior. + + - Fix use of exitstatfunc on an Action. + + - Introduce all man page function examples with "Example:" or "Examples:". + + - When a file gets added to a directory, make sure the directory gets + re-scanned for the new implicit dependency. + + - Fix handling a file that's specified multiple times in a target + list so that it doesn't cause dependent Nodes to "disappear" from + the dependency graph walk. + + From Carsten Koch: + + - Avoid race conditions with same-named files and directory creation + when pushing copies of files to CacheDir(). + + From Tzvetan Mikov: + + - Handle $ in Java class names. + + From Gary Oberbrunner: + + - Add support for the Intel C compiler on Windows64. + + - On SGI IRIX, have $SHCXX use $CXX by default (like other platforms). + + From Sohail Somani: + + - When Cloning a construction environment, set any variables before + applying tools (so the tool module can access the configured settings) + and re-set them after (so they end up matching what the user set). + + From Matthias Troffaes: + + - Make sure extra auxiliary files generated by some LaTeX packages + and not ending in .aux also get deleted by scons -c. + + From Greg Ward: + + - Add a $JAVABOOTCLASSPATH variable for directories to be passed to the + javac -bootclasspath option. + + From Christoph Wiedemann: + + - Add implicit dependencies on the commands used to build a target. + + + + +RELEASE 0.97.0d20070809 - Fri, 10 Aug 2007 10:51:27 -0500 + + From Lars Albertsson: + + - Don't error if a #include line happens to match a directory + somewhere on a path (like $CPPPATH, $FORTRANPATH, etc.). + + From Mark Bertoglio: + + - Fix listing multiple projects in Visual Studio 7.[01] solution files, + including generating individual project GUIDs instead of re-using + the solution GUID. + + From Jean Brouwers: + + - Add /opt/SUNWspro/bin to the default execution PATH on Solaris. + + From Allan Erskine: + + - Only expect the Microsoft IDL compiler to emit *_p.c and *_data.c + files if the /proxy and /dlldata switches are used (respectively). + + From Steven Knight: + + - Have --debug=explain report if a target is being rebuilt because + AlwaysBuild() is specified (instead of "unknown reasons"). + + - Support {Get,Set}Option('help') to make it easier for SConscript + files to tell if a help option (-h, --help, etc.) has been specified. + + - Support {Get,Set}Option('random') so random-dependency interaction + with CacheDir() is controllable from SConscript files. + + - Add a new AddOption() function to support user-defined command- + line flags (like --prefix=, --force, etc.). + + - Push and retrieve built symlinks to/from a CacheDir() as actual + symlinks, not by copying the file contents. + + - Fix how the Action module handles stringifying the shared library + generator in the Tool/mingw.py module. + + - When generating a config.h file, print "#define HAVE_{FEATURE} 1" + instad of just "#define HAVE_{FEATURE}", for more compatibility + with Autoconf-style projects. + + - Fix expansion of $TARGET, $TARGETS, $SOURCE and $SOURCES keywords in + Visual C/C++ PDB file names. + + - Fix locating Visual C/C++ PDB files in build directories. + + - Support an env.AddMethod() method and an AddMethod() global function + for adding a new method, respectively, to a construction environment + or an arbitrary object (such as a class). + + - Fix the --debug=time option when the -j option is specified and all + files are up to date. + + - Add a $SWIGOUTDIR variable to allow setting the swig -outdir option, + and use it to identify files created by the swig -java option. + + - Add a $SWIGPATH variable that specifies the path to be searched + for included SWIG files, Also add related $SWIGINCPREFIX and + $SWIGINCSUFFIX variables that specify the prefix and suffix to + be be added to each $SWIGPATH directory when expanded on the SWIG + command line. + + - More efficient copying of construction environments (mostly borrowed + from copy.deepcopy() in the standard Python library). + + - When printing --tree=prune output, don't print [brackets] around + source files, only do so for built targets with children. + + - Fix interpretation of Builder source arguments when the Builder has + a src_suffix *and* a source_builder and the argument has no suffix. + + - Fix use of expansions like ${TARGET.dir} or ${SOURCE.dir} in the + following construction variables: $FORTRANMODDIR, $JARCHDIR, + $JARFLAGS, $LEXFLAGS, $SWIGFLAGS, $SWIGOUTDIR and $YACCFLAGS. + + - Fix dependencies on Java files generated by SWIG so they can be + detected and built in one pass. + + - Fix SWIG when used with a BuildDir(). + + From Leanid Nazdrynau: + + - When applying Tool modules after a construction environment has + already been created, don't overwrite existing $CFILESUFFIX and + $CXXFILESUFFIX value. + + - Support passing the Java() builder a list of explicit .java files + (not only a list of directories to be scanned for .java files). + + - Support passing .java files to the Jar() and JavaH() builders, which + then use the builder underlying the Java() builder to turn them into + .class files. (That is, the Jar()-Java() chain of builders become + multi-step, like the Program()-Object()-CFile() builders.) + + - Support passing SWIG .i files to the Java builders (Java(), + Jar(), JavaH()), to cause intermediate .java files to be created + automatically. + + - Add $JAVACLASSPATH and $JAVASOURCEPATH variables, that get added to + the javac "-classpath" and "-sourcepath" options. (Note that SCons + does *not* currently search these paths for implicit dependencies.) + + - Commonize initialization of Java-related builders. + + From Jan Nijtmans: + + - Find Java anonymous classes when the next token after the name is + an open parenthesis. + + From Gary Oberbrunner: + + - Fix a code example in the man page. + + From Tilo Prutz: + + - Add support for the file names that Java 1.5 (and 1.6) generates for + nested anonymous inner classes, which are different from Java 1.4. + + From Adam Simpkins: + + - Allow worker threads to terminate gracefully when all jobs are + finished. + + From Sohail Somani: + + - Add LaTeX scanner support for finding dependencies specified with + the \usepackage{} directive. + + + +RELEASE 0.97 - Thu, 17 May 2007 08:59:41 -0500 + + From Steven Knight: + + - Fix a bug that would make parallel builds stop in their tracks if + Nodes that depended on lists that contained some Nodes built together + caused the reference count to drop below 0 if the Nodes were visited + and commands finished in the wrong order. + + - Make sure the DirEntryScanner doesn't choke if it's handed something + that's not a directory (Node.FS.Dir) Node. + + + +RELEASE 0.96.96 - Thu, 12 Apr 2007 12:36:25 -0500 + + NOTE: This is (Yet) a(nother) pre-release of 0.97 for testing purposes. + + From Joe Bloggs: + + - Man page fix: remove cut-and-paste sentence in NoCache() description. + + From Dmitry Grigorenko and Gary Oberbrunner: + + - Use the Intel C++ compiler, not $CC, to link C++ source. + + From Helmut Grohne: + + - Fix the man page example of propagating a user's external environment. + + From Steven Knight: + + - Back out (most of) the Windows registry installer patch, which + seems to not work on some versions of Windows. + + - Don't treat Java ".class" attributes as defining an inner class. + + - Fix detecting an erroneous Java anonymous class when the first + non-skipped token after a "new" keyword is a closing brace. + + - Fix a regression when a CPPDEFINES list contains a tuple, the second + item of which (the option value) is a construction variable expansion + (e.g. $VALUE) and the value of the variable isn't a string. + + - Improve the error message if an IOError (like trying to read a + directory as a file) occurs while deciding if a node is up-to-date. + + - Fix "maximum recursion" / "unhashable type" errors in $CPPPATH + PathList expansion if a subsidiary expansion yields a stringable, + non-Node object. + + - Generate API documentation from the docstrings (using epydoc). + + - Fix use of --debug=presub with Actions for out-of-the-box Builders. + + - Fix handling nested lists within $CPPPATH, $LIBPATH, etc. + + - Fix a "builders_used" AttributeError that real-world Qt initialization + triggered in the refactored suffix handling for Builders. + + - Make the reported --debug=time timings meaningful when used with -j. + Better documentation of what the times mean. + + - User Guide updates: --random, AlwaysBuild(), --tree=, + --debug=findlibs, --debug=presub, --debug=stacktrace, + --taskmastertrace. + + - Document (in both man page and User's Guide) that --implicit-cache + ignores changes in $CPPPATH, $LIBPATH, etc. + + From Jean-Baptiste Lab: + + - Remove hard-coded dependency on Python 2.2 from Debian packaging files. + + From Jeff Mahovsky: + + - Handle spaces in the build target name in Visual Studio project files. + + From Rob Managan: + + - Re-run LaTeX after BibTeX has been re-run in response to a changed + .bib file. + + From Joel B. Mohler: + + - Make additional TeX auxiliary files (.toc, .idx and .bbl files) + Precious so their removal doesn't affect whether the necessary + sections are included in output PDF or PostScript files. + + From Gary Oberbrunner: + + - Fix the ability to import modules in the site_scons directory from + a subdirectory. + + From Adam Simpkins: + + - Make sure parallel (-j) builds all targets even if they show up + multiple times in the child list (as a source and a dependency). + + From Matthias Troffaes: + + - Don't re-run TeX if the triggering strings (\makeindex, \bibliography + \tableofcontents) are commented out. + + From Richard Viney: + + - Fix use of custom include and lib paths with Visual Studio 8. + + - Select the default .NET Framework SDK Dir based on the version of + Visual Studio being used. + + + +RELEASE 0.96.95 - Mon, 12 Feb 2007 20:25:16 -0600 + + From Anatoly Techtonik: + + - Add the scons.org URL and a package description to the setup.py + arguments. + + - Have the Windows installer add a registry entry for scons.bat in the + "App Paths" key, so scons.bat can be executed without adding the + directory to the %PATH%. (Python itself works this way.) + + From Anonymous: + + - Fix looking for default paths in Visual Studio 8.0 (and later). + + - Add -lm to the list of default D libraries for linking. + + From Matt Doar: + + - Provide a more complete write-your-own-Scanner example in the man page. + + From Ralf W. Grosse-Kunstleve: + + - Contributed upstream Python change to our copied subprocess.py module + for more efficient standard input processing. + + From Steven Knight: + + - Fix the Node.FS.Base.rel_path() method when the two nodes are on + different drive letters. (This caused an infinite loop when + trying to write .sconsign files.) + + - Fully support Scanners that use a dictionary to map file suffixes + to other scanners. + + - Support delayed evaluation of the $SPAWN variable to allow selection + of a function via ${} string expansions. + + - Add --srcdir as a synonym for -Y/--repository. + + - Document limitations of #include "file.h" with Repository(). + + - Fix use of a toolpath under the source directory of a BuildDir(). + + - Fix env.Install() with a file name portion that begins with '#'. + + - Fix ParseConfig()'s handling of multiple options in a string that's + replaced a *FLAGS construction variable. + + - Have the C++ tools initialize common C compilation variables ($CCFLAGS, + $SHCCFLAGS and $_CCCOMCOM) even if the 'cc' Tool isn't loaded. + + From Leanid Nazdrynau: + + - Fix detection of Java anonymous classes if a newline precedes the + opening brace. + + From Gary Oberbrunner: + + - Document use of ${} to execute arbitrary Python code. + + - Add support for: + 1) automatically adding a site_scons subdirectory (in the top-level + SConstruct directory) to sys.path (PYTHONPATH); + 2) automatically importing site_scons/site_init.py; + 3) automatically adding site_scons/site_tools to the toolpath. + + From John Pye: + + - Change ParseConfig() to preserve white space in arguments passed in + as a list. + + From a smith: + + - Fix adding explicitly-named Java inner class files (and any + other file names that may contain a '$') to Jar files. + + From David Vitek: + + - Add a NoCache() function to mark targets as unsuitable for propagating + to (or retrieving from) a CacheDir(). + + From Ben Webb: + + - If the swig -noproxy option is used, it won't generate a .py file, + so don't emit it as a target that we expect to be built. + + + +RELEASE 0.96.94 - Sun, 07 Jan 2007 18:36:20 -0600 + + NOTE: This is a pre-release of 0.97 for testing purposes. + + From Anonymous: + + - Allow arbitrary white space after a SWIG %module declaration. + + From Paul: + + - When compiling resources under MinGW, make sure there's a space + between the --include-dir option and its argument. + + From Jay Kint: + + - Alleviate long command line issues on Windows by executing command + lines directly via os.spawnv() if the command line doesn't need + shell interpretation (has no pipes, redirection, etc.). + + From Walter Franzini: + + - Exclude additional Debian packaging files from the copyright check. + + From Fawad Halim: + + - Handle the conflict between the impending Python 2.6 'as' keyword + and our Tool/as.py module name. + + From Steven Knight: + + - Speed up the Node.FS.Dir.rel_path() method used to generate path names + that get put into the .sconsign* file(s). + + - Optimize Node.FS.Base.get_suffix() by computing the suffix once, up + front, when we set the Node's name. (Duh...) + + - Reduce the Memoizer's responsibilities to simply counting hits and + misses when the --debug=memoizer option is used, not to actually + handling the key calculation and memoization itself. This speeds + up some configurations significantly, and should cause no functional + differences. + + - Add a new scons-time script with subcommands for generating + consistent timing output from SCons configurations, extracting + various information from those timings, and displaying them in + different formats. + + - Reduce some unnecessary stat() calls from on-disk entry type checks. + + - Fix SideEffect() when used with -j, which was badly broken in 0.96.93. + + - Propagate TypeError exceptions when evaluating construction variable + expansions up the stack, so users can see what's going on. + + - When disambiguating a Node.FS.Entry into a Dir or File, don't look + in the on-disk source directory until we've confirmed there's no + on-disk entry locally and there *is* one in the srcdir. This avoids + creating a phantom Node that can interfere with dependencies on + directory contents. + + - Add an AllowSubstExceptions() function that gives the SConscript + files control over what exceptions cause a string to expand to '' + vs. terminating processing with an error. + + - Allow the f90.py and f95.py Tool modules to compile earlier source + source files of earlier Fortran version. + + - Fix storing signatures of files retrieved from CacheDir() so they're + correctly identified as up-to-date next invocation. + + - Make sure lists of computed source suffixes cached by Builder objects + don't persist across changes to the list of source Builders (so the + addition of suffixes like .ui by the qt.py Tool module take effect). + + - Enhance the bootstrap.py script to allow it to be used to execute + SCons more easily from a checked-out source tree. + + From Ben Leslie: + + - Fix post-Memoizer value caching misspellings in Node.FS._doLookup(). + + From Rob Managan, Dmitry Mikhin and Joel B. Mohler: + + - Handle TeX/LaTeX files in subdirectories by changing directory + before invoking TeX/LaTeX. + + - Scan LaTeX files for \bibliography lines. + + - Support multiple file names in a "\bibliography{file1,file2}" string. + + - Handle TeX warnings about undefined citations. + + - Support re-running LaTeX if necessary due to a Table of Contents. + + From Dmitry Mikhin: + + - Return LaTeX if "Rerun to get citations correct" shows up on the next + line after the "Warning:" string. + + From Gary Oberbrunner: + + - Add #include lines to fix portability issues in two tests. + + - Eliminate some unnecessary os.path.normpath() calls. + + - Add a $CFLAGS variable for C-specific options, leaving $CCFLAGS + for options common to C and C++. + + From Tom Parker: + + - Have the error message print the missing file that Qt can't find. + + From John Pye: + + - Fix env.MergeFlags() appending to construction variable value of None. + + From Steve Robbins: + + - Fix the "sconsign" script when the .sconsign.dblite file is explicitly + specified on the command line (and not intuited from the old way of + calling it with just ".sconsign"). + + From Jose Pablo Ezequiel "Pupeno" Fernandez Silva: + + - Give the 'lex' tool knowledge of the additional target files produced + by the flex "--header-file=" and "--tables-file=" options. + + - Give the 'yacc' tool knowledge of the additional target files produced + by the bison "-g", "--defines=" and "--graph=" options. + + - Generate intermediate files with Objective C file suffixes (.m) when + the lex and yacc source files have appropriate suffixes (.lm and .ym). + + From Sohail Somain: + + - Have the mslink.py Tool only look for a 'link' executable on Windows + systems. + + From Vaclav Smilauer: + + - Add support for a "srcdir" keyword argument when calling a Builder, + which will add a srcdir prefix to all non-relative string sources. + + From Jonathan Ultis: + + - Allow Options converters to take the construction environment as + an optional argument. + + + +RELEASE 0.96.93 - Mon, 06 Nov 2006 00:44:11 -0600 + + NOTE: This is a pre-release of 0.97 for testing purposes. + + From Anonymous: + + - Allow Python Value Nodes to be Builder targets. + + From Matthias: + + - Only filter Visual Studio common filename prefixes on complete + directory names. + + From Chad Austin: + + - Fix the build of the SCons documentation on systems that don't + have "python" in the $PATH. + + From Ken Boortz: + + - Enhance ParseConfig() to recognize options that begin with '+'. + + From John Calcote, Elliot Murphy: + + - Document ways to override the CCPDBFLAGS variable to use the + Microsoft linker's /Zi option instead of the default /Z7. + + From Christopher Drexler: + + - Make SCons aware bibtex must be called if any \include files + cause creation of a bibliography. + + - Make SCons aware that "\bilbiography" in TeX source files means + that related .bbl and .blg bibliography files will be created. + (NOTE: This still needs to search for the string in \include files.) + + From David Gruener: + + - Fix inconsistent handling of Action strfunction arguments. + + - Preserve white space in display Action strfunction strings. + + From James Y. Knight and Gerard Patel: + + - Support creation of shared object files from assembly language. + + From Steven Knight: + + - Speed up the Taskmaster significantly by avoiding unnecessary + re-scans of Nodes to find out if there's work to be done, having it + track the currently-executed top-level target directly and not + through its presence on the target list, and eliminating some other + minor list(s), method(s) and manipulation. + + - Fix the expansion of $TARGET and $SOURCE in the expansion of + $INSTALLSTR displayed for non-environment calls to InstallAs(). + + - Fix the ability to have an Alias() call refer to a directory + name that's not identified as a directory until later. + + - Enhance runtest.py with an option to use QMTest as the harness. + This will become the default behavior as we add more functionality + to the QMTest side. + + - Let linking on mingw use the default function that chooses $CC (gcc) + or $CXX (g++) depending on whether there are any C++ source files. + + - Work around a bug in early versions of the Python 2.4 profile module + that caused the --profile= option to fail. + + - Only call Options validators and converters once when initializing a + construction environment. + + - Fix the ability of env.Append() and env.Prepend(), in all known Python + versions, to handle different input value types when the construction + variable being updated is a dictionary. + + - Add a --cache-debug option for information about what files it's + looking for in a CacheDir(). + + - Document the difference in construction variable expansion between + {Action,Builder}() and env.{Action,Builder}(). + + - Change the name of env.Copy() to env.Clone(), keeping the old name + around for backwards compatibility (with the intention of eventually + phasing it out to avoid confusion with the Copy() Action factory). + + From Arve Knudsen: + + - Support cleaning and scanning SWIG-generated files. + + From Carsten Koch: + + - Allow selection of Visual Studio version by setting $MSVS_VERSION + after construction environment initialization. + + From Jean-Baptiste Lab: + + - Try using zipimport if we can't import Tool or Platform modules + using the normal "imp" module. This allows SCons to be packaged + using py2exe's all-in-one-zip-file approach. + + From Ben Liblit: + + - Do not re-scan files if the scanner returns no implicit dependencies. + + From Sanjoy Mahajan: + + - Change use of $SOURCES to $SOURCE in all TeX-related Tool modules. + + From Joel B. Mohler: + + - Make SCons aware that "\makeindex" in TeX source files means that + related .ilg, .ind and .idx index files will be created. + (NOTE: This still needs to search for the string in \include files.) + + - Prevent scanning the TeX .aux file for additional files from + trying to remove it twice when the -c option is used. + + From Leanid Nazdrynau: + + - Give the MSVC RES (resource) Builder a src_builder list and a .rc + src_suffix so other builders can generate .rc files. + + From Matthew A. Nicholson: + + - Enhance Install() and InstallAs() to handle directory trees as sources. + + From Jan Nijtmans: + + - Don't use the -fPIC flag when using gcc on Windows (e.g. MinGW). + + From Greg Noel: + + - Add an env.ParseFlags() method that provides separate logic for + parsing GNU tool chain flags into a dictionary. + + - Add an env.MergeFlags() method to apply an arbitrary dictionary + of flags to a construction environment's variables. + + From Gary Oberbrunner: + + - Fix parsing tripartite Intel C compiler version numbers on Linux. + + - Extend the ParseConfig() function to recognize -arch and + -isysroot options. + + - Have the error message list the known suffixes when a Builder call + can't build a source file with an unknown suffix. + + From Karol Pietrzak: + + - Avoid recursive calls to main() in the program snippet used by the + SConf subsystem to test linking against libraries. This changes the + default behavior of CheckLib() and CheckLibWithHeader() to print + "Checking for C library foo..." instead of "Checking for main() + in C library foo...". + + From John Pye: + + - Throw an exception if a command called by ParseConfig() or + ParseFlags() returns an error. + + From Stefan Seefeld: + + - Initial infrastructure for running SCons tests under QMTest. + + From Sohail Somani: + + - Fix tests that fail due to gcc warnings. + + From Dobes Vandermeer: + + - In stack traces, print the full paths of SConscript files. + + From Atul Varma: + + - Fix detection of Visual C++ Express Edition. + + From Dobes Vandermeer: + + - Let the src_dir option to the SConscript() function affect all the + the source file paths, instead of treating all source files paths + as relative to the SConscript directory itself. + + From Nicolas Vigier: + + - Fix finding Fortran modules in build directories. + + - Fix use of BuildDir() when the source file in the source directory + is a symlink with a relative path. + + From Edward Wang: + + - Fix the Memoizer when the SCons Python modules are executed from + .pyo files at different locations from where they were compiled. + + From Johan Zander: + + - Fix missing os.path.join() when constructing the $FRAMEWORKSDKDIR/bin. + + + +RELEASE 0.96.92 - Mon, 10 Apr 2006 21:08:22 -0400 + + NOTE: This was a pre-release of 0.97 for testing purposes. + + From Anonymous: + + - Fix the intelc.py Tool module to not throw an exception if the + only installed version is something other than ia32. + + - Set $CCVERSION when using gcc. + + From Matthias: + + - Support generating project and solution files for Microsoft + Visual Studio version 8. + + - Support generating more than one project file for a Microsoft + Visual Studio solution file. + + - Add support for a support "runfile" parameter to Microsoft + Visual Studio project file creation. + + - Put the project GUID, not the solution GUID, in the right spot + in the solution file. + + From Erling Andersen: + + - Fix interpretation of Node.FS objects wrapped in Proxy instances, + allowing expansion of things like ${File(TARGET)} in command lines. + + From Stanislav Baranov: + + - Add a separate MSVSSolution() Builder, with support for the + following new construction variables: $MSVSBUILDCOM, $MSVSCLEANCOM, + $MSVSENCODING, $MSVSREBUILDCOM, $MSVSSCONS, $MSVSSCONSCOM, + $MSVSSCONSFLAGS, $MSVSSCONSCRIPT and $MSVSSOLUTIONCOM. + + From Ralph W. Grosse-Kunstleve and Patrick Mezard: + + - Remove unneceesary (and incorrect) SCons.Util strings on some function + calls in SCons.Util. + + From Bob Halley: + + - Fix C/C++ compiler selection on AIX to not always use the external $CC + environment variable. + + From August Hörandl: + + - Add a scanner for \include and \import files, with support for + searching a directory list in $TEXINPUTS (imported from the external + environment). + + - Support $MAKEINDEX, $MAKEINDEXCOM, $MAKEINDEXCOMSTR and + $MAKEINDEXFLAGS for generating indices from .idx files. + + From Steven Johnson: + + - Add a NoClean() Environment method and function to override removal + of targets during a -c clean, including documentation and tests. + + From Steven Knight: + + - Check for whether files exist on disk by listing the directory + contents, not calling os.path.exists() file by file. This is + somewhat more efficient in general, and may be significantly + more efficient on Windows. + + - Minor speedups in the internal is_Dict(), is_List() and is_String() + functions. + + - Fix a signature refactoring bug that caused Qt header files to + get re-generated every time. + + - Don't fail when writing signatures if the .sconsign.dblite file is + owned by a different user (e.g. root) from a previous run. + + - When deleting variables from stacked OverrideEnvironments, don't + throw a KeyError if we were able to delte the variable from any + Environment in the stack. + + - Get rid of the last indentation tabs in the SCons source files and + add -tt to the Python invocations in the packaging build and the + tests so they don't creep back in. + + - In Visual Studio project files, put quotes around the -C directory + so everything works even if the path has spaces in it. + + - The Intel Fortran compiler uses -object:$TARGET, not "-o $TARGET", + when building object files on Windows. Have the the ifort Tool + modify the default command lines appropriately. + + - Document the --debug=explain option in the man page. (How did we + miss this?) + + - Add a $LATEXRETRIES variable to allow configuration of the number of + times LaTex can be re-called to try to resolve undefined references. + + - Change the order of the arguments to Configure.Checklib() to match + the documentation. + + - Handle signature calculation properly when the Python function used + for a FunctionAction is an object method. + + - On Windows, assume that absolute path names without a drive letter + refer to the drive on which the SConstruct file lives. + + - Add /usr/ccs/bin to the end of the the default external execution + PATH on Solaris. + + - Add $PKGCHK and $PKGINFO variables for use on Solaris when searching + for the SunPRO C++ compiler. Make the default value for $PKGCHK + be /usr/sbin/pgkchk (since /usr/sbin isn't usually on the external + execution $PATH). + + - Fix a man page example of overriding variables when calling + SharedLibrary() to also set the $LIBSUFFIXES variable. + + - Add a --taskmastertrace=FILE option to give some insight on how + the taskmaster decides what Node to build next. + + - Changed the names of the old $WIN32DEFPREFIX, $WIN32DEFSUFFIX, + $WIN32DLLPREFIX and $WIN32IMPLIBPREFIX construction variables to + new $WINDOWSDEFPREFIX, $WINDOWSDEFSUFFIX, $WINDOWSDLLPREFIX and + $WINDOWSIMPLIBPREFIX construction variables. The old names are now + deprecated, but preserved for backwards compatibility. + + - Fix (?) a runtest.py hang on Windows when the --xml option is used. + + - Change the message when an error occurs trying to interact with the + file system to report the target(s) in square brackets (as before) and + the actual file or directory that encountered the error afterwards. + + From Chen Lee: + + - Add x64 support for Microsoft Visual Studio 8. + + From Baptiste Lepilleur: + + - Support the --debug=memory option on Windows when the Python version + has the win32process and win32api modules. + + - Add support for Visual Studio 2005 Pro. + + - Fix portability issues in various tests: test/Case.py, + Test/Java/{JAR,JARCHDIR,JARFLAGS,JAVAC,JAVACFLAGS,JAVAH,RMIC}.py, + test/MSVS/vs-{6.0,7.0,7.1,8.0}-exec.py, + test/Repository/{Java,JavaH,RMIC}.py, + test/QT/{generated-ui,installed,up-to-date,warnings}.py, + test/ZIP/ZIP.py. + + - Ignore pkgchk errors on Solaris when searching for the C++ compiler. + + - Speed up the SCons/EnvironmentTests.py unit tests. + + - Add a --verbose= option to runtest.py to print executed commands + and their output at various levels. + + From Christian Maaser: + + - Add support for Visual Studio Express Editions. + + - Add support for Visual Studio 8 *.manifest files, includng + new $WINDOWS_INSERT_MANIFEST, $WINDOWSPROGMANIFESTSUFFIX, + $WINDOWSPROGMANIFESTPREFIX, $WINDOWSPROGMANIFESTSUFFIX, + $WINDOWSSHLIBMANIFESTPREFIX and $WINDOWSSHLIBMANIFESTSUFFIX + construction variables. + + From Adam MacBeth: + + - Fix detection of additional Java inner classes following use of a + "new" keyword inside an inner class. + + From Sanjoy Mahajan: + + - Correct TeX-related command lines to just $SOURCE, not $SOURCES + + From Patrick Mezard: + + - Execute build commands for a command-line target if any of the + files built along with the target is out of date or non-existent, + not just if the command-line target itself is out of date. + + - Fix the -n option when used with -c to print all of the targets + that will be removed for a multi-target Builder call. + + - If there's no file in the source directory, make sure there isn't + one in the build directory, too, to avoid dangling files left + over from previous runs when a source file is removed. + + - Allow AppendUnique() and PrependUnique() to append strings (and + other atomic objects) to lists. + + From Joel B. Mohler: + + - Extend latex.py, pdflatex.py, pdftex.py and tex.py so that building + from both TeX and LaTeX files uses the same logic to call $BIBTEX + when it's necessary, to call $MAKEINDEX when it's necessary, and to + call $TEX or $LATEX multiple times to handle undefined references. + + - Add an emitter to the various TeX builders so that the generated + .aux and .log files also get deleted by the -c option. + + From Leanid Nazdrynau: + + - Fix the Qt UIC scanner to work with generated .ui files (by using + the FindFile() function instead of checking by-hand for the file). + + From Jan Nieuwenhuizen: + + - Fix a problem with interpreting quoted argument lists on command lines. + + From Greg Noel: + + - Add /sw/bin to the default execution PATH on Mac OS X. + + From Kian Win Ong: + + - When building a .jar file and there is a $JARCHDIR, put the -C + in front of each .class file on the command line. + + - Recognize the Java 1.5 enum keyword. + + From Asfand Yar Qazi: + + - Add /opt/bin to the default execution PATH on all POSIX platforms + (between /usr/local/bin and /bin). + + From Jon Rafkind: + + - Fix the use of Configure() contexts from nested subsidiary + SConscript files. + + From Christoph Schulz: + + - Add support for $CONFIGUREDIR and $CONFIGURELOG variables to control + the directory and logs for configuration tests. + + - Add support for a $INSTALLSTR variable. + + - Add support for $RANLIBCOM and $RANLIBCOMSTR variables (which fixes + a bug when setting $ARCOMSTR). + + From Amir Szekely: + + - Add use of $CPPDEFINES to $RCCOM (resource file compilation) on MinGW. + + From Erick Tryzelaar: + + - Fix the error message when trying to report that a given option is + not gettable/settable from an SConscript file. + + From Dobes Vandermeer: + + - Add support for SCC and other settings in Microsoft Visual + Studio project and solution files: $MSVS_PROJECT_BASE_PATH, + $MSVS_PROJECT_GUID, $MSVS_SCC_AUX_PATH, $MSVS_SCC_LOCAL_PATH, + $MSVS_SCC_PROJECT_NAME, $MSVS_SCC_PROVIDER, + + - Add support for using a $SCONS_HOME variable (imported from the + external environment, or settable internally) to put a shortened + SCons execution line in the Visual Studio project file. + + From David J. Van Maren: + + - Only filter common prefixes from source files names in Visual Studio + project files if the prefix is a complete (sub)directory name. + + From Thad Ward: + + - If $MSVSVERSIONS is already set, don't overwrite it with + information from the registry. + + + +RELEASE 0.96.91 - Thu, 08 Sep 2005 07:18:23 -0400 + + NOTE: This was a pre-release of 0.97 for testing purposes. + + From Chad Austin: + + - Have the environment store the toolpath and re-use it to find Tools + modules during later Copy() or Tool() calls (unless overridden). + + - Normalize the directory path names in SConsignFile() database + files so the same signature file can interoperate on Windows and + non-Windows systems. + + - Make --debug=stacktrace print a stacktrace when a UserError is thrown. + + - Remove an old, erroneous cut-and-paste comment in Scanner/Dir.py. + + From Stanislav Baranov: + + - Make it possible to support with custom Alias (sub-)classes. + + - Allow Builders to take empty source lists when called. + + - Allow access to both TARGET and SOURCE in $*PATH expansions. + + - Allow SConscript files to modify BUILD_TARGETS. + + From Timothee Besset: + + - Add support for Objective C/C++ .m and .mm file suffixes (for + Mac OS X). + + From Charles Crain + + - Fix the PharLap linkloc.py module to use target+source arguments + when calling env.subst(). + + From Bjorn Eriksson: + + - Fix an incorrect Command() keyword argument in the man page. + + - Add a $TEMPFILEPREFIX variable to control the prefix or flag used + to pass a long-command-line-execution tempfile to a command. + + From Steven Knight: + + - Enhanced the SCons setup.py script to install man pages on + UNIX/Linux systems. + + - Add support for an Options.FormatOptionHelpText() method that can + be overridden to customize the format of Options help text. + + - Add a global name for the Entry class (which had already been + documented). + + - Fix re-scanning of generated source files for implicit dependencies + when the -j option is used. + + - Fix a dependency problem that caused $LIBS scans to not be added + to all of the targets in a multiple-target builder call, which + could cause out-of-order builds when the -j option is used. + + - Store the paths of source files and dependencies in the .sconsign* + file(s) relative to the target's directory, not relative to the + top-level SConstruct directory. This starts to make it possible to + subdivide the dependency tree arbitrarily by putting an SConstruct + file in every directory and using content signatures. + + - Add support for $YACCHFILESUFFIX and $YACCHXXFILESUFFIX variables + that accomodate parser generators that write header files to a + different suffix than the hard-coded .hpp when the -d option is used. + + - The default behavior is now to store signature information in a + single .sconsign.dblite file in the top-level SConstruct directory. + The old behavior of a separate .sconsign file in each directory can + be specified by calling SConsignFile(None). + + - Remove line number byte codes within the signature calculation + of Python function actions, so that changing the location of an + otherwise unmodified Python function doesn't cause rebuilds. + + - Fix AddPreAction() and AddPostAction() when an action has more than + one target file: attach the actions to the Executor, not the Node. + + - Allow the source directory of a BuildDir / build_dir to be outside + of the top-level SConstruct directory tree. + + - Add a --debug=nomemoizer option that disables the Memoizer for clearer + looks at the counts and profiles of the underlying function calls, + not the Memoizer wrappers. + + - Print various --debug= stats even if we exit early (e.g. using -h). + + - Really only use the cached content signature value if the file + is older than --max-drift, not just if --max-drift is set. + + - Remove support for conversion from old (pre 0.96) .sconsign formats. + + - Add support for a --diskcheck option to enable or disable various + on-disk checks: that File and Dir nodes match on-disk entries; + whether an RCS file exists for a missing source file; whether an + SCCS file exists for a missing source file. + + - Add a --raw argument to the sconsign script, so it can print a + raw representation of each entry's NodeInfo dictionary. + + - Add the 'f90' and 'f95' tools to the list of Fortran compilers + searched for by default. + + - Add the +Z option by default when compiling shared objects on + HP-UX. + + From Chen Lee: + + - Handle Visual Studio project and solution files in Unicode. + + From Sanjoy Mahajan: + + - Fix a bad use of Copy() in an example in the man page, and a + bad regular expression example in the man page and User's Guide. + + From Shannon Mann: + + - Have the Visual Studio project file(s) echo "Starting SCons" before + executing SCons, mainly to work around a quote-stripping bug in + (some versions of?) the Windows cmd command executor. + + From Georg Mischler: + + - Remove the space after the -o option when invoking the Borland + BCC compiler; some versions apparently require that the file name + argument be concatenated with the option. + + From Leanid Nazdrynau: + + - Fix the Java parser's handling of backslashes in strings. + + From Greg Noel: + + - Add construction variables to support frameworks on Mac OS X: + $FRAMEWORKS, $FRAMEWORKPREFIX, $FRAMEWORKPATH, $FRAMEWORKPATHPREFIX. + + - Re-order link lines so the -o option always comes right after the + command name. + + From Gary Oberbrunner: + + - Add support for Intel C++ beta 9.0 (both 32 and 64 bit versions). + + - Document the new $FRAMEWORK* variables for Mac OS X. + + From Karol Pietrzak: + + - Add $RPATH (-R) support to the Sun linker Tool (sunlink). + + - Add a description of env.subst() to the man page. + + From Chris Prince: + + - Look in the right directory, not always the local directory, for a + same-named file or directory conflict on disk. + + - On Windows, preserve the external environment's %SYSTEMDRIVE% + variable, too. + + From Craig Scott: + + - Have the Fortran module emitter look for Fortan modules to be created + relative to $FORTRANMODDIR, not the top-level directory. + + - When saving Options to a file, run default values through the + converter before comparing them with the set values. This correctly + suppresses Boolean Option values from getting written to the saved + file when they're one of the many synonyms for a default True or + False value. + + - Fix the Fortran Scanner's ability to handle a module being used + in the same file in which it is defined. + + From Steve-o: + + - Add the -KPIC option by default when compiling shared objects on + Solaris. + + - Change the default suffix for Solaris objects to .o, to conform to + Sun WorkShop's expectations. Change the profix to so_ so they can + still be differentiated from static objects in the same directory. + + From Amir Szekely: + + - When calling the resource compiler on MinGW, add --include-dir and + the source directory so it finds the source file. + + - Update EnsureSConsVersion() to support revision numbers. + + From Greg Ward: + + - Fix a misplaced line in the man page. + + + +RELEASE 0.96.90 - Tue, 15 Feb 2005 21:21:12 +0000 + + NOTE: This was a pre-release of 0.97 for testing purposes. + + From Anonymous: + + - Fix Java parsing to avoid erroneously identifying a new array + of class instances as an anonymous inner class. + + - Fix a typo in the man page description of PathIsDirCreate. + + From Chad Austin: + + - Allow Help() to be called multiple times, appending to the help + text each call. + + - Allow Tools found on a toolpath to import Python modules from + their local directory. + + From Steve Christensen: + + - Handle exceptions from Python functions as build actions. + + - Add a set of canned PathOption validators: PathExists (the default), + PathIsFile, PathIsDir and PathIsDirCreate. + + From Matthew Doar: + + - Add support for .lex and .yacc file suffixes for Lex and Yacc files. + + From Eric Frias: + + - Huge performance improvement: wrap the tuples representing an + include path in an object, so that the time it takes to hash the + path doesn't grow porportionally to the length of the path. + + From Gottfried Ganssauge: + + - Fix SCons on SuSE/AMD-64 Linux by having the wrapper script also + check for the build engine in the parent directory of the Python + library directory (/usr/lib64 instead of /usr/lib). + + From Stephen Kennedy: + + - Speed up writing the .sconsign file at the end of a run by only + calling sync() once at the end, not after every entry. + + From Steven Knight: + + - When compiling with Microsoft Visual Studio, don't include the ATL and + MFC directories in the default INCLUDE and LIB environment variables. + + - Remove the following deprecated features: the ParseConfig() + global function (deprecated in 0.93); the misspelled "validater" + keyword to the Options.Add() method (deprecated in 0.91); the + SetBuildSignatureType(), SetContentSignatureType(), SetJobs() and + GetJobs() global functions (deprecated in 0.14). + + - Fix problems with corrupting the .sconsign.dblite file when + interrupting builds by writing to a temporary file and renaming, + not writing the file directly. + + - Fix a 0.96 regression where when running with -k, targets built from + walking dependencies later on the command line would not realize + that a dependency had failed an earlier build attempt, and would + try to rebuild the dependent targets. + + - Change the final messages when using -k and errors occur from + "{building,cleaning} terminated because of errors" to "done + {building,cleaning} targets (errors occurred during {build,clean})." + + - Allow Configure.CheckFunc() to take an optional header argument + (already supported by Conftest.py) to specify text at the top of + the compiled test file. + + - Fix the --debug=explain output when a Python function action changed + so it prints a meaningful string, not the binary representation of + the function contents. + + - Allow a ListOption's default value(s) to be a Python list of specified + values, not just a string containing a comma-separated list of names. + + - Add a ParseDepends() function that will parse up a list of explicit + dependencies from a "make depend" style file. + + - Support the ability to change directory when executing an Action + through "chdir" keyword arguments to Action and Builder creation + and calls. + + - Fix handling of Action ojects (and other callables that don't match + our calling arguments) in construction variable expansions. + + - On Win32, install scons.bat in the Python directory when installing + from setup.py. (The bdist_wininst installer was already doing this.) + + - Fix env.SConscript() when called with a list of SConscipt files. + (The SConscript() global function already worked properly.) + + - Add a missing newline to the end of the --debug=explain "unknown + reasons" message. + + - Enhance ParseConfig() to work properly for spaces in between the -I, + -L and -l options and their arguments. + + - Packaging build fix: Rebuild the files that are use to report the + --version of SCons whenever the development version number changes. + + - Fix the ability to specify a target_factory of Dir() to a Builder, + which the default create-a-directory Builder was interfering with. + + - Mark a directory as built if it's created as part of the preparation + for another target, to avoid trying to build it again when it comes + up in the target list. + + - Allow a function with the right calling signature to be put directly + in an Environment's BUILDERS dictionary, making for easier creation + and use of wrappers (pseudo-Builders) that call other Builders. + + - On Python 2.x, wrap lists of Nodes returned by Builders in a UserList + object that adds a method that makes str() object return a string + with all of the Nodes expanded to their path names. (Builders under + Python 1.5.2 still return lists to avoid TypeErrors when trying + to extend() list, so Python 1.5.2 doesn't get pretty-printing of Node + lists, but everything should still function.) + + - Allow Aliases to have actions that will be executed whenever + any of the expanded Alias targets are out of date. + + - Fix expansion of env.Command() overrides within target and + source file names. + + - Support easier customization of what's displayed by various default + actions by adding lots of new construction variables: $ARCOMSTR, + $ASCOMSTR, $ASPPCOMSTR, $BIBTEXCOMSTR, $BITKEEPERCOMSTR, $CCCOMSTR, + $CVSCOMSTR, $CXXCOMSTR, $DCOMSTR, $DVIPDFCOMSTR, $F77COMSTR, + $F90COMSTR, $F95COMSTR, $FORTRANCOMSTR, $GSCOMSTR, $JARCOMSTR, + $JAVACCOMSTR, $JAVAHCOMSTR, $LATEXCOMSTR, $LEXCOMSTR, $LINKCOMSTR, + $M4COMSTR, $MIDLCOMSTR, $P4COMSTR, $PCHCOMSTR, $PDFLATEXCOMSTR, + $PDFTEXCOMSTR, $PSCOMSTR, $QT_MOCFROMCXXCOMSTR, $QT_MOCFROMHCOMSTR, + $QT_UICCOMSTR, $RCCOMSTR, $REGSVRCOMSTR, $RCS_COCOMSTR, $RMICCOMSTR, + $SCCSCOMSTR, $SHCCCOMSTR, $SHCXXCOMSTR, $SHF77COMSTR, $SHF90COMSTR, + $SHF95COMSTR, $SHFORTRANCOMSTR, $SHLINKCOMSTR, $SWIGCOMSTR, + $TARCOMSTR, $TEXCOMSTR, $YACCCOMSTR and $ZIPCOMSTR. + + - Add an optional "map" keyword argument to ListOption() that takes a + dictionary to map user-specified values to legal values from the list + (like EnumOption() already doee). + + - Add specific exceptions to try:-except: blocks without any listed, + so that they won't catch and mask keyboard interrupts. + + - Make --debug={tree,dtree,stree} print something even when there's + a build failure. + + - Fix how Scanners sort the found dependencies so that it doesn't + matter whether the dependency file is in a Repository or not. + This may cause recompilations upon upgrade to this version. + + - Make AlwaysBuild() work with Alias and Python value Nodes (making + it much simpler to support aliases like "clean" that just invoke + an arbitrary action). + + - Have env.ParseConfig() use AppendUnique() by default to suppress + duplicate entries from multiple calls. Add a "unique" keyword + argument to allow the old behavior to be specified. + + - Allow the library modules imported by an SConscript file to get at + all of the normally-available global functions and variables by saying + "from SCons.Script import *". + + - Add a --debug=memoizer option to print Memoizer hit/mass statistics. + + - Allow more than one --debug= option to be set at a time. + + - Change --debug=count to report object counts before and after + reading SConscript files and before and after building targets. + + - Change --debug=memory output to line up the numbers and to better + match (more or less) the headers on the --debug=count columns. + + - Speed things up when there are lists of targets and/or sources by + getting rid of some N^2 walks of the lists involved. + + - Cache evaluation of LazyActions so we don't create a new object + for each invocation. + + - When scanning, don't create Nodes for include files that don't + actually exist on disk. + + - Make supported global variables CScanner, DScanner, ProgramScanner and + SourceFileScanner. Make SourceFileScanner.add_scanner() a supported + part of the public interface. Keep the old SCons.Defaults.*Scan names + around for a while longer since some people were already using them. + + - By default, don't scan directories for on-disk files. Add a + DirScanner global scanner that can be used in Builders or Command() + calls that want source directory trees scanned for on-disk changes. + Have the Tar() and Zip() Builders use the new DirScanner to preserve + the behavior of rebuilding a .tar or .zip file if any file or + directory under a source tree changes. Add Command() support for + a source_scanner keyword argument to Command() that can be set to + DirScanner to get this behavior. + + - Documentation changes: Explain that $CXXFLAGS contains $CCFLAGS + by default. Fix a bad target_factory example in the man page. + Add appendices to the User's Guide to cover the available Tools, + Builders and construction variables. Comment out the build of + the old Python 10 paper, which doesn't build on all systems and + is old enough at this point that it probably isn't worth the + effort to make it do so. + + From Wayne Lee: + + - Avoid "maximum recursion limit" errors when removing $(-$) pairs + from long command lines. + + From Clive Levinson: + + - Make ParseConfig() recognize and add -mno-cygwin to $LINKFLAGS and + $CCFLAGS, and -mwindows to $LINKFLAGS. + + From Michael McCracken: + + - Add a new "applelink" tool to handle the things like Frameworks and + bundles that Apple has added to gcc for linking. + + - Use more appropriate default search lists of linkers, compilers and + and other tools for the 'darwin' platform. + + - Add a LoadableModule Builder that builds a bundle on Mac OS X (Darwin) + and a shared library on other systems. + + - Improve SWIG tests for use on Mac OS X (Darwin). + + From Elliot Murphy: + + - Enhance the tests to guarantee persistence of ListOption + values in saved options files. + + - Supply the help text when -h is used with the -u, -U or -D options. + + From Christian Neeb: + + - Fix the Java parser's handling of string definitions to avoid ignoring + subsequent code. + + From Han-Wen Nienhuys: + + - Optimize variable expansion by: using the re.sub() method (when + possible); not using "eval" for variables for which we can fetch the + value directory; avoiding slowing substitution logic when there's no + '$' in the string. + + From Gary Oberbrunner: + + - Add an Environment.Dump() method to print the contents of a + construction environment. + + - Allow $LIBS (and similar variables) to contain explicit File Nodes. + + - Change ParseConfig to add the found library names directly to the + $LIBS variable, instead of returning them. + + - Add ParseConfig() support for the -framework GNU linker option. + + - Add a PRINT_CMD_LINE_FUNC construction variable to allow people + to filter (or log) command-line output. + + - Print an internal Python stack trace in response to an otherwise + unexplained error when --debug=stacktrace is specified. + + - Add a --debug=findlibs option to print what's happening when + the scanner is searching for libraries. + + - Allow Tool specifications to be passed a dictionary of keyword + arguments. + + - Support an Options default value of None, in which case the variable + will not be added to the construction environment unless it's set + explicitly by the user or from an Options file. + + - Avoid copying __builtin__ values into a construction environment's + dictionary when evaluating construction variables. + + - Add a new cross-platform intelc.py Tool that can detect and + configure the Intel C++ v8 compiler on both Windows, where it's + named icl, and Linux, where it's named icc. It also checks that + the directory specified in the Windows registry exists, and sets a + new $INTEL_C_COMPILER_VERSION construction variable to identify the + version being used. (Niall Douglas contributed an early prototype + of parts of this module.) + + - Fix the private Conftest._Have() function so it doesn't change + non-alphanumeric characters to underscores. + + - Supply a better error message when a construction variable expansion + has an unknown attribute. + + - Documentation changes: Update the man page to describe use of + filenames or Nodes in $LIBS. + + From Chris Pawling: + + - Have the linkloc tool use $MSVS_VERSION to select the Microsoft + Visual Studio version to use. + + From Kevin Quick: + + - Fix the Builder name returned from ListBuilders and other instances + of subclasses of the BuilderBase class. + + - Add Builders and construction variables to support rpcgen: + RPCGenClient(), RPCGenHeader(), RPCGenService(), RPCGenXDR(), + $RPCGEN, $RPCGENFLAGS, $RPCGENCLIENTFLAGS, $RPCGENHEADERFLAGS, + $RPCGENSERVICEFLAGS, $RPCGENXDRFLAGS. + + - Update the man page to document that prefix and suffix Builder + keyword arguments can be strings, callables or dictionaries. + + - Provide more info in the error message when a user tries to build + a target multiple ways. + + - Fix Delete() when a file doesn't exist and must_exist=1. (We were + unintentionally dependent on a bug in versions of the Python shutil.py + module prior to Python 2.3, which would generate an exception for + a nonexistent file even when ignore_errors was set.) + + - Only replace a Node's builder with a non-null source builder. + + - Fix a stack trace when a suffix selection dictionary is passed + an empty source file list. + + - Allow optional names to be attached to Builders, for default + Builders that don't get attached to construction environments. + + - Fix problems with Parallel Task Exception handling. + + - Build targets in an associated BuildDir even if there are targets + or subdirectories locally in the source directory. + + - If a FunctionAction has a callable class as its underlying Python + function, use its strfunction() method (if any) to display the + action. + + - Fix handling when BuildDir() exists but is unwriteable. Add + "Stop." to those error messages for consistency. + + - Catch incidents of bad builder creation (without an action) and + supply meaningful error messages. + + - Fix handling of src_suffix values that aren't extensions (don't + begin with a '.'). + + - Don't retrieve files from a CacheDir, but report what would happen, + when the -n option is used. + + - Use the source_scanner from the target Node, not the source node + itself. + + - Internal Scanners fixes: Make sure Scanners are only passed Nodes. + Fix how a Scanner.Selector called its base class initialization. + Make comparisons of Scanner objects more robust. Add a name to + an internal default ObjSourceScanner. + + - Add a deprecated warning for use of the old "scanner" keyword argument + to Builder creation. + + - Improve the --debug=explain message when the build action changes. + + - Test enhancements in SourceCode.py, option-n.py, midl.py. Better + Command() and Scanner test coverage. Improved test infrastructure + for -c output. + + - Refactor the interface between Action and Executor objects to treat + Actions atomically. + + - The --debug=presub option will now report the pre-substitution + each action seprately, instead of reporting the entire list before + executing the actions one by one. + + - The --debug=explain option explaining a changed action will now + (more correctly) show pre-substitution action strings, instead of + the commands with substituted file names. + + - A Node (file) will now be rebuilt if its PreAction or PostAction + actions change. + + - Python Function actions now have their calling signature (target, + source, env) reported correctly when displayed. + + - Fix BuildDir()/build_dir handling when the build_dir is underneath + the source directory and trying to use entries from the build_dir + as sources for other targets in the build-dir. + + - Fix hard-coding of JDK path names in various Java tests. + + - Handle Python stack traces consistently (stop at the SConscript stack + frame, by default) even if the Python source code isn't available. + + - Improve the performance of the --debug={tree,dtree} options. + + - Add --debug=objects logging of creation of OverrideWarner, + EnvironmentCopy and EnvironmentOverride objects. + + - Fix command-line expansion of Python Value Nodes. + + - Internal cleanups: Remove an unnecessary scan argument. Associate + Scanners only with Builders, not nodes. Apply overrides once when + a Builder is called, not in multiple places. Cache results from the + Node.FS.get_suffix() and Node.get_build_env() methods. Use the Python + md5 modules' hexdigest() method, if there is one. Have Taskmaster + call get_stat() once for each Node and re-use the value instead of + calling it each time it needs the value. Have Node.depends_on() + re-use the list from the children() method instead of calling it + multiple times. + + - Use the correct scanner if the same source file is used for targets in + two different environments with the same path but different scanners. + + - Collect logic for caching values in memory in a Memoizer class, + which cleans up a lot of special-case code in various methods and + caches additional values to speed up most configurations. + + - Add a PathAccept validator to the list of new canned PathOption + validators. + + From Jeff Squyres: + + - Documentation changes: Use $CPPDEFINES instead of $CCFLAGS in man + page examples. + + From Levi Stephen: + + - Allow $JARCHDIR to be expanded to other construction variables. + + From Christoph Wiedemann: + + - Add an Environment.SetDefault() method that only sets values if + they aren't already set. + + - Have the qt.py Tool not override variables already set by the user. + + - Add separate $QT_BINPATH, $QT_CPPPATH and $QT_LIBPATH variables + so these can be set individually, instead of being hard-wired + relative to $QTDIR. + + - The %TEMP% and %TMP% external environment variables are now propagated + automatically to the command execution environment on Windows systems. + + - A new --config= command-line option allows explicit control of + of when the Configure() tests are run: --config=force forces all + checks to be run, --config=cache uses all previously cached values, + --config=auto (the default) runs tests only when dependency analysis + determines it's necessary. + + - The Configure() subsystem can now write a config.h file with values + like HAVE_STDIO_H, HAVE_LIBM, etc. + + - The Configure() subsystem now executes its checks silently when the + -Q option is specified. + + - The Configure() subsystem now reports if a test result is being + taken from cache, and prints the standard output and error output + of tests even when cached. + + - Configure() test results are now reported as "yes" or "no" instead of + "ok" or "failed." + + - Fixed traceback printing when calling the env.Configure() method + instead of the Configure() global function. + + - The Configure() subsystem now caches build failures in a .sconsign + file in the subdirectory, not a .cache file. This may cause + tests to be re-executed the first time after you install 0.97. + + - Additional significant internal cleanups in the Configure() subsystem + and its tests. + + - Have the Qt Builder make uic-generated files dependent on the .ui.h + file, if one exists. + + - Add a test to make sure that SCons source code does not contain + try:-except: blocks that catch all errors, which potentially catch + and mask keyboard interrupts. + + - Fix us of TargetSignatures('content') with the SConf subsystem. + + From Russell Yanofsky: + + - Add support for the Metrowerks Codewarrior compiler and linker + (mwcc and mwld). + + + +RELEASE 0.96.1 - Mon, 23 Aug 2004 12:55:50 +0000 + + From Craig Bachelor: + + - Handle white space in the executable Python path name within in MSVS + project files by quoting the path. + + - Correct the format of a GUID string in a solution (.dsw) file so + MSVS can correctly "build enable" a project. + + From Steven Knight: + + - Add a must_exist flag to Delete() to let the user control whether + it's an error if the specified entry doesn't exist. The default + behavior is now to silently do nothing if it doesn't exist. + + - Package up the new Platform/darwin.py, mistakenly left out of 0.96. + + - Make the scons.bat REM statements into @REM so they aren't printed. + + - Make the SCons packaging SConscript files platform independent. + + From Anthony Roach: + + - Fix scanning of pre-compiled header (.pch) files for #includes, + broken in 0.96. + + + +RELEASE 0.96 - Wed, 18 Aug 2004 13:36:40 +0000 + + From Chad Austin: + + - Make the CacheDir() directory if it doesn't already exist. + + - Allow construction variable substitutions in $LIBS specifications. + + - Allow the emitter argument to a Builder() to be or expand to a list + of emitter functions, which will be called in sequence. + + - Suppress null values in construction variables like $LIBS that use + the internal _concat() function. + + - Remove .dll files from the construction variables searched for + libraries that can be fed to Win32 compilers. + + From Chad Austin and Christoph Wiedemann: + + - Add support for a $RPATH variable to supply a list of directories + to search for shared libraries when linking a program. Used by + the GNU and IRIX linkers (gnulink and sgilink). + + From Charles Crain: + + - Restore the ability to do construction variable substitutions in all + kinds of *PATH variables, even when the substitution returns a Node + or other object. + + From Tom Epperly: + + - Allow the Java() Builder to take more than one source directory. + + From Ralf W. Grosse-Kunstleve: + + - Have SConsignFile() use, by default, a custom "dblite.py" that we can + control and guarantee to work on all Python versions (or nearly so). + + From Jonathan Gurley: + + - Add support for the newer "ifort" versions of the Intel Fortran + Compiler for Linux. + + From Bob Halley: + + - Make the new *FLAGS variable type work with copied Environments. + + From Chris Hoeppler: + + - Initialize the name of a Scanner.Classic scanner correctly. + + From James Juhasz: + + - Add support for the .dylib shared library suffix and the -dynamiclib + linker option on Mac OS X. + + From Steven Knight: + + - Add an Execute() method for executing actions directly. + + - Support passing environment override keyword arguments to Command(). + + - Fix use of $MSVS_IGNORE_IDE_PATHS, which was broken when we added + support for $MSVS_USE_MFC_DIRS last release. + + - Make env.Append() and env.Prepend() act like the underlying Python + behavior when the variable being appended to is a UserList object. + + - Fix a regression that prevented the Command() global function in + 0.95 from working with command-line strings as actions. + + - Fix checking out a file from a source code management system when + the env.SourceCode() method was called with an individual file name + or node, not a directory name or node. + + - Enhance the Task.make_ready() method to create a list of the + out-of-date Nodes for the task for use by the wrapping interface. + + - Allow Scanners to pull the list of suffixes from the construction + environment when the "skeys" keyword argument is a string containing + a construction variable to be expanded. + + - Support new $CPPSUFFIXES, $DSUFFIXES $FORTRANSUFFIXES, and + $IDLSUFFIXES. construction variables that contain the default list + of suffixes to be scanned by a given type of scanner, allowing these + suffix lists to be easily added to or overridden. + + - Speed up Node creation when calling a Builder by comparing whether two + Environments are the same object, not if their underlying dictionaries + are equivalent. + + - Add a --debug=explain option that reports the reason(s) why SCons + thinks it must rebuild something. + + - Add support for functions that return platform-independent Actions + to Chmod(), Copy(), Delete(), Mkdir(), Move() and Touch() files + and/or directories. Like any other Actions, the returned Action + object may be executed directly using the Execute() global function + or env.Execute() environment method, or may be used as a Builder + action or in an env.Command() action list. + + - Add support for the strfunction argument to all types of Actions: + CommandAction, ListAction, and CommandGeneratorAction. + + - Speed up turning file system Nodes into strings by caching the + values after we're finished reading the SConscript files. + + - Have ParseConfig() recognize and supporting adding the -Wa, -Wl, + and -Wp, flags to ASFLAGS, LINKFLAGS and CPPFLAGS, respectively. + + - Change the .sconsign format and the checks for whether a Node is + up-to-date to make dependency checks more efficient and correct. + + - Add wrapper Actions to SCons.Defaults for $ASCOM, $ASPPCOM, $LINKCOM, + $SHLINKCOM, $ARCOM, $LEXCOM and $YACCCOM. This makes it possible + to replace the default print behavior with a custom strfunction() + for each of these. + + - When a Node has been built, don't walk the whole tree back to delete + the parents's implicit dependencies, let returning up the normal + Taskmaster descent take care of it for us. + + - Add documented support for separate target_scanner and source_scanner + arguments to Builder creation, which allows different scanners to + be applied to source files + + - Don't re-install or (re-generate) .h files when a subsidiary #included + .h file changes. This eliminates incorrect circular dependencies + with .h files generated from other source files. + + - Slim down the internal Sig.Calculator class by eliminating methods + whose functionality is now covered by Node methods. + + - Document use of the target_factory and source_factory keyword + arguments when creating Builder objects. Enhance Dir Nodes so that + they can be created with user-specified Builder objects. + + - Don't blow up with stack trace when the external $PATH environment + variable isn't set. + + - Make Builder calls return lists all the time, even if there's only + one target. This keeps things consistent and easier to program to + across platforms. + + - Add a Flatten() function to make it easier to deal with the Builders + all returning lists of targets, not individual targets. + + - Performance optimizations in Node.FS.__doLookup(). + + - Man page fixes: formatting typos, misspellings, bad example. + + - User's Guide fixes: Fix the signatures of the various example + *Options() calls. Triple-quote properly a multi-line Split example. + + - User's Guide additions: Chapter describing File and Directory + Nodes. Section describing declarative nature of SCons functions in + SConscript files. Better organization and clarification of points + raised by Robert P. J. Day. Chapter describing SConf (Autoconf-like) + functionality. Chapter describing how to install Python and + SCons. Chapter describing Java builds. + + From Chris Murray: + + - Add a .win32 attribute to force file names to expand with + Windows backslash path separators. + + - Fix escaping file names on command lines when the expansion is + concatenated with another string. + + - Add support for Fortran 90 and Fortran 95. This adds $FORTRAN* + variables that specify a default compiler, command-line, flags, + etc. for all Fortran versions, plus separate $F90* and $F95* + variables for when different compilers/flags/etc. must be specified + for different Fortran versions. + + - Have individual tools that create libraries override the default + $LIBPREFIX and $LIBSUFFIX values set by the platform. This makes + it easier to use Microsoft Visual Studio tools on a CygWin platform. + + From Gary Oberbrunner: + + - Add a --debug=presub option to print actions prior to substitution. + + - Add a warning upon use of the override keywords "targets" and + "sources" when calling Builders. These are usually mistakes which + are otherwise silently (and confusingly) turned into construction + variable overrides. + + - Try to find the ICL license file path name in the external environment + and the registry before resorting to the hard-coded path name. + + - Add support for fetching command-line keyword=value arguments in + order from an ARGLIST list. + + - Avoid stack traces when trying to read dangling symlinks. + + - Treat file "extensions" that only contain digits as part of the + file basename. This supports version numbers as part of shared + library names, for example. + + - Avoid problems when there are null entries (None or '') in tool + lists or CPPPATH. + + - Add an example and explanation of how to use "tools = ['default', ..." + when creating a construction environment. + + - Add a section describing File and Directory Nodes and some of their + attributes and methods. + + - Have ParseConfig() add a returned -pthread flag to both $CCFLAGS + and $LINKFLAGS. + + - Fix some test portability issues on Mac OS X (darwin). + + From Simon Perkins: + + - Fix a bug introduced in building shared libraries under MinGW. + + From Kevin Quick: + + - Handling SCons exceptions according to Pythonic standards. + + - Fix test/chained-build.py on systems that execute within one second. + + - Fix tests on systems where 'ar' warns about archive creation. + + From Anthony Roach: + + - Fix use of the --implicit-cache option with timestamp signatures. + + - If Visual Studio is installed, assume the C/C++ compiler, the linker + and the MIDL compiler that comes with it are available, too. + + - Better error messages when evaluating a construction variable + expansion yields a Python syntax error. + + - Change the generation of PDB files when using Visual Studio from + compile time to link time. + + From sam th: + + - Allow SConf.CheckLib() to search a list of libraries, like the + Autoconf AC_SEARCH_LIBS macro. + + - Allow the env.WhereIs() method to take a "reject" argument to + let it weed out specific path names. + + From Christoph Wiedemann: + + - Add new Moc() and Uic() Builders for more explicit control over + Qt builds, plus new construction variables to control them: + $QT_AUTOSCAN, $QT_DEBUG, $QT_MOCCXXPREFIX, $QT_MOCCXXSUFFIX, + $QT_MOCHPREFIX, $QT_MOCHSUFFIX, $QT_UICDECLPREFIX, $QT_UICDECLSUFFIX, + $QT_UICIMPLPREFIX, $QT_UICIMPLSUFFIX and $QT_UISUFFIX. + + - Add a new single_source keyword argument for Builders that enforces + a single source file on calls to the Builder. + + + +RELEASE 0.95 - Mon, 08 Mar 2004 06:43:20 -0600 + + From Chad Austin: + + - Replace print statements with calls to sys.stdout.write() so output + lines stay together when -j is used. + + - Add portability fixes for a number of tests. + + - Accomodate the fact that Cygwin's os.path.normcase() lies about + the underlying system being case-sensitive. + + - Fix an incorrect _concat() call in the $RCINCFLAGS definition for + the mingw Tool. + + - Fix a problem with the msvc tool with Python versions prior to 2.3. + + - Add support for a "toolpath" Tool() and Environment keyword that + allows Tool modules to be found in specified local directories. + + - Work around Cygwin Python's silly fiction that it's using a + case-sensitive file system. + + - More robust handling of data in VCComponents.dat. + + - If the "env" command is available, spawn commands with the more + general "env -" instead of "env -i". + + From Kerim Borchaev: + + - Fix a typo in a msvc.py's registry lookup: "VCComponents.dat", not + "VSComponents.dat". + + From Chris Burghart: + + - Fix the ability to save/restore a PackageOption to a file. + + From Steve Christensen: + + - Update the MSVS .NET and MSVC 6.0/7.0 path detection. + + From David M. Cooke: + + - Make the Fortran scanner case-insensitive for the INCLUDE string. + + From Charles Crain: + + - If no version of MSVC is detected but the tool is specified, + use the MSVC 6.0 paths by default. + + - Ignore any "6.1" version of MSVC found in the registry; this is a + phony version number (created by later service packs?) and would + throw off the logic if the user had any non-default paths configure. + + - Correctly detect if the user has independently configured the MSVC + "include," "lib" or "path" in the registry and use the appropriate + values. Previously, SCons would only use the values if all three + were set in the registry. + + - Make sure side-effect nodes are prepare()d before building their + corresponding target. + + - Preserve the ability to call BuildDir() multiple times with the + same target and source directory arguments. + + From Andy Friesen: + + - Add support for the Digital Mars "D" programming language. + + From Scott Lystig Fritchie: + + - Fix the ability to use a custom _concat() function in the + construction environment when calling _stripixes(). + + - Make the message about ignoring a missing SConscript file into a + suppressable Warning, not a hard-coded sys.stderr.write(). + + - If a builder can be called multiple times for a target (because + the sources and overrides are identical, or it's a builder with the + "multi" flag set), allow the builder to be called through multiple + environments so long as the builders have the same signature for + the environments in questions (that is, they're the same action). + + From Bob Halley: + + - When multiple targets are built by a single action, retrieve all + of them from cache, not just the first target, and exec the build + command if any of the targets isn't present in the cache. + + From Zephaniah Hull: + + - Fix command-line ARGUMENTS with multiple = in them. + + From Steven Knight: + + - Fix EnsureSConsVersion() so it checks against the SCons version, + not the Python version, on Pythons with sys.version_info. + + - Don't swallow the AttributeError when someone uses an expansion like + $TARGET.bak, so we can supply a more informative error message. + + - Fix an odd double-quote escape sequence in the man page. + + - Fix looking up a naked drive letter as a directory (Dir('C:')). + + - Support using File nodes in the LIBS construction variable. + + - Allow the LIBS construction variable to be a single string or File + node, not a list, when only one library is needed. + + - Fix typos in the man page: JAVACHDIR => JARCHDIR; add "for_signature" + to the __call__() example in the "Variable Substitution" section. + + - Correct error message spellings of "non-existant" to "non-existent." + + - When scanning for libraries to link with, don't append $LIBPREFIXES + or $LIBSUFFIXES values to the $LIBS values if they're already present. + + - Add a ZIPCOMPRESSION construction variable to control whether the + internal Python action for the Zip Builder compresses the file or + not. The default value is zipfile.ZIP_DEFLATED, which generates + a compressed file. + + - Refactor construction variable expansion to support recursive + expansion of variables (e.g. CCFLAGS = "$CCFLAGS -g") without going + into an infinite loop. Support this in all construction variable + overrides, as well as when copying Environments. + + - Fix calling Configure() from more than one subsidiary SConscript file. + + - Fix the env.Action() method so it returns the correct type of + Action for its argument(s). + + - Fix specifying .class files as input to JavaH with the .class suffix + when they weren't generated using the Java Builder. + + - Make the check for whether all of the objects going into a + SharedLibrary() are shared work even if the object was built in a + previous run. + + - Supply meaningful error messages, not stack traces, if we try to add + a non-Node as a source, dependency, or ignored dependency of a Node. + + - Generate MSVS Project files that re-invoke SCons properly regardless + of whether the file was built via scons.bat or scons.py. + (Thanks to Niall Douglas for contributing code and testing.) + + - Fix TestCmd.py, runtest.py and specific tests to accomodate being + run from directories whose paths include white space. + + - Provide a more useful error message if a construction variable + expansion contains a syntax error during evaluation. + + - Fix transparent checkout of implicit dependency files from SCCS + and RCS. + + - Added new --debug=count, --debug=memory and --debug=objects options. + --debug=count and --debug=objects only print anything when run + under Python 2.1 or later. + + - Deprecate the "overrides" keyword argument to Builder() creation + in favor of using keyword argument values directly (like we do + for builder execution and the like). + + - Always use the Builder overrides in substitutions, not just if + there isn't a target-specific environment. + + - Add new "rsrcpath" and "rsrcdir" and attributes to $TARGET/$SOURCE, + so Builder command lines can find things in Repository source + directories when using BuildDir. + + - Fix the M4 Builder so that it chdirs to the Repository directory + when the input file is in the source directory of a BuildDir. + + - Save memory at build time by allowing Nodes to delete their build + environments after they've been built. + + - Add AppendUnique() and PrependUnique() Environment methods, which + add values to construction variables like Append() and Prepend() + do, but suppress any duplicate elements in the list. + + - Allow the 'qt' tool to still be used successfully from a copied + Environment. The include and library directories previously ended up + having the same string re-appended to the end, yielding an incorrect + path name. + + - Supply a more descriptive error message when the source for a target + can't be found. + + - Initialize all *FLAGS variables with objects do the right thing with + appending flags as strings or lists. + + - Make things like ${TARGET.dir} work in *PATH construction variables. + + - Allow a $MSVS_USE_MFC_DIRS construction variable to control whether + ATL and MFC directories are included in the default INCLUDE and + LIB paths. + + - Document the dbm_module argument to the SConsignFile() function. + + From Vincent Risi: + + - Add support for the bcc32, ilink32 and tlib Borland tools. + + From Anthony Roach: + + - Supply an error message if the user tries to configure a BuildDir + for a directory that already has one. + + - Remove documentation of the still-unimplemented -e option. + + - Add -H help text listing the legal --debug values. + + - Don't choke if a construction variable is a non-string value. + + - Build Type Libraries in the target directory, not the source + directory. + + - Add an appendix to the User's Guide showing how to accomplish + various common tasks in Python. + + From Greg Spencer: + + - Add support for Microsoft Visual Studio 2003 (version 7.1). + + - Evaluate $MSVSPROJECTSUFFIX and $MSVSSOLUTIONSUFFIX when the Builder + is invoked, not when the tool is initialized. + + From Christoph Wiedemann: + + - When compiling Qt, make sure the moc_*.cc files are compiled using + the flags from the environment used to specify the target, not + the environment that first has the Qt Builders attached. + + + +RELEASE 0.94 - Fri, 07 Nov 2003 05:29:48 -0600 + + From Hartmut Goebel: + + - Add several new types of canned functions to help create options: + BoolOption(), EnumOption(), ListOption(), PackageOption(), + PathOption(). + + From Steven Knight: + + - Fix use of CPPDEFINES with C++ source files. + + - Fix env.Append() when the operand is an object with a __cmp__() + method (like a Scanner instance). + + - Fix subclassing the Environment and Scanner classes. + + - Add BUILD_TARGETS, COMMAND_LINE_TARGETS and DEFAULT_TARGETS variables. + + From Steve Leblanc: + + - SGI fixes: Fix C++ compilation, add a separate Tool/sgic++.py module. + + From Gary Oberbrunner: + + - Fix how the man page un-indents after examples in some browsers. + + From Vincent Risi: + + - Fix the C and C++ tool specifications for AIX. + + + +RELEASE 0.93 - Thu, 23 Oct 2003 07:26:55 -0500 + + From J.T. Conklin: + + - On POSIX, execute commands with the more modern os.spawnvpe() + function, if it's available. + + - Scan .S, .spp and .SPP files for C preprocessor dependencies. + + - Refactor the Job.Parallel() class to use a thread pool without a + condition variable. This improves parallel build performance and + handles keyboard interrupts properly when -j is used. + + From Charles Crain: + + - Add support for a JARCHDIR variable to control changing to a + directory using the jar -C option. + + - Add support for detecting Java manifest files when using jar, + and specifying them using the jar m flag. + + - Fix some Python 2.2 specific things in various tool modules. + + - Support directories as build sources, so that a rebuild of a target + can be triggered if anything underneath the directory changes. + + - Have the scons.bat and scons.py files look for the SCons modules + in site-packages as well. + + From Christian Engel: + + - Support more flexible inclusion of separate C and C++ compilers. + + - Use package management tools on AIX and Solaris to find where + the comilers are installed, and what version they are. + + - Add support for CCVERSION and CXXVERSION variables for a number + of C and C++ compilers. + + From Sergey Fogel: + + - Add test cases for the new capabilities to run bibtex and to rerun + latex as needed. + + From Ralf W. Grosse-Kunstleve: + + - Accomodate anydbm modules that don't have a sync() method. + + - Allow SConsignFile() to take an argument specifying the DBM + module to be used. + + From Stephen Kennedy: + + - Add support for a configurable global .sconsign.dbm file which + can be used to avoid cluttering each directory with an individual + .sconsign file. + + From John Johnson: + + - Fix (re-)scanning of dependencies in generated or installed + header files. + + From Steven Knight: + + - The -Q option suppressed too many messages; fix it so that it only + suppresses the Reading/Building messages. + + - Support #include when there's no space before the opening quote + or angle bracket. + + - Accomodate alphanumeric version strings in EnsurePythonVersion(). + + - Support arbitrary expansion of construction variables within + file and directory arguments to Builder calls and Environment methods. + + - Add Environment-method versions of the following global functions: + Action(), AddPostAction(), AddPreAction(), Alias(), Builder(), + BuildDir(), CacheDir(), Clean(), Configure(), Default(), + EnsurePythonVersion(), EnsureSConsVersion(), Environment(), + Exit(), Export(), FindFile(), GetBuildPath(), GetOption(), Help(), + Import(), Literal(), Local(), Platform(), Repository(), Scanner(), + SConscriptChdir(), SConsignFile(), SetOption(), SourceSignatures(), + Split(), TargetSignatures(), Tool(), Value(). + + - Add the following global functions that correspond to the same-named + Environment methods: AlwaysBuild(), Command(), Depends(), Ignore(), + Install(), InstallAs(), Precious(), SideEffect() and SourceCode(). + + - Add the following global functions that correspond to the default + Builder methods supported by SCons: CFile(), CXXFile(), DVI(), Jar(), + Java(), JavaH(), Library(), M4(), MSVSProject(), Object(), PCH(), + PDF(), PostScript(), Program(), RES(), RMIC(), SharedLibrary(), + SharedObject(), StaticLibrary(), StaticObject(), Tar(), TypeLibrary() + and Zip(). + + - Rearrange the man page to show construction environment methods and + global functions in the same list, and to explain the difference. + + - Alphabetize the explanations of the builder methods in the man page. + + - Rename the Environment.Environment class to Enviroment.Base. + Allow the wrapping interface to extend an Environment by using its own + subclass of Environment.Base and setting a new Environment.Environment + variable as the calling entry point. + + - Deprecate the ParseConfig() global function in favor of a same-named + construction environment method. + + - Allow the Environment.WhereIs() method to take explicit path and + pathext arguments (like the underlying SCons.Util.WhereIs() function). + + - Remove the long-obsolete {Get,Set}CommandHandler() functions. + + - Enhance env.Append() to suppress null values when appropriate. + + - Fix ParseConfig() so it works regardless of initial construction + variable values. + + Extend CheckHeader(), CheckCHeader(), CheckCXXHeader() and + CheckLibWithHeader() to accept a list of header files that will be + #included in the test. The last one in the list is assumed to be + the one being checked for. (Prototype code contributed by Gerard + Patel and Niall Douglas). + + - Supply a warning when -j is used and threading isn't built in to + the current version of Python. + + - First release of the User's Guide (finally, and despite a lot + of things still missing from it...). + + From Clark McGrew: + + - Generalize the action for .tex files so that it will decide whether + a file is TeX or LaTeX, check the .aux output to decide if it should + run bibtex, and check the .log output to re-run LaTeX if needed. + + From Bram Moolenaar: + + - Split the non-SCons-specific functionality from SConf.py to a new, + re-usable Conftest.py module. + + From Gary Oberbrunner: + + - Allow a directory to be the target or source or dependency of a + Depends(), Ignore(), Precious() or SideEffect() call. + + From Gerard Patel: + + - Use the %{_mandir} macro when building our RPM package. + + From Marko Rauhamaa: + + - Have the closing message say "...terminated because of errors" if + there were any. + + From Anthony Roach: + + - On Win32 systems, only use "rm" to delete files if Cygwin is being + used. ("rm" doesn't understand Win32-format path names.) + + From Christoph Wiedemann: + + - Fix test/SWIG.py to find the Python include directory in all cases. + + - Fix a bug in detection of Qt installed on the local system. + + - Support returning Python 2.3 BooleanType values from Configure checks. + + - Provide an error message if someone mistakenly tries to call a + Configure check from within a Builder function. + + - Support calling a Builder when a Configure context is still open. + + - Handle interrupts better by eliminating all try:-except: blocks + which caught any and all exceptions, including KeyboardInterrupt. + + - Add a --duplicate= option to control how files are duplicated. + + + +RELEASE 0.92 - Wed, 20 Aug 2003 03:45:28 -0500 + + From Charles Crain and Gary Oberbrunner: + + - Fix Tool import problems with the Intel and PharLap linkers. + + From Steven Knight + + - Refactor the DictCmdGenerator class to be a Selector subclass. + + - Allow the DefaultEnvironment() function to take arguments and pass + them to instantiation of the default construction environment. + + - Update the Debian package so it uses Python 2.2 and more closely + resembles the currently official Debian packaging info. + + From Gerard Patel + + - When the yacc -d flag is used, take the .h file base name from the + target .c file, not the source (matching what yacc does). + + + +RELEASE 0.91 - Thu, 14 Aug 2003 13:00:44 -0500 + + From Chad Austin: + + - Support specifying a list of tools when calling Environment.Copy(). + + - Give a Value Nodes a timestamp of the system time when they're + created, so they'll work when using timestamp-based signatures. + + - Add a DefaultEnvironment() function that only creates a default + environment on-demand (for fetching source files, e.g.). + + - Portability fix for test/M4.py. + + From Steven Knight: + + - Tighten up the scons -H help output. + + - When the input yacc file ends in .yy and the -d flag is specified, + recognize that a .hpp file (not a .h file) will be created. + + - Make builder prefixes work correctly when deducing a target + from a source file name in another directory. + + - Documentation fixes: typo in the man page; explain up-front about + not propagating the external environment. + + - Use "cvs co -d" instead of "cvs co -p >" when checking out something + from CVS with a specified module name. This avoids zero-length + files when there is a checkout error. + + - Add an "sconsign" script to print the contents of .sconsign files. + + - Speed up maintaining the various lists of Node children by using + dictionaries to avoid "x in list" searches. + + - Cache the computed list of Node children minus those being Ignored + so it's only calculated once. + + - Fix use of the --cache-show option when building a Program() + (or using any other arbitrary action) by making sure all Action + instances have strfunction() methods. + + - Allow the source of Command() to be a directory. + + - Better error handling of things like raw TypeErrors in SConscripts. + + - When installing using "setup.py install --prefix=", suppress the + distutils warning message about adding the (incorrect) library + directory to your search path. + + - Correct the spelling of the "validater" option to "validator." + Add a DeprecatedWarning when the old spelling is used. + + - Allow a Builder's emitter to be a dictionary that maps source file + suffixes to emitter functions, using the suffix of the first file + in the source list to pick the right one. + + - Refactor the creation of the Program, *Object and *Library Builders + so that they're moved out of SCons.Defaults and created on demand. + + - Don't split SConscript file names on white space. + + - Document the SConscript function's "dirs" and "name" keywords. + + - Remove the internal (and superfluous) SCons.Util.argmunge() function. + + - Add /TP to the default CXXFLAGS for msvc, so it can compile all + of the suffixes we use as C++ files. + + - Allow the "prefix" and "suffix" attributes of a Builder to be + callable objects that return generated strings, or dictionaries + that map a source file suffix to the right prefix/suffix. + + - Support a MAXLINELINELENGTH construction variable on Win32 systems + to control when a temporary file is used for long command lines. + + - Make how we build .rpm packages not depend on the installation + locations from the distutils being used. + + - When deducing a target Node, create it directly from the first + source Node, not by trying to create the right string to pass to + arg2nodes(). + + - Add support for SWIG. + + From Bram Moolenaar: + + - Test portability fixes for FreeBSD. + + From Gary Oberbrunner: + + - Report the target being built in error messages when building + multiple sources from different extensions, or when the target file + extension can't be deduced, or when we don't have an action for a + file suffix. + + - Provide helpful error messages when the arguments to env.Install() + are incorrect. + + - Fix the value returned by the Node.prevsiginfo() method to conform + to a previous change when checking whether a node is current. + + - Supply a stack trace if the Taskmaster catches an exception. + + - When using a temporary file for a long link line on Win32 systems, + (also) print the command line that is being executed through the + temporary file. + + - Initialize the LIB environment variable when using the Intel + compiler (icl). + + - Documentation fixes: better explain the AlwaysBuild() function. + + From Laurent Pelecq: + + - When the -debug=pdb option is specified, use pdb.Pdb().runcall() to + call pdb directly, don't call Python recursively. + + From Ben Scott: + + - Add support for a platform-independent CPPDEFINES variable. + + From Christoph Wiedemann: + + - Have the g++ Tool actually use g++ in preference to c++. + + - Have the gcc Tool actually use gcc in preference to cc. + + - Add a gnutools.py test of the GNU tool chain. + + - Be smarter about linking: use $CC by default and $CXX only if we're + linking with any C++ objects. + + - Avoid SCons hanging when a piped command has a lot of output to read. + + - Add QT support for preprocessing .ui files into .c files. + + + +RELEASE 0.90 - Wed, 25 Jun 2003 14:24:52 -0500 + + From Chad Austin: + + - Fix the _concat() documentation, and add a test for it. + + - Portability fixes for non-GNU versions of lex and yacc. + + From Matt Balvin: + + - Fix handling of library prefixes when the subdirectory matches + the prefix. + + From Timothee Bessett: + + - Add an M4 Builder. + + From Charles Crain: + + - Use '.lnk' as the suffix on the temporary file for linking long + command lines (necessary for the Phar Lap linkloc linker). + + - Save non-string Options values as their actual type. + + - Save Options string values that contain a single quote correctly. + + - Save any Options values that are changed from the default + Environment values, not just ones changed on the command line or in + an Options file. + + - Make closing the Options file descriptor exception-safe. + + From Steven Knight: + + - SCons now enforces (with an error) that construction variables + must have the same form as valid Python identifiers. + + - Fix man page bugs: remove duplicate AddPostAction() description; + document no_import_lib; mention that CPPFLAGS does not contain + $_CPPINCFLAGS; mention that F77FLAGS does not contain $_F77INCFLAGS; + mention that LINKFLAGS and SHLINKFLAGS contains neither $_LIBFLAGS + nor $_LIBDIRFLAGS. + + - Eliminate a dependency on the distutils.fancy_getopt module by + copying and pasting its wrap_text() function directly. + + - Make the Script.Options() subclass match the underlying base class + implementation. + + - When reporting a target is up to date, quote the target like make + (backquote-quote) instead of with double quotes. + + - Fix handling of ../* targets when using -U, -D or -u. + + From Steve Leblanc: + + - Don't update the .sconsign files when run with -n. + + From Gary Oberbrunner: + + - Add support for the Intel C Compiler (icl.exe). + + From Anthony Roach + + - Fix Import('*'). + + From David Snopek + + - Fix use of SConf in paths with white space in them. + + - Add CheckFunc and CheckType functionality to SConf. + + - Fix use of SConf with Builders that return a list of nodes. + + From David Snopek and Christoph Wiedemann + + - Fix use of the SConf subsystem with SConscriptChdir(). + + From Greg Spencer + + - Check for the existence of MS Visual Studio on disk before using it, + to avoid getting fooled by leftover junk in the registry. + + - Add support for MSVC++ .NET. + + - Add support for MS Visual Studio project files (DSP, DSW, + SLN and VCPROJ files). + + From Christoph Wiedemann + + - SConf now works correctly when the -n and -q options are used. + + + +RELEASE 0.14 - Wed, 21 May 2003 05:16:32 -0500 + + From Chad Austin: + + - Use .dll (not .so) for shared libraries on Cygwin; use -fPIC + when compiling them. + + - Use 'rm' to remove files under Cygwin. + + - Add a PLATFORM variable to construction environments. + + - Remove the "platform" argument from tool specifications. + + - Propogate PYTHONPATH when running the regression tests so distutils + can be found in non-standard locations. + + - Using MSVC long command-line linking when running Cygwin. + + - Portability fixes for a lot of tests. + + - Add a Value Node class for dependencies on in-core Python values. + + From Allen Bierbaum: + + - Pass an Environment to the Options validator method, and + add an Options.Save() method. + + From Steve Christensen: + + - Add an optional sort function argument to the GenerateHelpText() + Options function. + + - Evaluate the "varlist" variables when computing the signature of a + function action. + + From Charles Crain: + + - Parse the source .java files for class names (including inner class + names) to figure out the target .class files that will be created. + + - Make Java support work with Repositories and SConscriptChdir(0). + + - Pass Nodes, not strings, to Builder emitter functions. + + - Refactor command-line interpolation and signature calculation + so we can use real Node attributes. + + From Steven Knight: + + - Add Java support (javac, javah, jar and rmic). + + - Propagate the external SYSTEMROOT environment variable into ENV on + Win32 systems, so external commands that use sockets will work. + + - Add a .posix attribute to PathList expansions. + + - Check out CVS source files using POSIX path names (forward slashes + as separators) even on Win32. + + - Add Node.clear() and Node.FS.Entry.clear() methods to wipe out a + Node's state, allowing it to be re-evaluated by continuous + integration build interfaces. + + - Change the name of the Set{Build,Content}SignatureType() functions + to {Target,Source}Signatures(). Deprecate the old names but support + them for backwards compatibility. + + - Add internal SCons.Node.FS.{Dir,File}.Entry() methods. + + - Interpolate the null string if an out-of-range subscript is used + for a construction variable. + + - Fix the internal Link function so that it properly links or copies + files in subsidiary BuildDir directories. + + - Refactor the internal representation of a single execution instance + of an action to eliminate redundant signature calculations. + + - Eliminate redundant signature calculations for Nodes. + + - Optimize out calling hasattr() before accessing attributes. + + - Say "Cleaning targets" (not "Building...") when the -c option is + used. + + From Damyan Pepper: + + - Quote the "Entering directory" message like Make. + + From Stefan Reichor: + + - Add support for using Ghostscript to convert Postscript to PDF files. + + From Anthony Roach: + + - Add a standalone "Alias" function (separate from an Environment). + + - Make Export() work for local variables. + + - Support passing a dictionary to Export(). + + - Support Import('*') to import everything that's been Export()ed. + + - Fix an undefined exitvalmap on Win32 systems. + + - Support new SetOption() and GetOption() functions for setting + various command-line options from with an SConscript file. + + - Deprecate the old SetJobs() and GetJobs() functions in favor of + using the new generic {Set,Get}Option() functions. + + - Fix a number of tests that searched for a Fortran compiler using the + external PATH instead of what SCons would use. + + - Fix the interaction of SideEffect() and BuildDir() so that (for + example) PDB files get put correctly in a BuildDir(). + + From David Snopek: + + - Contribute the "Autoscons" code for Autoconf-like checking for + the existence of libraries, header files and the like. + + - Have the Tool() function add the tool name to the $TOOLS + construction variable. + + From Greg Spencer: + + - Support the C preprocessor #import statement. + + - Allow the SharedLibrary() Builder on Win32 systems to be able to + register a newly-built dll using regsvr32. + + - Add a Builder for Windows type library (.tlb) files from IDL files. + + - Add an IDL scanner. + + - Refactor the Fortran, C and IDL scanners to share common logic. + + - Add .srcpath and .srcdir attributes to $TARGET and $SOURCE. + + From Christoph Wiedemann: + + - Integrate David Snopek's "Autoscons" code as the new SConf + configuration subsystem, including caching of values between + runs (using normal SCons dependency mechanisms), tests, and + documentation. + + + +RELEASE 0.13 - Mon, 31 Mar 2003 20:22:00 -0600 + + From Charles Crain: + + - Fix a bug when BuildDir(duplicate=0) is used and SConscript + files are called from within other SConscript files. + + - Support (older) versions of Perforce which don't set the Windows + registry. + + + +RELEASE 0.12 - Thu, 27 Mar 2003 23:52:09 -0600 + + From Charles Crain: + + - Added support for the Perforce source code management system. + + - Fix str(Node.FS) so that it returns a path relative to the calling + SConscript file's directory, not the top-level directory. + + - Added support for a separate src_dir argument to SConscript() + that allows explicit specification of where the source files + for an SConscript file can be found. + + - Support more easily re-usable flavors of command generators by + calling callable variables when strings are expanded. + + From Steven Knight: + + - Added an INSTALL construction variable that can be set to a function + to control how the Install() and InstallAs() Builders install files. + The default INSTALL function now copies, not links, files. + + - Remove deprecated features: the "name" argument to Builder objects, + and the Environment.Update() method. + + - Add an Environment.SourceCode() method to support fetching files + from source code systems. Add factory methods that create Builders + to support BitKeeper, CVS, RCS, and SCCS. Add support for fetching + files from RCS or SCCS transparently (like GNU Make). + + - Make the internal to_String() function more efficient. + + - Make the error message the same as other build errors when there's a + problem unlinking a target file in preparation for it being built. + + - Make TARGET, TARGETS, SOURCE and SOURCES reserved variable names and + warn if the user tries to set them in a construction environment. + + - Add support for Tar and Zip files. + + - Better documentation of the different ways to export variables to a + subsidiary SConscript file. Fix documentation bugs in a tools + example, places that still assumed SCons split strings on white + space, and typos. + + - Support fetching arbitrary files from the TARGETS or SOURCES lists + (e.g. ${SOURCES[2]}) when calculating the build signature of a + command. + + - Don't silently swallow exceptions thrown by Scanners (or other + exceptions while finding a node's dependent children). + + - Push files to CacheDir() before calling the superclass built() + method (which may clear the build signature as part of clearing + cached implicit dependencies, if the file has a source scanner). + (Bug reported by Jeff Petkau.) + + - Raise an internal error if we attempt to push a file to CacheDir() + with a build signature of None. + + - Add an explicit Exit() function for terminating early. + + - Change the documentation to correctly describe that the -f option + doesn't change to the directory in which the specified file lives. + + - Support changing directories locally with SConscript directory + path names relative to any SConstruct file specified with -f. + This allows you to build in another directory by simply changing + there and pointing at the SConstruct file in another directory. + + - Change the default SConscriptChdir() behavior to change to the + SConscript directory while it's being read. + + - Fix an exception thrown when the -U option was used with no + Default() target specified. + + - Fix -u so that it builds things in corresponding build directories + when used in a source directory. + + From Lachlan O'Dea: + + - Add SharedObject() support to the masm tool. + + - Fix WhereIs() to return normalized paths. + + From Jeff Petkau: + + - Don't copy a built file to a CacheDir() if it's already there. + + - Avoid partial copies of built files in a CacheDir() by copying + to a temporary file and renaming. + + From Anthony Roach: + + - Fix incorrect dependency-cycle errors when an Aliased source doesn't + exist. + + + +RELEASE 0.11 - Tue, 11 Feb 2003 05:24:33 -0600 + + From Chad Austin: + + - Add support for IRIX and the SGI MIPSPro tool chain. + + - Support using the MSVC tool chain when running Cygwin Python. + + From Michael Cook: + + - Avoid losing signal bits in the exit status from a command, + helping terminate builds on interrupt (CTRL+C). + + From Charles Crain: + + - Added new AddPreAction() and AddPostAction() functions that support + taking additional actions before or after building specific targets. + + - Add support for the PharLap ETS tool chain. + + From Steven Knight: + + - Allow Python function Actions to specify a list of construction + variables that should be included in the Action's signature. + + - Allow libraries in the LIBS variable to explicitly include the prefix + and suffix, even when using the GNU linker. + (Bug reported by Neal Becker.) + + - Use DOS-standard CR-LF line endings in the scons.bat file. + (Bug reported by Gary Ruben.) + + - Doc changes: Eliminate description of deprecated "name" keyword + argument from Builder definition (reported by Gary Ruben). + + - Support using env.Append() on BUILDERS (and other dictionaries). + (Bug reported by Bj=F6rn Bylander.) + + - Setting the BUILDERS construction variable now properly clears + the previous Builder attributes from the construction Environment. + (Bug reported by Bj=F6rn Bylander.) + + - Fix adding a prefix to a file when the target isn't specified. + (Bug reported by Esa Ilari Vuokko.) + + - Clean up error messages from problems duplicating into read-only + BuildDir directories or into read-only files. + + - Add a CommandAction.strfunction() method, and add an "env" argument + to the FunctionAction.strfunction() method, so that all Action + objects have strfunction() methods, and the functions for building + and returning a string both take the same arguments. + + - Add support for new CacheDir() functionality to share derived files + between builds, with related options --cache-disable, --cache-force, + and --cache-show. + + - Change the default behavior when no targets are specified to build + everything in the current directory and below (like Make). This + can be disabled by specifying Default(None) in an SConscript. + + - Revamp SCons installation to fix a case-sensitive installation + on Win32 systems, and to add SCons-specific --standard-lib, + --standalone-lib, and --version-lib options for easier user + control of where the libraries get installed. + + - Fix the ability to directly import and use Platform and Tool modules + that have been implicitly imported into an Environment(). + + - Add support for allowing an embedding interface to annotate a node + when it's created. + + - Extend the SConscript() function to accept build_dir and duplicate + keyword arguments that function like a BuildDir() call. + + From Steve Leblanc: + + - Fix the output of -c -n when directories are involved, so it + matches -c. + + From Anthony Roach: + + - Use a different shared object suffix (.os) when using gcc so shared + and static objects can exist side-by-side in the same directory. + + - Allow the same object files on Win32 to be linked into either + shared or static libraries. + + - Cache implicit cache values when using --implicit-cache. + + + +RELEASE 0.10 - Thu, 16 Jan 2003 04:11:46 -0600 + + From Derrick 'dman' Hudson: + + - Support Repositories on other file systems by symlinking or + copying files when hard linking won't work. + + From Steven Knight: + + - Remove Python bytecode (*.pyc) files from the scons-local packages. + + - Have FunctionActions print a description of what they're doing + (a representation of the Python call). + + - Fix the Install() method so that, like other actions, it prints + what would have happened when the -n option is used. + + - Don't create duplicate source files in a BuildDir when the -n + option is used. + + - Refactor the Scanner interface to eliminate unnecessary Scanner + calls and make it easier to write efficient scanners. + + - Added a "recursive" flag to Scanner creation that specifies the + Scanner should be invoked recursively on dependency files returned + by the scanner. + + - Significant performance improvement from using a more efficient + check, throughout the code, for whether a Node has a Builder. + + - Fix specifying only the source file to MultiStepBuilders such as + the Program Builder. (Bug reported by Dean Bair.) + + - Fix an exception when building from a file with the same basename as + the subdirectory in which it lives. (Bug reported by Gerard Patel.) + + - Fix automatic deduction of a target file name when there are + multiple source files specified; the target is now deduced from just + the first source file in the list. + + - Documentation fixes: better initial explanation of SConscript files; + fix a misformatted "table" in the StaticObject explanation. + + From Steven Knight and Steve Leblanc: + + - Fix the -c option so it will remove symlinks. + + From Steve Leblanc: + + - Add a Clean() method to support removing user-specified targets + when using the -c option. + + - Add a development script for running SCons through PyChecker. + + - Clean up things found by PyChecker (mostly unnecessary imports). + + - Add a script to use HappyDoc to create HTML class documentation. + + From Lachlan O'Dea: + + - Make the Environment.get() method return None by default. + + From Anthony Roach: + + - Add SetJobs() and GetJobs() methods to allow configuration of the + number of default jobs (still overridden by -j). + + - Convert the .sconsign file format from ASCII to a pickled Python + data structure. + + - Error message cleanups: Made consistent the format of error + messages (now all start with "scons: ***") and warning messages (now + all start with "scons: warning:"). Caught more cases with the "Do + not know how to build" error message. + + - Added support for the MinGW tool chain. + + - Added a --debug=includes option. + + + +RELEASE 0.09 - Thu, 5 Dec 2002 04:48:25 -0600 + + From Chad Austin: + + - Add a Prepend() method to Environments, to append values to + the beginning of construction variables. + + From Matt Balvin: + + - Add long command-line support to the "lib" Tool (Microsoft library + archiver), too. + + From Charles Crain: + + - Allow $$ in a string to be passed through as $. + + - Support file names with odd characters in them. + + - Add support for construction variable substition on scanner + directories (in CPPPATH, F77PATH, LIBPATH, etc.). + + From Charles Crain and Steven Knight: + + - Add Repository() functionality, including the -Y option. + + From Steven Knight: + + - Fix auto-deduction of target names so that deduced targets end + up in the same subdirectory as the source. + + - Don't remove source files specified on the command line! + + - Suport the Intel Fortran Compiler (ifl.exe). + + - Supply an error message if there are no command-line or + Default() targets specified. + + - Fix the ASPPCOM values for the GNU assembler. + (Bug reported by Brett Polivka.) + + - Fix an exception thrown when a Default() directory was specified + when using the -U option. + + - Issue a warning when -c can't remove a target. + + - Eliminate unnecessary Scanner calls by checking for the + existence of a file before scanning it. (This adds a generic + hook to check an arbitrary condition before scanning.) + + - Add explicit messages to tell when we're "Reading SConscript files + ...," "done reading SConscript files," "Building targets," and + "done building targets." Add a -Q option to supress these. + + - Add separate $SHOBJPREFIX and $SHOBJSUFFIX construction variables + (by default, the same as $OBJPREFIX and $OBJSUFFIX). + + - Add Make-like error messages when asked to build a source file, + and before trying to build a file that doesn't have all its source + files (including when an invalid drive letter is used on WIN32). + + - Add an scons-local-{version} package (in both .tar.gz and .zip + flavors) to help people who want to ship SCons as a stand-alone + build tool in their software packages. + + - Prevent SCons from unlinking files in certain situations when + the -n option is used. + + - Change the name of Tool/lib.py to Tool/mslib.py. + + From Steven Knight and Anthony Roach: + + - Man page: document the fact that Builder calls return Node objects. + + From Steve LeBlanc: + + - Refactor option processing to use our own version of Greg Ward's + Optik module, modified to run under Python 1.5.2. + + - Add a ParseConfig() command to modify an environment based on + parsing output from a *-config command. + + From Jeff Petkau: + + - Fix interpretation of '#/../foo' on Win32 systems. + + From Anthony Roach: + + - Fixed use of command lines with spaces in their arguments, + and use of Nodes with spaces in their string representation. + + - Make access and modification times of files in a BuildDir match + the source file, even when hard linking isn't available. + + - Make -U be case insensitive on Win32 systems. + + - Issue a warning and continue when finding a corrupt .sconsign file. + + - Fix using an alias as a dependency of a target so that if one of the + alias' dependencies gets rebuilt, the resulting target will, too. + + - Fix differently ordered targets causing unnecessary rebuilds + on case insensitive systems. + + - Use os.system() to execute external commands whenever the "env" + utility is available, which is much faster than fork()/exec(), + and fixes the -j option on several platforms. + + - Fix use of -j with multiple targets. + + - Add an Options() object for friendlier accomodation of command- + line arguments. + + - Add support for Microsoft VC++ precompiled header (.pch) files, + debugger (.pdb) files, and resource (.rc) files. + + - Don't compute the $_CPPINCFLAGS, $_F77INCFLAGS, $_LIBFLAGS and + $_LIBDIRFLAGS variables each time a command is executed, define + them so they're computed only as needed. Add a new _concat + function to the Environment that allows people to define their + own similar variables. + + - Fix dependency scans when $LIBS is overridden. + + - Add EnsurePythonVersion() and EnsureSConsVersion() functions. + + - Fix the overly-verbose stack trace on ListBuilder build errors. + + - Add a SetContentSignatureType() function, allowing use of file + timestamps instead of MD5 signatures. + + - Make -U and Default('source') fail gracefully. + + - Allow the File() and Dir() methods to take a path-name string as + the starting directory, in addition to a Dir object. + + - Allow the command handler to be selected via the SPAWN, SHELL + and ESCAPE construction variables. + + - Allow construction variables to be overridden when a Builder + is called. + + From sam th: + + - Dynamically check for the existence of utilities with which to + initialize Environments by default. + + + +RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500 + + From Charles Crain: + + - Fixed a bug with relative CPPPATH dirs when using BuildDir(). + (Bug reported by Bob Summerwill.) + + - Added a warnings framework and a --warn option to enable or + disable warnings. + + - Make the C scanner warn users if files referenced by #include + directives cannot be found and --warn=dependency is specified. + + - The BUILDERS construction variable should now be a dictionary + that maps builder names to actions. Existing uses of lists, + and the Builder name= keyword argument, generate warnings + about use of deprecated features. + + - Removed the "shared" keyword argument from the Object and + Library builders. + + - Added separated StaticObject, SharedObject, StaticLibrary and + SharedLibrary builders. Made Object and Library synonyms for + StaticObject and StaticLibrary, respectively. + + - Add LIBS and LIBPATH dependencies for shared libraries. + + - Removed support for the prefix, suffix and src_suffix arguments + to Builder() to be callable functions. + + - Fix handling file names with multiple dots. + + - Allow a build directory to be outside of the SConstruct tree. + + - Add a FindFile() function that searches for a file node with a + specified name. + + - Add $CPPFLAGS to the shared-object command lines for g++ and gcc. + + From Charles Crain and Steven Knight: + + - Add a "tools=" keyword argument to Environment instantiation, + and a separate Tools() method, for more flexible specification + of tool-specific environment changes. + + From Steven Knight: + + - Add a "platform=" keyword argument to Environment instantiation, + and a separate Platform() method, for more flexible specification + of platform-specific environment changes. + + - Updated README instructions and setup.py code to catch an + installation failure from not having distutils installed. + + - Add descriptions to the -H help text for -D, -u and -U so + people can tell them apart. + + - Remove the old feature of automatically splitting strings + of file names on white space. + + - Add a dependency Scanner for native Fortran "include" statements, + using a new "F77PATH" construction variable. + + - Fix C #include scanning to detect file names with characters like + '-' in them. + + - Add more specific version / build output to the -v option. + + - Add support for the GNU as, Microsoft masm, and nasm assemblers. + + - Allow the "target" argument to a Builder call to be omitted, in + which case the target(s) are deduced from the source file(s) and the + Builder's specified suffix. + + - Add a tar archive builder. + + - Add preliminary support for the OS/2 Platform, including the icc + and ilink Tools. + + From Jeff Petkau: + + - Fix --implicit-cache if the scanner returns an empty list. + + From Anthony Roach: + + - Add a "multi" keyword argument to Builder creation that specifies + it's okay to call the builder multiple times for a target. + + - Set a "multi" on Aliases so multiple calls will append to an Alias. + + - Fix emitter functions' use of path names when using BuildDir or + in subdirectories. + + - Fix --implicit-cache causing redundant rebuilds when the header + file list changed. + + - Fix --implicit-cache when a file has no implicit dependencies and + its source is generated. + + - Make the drive letters on Windows always be the same case, so that + changes in the case of drive letters don't cause a rebuild. + + - Fall back to importing the SCons.TimeStamp module if the SCons.MD5 + module can't be imported. + + - Fix interrupt handling to guarantee that a single interrupt will + halt SCons both when using -j and not. + + - Fix .sconsign signature storage so that output files of one build + can be safely used as input files to another build. + + - Added a --debug=time option to print SCons execution times. + + - Print an error message if a file can't be unlinked before being + built, rather than just silently terminating the build. + + - Add a SideEffect() method that can be used to tell the build + engine that a given file is created as a side effect of building + a target. A file can be specified as a side effect of more than + one build comand, in which case the commands will not be executed + simultaneously. + + - Significant performance gains from not using our own version of + the inefficient stock os.path.splitext() method, caching source + suffix computation, code cleanup in MultiStepBuilder.__call__(), + and replicating some logic in scons_subst(). + + - Add --implicit-deps-changed and --implicit-deps-unchanged options. + + - Add a GetLaunchDir() function. + + - Add a SetBuildSignatureType() function. + + From Zed Shaw: + + - Add an Append() method to Environments, to append values to + construction variables. + + - Change the name of Update() to Replace(). Keep Update() as a + deprecated synonym, at least for now. + + From Terrel Shumway: + + - Use a $PYTHON construction variable, initialized to sys.executable, + when using Python to build parts of the SCons packages. + + - Use sys.prefix, not sys.exec_prefix, to find pdb.py. + + + +RELEASE 0.07 - Thu, 2 May 2002 13:37:16 -0500 + + From Chad Austin: + + - Changes to build SCons packages on IRIX (and other *NIces). + + - Don't create a directory Node when a file already exists there, + and vice versa. + + - Add 'dirs' and 'names' keyword arguments to SConscript for + easier specification of subsidiary SConscript files. + + From Charles Crain: + + - Internal cleanup of environment passing to function Actions. + + - Builders can now take arbitrary keyword arguments to create + attributes to be passed to: command generator functions, + FunctionAction functions, Builder emitter functions (below), + and prefix/suffix generator functions (below). + + - Command generator functions can now return ANYTHING that can be + converted into an Action (a function, a string, a CommandGenerator + instance, even an ActionBase instance). + + - Actions now call get_contents() with the actual target and source + nodes used for the build. + + - A new DictCmdGenerator class replaces CompositeBuilder to support + more flexible Builder behavior internally. + + - Builders can now take an emitter= keyword argument. An emitter + is a function that takes target, source, and env argument, then + return a 2-tuple of (new sources, new targets). The emitter is + called when the Builder is __call__'ed, allowing a user to modify + source and target lists. + + - The prefix, suffix and src_suffix Builder arguments now take a + callable as well a string. The callable is passed the Environment + and any extra Builder keyword arguments and is expected to return + the appropriate prefix or suffix. + + - CommandActions can now be a string, a list of command + argument + strings, or a list of commands (strings or lists). + + - Added shared library support. The Object and Library Builders now + take a "shared=1" keyword argument to specify that a shared object + or shared library should be built. It is an error to try to build + static objects into a shared library or vice versa. + + - Win32 support for .def files has been added. Added the Win32-specific + construction variables $WIN32DEFPREFIX, $WIN32DEFSUFFIX, + $WIN32DLLPREFIX and $WIN32IMPLIBPREFIX. When building a .dll, + the new construction variable $WIN32_INSERT_DEF, controls whether + the appropriately-named .def file is inserted into the target + list (if not already present). A .lib file is always added to + a Library build if not present in the list of targets. + + - ListBuilder now passes all targets to the action, not just the first. + + - Fix so that -c now deletes generated yacc .h files. + + - Builder actions and emitter functions can now be initialized, through + construction variables, to things other than strings. + + - Make top-relative '#/dir' lookups work like '#dir'. + + - Fix for relative CPPPATH directories in subsidiary SConscript files + (broken in 0.06). + + - Add a for_signature argument to command generators, so that + generators that need to can return distinct values for the + command signature and for executing the command. + + From Alex Jacques: + + - Create a better scons.bat file from a py2bat.py script on the Python + mailing list two years ago (modeled after pl2bat.pl). + + From Steven Knight: + + - Fix so that -c -n does *not* remove the targets! + + - Man page: Add a hierarchical libraries + Program example. + + - Support long MSVC linker command lines through a builder action + that writes to a temporary file and uses the magic MSVC "link @file" + argument syntax if the line is longer than 2K characters. + + - Fix F77 command-line options on Win32 (use /Fo instead of -o). + + - Use the same action to build from .c (lower case) and .C (upper + case) files on case-insensitive systems like Win32. + + - Support building a PDF file directly from a TeX or LaTeX file + using pdftex or pdflatex. + + - Add a -x option to runtest.py to specify the script being tested. + A -X option indicates it's an executable, not a script to feed + to the Python interpreter. + + - Add a Split() function (identical to SCons.Util.argmunge()) for use + in the next release, when Builders will no longer automatically split + strings on white space. + + From Steve Leblanc: + + - Add the SConscriptChdir() method. + + From Anthony Roach: + + - Fix --debug=tree when used with directory targets. + + - Significant internal restructuring of Scanners and Taskmaster. + + - Added new --debug=dtree option. + + - Fixes for --profile option. + + - Performance improvement in construction variable substitution. + + - Implemented caching of content signatures, plus added --max-drift + option to control caching. + + - Implemented caching of dependency signatures, enabled by new + --implicit-cache option. + + - Added abspath construction variable modifier. + + - Added $SOURCE variable as a synonym for $SOURCES[0]. + + - Write out .sconsign files on error or interrupt so intermediate + build results are saved. + + - Change the -U option to -D. Make a new -U that builds just the + targets from the local SConscript file. + + - Fixed use of sys.path so Python modules can be imported from + the SConscript directory. + + - Fix for using Aliases with the -u, -U and -D options. + + - Fix so that Nodes can be passed to SConscript files. + + From Moshe Zadka: + + - Changes for official Debian packaging. + + + +RELEASE 0.06 - Thu, 28 Mar 2002 01:24:29 -0600 + + From Charles Crain: + + - Fix command generators to expand construction variables. + + - Make FunctionAction arguments be Nodes, not strings. + + From Stephen Kennedy: + + - Performance: Use a dictionary, not a list, for a Node's parents. + + From Steven Knight: + + - Add .zip files to the packages we build. + + - Man page: document LIBS, fix a typo, document ARGUMENTS. + + - Added RANLIB and RANLIBFLAGS construction variables. Only use them + in ARCOM if there's a "ranlib" program on the system. + + - Add a configurable CFILESUFFIX for the Builder of .l and .y files + into C files. + + - Add a CXXFile Builder that turns .ll and .yy files into .cc files + (configurable via a CXXFILESUFFIX construction variable). + + - Use the POSIX-standard lex -t flag, not the GNU-specific -o flag. + (Bug reported by Russell Christensen.) + + - Fixed an exception when CPPPATH or LIBPATH is a null string. + (Bug reported by Richard Kiss.) + + - Add a --profile=FILE option to make profiling SCons easier. + + - Modify the new DVI builder to create .dvi files from LaTeX (.ltx + and .latex) files. + + - Add support for Aliases (phony targets). + + - Add a WhereIs() method for searching for path names to executables. + + - Add PDF and PostScript document builders. + + - Add support for compiling Fortran programs from a variety of + suffixes (a la GNU Make): .f, .F, .for, .FOR, .fpp and .FPP + + - Support a CPPFLAGS variable on all default commands that use the + C preprocessor. + + From Steve Leblanc: + + - Add support for the -U option. + + - Allow CPPPATH, LIBPATH and LIBS to be specified as white-space + separated strings. + + - Add a document builder to create .dvi files from TeX (.tex) files. + + From Anthony Roach: + + - Fix: Construction variables with values of 0 were incorrectly + interpolated as ''. + + - Support env['VAR'] to fetch construction variable values. + + - Man page: document Precious(). + + + +RELEASE 0.05 - Thu, 21 Feb 2002 16:50:03 -0600 + + From Chad Austin: + + - Set PROGSUFFIX to .exe under Cygwin. + + From Charles Crain: + + - Allow a library to specified as a command-line source file, not just + in the LIBS construction variable. + + - Compensate for a bug in os.path.normpath() that returns '' for './' + on WIN32. + + - More performance optimizations: cache #include lines from files, + eliminate unnecessary calls. + + - If a prefix or suffix contains white space, treat the resulting + concatenation as separate arguments. + + - Fix irregularities in the way we fetch DevStudio information from + the Windows registry, and in our registry error handling. + + From Steven Knight: + + - Flush stdout after print so it intermixes correctly with stderr + when redirected. + + - Allow Scanners to return a list of strings, and document how to + write your own Scanners. + + - Look up implicit (scanned) dependencies relative to the directory + of file being scanned. + + - Make writing .sconsign files more robust by first trying to write + to a temp file that gets renamed. + + - Create all of the directories for a list of targets before trying + to build any of the targets. + + - WIN32 portability fixes in tests. + + - Allow the list of variables exported to an SConscript file to be + a UserList, too. + + - Document the overlooked LIBPATH construction variable. + (Bug reported by Eicke Godehardt.) + + - Fix so that Ignore() ignores indirect, implicit dependencies + (included files), not just direct dependencies. + + - Put the man page in the Debian distribution. + + - Run HTML docs through tidy to clean up the HTML (for Konqueror). + + - Add preliminary support for Unicode strings. + + - Efficiency: don't scan dependencies more than once during the + walk of a tree. + + - Fix the -c option so it doesn't stop removing targets if one doesn't + already exist. + (Bug reported by Paul Connell.) + + - Fix the --debug=pdb option when run on Windows NT. + (Bug reported by Paul Connell.) + + - Add support for the -q option. + + From Steve Leblanc: + + - Add support for the -u option. + + - Add .cc and .hh file suffixes to the C Scanner. + + From Anthony Roach: + + - Make the scons script return an error code on failures. + + - Add support for using code to generate a command to build a target. + + + +RELEASE 0.04 - Wed, 30 Jan 2002 11:09:42 -0600 + + From Charles Crain: + + - Significant performance improvements in the Node.FS and + Scanner subsystems. + + - Fix signatures of binary files on Win32 systems. + + - Allow LIBS and LIBPATH to be strings, not just arrays. + + - Print a traceback if a Python-function builder throws an exception. + + From Steven Knight: + + - Fix using a directory as a Default(), and allow Default() to + support white space in file names for strings in arrays. + + - Man page updates: corrected some mistakes, documented various + missing Environment methods, alphabetized the construction + variables and other functions, defined begin and end macros for + the example sections, regularized white space separation, fixed + the use of Export() in the Multiple Variants example. + + - Function action fixes: None is now a successful return value. + Exceptions are now reported. Document function actions. + + - Add 'Action' and 'Scanner' to the global keywords so SConscript + files can use them too. + + - Removed the Wrapper class between Nodes and Walkers. + + - Add examples using Library, LIBS, and LIBPATH. + + - The C Scanner now always returns a sorted list of dependencies + so order changes don't cause unnecessary rebuilds. + + - Strip $(-$) bracketed text from command lines. Use this to + surround $_INCDIRS and $_LIBDIRS so we don't rebuild in response + to changes to -I or -L options. + + - Add the Ignore() method to ignore dependencies. + + - Provide an error message when a nonexistent target is specified + on the command line. + + - Remove targets before building them, and add an Environment + Precious() method to override that. + + - Eliminate redundant calls to the same builder when the target is a + list of targets: Add a ListBuilder class that wraps Builders to + handle lists atomically. Extend the Task class to support building + and updating multiple targets in a single Task. Simplify the + interface between Task and Taskmaster. + + - Add a --debug=pdb option to re-run SCons under the Python debugger. + + - Only compute a build signature once for each node. + + - Changes to our sys.path[] manipulation to support installation into + an arbitrary --prefix value. + + From Steve Leblanc: + + - Add var=value command-line arguments. + + + +RELEASE 0.03 - Fri, 11 Jan 2002 01:09:30 -0600 + + From Charles Crain: + + - Performance improvements in the Node.FS and Sig.Calculator classes. + + - Add the InstallAs() method. + + - Execute commands through an external interpreter (sh, cmd.exe, or + command.com) to handle redirection metacharacters. + + - Allow the user to supply a command handler. + + From Steven Knight: + + - Search both /usr/lib and /usr/local/lib for scons directories by + adding them both to sys.path, with whichever is in sys.prefix first. + + - Fix interpreting strings of multiple white-space separated file names + as separate file names, allowing prefixes and suffixes to be appended + to each individually. + + - Refactor to move CompositeBuilder initialization logic from the + factory wrapper to the __init__() method, and allow a Builder to + have both an action and a src_builder (or array of them). + + - Refactor BuilderBase.__call__() to separate Node creation/lookup + from initialization of the Node's builder information. + + - Add a CFile Builder object that supports turning lex (.l) and + yacc (.y) files into .c files. + + - Document: variable interpretation attributes; how to propogate + the user's environment variables to executed commands; how to + build variants in multiple BuildDirs. + + - Collect String, Dict, and List type-checking in common utility + routines so we can accept User{String,Dict,List}s all over. + + - Put the Action factory and classes into their own module. + + - Use one CPlusPlusAction in the Object Builder's action dictionary, + instead of letting it create multiple identical instances. + + - Document the Install() and InstallAs() methods. + + From Steve Leblanc: + + - Require that a Builder be given a name argument, supplying a + useful error message when it isn't. + + From Anthony Roach: + + - Add a "duplicate" keyword argument to BuildDir() that can be set + to prevent linking/copying source files into build directories. + + - Add a "--debug=tree" option to print an ASCII dependency tree. + + - Fetch the location of the Microsoft Visual C++ compiler(s) from + the Registry, instead of hard-coding the location. + + - Made Scanner objects take Nodes, not path names. + + - Have the C Scanner cache the #include file names instead of + (re-)scanning the file each time it's called. + + - Created a separate class for parent "nodes" of file system roots, + eliminating the need for separate is-parent-null checks everywhere. + + - Removed defined __hash__() and __cmp() methods from FS.Entry, in + favor of Python's more efficient built-in identity comparisons. + + + +RELEASE 0.02 - Sun, 23 Dec 2001 19:05:09 -0600 + + From Charles Crain: + + - Added the Install(), BuildDir(), and Export() methods. + + - Fix the -C option by delaying setting the top of the FS tree. + + - Avoid putting the directory path on the libraries in the LIBS + construction variable. + + - Added a GetBuildPath() method to return the full path to the + Node for a specified string. + + - Fixed variable substitution in CPPPATH and LIBPATH. + + From Steven Knight: + + - Fixed the version comment in the scons.bat (the UNIX geek used + # instead of @rem). + + - Fix to setup.py so it doesn't require a sys.argv[1] argument. + + - Provide make-like warning message for "command not found" and + similar errors. + + - Added an EXAMPLES section to the man page. + + - Make Default() targets properly relative to their SConscript + file's subdirectory. + + From Anthony Roach: + + - Documented CXXFLAGS, CXXCOM, and CPPPATH. + + - Fixed SCONS_LIB_DIR to work as documented. + + - Made Default() accept Nodes as arguments. + + - Changed Export() to make it easier to use. + + - Added the Import() and Return() methods. + + + +RELEASE 0.01 - Thu Dec 13 19:25:23 CST 2001 + +A brief overview of important functionality available in release 0.01: + + - C and C++ compilation on POSIX and Windows NT. + + - Automatic scanning of C/C++ source files for #include dependencies. + + - Support for building libraries; setting construction variables + allows creation of shared libraries. + + - Library and C preprocessor search paths. + + - File changes detected using MD5 signatures. + + - User-definable Builder objects for building files. + + - User-definable Scanner objects for scanning for dependencies. + + - Parallel build (-j) support. + + - Dependency cycles detected. + + - Linux packages available in RPM and Debian format. + + - Windows installer available. + +Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +src/CHANGES.txt 5357 2011/09/09 21:31:03 bdeegan diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c0de36f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..74c74f3 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,200 @@ +CHANGES.txt +LICENSE.txt +MANIFEST +README.txt +RELEASE.txt +engine/SCons/Action.py +engine/SCons/Builder.py +engine/SCons/CacheDir.py +engine/SCons/Conftest.py +engine/SCons/Debug.py +engine/SCons/Defaults.py +engine/SCons/Environment.py +engine/SCons/Errors.py +engine/SCons/Executor.py +engine/SCons/Job.py +engine/SCons/Memoize.py +engine/SCons/Node/Alias.py +engine/SCons/Node/FS.py +engine/SCons/Node/Python.py +engine/SCons/Node/__init__.py +engine/SCons/Options/BoolOption.py +engine/SCons/Options/EnumOption.py +engine/SCons/Options/ListOption.py +engine/SCons/Options/PackageOption.py +engine/SCons/Options/PathOption.py +engine/SCons/Options/__init__.py +engine/SCons/PathList.py +engine/SCons/Platform/__init__.py +engine/SCons/Platform/aix.py +engine/SCons/Platform/cygwin.py +engine/SCons/Platform/darwin.py +engine/SCons/Platform/hpux.py +engine/SCons/Platform/irix.py +engine/SCons/Platform/os2.py +engine/SCons/Platform/posix.py +engine/SCons/Platform/sunos.py +engine/SCons/Platform/win32.py +engine/SCons/SConf.py +engine/SCons/SConsign.py +engine/SCons/Scanner/C.py +engine/SCons/Scanner/D.py +engine/SCons/Scanner/Dir.py +engine/SCons/Scanner/Fortran.py +engine/SCons/Scanner/IDL.py +engine/SCons/Scanner/LaTeX.py +engine/SCons/Scanner/Prog.py +engine/SCons/Scanner/RC.py +engine/SCons/Scanner/__init__.py +engine/SCons/Script/Interactive.py +engine/SCons/Script/Main.py +engine/SCons/Script/SConsOptions.py +engine/SCons/Script/SConscript.py +engine/SCons/Script/__init__.py +engine/SCons/Sig.py +engine/SCons/Subst.py +engine/SCons/Taskmaster.py +engine/SCons/Tool/386asm.py +engine/SCons/Tool/BitKeeper.py +engine/SCons/Tool/CVS.py +engine/SCons/Tool/FortranCommon.py +engine/SCons/Tool/JavaCommon.py +engine/SCons/Tool/MSCommon/__init__.py +engine/SCons/Tool/MSCommon/arch.py +engine/SCons/Tool/MSCommon/common.py +engine/SCons/Tool/MSCommon/netframework.py +engine/SCons/Tool/MSCommon/sdk.py +engine/SCons/Tool/MSCommon/vc.py +engine/SCons/Tool/MSCommon/vs.py +engine/SCons/Tool/Perforce.py +engine/SCons/Tool/PharLapCommon.py +engine/SCons/Tool/RCS.py +engine/SCons/Tool/SCCS.py +engine/SCons/Tool/Subversion.py +engine/SCons/Tool/__init__.py +engine/SCons/Tool/aixc++.py +engine/SCons/Tool/aixcc.py +engine/SCons/Tool/aixf77.py +engine/SCons/Tool/aixlink.py +engine/SCons/Tool/applelink.py +engine/SCons/Tool/ar.py +engine/SCons/Tool/as.py +engine/SCons/Tool/bcc32.py +engine/SCons/Tool/c++.py +engine/SCons/Tool/cc.py +engine/SCons/Tool/cvf.py +engine/SCons/Tool/default.py +engine/SCons/Tool/dmd.py +engine/SCons/Tool/dvi.py +engine/SCons/Tool/dvipdf.py +engine/SCons/Tool/dvips.py +engine/SCons/Tool/f03.py +engine/SCons/Tool/f77.py +engine/SCons/Tool/f90.py +engine/SCons/Tool/f95.py +engine/SCons/Tool/filesystem.py +engine/SCons/Tool/fortran.py +engine/SCons/Tool/g++.py +engine/SCons/Tool/g77.py +engine/SCons/Tool/gas.py +engine/SCons/Tool/gcc.py +engine/SCons/Tool/gfortran.py +engine/SCons/Tool/gnulink.py +engine/SCons/Tool/gs.py +engine/SCons/Tool/hpc++.py +engine/SCons/Tool/hpcc.py +engine/SCons/Tool/hplink.py +engine/SCons/Tool/icc.py +engine/SCons/Tool/icl.py +engine/SCons/Tool/ifl.py +engine/SCons/Tool/ifort.py +engine/SCons/Tool/ilink.py +engine/SCons/Tool/ilink32.py +engine/SCons/Tool/install.py +engine/SCons/Tool/intelc.py +engine/SCons/Tool/ipkg.py +engine/SCons/Tool/jar.py +engine/SCons/Tool/javac.py +engine/SCons/Tool/javah.py +engine/SCons/Tool/latex.py +engine/SCons/Tool/lex.py +engine/SCons/Tool/link.py +engine/SCons/Tool/linkloc.py +engine/SCons/Tool/m4.py +engine/SCons/Tool/masm.py +engine/SCons/Tool/midl.py +engine/SCons/Tool/mingw.py +engine/SCons/Tool/mslib.py +engine/SCons/Tool/mslink.py +engine/SCons/Tool/mssdk.py +engine/SCons/Tool/msvc.py +engine/SCons/Tool/msvs.py +engine/SCons/Tool/mwcc.py +engine/SCons/Tool/mwld.py +engine/SCons/Tool/nasm.py +engine/SCons/Tool/packaging/__init__.py +engine/SCons/Tool/packaging/ipk.py +engine/SCons/Tool/packaging/msi.py +engine/SCons/Tool/packaging/rpm.py +engine/SCons/Tool/packaging/src_tarbz2.py +engine/SCons/Tool/packaging/src_targz.py +engine/SCons/Tool/packaging/src_zip.py +engine/SCons/Tool/packaging/tarbz2.py +engine/SCons/Tool/packaging/targz.py +engine/SCons/Tool/packaging/zip.py +engine/SCons/Tool/pdf.py +engine/SCons/Tool/pdflatex.py +engine/SCons/Tool/pdftex.py +engine/SCons/Tool/qt.py +engine/SCons/Tool/rmic.py +engine/SCons/Tool/rpcgen.py +engine/SCons/Tool/rpm.py +engine/SCons/Tool/sgiar.py +engine/SCons/Tool/sgic++.py +engine/SCons/Tool/sgicc.py +engine/SCons/Tool/sgilink.py +engine/SCons/Tool/sunar.py +engine/SCons/Tool/sunc++.py +engine/SCons/Tool/suncc.py +engine/SCons/Tool/sunf77.py +engine/SCons/Tool/sunf90.py +engine/SCons/Tool/sunf95.py +engine/SCons/Tool/sunlink.py +engine/SCons/Tool/swig.py +engine/SCons/Tool/tar.py +engine/SCons/Tool/tex.py +engine/SCons/Tool/textfile.py +engine/SCons/Tool/tlib.py +engine/SCons/Tool/wix.py +engine/SCons/Tool/yacc.py +engine/SCons/Tool/zip.py +engine/SCons/Util.py +engine/SCons/Variables/BoolVariable.py +engine/SCons/Variables/EnumVariable.py +engine/SCons/Variables/ListVariable.py +engine/SCons/Variables/PackageVariable.py +engine/SCons/Variables/PathVariable.py +engine/SCons/Variables/__init__.py +engine/SCons/Warnings.py +engine/SCons/__init__.py +engine/SCons/compat/__init__.py +engine/SCons/compat/_scons_builtins.py +engine/SCons/compat/_scons_collections.py +engine/SCons/compat/_scons_dbm.py +engine/SCons/compat/_scons_hashlib.py +engine/SCons/compat/_scons_io.py +engine/SCons/compat/_scons_sets.py +engine/SCons/compat/_scons_subprocess.py +engine/SCons/cpp.py +engine/SCons/dblite.py +engine/SCons/exitfuncs.py +os_spawnv_fix.diff +scons-time.1 +scons.1 +sconsign.1 +script/scons +script/scons-time +script/scons.bat +script/sconsign +setup.cfg +setup.py diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..5f658bc --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,13 @@ +Metadata-Version: 1.0 +Name: scons +Version: 2.1.0 +Summary: Open Source next-generation build tool. +Home-page: http://www.scons.org/ +Author: Steven Knight +Author-email: knight@baldmt.com +License: UNKNOWN +Description: Open Source next-generation build tool. + Improved, cross-platform substitute for the classic Make + utility. In short, SCons is an easier, more reliable + and faster way to build software. +Platform: UNKNOWN diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..1d63921 --- /dev/null +++ b/README.txt @@ -0,0 +1,243 @@ + + + SCons - a software construction tool + + Version 2.1.0 + + +This is SCons, a tool for building software (and other files). SCons is +implemented in Python, and its "configuration files" are actually Python +scripts, allowing you to use the full power of a real scripting language +to solve build problems. You do not, however, need to know Python to +use SCons effectively. + +See the RELEASE.txt file for notes about this specific release, +including known problems. See the CHANGES.txt file for a list of +changes since the previous release. + + +LATEST VERSION +============== + +Before going further, you can check that this package you have is +the latest version by checking the SCons download page at: + + http://www.scons.org/download.html + + +EXECUTION REQUIREMENTS +====================== + +Running SCons requires Python version 2.4 or later. There should be +no other dependencies or requirements to run SCons. (There is, however, +an additional requirement to *install* SCons from this particular +package; see the next section.) + +By default, SCons knows how to search for available programming tools +on various systems--see the SCons man page for details. You may, +of course, override the default SCons choices made by appropriate +configuration of Environment construction variables. + + +INSTALLATION REQUIREMENTS +========================= + +Nothing special. + + +INSTALLATION +============ + +Assuming your system satisfies the installation requirements in the +previous section, install SCons from this package simply by running the +provided Python-standard setup script as follows: + + # python setup.py install + +By default, the above command will do the following: + + -- Install the version-numbered "scons-2.1.0" and "sconsign-2.1.0" + scripts in the default system script directory (/usr/bin or + C:\Python*\Scripts, for example). This can be disabled by + specifying the "--no-version-script" option on the command + line. + + -- Install scripts named "scons" and "sconsign" scripts in the + default system script directory (/usr/bin or C:\Python*\Scripts, + for example). This can be disabled by specifying the + "--no-scons-script" option on the command line, which is useful + if you want to install and experiment with a new version before + making it the default on your system. + + On UNIX or Linux systems, you can have the "scons" and "sconsign" + scripts be hard links or symbolic links to the "scons-2.1.0" and + "sconsign-2.1.0" scripts by specifying the "--hardlink-scons" + or "--symlink-scons" options on the command line. + + -- Install "scons-2.1.0.bat" and "scons.bat" wrapper scripts in the + Python prefix directory on Windows (C:\Python*, for example). + This can be disabled by specifying the "--no-install-bat" option + on the command line. + + On UNIX or Linux systems, the "--install-bat" option may be + specified to have "scons-2.1.0.bat" and "scons.bat" files + installed in the default system script directory, which is useful + if you want to install SCons in a shared file system directory + that can be used to execute SCons from both UNIX/Linux and + Windows systems. + + -- Install the SCons build engine (a Python module) in an + appropriate version-numbered SCons library directory + (/usr/lib/scons-2.1.0 or C:\Python*\scons-2.1.0, for example). + See below for more options related to installing the build + engine library. + + -- Install the troff-format man pages in an appropriate directory + on UNIX or Linux systems (/usr/share/man/man1 or /usr/man/man1, + for example). This can be disabled by specifying the + "--no-install-man" option on the command line. The man pages + can be installed on Windows systems by specifying the + "--install-man" option on the command line. + +Note that, by default, SCons does not install its build engine library +in the standard Python library directories. If you want to be able to +use the SCons library modules (the build engine) in other Python +scripts, specify the "--standard-lib" option on the command line, as +follows: + + # python setup.py install --standard-lib + +This will install the build engine in the standard Python library +directory (/usr/lib/python*/site-packages or +C:\Python*\Lib\site-packages). + +Alternatively, you can have SCons install its build engine library in a +hard-coded standalone library directory, instead of the default +version-numbered directory, by specifying the "--standalone-lib" option +on the command line, as follows: + + # python setup.py install --standalone-lib + +This is usually not recommended, however. + +Note that, to install SCons in any of the above system directories, +you should have system installation privileges (that is, "root" or +"Administrator") when running the setup.py script. If you don't have +system installation privileges, you can use the --prefix option to +specify an alternate installation location, such as your home directory: + + $ python setup.py install --prefix=$HOME + +This will install SCons in the appropriate locations relative to +$HOME--that is, the scons script itself $HOME/bin and the associated +library in $HOME/lib/scons, for example. + + +DOCUMENTATION +============= + +See the RELEASE.txt file for notes about this specific release, +including known problems. See the CHANGES.txt file for a list of +changes since the previous release. + +The scons.1 man page is included in this package, and 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 LICENSE.txt file. 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 +============== + +Please report bugs by following the detailed instructions on our Bug +Submission page: + + http://scons.tigris.org/bug-submission.html + +You can also send mail to the SCons developers' mailing list: + + dev@scons.tigris.org + +But even if you send email to the mailing list please make sure that you +ALSO submit a bug report to the project page bug tracker, because bug +reports in email often get overlooked in the general flood of messages. + + +MAILING LISTS +============= + +An active mailing list for users of SCons is available. You may send +questions or comments to the list at: + + users@scons.tigris.org + +You may subscribe to the mailing list by sending email to: + + users-subscribe@scons.tigris.org + +There is also a low-volume mailing list available for announcements +about SCons. Subscribe by sending email to: + + announce-subscribe@scons.tigris.org + +There are other mailing lists available for SCons developers, for +notification of SCons code changes, and for notification of updated +bug reports and project documents. Please see our mailing lists page +for details. + + +DONATIONS +========= + +If you find SCons helpful, please consider making a donation (of cash, +software, or hardware) to support continued work on the project. +Information is available at: + + http://www.scons.org/donate.html + + +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 + Greg Noel + Gary Oberbrunner + Anthony Roach + Greg Spencer + Christoph Wiedemann + +Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +src/README.txt 5357 2011/09/09 21:31:03 bdeegan diff --git a/RELEASE.txt b/RELEASE.txt new file mode 100644 index 0000000..d98e096 --- /dev/null +++ b/RELEASE.txt @@ -0,0 +1,100 @@ + A new SCons checkpoint release, 2.1.0.final.0, is now available + on the SCons download page: + + http://www.scons.org/download.php + + Here is a summary of the changes since 2.0: + + NEW FUNCTIONALITY + + - SCons can now automatically embed manifests in Windows executables + and DLLs, by setting WINDOWS_EMBED_MANIFEST in the environment. + + - SCons now searches for site_scons dirs in several system-wide + and per-user locations, in addition to the SConstruct top dir. + This should enable much easier use of third-party (non-core) + Tools. + + CHANGED/ENHANCED EXISTING FUNCTIONALITY + + - scons --version now prints the path to the SCons package in use + + - List modifications to existing features, where the previous behavior + wouldn't actually be considered a bug + + - Add initial support for VS/VC 2010 + + FIXES + + - Windows resource compiler (RC) scanner now handles DOS line endings + - Visual Studio project generation now works when CPPPATH contains Dir nodes + - Visual Studio projects are regenerated when CPPPATH or CPPDEFINES change + NOTE: this will cause all MSVS projects to be regenerated with this version. + - Passing MSVC_BATCH=False works now (treated same as 0) + - Long compile lines no longer break MSVC_BATCH mode + - RPATH is now in LINKCOM rather than LINKFLAGS, so resetting + LINKFLAGS doesn't kill RPATH + - Precompiled headers on Windows no longer break when used with + variant dirs containing spaces. + - Delete can now delete symlinks to directories and broken symlinks + - CPPDEFINES containing dictionaries now work better. + - A problem with using implicit-cache and removing dependencies on + disk is corrected. + - A problem with FS Entries which are dirs and have builders + is corrected. + - A problem with Install() of a dir when the dest dir exists + is corrected. + - Windows subprocess output should now be more reliable. + - The users guide and man page have various fixes. + - Appending to default $*FLAGS in a copied environment + now works properly. + - LaTeX scanner is improved for broken lines or embedded spaces. + - Windows UNC paths (\\SERVER\SHARE\dir) now work much better. + + IMPROVEMENTS + + - ParseFlags now supports -std=c++0x and related CXXFLAGS + - ParseFlags now supports -dylib_file from pkgconfig + - New debugging options to print unlink/relinking of variant files + (--debug=duplicate) and preparation of targets (--debug=prepare). + - SCons can now generate MSVS 9.0 and 10.0 Projects and Solutions. + - MSVS Solution generation is improved. + - Fortran 03 is supported (preliminary) + - .sx files are now treated as assembly sources. + - site_scons/site_init.py is now treated as a proper module + with __doc__, __file__ and __name__. + - TeX command strings now work on Windows when the new dir is + on a different drive letter. + - DMD version 2 is supported (using the phobos2 library) + - New --debug=prepare option shows each target as it's prepared + for building; can help when you don't know why a target isn't + being built. + + DOCUMENTATION + + - Added new getting started section + + Thanks to + Dirk Baechle, + Vincent Beffara, + Grzegorz Bizoń, + Jean-François Colson, + Bauke Conijn, + Bill Deegan, + Ken Deeter, + Luca Falavigna, + Alexander Goomenyuk, + Justin Gullingsrud, + Steven Knight, + Arve Knudsen, + Jean-Baptiste Lab, + Rob Managan, + Gary Oberbrunner, + Evgeny Podjachev, + Sohail Somani, + Anatoly Techtonik, + Allen Weeks, + Russel Winder, + Joe Zuntz + for their contributions to this release. + diff --git a/engine/SCons/Action.py b/engine/SCons/Action.py new file mode 100644 index 0000000..f4117a4 --- /dev/null +++ b/engine/SCons/Action.py @@ -0,0 +1,1257 @@ +"""SCons.Action + +This encapsulates information about executing any sort of action that +can build one or more target Nodes (typically files) from one or more +source Nodes (also typically files) given a specific Environment. + +The base class here is ActionBase. The base class supplies just a few +OO utility methods and some generic methods for displaying information +about an Action in response to the various commands that control printing. + +A second-level base class is _ActionAction. This extends ActionBase +by providing the methods that can be used to show and perform an +action. True Action objects will subclass _ActionAction; Action +factory class objects will subclass ActionBase. + +The heavy lifting is handled by subclasses for the different types of +actions we might execute: + + CommandAction + CommandGeneratorAction + FunctionAction + ListAction + +The subclasses supply the following public interface methods used by +other modules: + + __call__() + THE public interface, "calling" an Action object executes the + command or Python function. This also takes care of printing + a pre-substitution command for debugging purposes. + + get_contents() + Fetches the "contents" of an Action for signature calculation + plus the varlist. This is what gets MD5 checksummed to decide + if a target needs to be rebuilt because its action changed. + + genstring() + Returns a string representation of the Action *without* + command substitution, but allows a CommandGeneratorAction to + generate the right action based on the specified target, + source and env. This is used by the Signature subsystem + (through the Executor) to obtain an (imprecise) representation + of the Action operation for informative purposes. + + +Subclasses also supply the following methods for internal use within +this module: + + __str__() + Returns a string approximation of the Action; no variable + substitution is performed. + + execute() + The internal method that really, truly, actually handles the + execution of a command or Python function. This is used so + that the __call__() methods can take care of displaying any + pre-substitution representations, and *then* execute an action + without worrying about the specific Actions involved. + + get_presig() + Fetches the "contents" of a subclass for signature calculation. + The varlist is added to this to produce the Action's contents. + + strfunction() + Returns a substituted string representation of the Action. + This is used by the _ActionAction.show() command to display the + command/function that will be executed to generate the target(s). + +There is a related independent ActionCaller class that looks like a +regular Action, and which serves as a wrapper for arbitrary functions +that we want to let the user specify the arguments to now, but actually +execute later (when an out-of-date check determines that it's needed to +be executed, for example). Objects of this class are returned by an +ActionFactory class that provides a __call__() method as a convenient +way for wrapping up the functions. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Action.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.compat + +import dis +import os +# compat layer imports "cPickle" for us if it's available. +import pickle +import re +import sys +import subprocess + +from SCons.Debug import logInstanceCreation +import SCons.Errors +import SCons.Executor +import SCons.Util +import SCons.Subst + +# we use these a lot, so try to optimize them +is_String = SCons.Util.is_String +is_List = SCons.Util.is_List + +class _null(object): + pass + +print_actions = 1 +execute_actions = 1 +print_actions_presub = 0 + +def rfile(n): + try: + return n.rfile() + except AttributeError: + return n + +def default_exitstatfunc(s): + return s + +try: + SET_LINENO = dis.SET_LINENO + HAVE_ARGUMENT = dis.HAVE_ARGUMENT +except AttributeError: + remove_set_lineno_codes = lambda x: x +else: + def remove_set_lineno_codes(code): + result = [] + n = len(code) + i = 0 + while i < n: + c = code[i] + op = ord(c) + if op >= HAVE_ARGUMENT: + if op != SET_LINENO: + result.append(code[i:i+3]) + i = i+3 + else: + result.append(c) + i = i+1 + return ''.join(result) + +strip_quotes = re.compile('^[\'"](.*)[\'"]$') + + +def _callable_contents(obj): + """Return the signature contents of a callable Python object. + """ + try: + # Test if obj is a method. + return _function_contents(obj.im_func) + + except AttributeError: + try: + # Test if obj is a callable object. + return _function_contents(obj.__call__.im_func) + + except AttributeError: + try: + # Test if obj is a code object. + return _code_contents(obj) + + except AttributeError: + # Test if obj is a function object. + return _function_contents(obj) + + +def _object_contents(obj): + """Return the signature contents of any Python object. + + We have to handle the case where object contains a code object + since it can be pickled directly. + """ + try: + # Test if obj is a method. + return _function_contents(obj.im_func) + + except AttributeError: + try: + # Test if obj is a callable object. + return _function_contents(obj.__call__.im_func) + + except AttributeError: + try: + # Test if obj is a code object. + return _code_contents(obj) + + except AttributeError: + try: + # Test if obj is a function object. + return _function_contents(obj) + + except AttributeError: + # Should be a pickable Python object. + try: + return pickle.dumps(obj) + except (pickle.PicklingError, TypeError): + # This is weird, but it seems that nested classes + # are unpickable. The Python docs say it should + # always be a PicklingError, but some Python + # versions seem to return TypeError. Just do + # the best we can. + return str(obj) + + +def _code_contents(code): + """Return the signature contents of a code object. + + By providing direct access to the code object of the + function, Python makes this extremely easy. Hooray! + + Unfortunately, older versions of Python include line + number indications in the compiled byte code. Boo! + So we remove the line number byte codes to prevent + recompilations from moving a Python function. + """ + + contents = [] + + # The code contents depends on the number of local variables + # but not their actual names. + contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames))) + try: + contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars))) + except AttributeError: + # Older versions of Python do not support closures. + contents.append(",0,0") + + # The code contents depends on any constants accessed by the + # function. Note that we have to call _object_contents on each + # constants because the code object of nested functions can + # show-up among the constants. + # + # Note that we also always ignore the first entry of co_consts + # which contains the function doc string. We assume that the + # function does not access its doc string. + contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')') + + # The code contents depends on the variable names used to + # accessed global variable, as changing the variable name changes + # the variable actually accessed and therefore changes the + # function result. + contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')') + + + # The code contents depends on its actual code!!! + contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')') + + return ''.join(contents) + + +def _function_contents(func): + """Return the signature contents of a function.""" + + contents = [_code_contents(func.func_code)] + + # The function contents depends on the value of defaults arguments + if func.func_defaults: + contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')') + else: + contents.append(',()') + + # The function contents depends on the closure captured cell values. + try: + closure = func.func_closure or [] + except AttributeError: + # Older versions of Python do not support closures. + closure = [] + + #xxx = [_object_contents(x.cell_contents) for x in closure] + try: + xxx = [_object_contents(x.cell_contents) for x in closure] + except AttributeError: + xxx = [] + contents.append(',(' + ','.join(xxx) + ')') + + return ''.join(contents) + + +def _actionAppend(act1, act2): + # This function knows how to slap two actions together. + # Mainly, it handles ListActions by concatenating into + # a single ListAction. + a1 = Action(act1) + a2 = Action(act2) + if a1 is None: + return a2 + if a2 is None: + return a1 + if isinstance(a1, ListAction): + if isinstance(a2, ListAction): + return ListAction(a1.list + a2.list) + else: + return ListAction(a1.list + [ a2 ]) + else: + if isinstance(a2, ListAction): + return ListAction([ a1 ] + a2.list) + else: + return ListAction([ a1, a2 ]) + +def _do_create_keywords(args, kw): + """This converts any arguments after the action argument into + their equivalent keywords and adds them to the kw argument. + """ + v = kw.get('varlist', ()) + # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O'] + if is_String(v): v = (v,) + kw['varlist'] = tuple(v) + if args: + # turn positional args into equivalent keywords + cmdstrfunc = args[0] + if cmdstrfunc is None or is_String(cmdstrfunc): + kw['cmdstr'] = cmdstrfunc + elif callable(cmdstrfunc): + kw['strfunction'] = cmdstrfunc + else: + raise SCons.Errors.UserError( + 'Invalid command display variable type. ' + 'You must either pass a string or a callback which ' + 'accepts (target, source, env) as parameters.') + if len(args) > 1: + kw['varlist'] = args[1:] + kw['varlist'] + if kw.get('strfunction', _null) is not _null \ + and kw.get('cmdstr', _null) is not _null: + raise SCons.Errors.UserError( + 'Cannot have both strfunction and cmdstr args to Action()') + +def _do_create_action(act, kw): + """This is the actual "implementation" for the + Action factory method, below. This handles the + fact that passing lists to Action() itself has + different semantics than passing lists as elements + of lists. + + The former will create a ListAction, the latter + will create a CommandAction by converting the inner + list elements to strings.""" + + if isinstance(act, ActionBase): + return act + + if is_List(act): + return CommandAction(act, **kw) + + if callable(act): + try: + gen = kw['generator'] + del kw['generator'] + except KeyError: + gen = 0 + if gen: + action_type = CommandGeneratorAction + else: + action_type = FunctionAction + return action_type(act, kw) + + if is_String(act): + var=SCons.Util.get_environment_var(act) + if var: + # This looks like a string that is purely an Environment + # variable reference, like "$FOO" or "${FOO}". We do + # something special here...we lazily evaluate the contents + # of that Environment variable, so a user could put something + # like a function or a CommandGenerator in that variable + # instead of a string. + return LazyAction(var, kw) + commands = str(act).split('\n') + if len(commands) == 1: + return CommandAction(commands[0], **kw) + # The list of string commands may include a LazyAction, so we + # reprocess them via _do_create_list_action. + return _do_create_list_action(commands, kw) + # Catch a common error case with a nice message: + if isinstance(act, int) or isinstance(act, float): + raise TypeError("Don't know how to create an Action from a number (%s)"%act) + # Else fail silently (???) + return None + +def _do_create_list_action(act, kw): + """A factory for list actions. Convert the input list into Actions + and then wrap them in a ListAction.""" + acts = [] + for a in act: + aa = _do_create_action(a, kw) + if aa is not None: acts.append(aa) + if not acts: + return ListAction([]) + elif len(acts) == 1: + return acts[0] + else: + return ListAction(acts) + +def Action(act, *args, **kw): + """A factory for action objects.""" + # Really simple: the _do_create_* routines do the heavy lifting. + _do_create_keywords(args, kw) + if is_List(act): + return _do_create_list_action(act, kw) + return _do_create_action(act, kw) + +class ActionBase(object): + """Base class for all types of action objects that can be held by + other objects (Builders, Executors, etc.) This provides the + common methods for manipulating and combining those actions.""" + + def __cmp__(self, other): + return cmp(self.__dict__, other) + + def no_batch_key(self, env, target, source): + return None + + batch_key = no_batch_key + + def genstring(self, target, source, env): + return str(self) + + def get_contents(self, target, source, env): + result = [ self.get_presig(target, source, env) ] + # This should never happen, as the Action() factory should wrap + # the varlist, but just in case an action is created directly, + # we duplicate this check here. + vl = self.get_varlist(target, source, env) + if is_String(vl): vl = (vl,) + for v in vl: + result.append(env.subst('${'+v+'}')) + return ''.join(result) + + def __add__(self, other): + return _actionAppend(self, other) + + def __radd__(self, other): + return _actionAppend(other, self) + + def presub_lines(self, env): + # CommandGeneratorAction needs a real environment + # in order to return the proper string here, since + # it may call LazyAction, which looks up a key + # in that env. So we temporarily remember the env here, + # and CommandGeneratorAction will use this env + # when it calls its _generate method. + self.presub_env = env + lines = str(self).split('\n') + self.presub_env = None # don't need this any more + return lines + + def get_varlist(self, target, source, env, executor=None): + return self.varlist + + def get_targets(self, env, executor): + """ + Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used + by this action. + """ + return self.targets + +class _ActionAction(ActionBase): + """Base class for actions that create output objects.""" + def __init__(self, cmdstr=_null, strfunction=_null, varlist=(), + presub=_null, chdir=None, exitstatfunc=None, + batch_key=None, targets='$TARGETS', + **kw): + self.cmdstr = cmdstr + if strfunction is not _null: + if strfunction is None: + self.cmdstr = None + else: + self.strfunction = strfunction + self.varlist = varlist + self.presub = presub + self.chdir = chdir + if not exitstatfunc: + exitstatfunc = default_exitstatfunc + self.exitstatfunc = exitstatfunc + + self.targets = targets + + if batch_key: + if not callable(batch_key): + # They have set batch_key, but not to their own + # callable. The default behavior here will batch + # *all* targets+sources using this action, separated + # for each construction environment. + def default_batch_key(self, env, target, source): + return (id(self), id(env)) + batch_key = default_batch_key + SCons.Util.AddMethod(self, batch_key, 'batch_key') + + def print_cmd_line(self, s, target, source, env): + # In python 3, and in some of our tests, sys.stdout is + # a String io object, and it takes unicode strings only + # In other cases it's a regular Python 2.x file object + # which takes strings (bytes), and if you pass those a + # unicode object they try to decode with 'ascii' codec + # which fails if the cmd line has any hi-bit-set chars. + # This code assumes s is a regular string, but should + # work if it's unicode too. + try: + sys.stdout.write(unicode(s + "\n")) + except UnicodeDecodeError: + sys.stdout.write(s + "\n") + + def __call__(self, target, source, env, + exitstatfunc=_null, + presub=_null, + show=_null, + execute=_null, + chdir=_null, + executor=None): + if not is_List(target): + target = [target] + if not is_List(source): + source = [source] + + if presub is _null: + presub = self.presub + if presub is _null: + presub = print_actions_presub + if exitstatfunc is _null: exitstatfunc = self.exitstatfunc + if show is _null: show = print_actions + if execute is _null: execute = execute_actions + if chdir is _null: chdir = self.chdir + save_cwd = None + if chdir: + save_cwd = os.getcwd() + try: + chdir = str(chdir.abspath) + except AttributeError: + if not is_String(chdir): + if executor: + chdir = str(executor.batches[0].targets[0].dir) + else: + chdir = str(target[0].dir) + if presub: + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + t = ' and '.join(map(str, target)) + l = '\n '.join(self.presub_lines(env)) + out = u"Building %s with action:\n %s\n" % (t, l) + sys.stdout.write(out) + cmd = None + if show and self.strfunction: + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + try: + cmd = self.strfunction(target, source, env, executor) + except TypeError: + cmd = self.strfunction(target, source, env) + if cmd: + if chdir: + cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd + try: + get = env.get + except AttributeError: + print_func = self.print_cmd_line + else: + print_func = get('PRINT_CMD_LINE_FUNC') + if not print_func: + print_func = self.print_cmd_line + print_func(cmd, target, source, env) + stat = 0 + if execute: + if chdir: + os.chdir(chdir) + try: + stat = self.execute(target, source, env, executor=executor) + if isinstance(stat, SCons.Errors.BuildError): + s = exitstatfunc(stat.status) + if s: + stat.status = s + else: + stat = s + else: + stat = exitstatfunc(stat) + finally: + if save_cwd: + os.chdir(save_cwd) + if cmd and save_cwd: + print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) + + return stat + + +def _string_from_cmd_list(cmd_list): + """Takes a list of command line arguments and returns a pretty + representation for printing.""" + cl = [] + for arg in map(str, cmd_list): + if ' ' in arg or '\t' in arg: + arg = '"' + arg + '"' + cl.append(arg) + return ' '.join(cl) + +# A fiddlin' little function that has an 'import SCons.Environment' which +# can't be moved to the top level without creating an import loop. Since +# this import creates a local variable named 'SCons', it blocks access to +# the global variable, so we move it here to prevent complaints about local +# variables being used uninitialized. +default_ENV = None +def get_default_ENV(env): + global default_ENV + try: + return env['ENV'] + except KeyError: + if not default_ENV: + import SCons.Environment + # This is a hideously expensive way to get a default shell + # environment. What it really should do is run the platform + # setup to get the default ENV. Fortunately, it's incredibly + # rare for an Environment not to have a shell environment, so + # we're not going to worry about it overmuch. + default_ENV = SCons.Environment.Environment()['ENV'] + return default_ENV + +# This function is still in draft mode. We're going to need something like +# it in the long run as more and more places use subprocess, but I'm sure +# it'll have to be tweaked to get the full desired functionality. +# one special arg (so far?), 'error', to tell what to do with exceptions. +def _subproc(scons_env, cmd, error = 'ignore', **kw): + """Do common setup for a subprocess.Popen() call""" + # allow std{in,out,err} to be "'devnull'" + io = kw.get('stdin') + if is_String(io) and io == 'devnull': + kw['stdin'] = open(os.devnull) + io = kw.get('stdout') + if is_String(io) and io == 'devnull': + kw['stdout'] = open(os.devnull, 'w') + io = kw.get('stderr') + if is_String(io) and io == 'devnull': + kw['stderr'] = open(os.devnull, 'w') + + # Figure out what shell environment to use + ENV = kw.get('env', None) + if ENV is None: ENV = get_default_ENV(scons_env) + + # Ensure that the ENV values are all strings: + new_env = {} + for key, value in ENV.items(): + if is_List(value): + # If the value is a list, then we assume it is a path list, + # because that's a pretty common list-like value to stick + # in an environment variable: + value = SCons.Util.flatten_sequence(value) + new_env[key] = os.pathsep.join(map(str, value)) + else: + # It's either a string or something else. If it's a string, + # we still want to call str() because it might be a *Unicode* + # string, which makes subprocess.Popen() gag. If it isn't a + # string or a list, then we just coerce it to a string, which + # is the proper way to handle Dir and File instances and will + # produce something reasonable for just about everything else: + new_env[key] = str(value) + kw['env'] = new_env + + try: + return subprocess.Popen(cmd, **kw) + except EnvironmentError, e: + if error == 'raise': raise + # return a dummy Popen instance that only returns error + class dummyPopen(object): + def __init__(self, e): self.exception = e + def communicate(self): return ('','') + def wait(self): return -self.exception.errno + stdin = None + class f(object): + def read(self): return '' + def readline(self): return '' + stdout = stderr = f() + return dummyPopen(e) + +class CommandAction(_ActionAction): + """Class for command-execution actions.""" + def __init__(self, cmd, **kw): + # Cmd can actually be a list or a single item; if it's a + # single item it should be the command string to execute; if a + # list then it should be the words of the command string to + # execute. Only a single command should be executed by this + # object; lists of commands should be handled by embedding + # these objects in a ListAction object (which the Action() + # factory above does). cmd will be passed to + # Environment.subst_list() for substituting environment + # variables. + if __debug__: logInstanceCreation(self, 'Action.CommandAction') + + _ActionAction.__init__(self, **kw) + if is_List(cmd): + if list(filter(is_List, cmd)): + raise TypeError("CommandAction should be given only " \ + "a single command") + self.cmd_list = cmd + + def __str__(self): + if is_List(self.cmd_list): + return ' '.join(map(str, self.cmd_list)) + return str(self.cmd_list) + + def process(self, target, source, env, executor=None): + if executor: + result = env.subst_list(self.cmd_list, 0, executor=executor) + else: + result = env.subst_list(self.cmd_list, 0, target, source) + silent = None + ignore = None + while True: + try: c = result[0][0][0] + except IndexError: c = None + if c == '@': silent = 1 + elif c == '-': ignore = 1 + else: break + result[0][0] = result[0][0][1:] + try: + if not result[0][0]: + result[0] = result[0][1:] + except IndexError: + pass + return result, ignore, silent + + def strfunction(self, target, source, env, executor=None): + if self.cmdstr is None: + return None + if self.cmdstr is not _null: + from SCons.Subst import SUBST_RAW + if executor: + c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) + else: + c = env.subst(self.cmdstr, SUBST_RAW, target, source) + if c: + return c + cmd_list, ignore, silent = self.process(target, source, env, executor) + if silent: + return '' + return _string_from_cmd_list(cmd_list[0]) + + def execute(self, target, source, env, executor=None): + """Execute a command action. + + This will handle lists of commands as well as individual commands, + because construction variable substitution may turn a single + "command" into a list. This means that this class can actually + handle lists of commands, even though that's not how we use it + externally. + """ + escape_list = SCons.Subst.escape_list + flatten_sequence = SCons.Util.flatten_sequence + + try: + shell = env['SHELL'] + except KeyError: + raise SCons.Errors.UserError('Missing SHELL construction variable.') + + try: + spawn = env['SPAWN'] + except KeyError: + raise SCons.Errors.UserError('Missing SPAWN construction variable.') + else: + if is_String(spawn): + spawn = env.subst(spawn, raw=1, conv=lambda x: x) + + escape = env.get('ESCAPE', lambda x: x) + + ENV = get_default_ENV(env) + + # Ensure that the ENV values are all strings: + for key, value in ENV.items(): + if not is_String(value): + if is_List(value): + # If the value is a list, then we assume it is a + # path list, because that's a pretty common list-like + # value to stick in an environment variable: + value = flatten_sequence(value) + ENV[key] = os.pathsep.join(map(str, value)) + else: + # If it isn't a string or a list, then we just coerce + # it to a string, which is the proper way to handle + # Dir and File instances and will produce something + # reasonable for just about everything else: + ENV[key] = str(value) + + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor) + + # Use len() to filter out any "command" that's zero-length. + for cmd_line in filter(len, cmd_list): + # Escape the command line for the interpreter we are using. + cmd_line = escape_list(cmd_line, escape) + result = spawn(shell, escape, cmd_line[0], cmd_line, ENV) + if not ignore and result: + msg = "Error %s" % result + return SCons.Errors.BuildError(errstr=msg, + status=result, + action=self, + command=cmd_line) + return 0 + + def get_presig(self, target, source, env, executor=None): + """Return the signature contents of this action's command line. + + This strips $(-$) and everything in between the string, + since those parts don't affect signatures. + """ + from SCons.Subst import SUBST_SIG + cmd = self.cmd_list + if is_List(cmd): + cmd = ' '.join(map(str, cmd)) + else: + cmd = str(cmd) + if executor: + return env.subst_target_source(cmd, SUBST_SIG, executor=executor) + else: + return env.subst_target_source(cmd, SUBST_SIG, target, source) + + def get_implicit_deps(self, target, source, env, executor=None): + icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) + if is_String(icd) and icd[:1] == '$': + icd = env.subst(icd) + if not icd or icd in ('0', 'None'): + return [] + from SCons.Subst import SUBST_SIG + if executor: + cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor) + else: + cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source) + res = [] + for cmd_line in cmd_list: + if cmd_line: + d = str(cmd_line[0]) + m = strip_quotes.match(d) + if m: + d = m.group(1) + d = env.WhereIs(d) + if d: + res.append(env.fs.File(d)) + return res + +class CommandGeneratorAction(ActionBase): + """Class for command-generator actions.""" + def __init__(self, generator, kw): + if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction') + self.generator = generator + self.gen_kw = kw + self.varlist = kw.get('varlist', ()) + self.targets = kw.get('targets', '$TARGETS') + + def _generate(self, target, source, env, for_signature, executor=None): + # ensure that target is a list, to make it easier to write + # generator functions: + if not is_List(target): + target = [target] + + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + ret = self.generator(target=target, + source=source, + env=env, + for_signature=for_signature) + gen_cmd = Action(ret, **self.gen_kw) + if not gen_cmd: + raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) + return gen_cmd + + def __str__(self): + try: + env = self.presub_env + except AttributeError: + env = None + if env is None: + env = SCons.Defaults.DefaultEnvironment() + act = self._generate([], [], env, 1) + return str(act) + + def batch_key(self, env, target, source): + return self._generate(target, source, env, 1).batch_key(env, target, source) + + def genstring(self, target, source, env, executor=None): + return self._generate(target, source, env, 1, executor).genstring(target, source, env) + + def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, + show=_null, execute=_null, chdir=_null, executor=None): + act = self._generate(target, source, env, 0, executor) + if act is None: + raise UserError("While building `%s': " + "Cannot deduce file extension from source files: %s" + % (repr(list(map(str, target))), repr(list(map(str, source))))) + return act(target, source, env, exitstatfunc, presub, + show, execute, chdir, executor) + + def get_presig(self, target, source, env, executor=None): + """Return the signature contents of this action's command line. + + This strips $(-$) and everything in between the string, + since those parts don't affect signatures. + """ + return self._generate(target, source, env, 1, executor).get_presig(target, source, env) + + def get_implicit_deps(self, target, source, env, executor=None): + return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env) + + def get_varlist(self, target, source, env, executor=None): + return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor) + + def get_targets(self, env, executor): + return self._generate(None, None, env, 1, executor).get_targets(env, executor) + + + +# A LazyAction is a kind of hybrid generator and command action for +# strings of the form "$VAR". These strings normally expand to other +# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also +# want to be able to replace them with functions in the construction +# environment. Consequently, we want lazy evaluation and creation of +# an Action in the case of the function, but that's overkill in the more +# normal case of expansion to other strings. +# +# So we do this with a subclass that's both a generator *and* +# a command action. The overridden methods all do a quick check +# of the construction variable, and if it's a string we just call +# the corresponding CommandAction method to do the heavy lifting. +# If not, then we call the same-named CommandGeneratorAction method. +# The CommandGeneratorAction methods work by using the overridden +# _generate() method, that is, our own way of handling "generation" of +# an action based on what's in the construction variable. + +class LazyAction(CommandGeneratorAction, CommandAction): + + def __init__(self, var, kw): + if __debug__: logInstanceCreation(self, 'Action.LazyAction') + #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw) + CommandAction.__init__(self, '${'+var+'}', **kw) + self.var = SCons.Util.to_String(var) + self.gen_kw = kw + + def get_parent_class(self, env): + c = env.get(self.var) + if is_String(c) and not '\n' in c: + return CommandAction + return CommandGeneratorAction + + def _generate_cache(self, env): + if env: + c = env.get(self.var, '') + else: + c = '' + gen_cmd = Action(c, **self.gen_kw) + if not gen_cmd: + raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) + return gen_cmd + + def _generate(self, target, source, env, for_signature, executor=None): + return self._generate_cache(env) + + def __call__(self, target, source, env, *args, **kw): + c = self.get_parent_class(env) + return c.__call__(self, target, source, env, *args, **kw) + + def get_presig(self, target, source, env): + c = self.get_parent_class(env) + return c.get_presig(self, target, source, env) + + def get_varlist(self, target, source, env, executor=None): + c = self.get_parent_class(env) + return c.get_varlist(self, target, source, env, executor) + + +class FunctionAction(_ActionAction): + """Class for Python function actions.""" + + def __init__(self, execfunction, kw): + if __debug__: logInstanceCreation(self, 'Action.FunctionAction') + + self.execfunction = execfunction + try: + self.funccontents = _callable_contents(execfunction) + except AttributeError: + try: + # See if execfunction will do the heavy lifting for us. + self.gc = execfunction.get_contents + except AttributeError: + # This is weird, just do the best we can. + self.funccontents = _object_contents(execfunction) + + _ActionAction.__init__(self, **kw) + + def function_name(self): + try: + return self.execfunction.__name__ + except AttributeError: + try: + return self.execfunction.__class__.__name__ + except AttributeError: + return "unknown_python_function" + + def strfunction(self, target, source, env, executor=None): + if self.cmdstr is None: + return None + if self.cmdstr is not _null: + from SCons.Subst import SUBST_RAW + if executor: + c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) + else: + c = env.subst(self.cmdstr, SUBST_RAW, target, source) + if c: + return c + def array(a): + def quote(s): + try: + str_for_display = s.str_for_display + except AttributeError: + s = repr(s) + else: + s = str_for_display() + return s + return '[' + ", ".join(map(quote, a)) + ']' + try: + strfunc = self.execfunction.strfunction + except AttributeError: + pass + else: + if strfunc is None: + return None + if callable(strfunc): + return strfunc(target, source, env) + name = self.function_name() + tstr = array(target) + sstr = array(source) + return "%s(%s, %s)" % (name, tstr, sstr) + + def __str__(self): + name = self.function_name() + if name == 'ActionCaller': + return str(self.execfunction) + return "%s(target, source, env)" % name + + def execute(self, target, source, env, executor=None): + exc_info = (None,None,None) + try: + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + rsources = list(map(rfile, source)) + try: + result = self.execfunction(target=target, source=rsources, env=env) + except KeyboardInterrupt, e: + raise + except SystemExit, e: + raise + except Exception, e: + result = e + exc_info = sys.exc_info() + + if result: + result = SCons.Errors.convert_to_BuildError(result, exc_info) + result.node=target + result.action=self + try: + result.command=self.strfunction(target, source, env, executor) + except TypeError: + result.command=self.strfunction(target, source, env) + + # FIXME: This maintains backward compatibility with respect to + # which type of exceptions were returned by raising an + # exception and which ones were returned by value. It would + # probably be best to always return them by value here, but + # some codes do not check the return value of Actions and I do + # not have the time to modify them at this point. + if (exc_info[1] and + not isinstance(exc_info[1],EnvironmentError)): + raise result + + return result + finally: + # Break the cycle between the traceback object and this + # function stack frame. See the sys.exc_info() doc info for + # more information about this issue. + del exc_info + + + def get_presig(self, target, source, env): + """Return the signature contents of this callable action.""" + try: + return self.gc(target, source, env) + except AttributeError: + return self.funccontents + + def get_implicit_deps(self, target, source, env): + return [] + +class ListAction(ActionBase): + """Class for lists of other actions.""" + def __init__(self, actionlist): + if __debug__: logInstanceCreation(self, 'Action.ListAction') + def list_of_actions(x): + if isinstance(x, ActionBase): + return x + return Action(x) + self.list = list(map(list_of_actions, actionlist)) + # our children will have had any varlist + # applied; we don't need to do it again + self.varlist = () + self.targets = '$TARGETS' + + def genstring(self, target, source, env): + return '\n'.join([a.genstring(target, source, env) for a in self.list]) + + def __str__(self): + return '\n'.join(map(str, self.list)) + + def presub_lines(self, env): + return SCons.Util.flatten_sequence( + [a.presub_lines(env) for a in self.list]) + + def get_presig(self, target, source, env): + """Return the signature contents of this action list. + + Simple concatenation of the signatures of the elements. + """ + return "".join([x.get_contents(target, source, env) for x in self.list]) + + def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, + show=_null, execute=_null, chdir=_null, executor=None): + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + for act in self.list: + stat = act(target, source, env, exitstatfunc, presub, + show, execute, chdir, executor) + if stat: + return stat + return 0 + + def get_implicit_deps(self, target, source, env): + result = [] + for act in self.list: + result.extend(act.get_implicit_deps(target, source, env)) + return result + + def get_varlist(self, target, source, env, executor=None): + result = SCons.Util.OrderedDict() + for act in self.list: + for var in act.get_varlist(target, source, env, executor): + result[var] = True + return list(result.keys()) + +class ActionCaller(object): + """A class for delaying calling an Action function with specific + (positional and keyword) arguments until the Action is actually + executed. + + This class looks to the rest of the world like a normal Action object, + but what it's really doing is hanging on to the arguments until we + have a target, source and env to use for the expansion. + """ + def __init__(self, parent, args, kw): + self.parent = parent + self.args = args + self.kw = kw + + def get_contents(self, target, source, env): + actfunc = self.parent.actfunc + try: + # "self.actfunc" is a function. + contents = str(actfunc.func_code.co_code) + except AttributeError: + # "self.actfunc" is a callable object. + try: + contents = str(actfunc.__call__.im_func.func_code.co_code) + except AttributeError: + # No __call__() method, so it might be a builtin + # or something like that. Do the best we can. + contents = str(actfunc) + contents = remove_set_lineno_codes(contents) + return contents + + def subst(self, s, target, source, env): + # If s is a list, recursively apply subst() + # to every element in the list + if is_List(s): + result = [] + for elem in s: + result.append(self.subst(elem, target, source, env)) + return self.parent.convert(result) + + # Special-case hack: Let a custom function wrapped in an + # ActionCaller get at the environment through which the action + # was called by using this hard-coded value as a special return. + if s == '$__env__': + return env + elif is_String(s): + return env.subst(s, 1, target, source) + return self.parent.convert(s) + + def subst_args(self, target, source, env): + return [self.subst(x, target, source, env) for x in self.args] + + def subst_kw(self, target, source, env): + kw = {} + for key in self.kw.keys(): + kw[key] = self.subst(self.kw[key], target, source, env) + return kw + + def __call__(self, target, source, env, executor=None): + args = self.subst_args(target, source, env) + kw = self.subst_kw(target, source, env) + return self.parent.actfunc(*args, **kw) + + def strfunction(self, target, source, env): + args = self.subst_args(target, source, env) + kw = self.subst_kw(target, source, env) + return self.parent.strfunc(*args, **kw) + + def __str__(self): + return self.parent.strfunc(*self.args, **self.kw) + +class ActionFactory(object): + """A factory class that will wrap up an arbitrary function + as an SCons-executable Action object. + + The real heavy lifting here is done by the ActionCaller class. + We just collect the (positional and keyword) arguments that we're + called with and give them to the ActionCaller object we create, + so it can hang onto them until it needs them. + """ + def __init__(self, actfunc, strfunc, convert=lambda x: x): + self.actfunc = actfunc + self.strfunc = strfunc + self.convert = convert + + def __call__(self, *args, **kw): + ac = ActionCaller(self, args, kw) + action = Action(ac, strfunction=ac.strfunction) + return action + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Builder.py b/engine/SCons/Builder.py new file mode 100644 index 0000000..8d480be --- /dev/null +++ b/engine/SCons/Builder.py @@ -0,0 +1,877 @@ +"""SCons.Builder + +Builder object subsystem. + +A Builder object is a callable that encapsulates information about how +to execute actions to create a target Node (file) from source Nodes +(files), and how to create those dependencies for tracking. + +The main entry point here is the Builder() factory method. This provides +a procedural interface that creates the right underlying Builder object +based on the keyword arguments supplied and the types of the arguments. + +The goal is for this external interface to be simple enough that the +vast majority of users can create new Builders as necessary to support +building new types of files in their configurations, without having to +dive any deeper into this subsystem. + +The base class here is BuilderBase. This is a concrete base class which +does, in fact, represent the Builder objects that we (or users) create. + +There is also a proxy that looks like a Builder: + + CompositeBuilder + + This proxies for a Builder with an action that is actually a + dictionary that knows how to map file suffixes to a specific + action. This is so that we can invoke different actions + (compilers, compile options) for different flavors of source + files. + +Builders and their proxies have the following public interface methods +used by other modules: + + __call__() + THE public interface. Calling a Builder object (with the + use of internal helper methods) sets up the target and source + dependencies, appropriate mapping to a specific action, and the + environment manipulation necessary for overridden construction + variable. This also takes care of warning about possible mistakes + in keyword arguments. + + add_emitter() + Adds an emitter for a specific file suffix, used by some Tool + modules to specify that (for example) a yacc invocation on a .y + can create a .h *and* a .c file. + + add_action() + Adds an action for a specific file suffix, heavily used by + Tool modules to add their specific action(s) for turning + a source file into an object file to the global static + and shared object file Builders. + +There are the following methods for internal use within this module: + + _execute() + The internal method that handles the heavily lifting when a + Builder is called. This is used so that the __call__() methods + can set up warning about possible mistakes in keyword-argument + overrides, and *then* execute all of the steps necessary so that + the warnings only occur once. + + get_name() + Returns the Builder's name within a specific Environment, + primarily used to try to return helpful information in error + messages. + + adjust_suffix() + get_prefix() + get_suffix() + get_src_suffix() + set_src_suffix() + Miscellaneous stuff for handling the prefix and suffix + manipulation we use in turning source file names into target + file names. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Builder.py 5357 2011/09/09 21:31:03 bdeegan" + +import collections + +import SCons.Action +from SCons.Debug import logInstanceCreation +from SCons.Errors import InternalError, UserError +import SCons.Executor +import SCons.Memoize +import SCons.Node +import SCons.Node.FS +import SCons.Util +import SCons.Warnings + +class _Null(object): + pass + +_null = _Null + +def match_splitext(path, suffixes = []): + if suffixes: + matchsuf = [S for S in suffixes if path[-len(S):] == S] + if matchsuf: + suf = max([(len(_f),_f) for _f in matchsuf])[1] + return [path[:-len(suf)], path[-len(suf):]] + return SCons.Util.splitext(path) + +class DictCmdGenerator(SCons.Util.Selector): + """This is a callable class that can be used as a + command generator function. It holds on to a dictionary + mapping file suffixes to Actions. It uses that dictionary + to return the proper action based on the file suffix of + the source file.""" + + def __init__(self, dict=None, source_ext_match=1): + SCons.Util.Selector.__init__(self, dict) + self.source_ext_match = source_ext_match + + def src_suffixes(self): + return list(self.keys()) + + def add_action(self, suffix, action): + """Add a suffix-action pair to the mapping. + """ + self[suffix] = action + + def __call__(self, target, source, env, for_signature): + if not source: + return [] + + if self.source_ext_match: + suffixes = self.src_suffixes() + ext = None + for src in map(str, source): + my_ext = match_splitext(src, suffixes)[1] + if ext and my_ext != ext: + raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" + % (repr(list(map(str, target))), src, ext, my_ext)) + ext = my_ext + else: + ext = match_splitext(str(source[0]), self.src_suffixes())[1] + + if not ext: + #return ext + raise UserError("While building `%s': " + "Cannot deduce file extension from source files: %s" + % (repr(list(map(str, target))), repr(list(map(str, source))))) + + try: + ret = SCons.Util.Selector.__call__(self, env, source, ext) + except KeyError, e: + raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2])) + if ret is None: + raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ + (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys())))) + return ret + +class CallableSelector(SCons.Util.Selector): + """A callable dictionary that will, in turn, call the value it + finds if it can.""" + def __call__(self, env, source): + value = SCons.Util.Selector.__call__(self, env, source) + if callable(value): + value = value(env, source) + return value + +class DictEmitter(SCons.Util.Selector): + """A callable dictionary that maps file suffixes to emitters. + When called, it finds the right emitter in its dictionary for the + suffix of the first source file, and calls that emitter to get the + right lists of targets and sources to return. If there's no emitter + for the suffix in its dictionary, the original target and source are + returned. + """ + def __call__(self, target, source, env): + emitter = SCons.Util.Selector.__call__(self, env, source) + if emitter: + target, source = emitter(target, source, env) + return (target, source) + +class ListEmitter(collections.UserList): + """A callable list of emitters that calls each in sequence, + returning the result. + """ + def __call__(self, target, source, env): + for e in self.data: + target, source = e(target, source, env) + return (target, source) + +# These are a common errors when calling a Builder; +# they are similar to the 'target' and 'source' keyword args to builders, +# so we issue warnings when we see them. The warnings can, of course, +# be disabled. +misleading_keywords = { + 'targets' : 'target', + 'sources' : 'source', +} + +class OverrideWarner(collections.UserDict): + """A class for warning about keyword arguments that we use as + overrides in a Builder call. + + This class exists to handle the fact that a single Builder call + can actually invoke multiple builders. This class only emits the + warnings once, no matter how many Builders are invoked. + """ + def __init__(self, dict): + collections.UserDict.__init__(self, dict) + if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner') + self.already_warned = None + def warn(self): + if self.already_warned: + return + for k in self.keys(): + if k in misleading_keywords: + alt = misleading_keywords[k] + msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) + SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) + self.already_warned = 1 + +def Builder(**kw): + """A factory for builder objects.""" + composite = None + if 'generator' in kw: + if 'action' in kw: + raise UserError("You must not specify both an action and a generator.") + kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) + del kw['generator'] + elif 'action' in kw: + source_ext_match = kw.get('source_ext_match', 1) + if 'source_ext_match' in kw: + del kw['source_ext_match'] + if SCons.Util.is_Dict(kw['action']): + composite = DictCmdGenerator(kw['action'], source_ext_match) + kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) + kw['src_suffix'] = composite.src_suffixes() + else: + kw['action'] = SCons.Action.Action(kw['action']) + + if 'emitter' in kw: + emitter = kw['emitter'] + if SCons.Util.is_String(emitter): + # This allows users to pass in an Environment + # variable reference (like "$FOO") as an emitter. + # We will look in that Environment variable for + # a callable to use as the actual emitter. + var = SCons.Util.get_environment_var(emitter) + if not var: + raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) + kw['emitter'] = EmitterProxy(var) + elif SCons.Util.is_Dict(emitter): + kw['emitter'] = DictEmitter(emitter) + elif SCons.Util.is_List(emitter): + kw['emitter'] = ListEmitter(emitter) + + result = BuilderBase(**kw) + + if not composite is None: + result = CompositeBuilder(result, composite) + + return result + +def _node_errors(builder, env, tlist, slist): + """Validate that the lists of target and source nodes are + legal for this builder and environment. Raise errors or + issue warnings as appropriate. + """ + + # First, figure out if there are any errors in the way the targets + # were specified. + for t in tlist: + if t.side_effect: + raise UserError("Multiple ways to build the same target were specified for: %s" % t) + if t.has_explicit_builder(): + if not t.env is None and not t.env is env: + action = t.builder.action + t_contents = action.get_contents(tlist, slist, t.env) + contents = action.get_contents(tlist, slist, env) + + if t_contents == contents: + msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env)) + SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) + else: + msg = "Two environments with different actions were specified for the same target: %s" % t + raise UserError(msg) + if builder.multi: + if t.builder != builder: + msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) + raise UserError(msg) + # TODO(batch): list constructed each time! + if t.get_executor().get_all_targets() != tlist: + msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist))) + raise UserError(msg) + elif t.sources != slist: + msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist))) + raise UserError(msg) + + if builder.single_source: + if len(slist) > 1: + raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist)))) + +class EmitterProxy(object): + """This is a callable class that can act as a + Builder emitter. It holds on to a string that + is a key into an Environment dictionary, and will + look there at actual build time to see if it holds + a callable. If so, we will call that as the actual + emitter.""" + def __init__(self, var): + self.var = SCons.Util.to_String(var) + + def __call__(self, target, source, env): + emitter = self.var + + # Recursively substitute the variable. + # We can't use env.subst() because it deals only + # in strings. Maybe we should change that? + while SCons.Util.is_String(emitter) and emitter in env: + emitter = env[emitter] + if callable(emitter): + target, source = emitter(target, source, env) + elif SCons.Util.is_List(emitter): + for e in emitter: + target, source = e(target, source, env) + + return (target, source) + + + def __cmp__(self, other): + return cmp(self.var, other.var) + +class BuilderBase(object): + """Base class for Builders, objects that create output + nodes (files) from input nodes (files). + """ + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + def __init__(self, action = None, + prefix = '', + suffix = '', + src_suffix = '', + target_factory = None, + source_factory = None, + target_scanner = None, + source_scanner = None, + emitter = None, + multi = 0, + env = None, + single_source = 0, + name = None, + chdir = _null, + is_explicit = 1, + src_builder = None, + ensure_suffix = False, + **overrides): + if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') + self._memo = {} + self.action = action + self.multi = multi + if SCons.Util.is_Dict(prefix): + prefix = CallableSelector(prefix) + self.prefix = prefix + if SCons.Util.is_Dict(suffix): + suffix = CallableSelector(suffix) + self.env = env + self.single_source = single_source + if 'overrides' in overrides: + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, + "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ + "\tspecify the items as keyword arguments to the Builder() call instead.") + overrides.update(overrides['overrides']) + del overrides['overrides'] + if 'scanner' in overrides: + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, + "The \"scanner\" keyword to Builder() creation has been deprecated;\n" + "\tuse: source_scanner or target_scanner as appropriate.") + del overrides['scanner'] + self.overrides = overrides + + self.set_suffix(suffix) + self.set_src_suffix(src_suffix) + self.ensure_suffix = ensure_suffix + + self.target_factory = target_factory + self.source_factory = source_factory + self.target_scanner = target_scanner + self.source_scanner = source_scanner + + self.emitter = emitter + + # Optional Builder name should only be used for Builders + # that don't get attached to construction environments. + if name: + self.name = name + self.executor_kw = {} + if not chdir is _null: + self.executor_kw['chdir'] = chdir + self.is_explicit = is_explicit + + if src_builder is None: + src_builder = [] + elif not SCons.Util.is_List(src_builder): + src_builder = [ src_builder ] + self.src_builder = src_builder + + def __nonzero__(self): + raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead") + + def get_name(self, env): + """Attempts to get the name of the Builder. + + Look at the BUILDERS variable of env, expecting it to be a + dictionary containing this Builder, and return the key of the + dictionary. If there's no key, then return a directly-configured + name (if there is one) or the name of the class (by default).""" + + try: + index = list(env['BUILDERS'].values()).index(self) + return list(env['BUILDERS'].keys())[index] + except (AttributeError, KeyError, TypeError, ValueError): + try: + return self.name + except AttributeError: + return str(self.__class__) + + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + + def splitext(self, path, env=None): + if not env: + env = self.env + if env: + suffixes = self.src_suffixes(env) + else: + suffixes = [] + return match_splitext(path, suffixes) + + def _adjustixes(self, files, pre, suf, ensure_suffix=False): + if not files: + return [] + result = [] + if not SCons.Util.is_List(files): + files = [files] + + for f in files: + if SCons.Util.is_String(f): + f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) + result.append(f) + return result + + def _create_nodes(self, env, target = None, source = None): + """Create and return lists of target and source nodes. + """ + src_suf = self.get_src_suffix(env) + + target_factory = env.get_factory(self.target_factory) + source_factory = env.get_factory(self.source_factory) + + source = self._adjustixes(source, None, src_suf) + slist = env.arg2nodes(source, source_factory) + + pre = self.get_prefix(env, slist) + suf = self.get_suffix(env, slist) + + if target is None: + try: + t_from_s = slist[0].target_from_source + except AttributeError: + raise UserError("Do not know how to create a target from source `%s'" % slist[0]) + except IndexError: + tlist = [] + else: + splitext = lambda S: self.splitext(S,env) + tlist = [ t_from_s(pre, suf, splitext) ] + else: + target = self._adjustixes(target, pre, suf, self.ensure_suffix) + tlist = env.arg2nodes(target, target_factory, target=target, source=source) + + if self.emitter: + # The emitter is going to do str(node), but because we're + # being called *from* a builder invocation, the new targets + # don't yet have a builder set on them and will look like + # source files. Fool the emitter's str() calls by setting + # up a temporary builder on the new targets. + new_targets = [] + for t in tlist: + if not t.is_derived(): + t.builder_set(self) + new_targets.append(t) + + orig_tlist = tlist[:] + orig_slist = slist[:] + + target, source = self.emitter(target=tlist, source=slist, env=env) + + # Now delete the temporary builders that we attached to any + # new targets, so that _node_errors() doesn't do weird stuff + # to them because it thinks they already have builders. + for t in new_targets: + if t.builder is self: + # Only delete the temporary builder if the emitter + # didn't change it on us. + t.builder_set(None) + + # Have to call arg2nodes yet again, since it is legal for + # emitters to spit out strings as well as Node instances. + tlist = env.arg2nodes(target, target_factory, + target=orig_tlist, source=orig_slist) + slist = env.arg2nodes(source, source_factory, + target=orig_tlist, source=orig_slist) + + return tlist, slist + + def _execute(self, env, target, source, overwarn={}, executor_kw={}): + # We now assume that target and source are lists or None. + if self.src_builder: + source = self.src_builder_sources(env, source, overwarn) + + if self.single_source and len(source) > 1 and target is None: + result = [] + if target is None: target = [None]*len(source) + for tgt, src in zip(target, source): + if not tgt is None: tgt = [tgt] + if not src is None: src = [src] + result.extend(self._execute(env, tgt, src, overwarn)) + return SCons.Node.NodeList(result) + + overwarn.warn() + + tlist, slist = self._create_nodes(env, target, source) + + # Check for errors with the specified target/source lists. + _node_errors(self, env, tlist, slist) + + # The targets are fine, so find or make the appropriate Executor to + # build this particular list of targets from this particular list of + # sources. + + executor = None + key = None + + if self.multi: + try: + executor = tlist[0].get_executor(create = 0) + except (AttributeError, IndexError): + pass + else: + executor.add_sources(slist) + + if executor is None: + if not self.action: + fmt = "Builder %s must have an action to build %s." + raise UserError(fmt % (self.get_name(env or self.env), + list(map(str,tlist)))) + key = self.action.batch_key(env or self.env, tlist, slist) + if key: + try: + executor = SCons.Executor.GetBatchExecutor(key) + except KeyError: + pass + else: + executor.add_batch(tlist, slist) + + if executor is None: + executor = SCons.Executor.Executor(self.action, env, [], + tlist, slist, executor_kw) + if key: + SCons.Executor.AddBatchExecutor(key, executor) + + # Now set up the relevant information in the target Nodes themselves. + for t in tlist: + t.cwd = env.fs.getcwd() + t.builder_set(self) + t.env_set(env) + t.add_source(slist) + t.set_executor(executor) + t.set_explicit(self.is_explicit) + + return SCons.Node.NodeList(tlist) + + def __call__(self, env, target=None, source=None, chdir=_null, **kw): + # We now assume that target and source are lists or None. + # The caller (typically Environment.BuilderWrapper) is + # responsible for converting any scalar values to lists. + if chdir is _null: + ekw = self.executor_kw + else: + ekw = self.executor_kw.copy() + ekw['chdir'] = chdir + if kw: + if 'srcdir' in kw: + def prependDirIfRelative(f, srcdir=kw['srcdir']): + import os.path + if SCons.Util.is_String(f) and not os.path.isabs(f): + f = os.path.join(srcdir, f) + return f + if not SCons.Util.is_List(source): + source = [source] + source = list(map(prependDirIfRelative, source)) + del kw['srcdir'] + if self.overrides: + env_kw = self.overrides.copy() + env_kw.update(kw) + else: + env_kw = kw + else: + env_kw = self.overrides + env = env.Override(env_kw) + return self._execute(env, target, source, OverrideWarner(kw), ekw) + + def adjust_suffix(self, suff): + if suff and not suff[0] in [ '.', '_', '$' ]: + return '.' + suff + return suff + + def get_prefix(self, env, sources=[]): + prefix = self.prefix + if callable(prefix): + prefix = prefix(env, sources) + return env.subst(prefix) + + def set_suffix(self, suffix): + if not callable(suffix): + suffix = self.adjust_suffix(suffix) + self.suffix = suffix + + def get_suffix(self, env, sources=[]): + suffix = self.suffix + if callable(suffix): + suffix = suffix(env, sources) + return env.subst(suffix) + + def set_src_suffix(self, src_suffix): + if not src_suffix: + src_suffix = [] + elif not SCons.Util.is_List(src_suffix): + src_suffix = [ src_suffix ] + self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix] + + def get_src_suffix(self, env): + """Get the first src_suffix in the list of src_suffixes.""" + ret = self.src_suffixes(env) + if not ret: + return '' + return ret[0] + + def add_emitter(self, suffix, emitter): + """Add a suffix-emitter mapping to this Builder. + + This assumes that emitter has been initialized with an + appropriate dictionary type, and will throw a TypeError if + not, so the caller is responsible for knowing that this is an + appropriate method to call for the Builder in question. + """ + self.emitter[suffix] = emitter + + def add_src_builder(self, builder): + """ + Add a new Builder to the list of src_builders. + + This requires wiping out cached values so that the computed + lists of source suffixes get re-calculated. + """ + self._memo = {} + self.src_builder.append(builder) + + def _get_sdict(self, env): + """ + Returns a dictionary mapping all of the source suffixes of all + src_builders of this Builder to the underlying Builder that + should be called first. + + This dictionary is used for each target specified, so we save a + lot of extra computation by memoizing it for each construction + environment. + + Note that this is re-computed each time, not cached, because there + might be changes to one of our source Builders (or one of their + source Builders, and so on, and so on...) that we can't "see." + + The underlying methods we call cache their computed values, + though, so we hope repeatedly aggregating them into a dictionary + like this won't be too big a hit. We may need to look for a + better way to do this if performance data show this has turned + into a significant bottleneck. + """ + sdict = {} + for bld in self.get_src_builders(env): + for suf in bld.src_suffixes(env): + sdict[suf] = bld + return sdict + + def src_builder_sources(self, env, source, overwarn={}): + sdict = self._get_sdict(env) + + src_suffixes = self.src_suffixes(env) + + lengths = list(set(map(len, src_suffixes))) + + def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): + node_suffixes = [name[-l:] for l in lengths] + for suf in src_suffixes: + if suf in node_suffixes: + return suf + return None + + result = [] + for s in SCons.Util.flatten(source): + if SCons.Util.is_String(s): + match_suffix = match_src_suffix(env.subst(s)) + if not match_suffix and not '.' in s: + src_suf = self.get_src_suffix(env) + s = self._adjustixes(s, None, src_suf)[0] + else: + match_suffix = match_src_suffix(s.name) + if match_suffix: + try: + bld = sdict[match_suffix] + except KeyError: + result.append(s) + else: + tlist = bld._execute(env, None, [s], overwarn) + # If the subsidiary Builder returned more than one + # target, then filter out any sources that this + # Builder isn't capable of building. + if len(tlist) > 1: + tlist = [t for t in tlist if match_src_suffix(t.name)] + result.extend(tlist) + else: + result.append(s) + + source_factory = env.get_factory(self.source_factory) + + return env.arg2nodes(result, source_factory) + + def _get_src_builders_key(self, env): + return id(env) + + memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key)) + + def get_src_builders(self, env): + """ + Returns the list of source Builders for this Builder. + + This exists mainly to look up Builders referenced as + strings in the 'BUILDER' variable of the construction + environment and cache the result. + """ + memo_key = id(env) + try: + memo_dict = self._memo['get_src_builders'] + except KeyError: + memo_dict = {} + self._memo['get_src_builders'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + + builders = [] + for bld in self.src_builder: + if SCons.Util.is_String(bld): + try: + bld = env['BUILDERS'][bld] + except KeyError: + continue + builders.append(bld) + + memo_dict[memo_key] = builders + return builders + + def _subst_src_suffixes_key(self, env): + return id(env) + + memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key)) + + def subst_src_suffixes(self, env): + """ + The suffix list may contain construction variable expansions, + so we have to evaluate the individual strings. To avoid doing + this over and over, we memoize the results for each construction + environment. + """ + memo_key = id(env) + try: + memo_dict = self._memo['subst_src_suffixes'] + except KeyError: + memo_dict = {} + self._memo['subst_src_suffixes'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + suffixes = [env.subst(x) for x in self.src_suffix] + memo_dict[memo_key] = suffixes + return suffixes + + def src_suffixes(self, env): + """ + Returns the list of source suffixes for all src_builders of this + Builder. + + This is essentially a recursive descent of the src_builder "tree." + (This value isn't cached because there may be changes in a + src_builder many levels deep that we can't see.) + """ + sdict = {} + suffixes = self.subst_src_suffixes(env) + for s in suffixes: + sdict[s] = 1 + for builder in self.get_src_builders(env): + for s in builder.src_suffixes(env): + if s not in sdict: + sdict[s] = 1 + suffixes.append(s) + return suffixes + +class CompositeBuilder(SCons.Util.Proxy): + """A Builder Proxy whose main purpose is to always have + a DictCmdGenerator as its action, and to provide access + to the DictCmdGenerator's add_action() method. + """ + + def __init__(self, builder, cmdgen): + if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') + SCons.Util.Proxy.__init__(self, builder) + + # cmdgen should always be an instance of DictCmdGenerator. + self.cmdgen = cmdgen + self.builder = builder + + __call__ = SCons.Util.Delegate('__call__') + + def add_action(self, suffix, action): + self.cmdgen.add_action(suffix, action) + self.set_src_suffix(self.cmdgen.src_suffixes()) + +def is_a_Builder(obj): + """"Returns True iff the specified obj is one of our Builder classes. + + The test is complicated a bit by the fact that CompositeBuilder + is a proxy, not a subclass of BuilderBase. + """ + return (isinstance(obj, BuilderBase) + or isinstance(obj, CompositeBuilder) + or callable(obj)) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/CacheDir.py b/engine/SCons/CacheDir.py new file mode 100644 index 0000000..09fbce0 --- /dev/null +++ b/engine/SCons/CacheDir.py @@ -0,0 +1,216 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# 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 5357 2011/09/09 21:31:03 bdeegan" + +__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/engine/SCons/Conftest.py b/engine/SCons/Conftest.py new file mode 100644 index 0000000..04a6bc2 --- /dev/null +++ b/engine/SCons/Conftest.py @@ -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 because on OSF/1 3.0 it includes + # which includes 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 +%(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 +#include +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" % (type_name, 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/engine/SCons/Debug.py b/engine/SCons/Debug.py new file mode 100644 index 0000000..b28f77d --- /dev/null +++ b/engine/SCons/Debug.py @@ -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 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 5357 2011/09/09 21:31:03 bdeegan" + +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/engine/SCons/Defaults.py b/engine/SCons/Defaults.py new file mode 100644 index 0000000..c170940 --- /dev/null +++ b/engine/SCons/Defaults.py @@ -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 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 5357 2011/09/09 21:31:03 bdeegan" + + +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/engine/SCons/Environment.py b/engine/SCons/Environment.py new file mode 100644 index 0000000..de8e9ef --- /dev/null +++ b/engine/SCons/Environment.py @@ -0,0 +1,2417 @@ +"""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 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 5357 2011/09/09 21:31:03 bdeegan" + + +import copy +import os +import sys +import re +import shlex +from collections import UserDict + +import SCons.Action +import SCons.Builder +from SCons.Debug import logInstanceCreation +import SCons.Defaults +import SCons.Errors +import SCons.Memoize +import SCons.Node +import SCons.Node.Alias +import SCons.Node.FS +import SCons.Node.Python +import SCons.Platform +import SCons.SConf +import SCons.SConsign +import SCons.Subst +import SCons.Tool +import SCons.Util +import SCons.Warnings + +class _Null(object): + pass + +_null = _Null + +_warn_copy_deprecated = True +_warn_source_signatures_deprecated = True +_warn_target_signatures_deprecated = True + +CleanTargets = {} +CalculatorArgs = {} + +semi_deepcopy = SCons.Util.semi_deepcopy + +# Pull UserError into the global name space for the benefit of +# Environment().SourceSignatures(), which has some import statements +# which seem to mess up its ability to reference SCons directly. +UserError = SCons.Errors.UserError + +def alias_builder(env, target, source): + pass + +AliasBuilder = SCons.Builder.Builder(action = alias_builder, + target_factory = SCons.Node.Alias.default_ans.Alias, + source_factory = SCons.Node.FS.Entry, + multi = 1, + is_explicit = None, + name='AliasBuilder') + +def apply_tools(env, tools, toolpath): + # Store the toolpath in the Environment. + if toolpath is not None: + env['toolpath'] = toolpath + + if not tools: + return + # Filter out null tools from the list. + for tool in [_f for _f in tools if _f]: + if SCons.Util.is_List(tool) or isinstance(tool, tuple): + toolname = tool[0] + toolargs = tool[1] # should be a dict of kw args + tool = env.Tool(toolname, **toolargs) + else: + env.Tool(tool) + +# These names are (or will be) controlled by SCons; users should never +# set or override them. This warning can optionally be turned off, +# but scons will still ignore the illegal variable names even if it's off. +reserved_construction_var_names = [ + 'CHANGED_SOURCES', + 'CHANGED_TARGETS', + 'SOURCE', + 'SOURCES', + 'TARGET', + 'TARGETS', + 'UNCHANGED_SOURCES', + 'UNCHANGED_TARGETS', +] + +future_reserved_construction_var_names = [ + #'HOST_OS', + #'HOST_ARCH', + #'HOST_CPU', + ] + +def copy_non_reserved_keywords(dict): + result = semi_deepcopy(dict) + for k in result.keys(): + if k in reserved_construction_var_names: + msg = "Ignoring attempt to set reserved variable `$%s'" + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k) + del result[k] + return result + +def _set_reserved(env, key, value): + msg = "Ignoring attempt to set reserved variable `$%s'" + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key) + +def _set_future_reserved(env, key, value): + env._dict[key] = value + msg = "`$%s' will be reserved in a future release and setting it will become ignored" + SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key) + +def _set_BUILDERS(env, key, value): + try: + bd = env._dict[key] + for k in bd.keys(): + del bd[k] + except KeyError: + bd = BuilderDict(kwbd, env) + env._dict[key] = bd + for k, v in value.items(): + if not SCons.Builder.is_a_Builder(v): + raise SCons.Errors.UserError('%s is not a Builder.' % repr(v)) + bd.update(value) + +def _del_SCANNERS(env, key): + del env._dict[key] + env.scanner_map_delete() + +def _set_SCANNERS(env, key, value): + env._dict[key] = value + env.scanner_map_delete() + +def _delete_duplicates(l, keep_last): + """Delete duplicates from a sequence, keeping the first or last.""" + seen={} + result=[] + if keep_last: # reverse in & out, then keep first + l.reverse() + for i in l: + try: + if i not in seen: + result.append(i) + seen[i]=1 + except TypeError: + # probably unhashable. Just keep it. + result.append(i) + if keep_last: + result.reverse() + return result + + + +# The following is partly based on code in a comment added by Peter +# Shannon at the following page (there called the "transplant" class): +# +# ASPN : Python Cookbook : Dynamically added methods to a class +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 +# +# We had independently been using the idiom as BuilderWrapper, but +# factoring out the common parts into this base class, and making +# BuilderWrapper a subclass that overrides __call__() to enforce specific +# Builder calling conventions, simplified some of our higher-layer code. + +class MethodWrapper(object): + """ + A generic Wrapper class that associates a method (which can + actually be any callable) with an object. As part of creating this + MethodWrapper object an attribute with the specified (by default, + the name of the supplied method) is added to the underlying object. + When that new "method" is called, our __call__() method adds the + object as the first argument, simulating the Python behavior of + supplying "self" on method calls. + + We hang on to the name by which the method was added to the underlying + base class so that we can provide a method to "clone" ourselves onto + a new underlying object being copied (without which we wouldn't need + to save that info). + """ + def __init__(self, object, method, name=None): + if name is None: + name = method.__name__ + self.object = object + self.method = method + self.name = name + setattr(self.object, name, self) + + def __call__(self, *args, **kwargs): + nargs = (self.object,) + args + return self.method(*nargs, **kwargs) + + def clone(self, new_object): + """ + Returns an object that re-binds the underlying "method" to + the specified new object. + """ + return self.__class__(new_object, self.method, self.name) + +class BuilderWrapper(MethodWrapper): + """ + A MethodWrapper subclass that that associates an environment with + a Builder. + + This mainly exists to wrap the __call__() function so that all calls + to Builders can have their argument lists massaged in the same way + (treat a lone argument as the source, treat two arguments as target + then source, make sure both target and source are lists) without + having to have cut-and-paste code to do it. + + As a bit of obsessive backwards compatibility, we also intercept + attempts to get or set the "env" or "builder" attributes, which were + the names we used before we put the common functionality into the + MethodWrapper base class. We'll keep this around for a while in case + people shipped Tool modules that reached into the wrapper (like the + Tool/qt.py module does, or did). There shouldn't be a lot attribute + fetching or setting on these, so a little extra work shouldn't hurt. + """ + def __call__(self, target=None, source=_null, *args, **kw): + if source is _null: + source = target + target = None + if target is not None and not SCons.Util.is_List(target): + target = [target] + if source is not None and not SCons.Util.is_List(source): + source = [source] + return MethodWrapper.__call__(self, target, source, *args, **kw) + + def __repr__(self): + return '' % repr(self.name) + + def __str__(self): + return self.__repr__() + + def __getattr__(self, name): + if name == 'env': + return self.object + elif name == 'builder': + return self.method + else: + raise AttributeError(name) + + def __setattr__(self, name, value): + if name == 'env': + self.object = value + elif name == 'builder': + self.method = value + else: + self.__dict__[name] = value + + # This allows a Builder to be executed directly + # through the Environment to which it's attached. + # In practice, we shouldn't need this, because + # builders actually get executed through a Node. + # But we do have a unit test for this, and can't + # yet rule out that it would be useful in the + # future, so leave it for now. + #def execute(self, **kw): + # kw['env'] = self.env + # self.builder.execute(**kw) + +class BuilderDict(UserDict): + """This is a dictionary-like class used by an Environment to hold + the Builders. We need to do this because every time someone changes + the Builders in the Environment's BUILDERS dictionary, we must + update the Environment's attributes.""" + def __init__(self, dict, env): + # Set self.env before calling the superclass initialization, + # because it will end up calling our other methods, which will + # need to point the values in this dictionary to self.env. + self.env = env + UserDict.__init__(self, dict) + + def __semi_deepcopy__(self): + return self.__class__(self.data, self.env) + + def __setitem__(self, item, val): + try: + method = getattr(self.env, item).method + except AttributeError: + pass + else: + self.env.RemoveMethod(method) + UserDict.__setitem__(self, item, val) + BuilderWrapper(self.env, val, item) + + def __delitem__(self, item): + UserDict.__delitem__(self, item) + delattr(self.env, item) + + def update(self, dict): + for i, v in dict.items(): + self.__setitem__(i, v) + + + +_is_valid_var = re.compile(r'[_a-zA-Z]\w*$') + +def is_valid_construction_var(varstr): + """Return if the specified string is a legitimate construction + variable. + """ + return _is_valid_var.match(varstr) + + + +class SubstitutionEnvironment(object): + """Base class for different flavors of construction environments. + + This class contains a minimal set of methods that handle contruction + variable expansion and conversion of strings to Nodes, which may or + may not be actually useful as a stand-alone class. Which methods + ended up in this class is pretty arbitrary right now. They're + basically the ones which we've empirically determined are common to + the different construction environment subclasses, and most of the + others that use or touch the underlying dictionary of construction + variables. + + Eventually, this class should contain all the methods that we + determine are necessary for a "minimal" interface to the build engine. + A full "native Python" SCons environment has gotten pretty heavyweight + with all of the methods and Tools and construction variables we've + jammed in there, so it would be nice to have a lighter weight + alternative for interfaces that don't need all of the bells and + whistles. (At some point, we'll also probably rename this class + "Base," since that more reflects what we want this class to become, + but because we've released comments that tell people to subclass + Environment.Base to create their own flavors of construction + environment, we'll save that for a future refactoring when this + class actually becomes useful.) + """ + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + def __init__(self, **kw): + """Initialization of an underlying SubstitutionEnvironment class. + """ + if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment') + self.fs = SCons.Node.FS.get_default_fs() + self.ans = SCons.Node.Alias.default_ans + self.lookup_list = SCons.Node.arg2nodes_lookups + self._dict = kw.copy() + self._init_special() + self.added_methods = [] + #self._memo = {} + + def _init_special(self): + """Initial the dispatch tables for special handling of + special construction variables.""" + self._special_del = {} + self._special_del['SCANNERS'] = _del_SCANNERS + + self._special_set = {} + for key in reserved_construction_var_names: + self._special_set[key] = _set_reserved + for key in future_reserved_construction_var_names: + self._special_set[key] = _set_future_reserved + self._special_set['BUILDERS'] = _set_BUILDERS + self._special_set['SCANNERS'] = _set_SCANNERS + + # Freeze the keys of self._special_set in a list for use by + # methods that need to check. (Empirically, list scanning has + # gotten better than dict.has_key() in Python 2.5.) + self._special_set_keys = list(self._special_set.keys()) + + def __cmp__(self, other): + return cmp(self._dict, other._dict) + + def __delitem__(self, key): + special = self._special_del.get(key) + if special: + special(self, key) + else: + del self._dict[key] + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + # This is heavily used. This implementation is the best we have + # according to the timings in bench/env.__setitem__.py. + # + # The "key in self._special_set_keys" test here seems to perform + # pretty well for the number of keys we have. A hard-coded + # list works a little better in Python 2.5, but that has the + # disadvantage of maybe getting out of sync if we ever add more + # variable names. Using self._special_set.has_key() works a + # little better in Python 2.4, but is worse than this test. + # So right now it seems like a good trade-off, but feel free to + # revisit this with bench/env.__setitem__.py as needed (and + # as newer versions of Python come out). + if key in self._special_set_keys: + self._special_set[key](self, key, value) + else: + # If we already have the entry, then it's obviously a valid + # key and we don't need to check. If we do check, using a + # global, pre-compiled regular expression directly is more + # efficient than calling another function or a method. + if key not in self._dict \ + and not _is_valid_var.match(key): + raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) + self._dict[key] = value + + def get(self, key, default=None): + """Emulates the get() method of dictionaries.""" + return self._dict.get(key, default) + + def has_key(self, key): + return key in self._dict + + def __contains__(self, key): + return self._dict.__contains__(key) + + def items(self): + return list(self._dict.items()) + + def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw): + if node_factory is _null: + node_factory = self.fs.File + if lookup_list is _null: + lookup_list = self.lookup_list + + if not args: + return [] + + args = SCons.Util.flatten(args) + + nodes = [] + for v in args: + if SCons.Util.is_String(v): + n = None + for l in lookup_list: + n = l(v) + if n is not None: + break + if n is not None: + if SCons.Util.is_String(n): + # n = self.subst(n, raw=1, **kw) + kw['raw'] = 1 + n = self.subst(n, **kw) + if node_factory: + n = node_factory(n) + if SCons.Util.is_List(n): + nodes.extend(n) + else: + nodes.append(n) + elif node_factory: + # v = node_factory(self.subst(v, raw=1, **kw)) + kw['raw'] = 1 + v = node_factory(self.subst(v, **kw)) + if SCons.Util.is_List(v): + nodes.extend(v) + else: + nodes.append(v) + else: + nodes.append(v) + + return nodes + + def gvars(self): + return self._dict + + def lvars(self): + return {} + + def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None): + """Recursively interpolates construction variables from the + Environment into the specified string, returning the expanded + result. Construction variables are specified by a $ prefix + in the string and begin with an initial underscore or + alphabetic character followed by any number of underscores + or alphanumeric characters. The construction variable names + may be surrounded by curly braces to separate the name from + trailing characters. + """ + gvars = self.gvars() + lvars = self.lvars() + lvars['__env__'] = self + if executor: + lvars.update(executor.get_lvars()) + return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv) + + def subst_kw(self, kw, raw=0, target=None, source=None): + nkw = {} + for k, v in kw.items(): + k = self.subst(k, raw, target, source) + if SCons.Util.is_String(v): + v = self.subst(v, raw, target, source) + nkw[k] = v + return nkw + + def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None): + """Calls through to SCons.Subst.scons_subst_list(). See + the documentation for that function.""" + gvars = self.gvars() + lvars = self.lvars() + lvars['__env__'] = self + if executor: + lvars.update(executor.get_lvars()) + return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv) + + def subst_path(self, path, target=None, source=None): + """Substitute a path list, turning EntryProxies into Nodes + and leaving Nodes (and other objects) as-is.""" + + if not SCons.Util.is_List(path): + path = [path] + + def s(obj): + """This is the "string conversion" routine that we have our + substitutions use to return Nodes, not strings. This relies + on the fact that an EntryProxy object has a get() method that + returns the underlying Node that it wraps, which is a bit of + architectural dependence that we might need to break or modify + in the future in response to additional requirements.""" + try: + get = obj.get + except AttributeError: + obj = SCons.Util.to_String_for_subst(obj) + else: + obj = get() + return obj + + r = [] + for p in path: + if SCons.Util.is_String(p): + p = self.subst(p, target=target, source=source, conv=s) + if SCons.Util.is_List(p): + if len(p) == 1: + p = p[0] + else: + # We have an object plus a string, or multiple + # objects that we need to smush together. No choice + # but to make them into a string. + p = ''.join(map(SCons.Util.to_String_for_subst, p)) + else: + p = s(p) + r.append(p) + return r + + subst_target_source = subst + + def backtick(self, command): + import subprocess + # common arguments + kw = { 'stdin' : 'devnull', + 'stdout' : subprocess.PIPE, + 'stderr' : subprocess.PIPE, + 'universal_newlines' : True, + } + # if the command is a list, assume it's been quoted + # othewise force a shell + if not SCons.Util.is_List(command): kw['shell'] = True + # run constructed command + p = SCons.Action._subproc(self, command, **kw) + out,err = p.communicate() + status = p.wait() + if err: + sys.stderr.write(unicode(err)) + if status: + raise OSError("'%s' exited %d" % (command, status)) + return out + + def AddMethod(self, function, name=None): + """ + Adds the specified function as a method of this construction + environment with the specified name. If the name is omitted, + the default name is the name of the function itself. + """ + method = MethodWrapper(self, function, name) + self.added_methods.append(method) + + def RemoveMethod(self, function): + """ + Removes the specified function's MethodWrapper from the + added_methods list, so we don't re-bind it when making a clone. + """ + self.added_methods = [dm for dm in self.added_methods if not dm.method is function] + + def Override(self, overrides): + """ + Produce a modified environment whose variables are overriden by + the overrides dictionaries. "overrides" is a dictionary that + will override the variables of this environment. + + This function is much more efficient than Clone() or creating + a new Environment because it doesn't copy the construction + environment dictionary, it just wraps the underlying construction + environment, and doesn't even create a wrapper object if there + are no overrides. + """ + if not overrides: return self + o = copy_non_reserved_keywords(overrides) + if not o: return self + overrides = {} + merges = None + for key, value in o.items(): + if key == 'parse_flags': + merges = value + else: + overrides[key] = SCons.Subst.scons_subst_once(value, self, key) + env = OverrideEnvironment(self, overrides) + if merges: env.MergeFlags(merges) + return env + + def ParseFlags(self, *flags): + """ + Parse the set of flags and return a dict with the flags placed + in the appropriate entry. The flags are treated as a typical + set of command-line flags for a GNU-like toolchain and used to + populate the entries in the dict immediately below. If one of + the flag strings begins with a bang (exclamation mark), it is + assumed to be a command and the rest of the string is executed; + the result of that evaluation is then added to the dict. + """ + dict = { + 'ASFLAGS' : SCons.Util.CLVar(''), + 'CFLAGS' : SCons.Util.CLVar(''), + 'CCFLAGS' : SCons.Util.CLVar(''), + '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. + """ + clone = copy.copy(self) + clone._dict = semi_deepcopy(self._dict) + + try: + cbd = clone._dict['BUILDERS'] + except KeyError: + pass + else: + clone._dict['BUILDERS'] = BuilderDict(cbd, clone) + + # Check the methods added via AddMethod() and re-bind them to + # the cloned environment. Only do this if the attribute hasn't + # been overwritten by the user explicitly and still points to + # the added method. + clone.added_methods = [] + for mw in self.added_methods: + if mw == getattr(self, mw.name): + clone.added_methods.append(mw.clone(clone)) + + clone._memo = {} + + # Apply passed-in variables before the tools + # so the tools can use the new variables + kw = copy_non_reserved_keywords(kw) + new = {} + for key, value in kw.items(): + new[key] = SCons.Subst.scons_subst_once(value, self, key) + clone.Replace(**new) + + apply_tools(clone, tools, toolpath) + + # apply them again in case the tools overwrote them + clone.Replace(**new) + + # Finally, apply any flags to be merged in + if parse_flags: clone.MergeFlags(parse_flags) + + if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone') + return clone + + def Copy(self, *args, **kw): + global _warn_copy_deprecated + if _warn_copy_deprecated: + msg = "The env.Copy() method is deprecated; use the env.Clone() method instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg) + _warn_copy_deprecated = False + return self.Clone(*args, **kw) + + def _changed_build(self, dependency, target, prev_ni): + if dependency.changed_state(target, prev_ni): + return 1 + return self.decide_source(dependency, target, prev_ni) + + def _changed_content(self, dependency, target, prev_ni): + return dependency.changed_content(target, prev_ni) + + def _changed_source(self, dependency, target, prev_ni): + target_env = dependency.get_build_env() + type = target_env.get_tgt_sig_type() + if type == 'source': + return target_env.decide_source(dependency, target, prev_ni) + else: + return target_env.decide_target(dependency, target, prev_ni) + + def _changed_timestamp_then_content(self, dependency, target, prev_ni): + return dependency.changed_timestamp_then_content(target, prev_ni) + + def _changed_timestamp_newer(self, dependency, target, prev_ni): + return dependency.changed_timestamp_newer(target, prev_ni) + + def _changed_timestamp_match(self, dependency, target, prev_ni): + return dependency.changed_timestamp_match(target, prev_ni) + + def _copy_from_cache(self, src, dst): + return self.fs.copy(src, dst) + + def _copy2_from_cache(self, src, dst): + return self.fs.copy2(src, dst) + + def Decider(self, function): + copy_function = self._copy2_from_cache + if function in ('MD5', 'content'): + if not SCons.Util.md5: + raise UserError("MD5 signatures are not available in this version of Python.") + function = self._changed_content + elif function == 'MD5-timestamp': + function = self._changed_timestamp_then_content + elif function in ('timestamp-newer', 'make'): + function = self._changed_timestamp_newer + copy_function = self._copy_from_cache + elif function == 'timestamp-match': + function = self._changed_timestamp_match + elif not callable(function): + raise UserError("Unknown Decider value %s" % repr(function)) + + # We don't use AddMethod because we don't want to turn the + # function, which only expects three arguments, into a bound + # method, which would add self as an initial, fourth argument. + self.decide_target = function + self.decide_source = function + + self.copy_from_cache = copy_function + + def Detect(self, progs): + """Return the first available program in progs. + """ + if not SCons.Util.is_List(progs): + progs = [ progs ] + for prog in progs: + path = self.WhereIs(prog) + if path: return prog + return None + + def Dictionary(self, *args): + if not args: + return self._dict + dlist = [self._dict[x] for x in args] + if len(dlist) == 1: + dlist = dlist[0] + return dlist + + def Dump(self, key = None): + """ + Using the standard Python pretty printer, dump the contents of the + scons build environment to stdout. + + If the key passed in is anything other than None, then that will + be used as an index into the build environment dictionary and + whatever is found there will be fed into the pretty printer. Note + that this key is case sensitive. + """ + import pprint + pp = pprint.PrettyPrinter(indent=2) + if key: + dict = self.Dictionary(key) + else: + dict = self.Dictionary() + return pp.pformat(dict) + + def FindIxes(self, paths, prefix, suffix): + """ + Search a list of paths for something that matches the prefix and suffix. + + paths - the list of paths or nodes. + prefix - construction variable for the prefix. + suffix - construction variable for the suffix. + """ + + suffix = self.subst('$'+suffix) + prefix = self.subst('$'+prefix) + + for path in paths: + dir,name = os.path.split(str(path)) + if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: + return path + + def ParseConfig(self, command, function=None, unique=1): + """ + Use the specified function to parse the output of the command + in order to modify the current environment. The 'command' can + be a string or a list of strings representing a command and + its arguments. 'Function' is an optional argument that takes + the environment, the output of the command, and the unique flag. + If no function is specified, MergeFlags, which treats the output + as the result of a typical 'X-config' command (i.e. gtk-config), + will merge the output into the appropriate variables. + """ + if function is None: + def parse_conf(env, cmd, unique=unique): + return env.MergeFlags(cmd, unique) + function = parse_conf + if SCons.Util.is_List(command): + command = ' '.join(command) + command = self.subst(command) + return function(self, self.backtick(command)) + + def ParseDepends(self, filename, must_exist=None, only_one=0): + """ + Parse a mkdep-style file for explicit dependencies. This is + completely abusable, and should be unnecessary in the "normal" + case of proper SCons configuration, but it may help make + the transition from a Make hierarchy easier for some people + to swallow. It can also be genuinely useful when using a tool + that can write a .d file, but for which writing a scanner would + be too complicated. + """ + filename = self.subst(filename) + try: + fp = open(filename, 'r') + except IOError: + if must_exist: + raise + return + lines = SCons.Util.LogicalLines(fp).readlines() + lines = [l for l in lines if l[0] != '#'] + tdlist = [] + for line in lines: + try: + target, depends = line.split(':', 1) + except (AttributeError, ValueError): + # Throws AttributeError if line isn't a string. Can throw + # ValueError if line doesn't split into two or more elements. + pass + else: + tdlist.append((target.split(), depends.split())) + if only_one: + targets = [] + for td in tdlist: + targets.extend(td[0]) + if len(targets) > 1: + raise SCons.Errors.UserError( + "More than one dependency target found in `%s': %s" + % (filename, targets)) + for target, depends in tdlist: + self.Depends(target, depends) + + def Platform(self, platform): + platform = self.subst(platform) + return SCons.Platform.Platform(platform)(self) + + def Prepend(self, **kw): + """Prepend values to existing construction variables + in an Environment. + """ + kw = copy_non_reserved_keywords(kw) + for key, val in kw.items(): + # It would be easier on the eyes to write this using + # "continue" statements whenever we finish processing an item, + # but Python 1.5.2 apparently doesn't let you use "continue" + # within try:-except: blocks, so we have to nest our code. + try: + orig = self._dict[key] + except KeyError: + # No existing variable in the environment, so just set + # it to the new value. + self._dict[key] = val + else: + try: + # Check if the original looks like a dictionary. + # If it is, we can't just try adding the value because + # dictionaries don't have __add__() methods, and + # things like UserList will incorrectly coerce the + # original dict to a list (which we don't want). + update_dict = orig.update + except AttributeError: + try: + # Most straightforward: just try to add them + # together. This will work in most cases, when the + # original and new values are of compatible types. + self._dict[key] = val + orig + except (KeyError, TypeError): + try: + # Check if the added value is a list. + add_to_val = val.append + except AttributeError: + # The added value isn't a list, but the + # original is (by process of elimination), + # so insert the the new value in the original + # (if there's one to insert). + if val: + orig.insert(0, val) + else: + # The added value is a list, so append + # the original to it (if there's a value + # to append). + if orig: + add_to_val(orig) + self._dict[key] = val + else: + # The original looks like a dictionary, so update it + # based on what we think the value looks like. + if SCons.Util.is_List(val): + for v in val: + orig[v] = None + else: + try: + update_dict(val) + except (AttributeError, TypeError, ValueError): + if SCons.Util.is_Dict(val): + for k, v in val.items(): + orig[k] = v + else: + orig[val] = None + self.scanner_map_delete(kw) + + def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep, + delete_existing=1): + """Prepend path elements to the path 'name' in the 'ENV' + dictionary for this environment. Will only add any particular + path once, and will normpath and normcase all paths to help + assure this. This can also handle the case where the env + variable is a list instead of a string. + + If delete_existing is 0, a newpath which is already in the path + will not be moved to the front (it will be left where it is). + """ + + orig = '' + if envname in self._dict and name in self._dict[envname]: + orig = self._dict[envname][name] + + nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing, + canonicalize=self._canonicalize) + + if envname not in self._dict: + self._dict[envname] = {} + + self._dict[envname][name] = nv + + def PrependUnique(self, delete_existing=0, **kw): + """Prepend values to existing construction variables + in an Environment, if they're not already there. + If delete_existing is 1, removes existing values first, so + values move to front. + """ + kw = copy_non_reserved_keywords(kw) + for key, val in kw.items(): + if SCons.Util.is_List(val): + val = _delete_duplicates(val, not delete_existing) + if key not in self._dict or self._dict[key] in ('', None): + self._dict[key] = val + elif SCons.Util.is_Dict(self._dict[key]) and \ + SCons.Util.is_Dict(val): + self._dict[key].update(val) + elif SCons.Util.is_List(val): + dk = self._dict[key] + if not SCons.Util.is_List(dk): + dk = [dk] + if delete_existing: + dk = [x for x in dk if x not in val] + else: + val = [x for x in val if x not in dk] + self._dict[key] = val + dk + else: + dk = self._dict[key] + if SCons.Util.is_List(dk): + # By elimination, val is not a list. Since dk is a + # list, wrap val in a list first. + if delete_existing: + dk = [x for x in dk if x not in val] + self._dict[key] = [val] + dk + else: + if not val in dk: + self._dict[key] = [val] + dk + else: + if delete_existing: + dk = [x for x in dk if x not in val] + self._dict[key] = val + dk + self.scanner_map_delete(kw) + + def Replace(self, **kw): + """Replace existing construction variables in an Environment + with new construction variables and/or values. + """ + try: + kwbd = kw['BUILDERS'] + except KeyError: + pass + else: + kwbd = semi_deepcopy(kwbd) + del kw['BUILDERS'] + self.__setitem__('BUILDERS', kwbd) + kw = copy_non_reserved_keywords(kw) + self._update(semi_deepcopy(kw)) + self.scanner_map_delete(kw) + + def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix): + """ + Replace old_prefix with new_prefix and old_suffix with new_suffix. + + env - Environment used to interpolate variables. + path - the path that will be modified. + old_prefix - construction variable for the old prefix. + old_suffix - construction variable for the old suffix. + new_prefix - construction variable for the new prefix. + new_suffix - construction variable for the new suffix. + """ + old_prefix = self.subst('$'+old_prefix) + old_suffix = self.subst('$'+old_suffix) + + new_prefix = self.subst('$'+new_prefix) + new_suffix = self.subst('$'+new_suffix) + + dir,name = os.path.split(str(path)) + if name[:len(old_prefix)] == old_prefix: + name = name[len(old_prefix):] + if name[-len(old_suffix):] == old_suffix: + name = name[:-len(old_suffix)] + return os.path.join(dir, new_prefix+name+new_suffix) + + def SetDefault(self, **kw): + for k in kw.keys(): + if k in self._dict: + del kw[k] + self.Replace(**kw) + + def _find_toolpath_dir(self, tp): + return self.fs.Dir(self.subst(tp)).srcnode().abspath + + def Tool(self, tool, toolpath=None, **kw): + if SCons.Util.is_String(tool): + tool = self.subst(tool) + if toolpath is None: + toolpath = self.get('toolpath', []) + toolpath = list(map(self._find_toolpath_dir, toolpath)) + tool = SCons.Tool.Tool(tool, toolpath, **kw) + tool(self) + + def WhereIs(self, prog, path=None, pathext=None, reject=[]): + """Find prog in the path. + """ + if path is None: + try: + path = self['ENV']['PATH'] + except KeyError: + pass + elif SCons.Util.is_String(path): + path = self.subst(path) + if pathext is None: + try: + pathext = self['ENV']['PATHEXT'] + except KeyError: + pass + elif SCons.Util.is_String(pathext): + pathext = self.subst(pathext) + prog = self.subst(prog) + path = SCons.Util.WhereIs(prog, path, pathext, reject) + if path: return path + return None + + ####################################################################### + # Public methods for doing real "SCons stuff" (manipulating + # dependencies, setting attributes on targets, etc.). These begin + # with upper-case letters. The essential characteristic of methods + # in this section is that they all *should* have corresponding + # same-named global functions. + ####################################################################### + + def Action(self, *args, **kw): + def subst_string(a, self=self): + if SCons.Util.is_String(a): + a = self.subst(a) + return a + nargs = list(map(subst_string, args)) + nkw = self.subst_kw(kw) + return SCons.Action.Action(*nargs, **nkw) + + def AddPreAction(self, files, action): + nodes = self.arg2nodes(files, self.fs.Entry) + action = SCons.Action.Action(action) + uniq = {} + for executor in [n.get_executor() for n in nodes]: + uniq[executor] = 1 + for executor in uniq.keys(): + executor.add_pre_action(action) + return nodes + + def AddPostAction(self, files, action): + nodes = self.arg2nodes(files, self.fs.Entry) + action = SCons.Action.Action(action) + uniq = {} + for executor in [n.get_executor() for n in nodes]: + uniq[executor] = 1 + for executor in uniq.keys(): + executor.add_post_action(action) + return nodes + + def Alias(self, target, source=[], action=None, **kw): + tlist = self.arg2nodes(target, self.ans.Alias) + if not SCons.Util.is_List(source): + source = [source] + source = [_f for _f in source if _f] + + if not action: + if not source: + # There are no source files and no action, so just + # return a target list of classic Alias Nodes, without + # any builder. The externally visible effect is that + # this will make the wrapping Script.BuildTask class + # say that there's "Nothing to be done" for this Alias, + # instead of that it's "up to date." + return tlist + + # No action, but there are sources. Re-call all the target + # builders to add the sources to each target. + result = [] + for t in tlist: + bld = t.get_builder(AliasBuilder) + result.extend(bld(self, t, source)) + return result + + nkw = self.subst_kw(kw) + nkw.update({ + 'action' : SCons.Action.Action(action), + 'source_factory' : self.fs.Entry, + 'multi' : 1, + 'is_explicit' : None, + }) + bld = SCons.Builder.Builder(**nkw) + + # Apply the Builder separately to each target so that the Aliases + # stay separate. If we did one "normal" Builder call with the + # whole target list, then all of the target Aliases would be + # associated under a single Executor. + result = [] + for t in tlist: + # Calling the convert() method will cause a new Executor to be + # created from scratch, so we have to explicitly initialize + # it with the target's existing sources, plus our new ones, + # so nothing gets lost. + b = t.get_builder() + if b is None or b is AliasBuilder: + b = bld + else: + nkw['action'] = b.action + action + b = SCons.Builder.Builder(**nkw) + t.convert() + result.extend(b(self, t, t.sources + source)) + return result + + def AlwaysBuild(self, *targets): + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_always_build() + return tlist + + def BuildDir(self, *args, **kw): + msg = """BuildDir() and the build_dir keyword have been deprecated;\n\tuse VariantDir() and the variant_dir keyword instead.""" + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) + if 'build_dir' in kw: + kw['variant_dir'] = kw['build_dir'] + del kw['build_dir'] + return self.VariantDir(*args, **kw) + + def Builder(self, **kw): + nkw = self.subst_kw(kw) + return SCons.Builder.Builder(**nkw) + + def CacheDir(self, path): + import SCons.CacheDir + if path is not None: + path = self.subst(path) + self._CacheDir_path = path + + def Clean(self, targets, files): + global CleanTargets + tlist = self.arg2nodes(targets, self.fs.Entry) + flist = self.arg2nodes(files, self.fs.Entry) + for t in tlist: + try: + CleanTargets[t].extend(flist) + except KeyError: + CleanTargets[t] = flist + + def Configure(self, *args, **kw): + nargs = [self] + if args: + nargs = nargs + self.subst_list(args)[0] + nkw = self.subst_kw(kw) + nkw['_depth'] = kw.get('_depth', 0) + 1 + try: + nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) + except KeyError: + pass + return SCons.SConf.SConf(*nargs, **nkw) + + def Command(self, target, source, action, **kw): + """Builds the supplied target files from the supplied + source files using the supplied action. Action may + be any type that the Builder constructor will accept + for an action.""" + bkw = { + 'action' : action, + 'target_factory' : self.fs.Entry, + 'source_factory' : self.fs.Entry, + } + try: bkw['source_scanner'] = kw['source_scanner'] + except KeyError: pass + else: del kw['source_scanner'] + bld = SCons.Builder.Builder(**bkw) + return bld(self, target, source, **kw) + + def Depends(self, target, dependency): + """Explicity specify that 'target's depend on 'dependency'.""" + tlist = self.arg2nodes(target, self.fs.Entry) + dlist = self.arg2nodes(dependency, self.fs.Entry) + for t in tlist: + t.add_dependency(dlist) + return tlist + + def Dir(self, name, *args, **kw): + """ + """ + s = self.subst(name) + if SCons.Util.is_Sequence(s): + result=[] + for e in s: + result.append(self.fs.Dir(e, *args, **kw)) + return result + return self.fs.Dir(s, *args, **kw) + + def NoClean(self, *targets): + """Tags a target so that it will not be cleaned by -c""" + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_noclean() + return tlist + + def NoCache(self, *targets): + """Tags a target so that it will not be cached""" + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_nocache() + return tlist + + def Entry(self, name, *args, **kw): + """ + """ + s = self.subst(name) + if SCons.Util.is_Sequence(s): + result=[] + for e in s: + result.append(self.fs.Entry(e, *args, **kw)) + return result + return self.fs.Entry(s, *args, **kw) + + def Environment(self, **kw): + return SCons.Environment.Environment(**self.subst_kw(kw)) + + def Execute(self, action, *args, **kw): + """Directly execute an action through an Environment + """ + action = self.Action(action, *args, **kw) + result = action([], [], self) + if isinstance(result, SCons.Errors.BuildError): + errstr = result.errstr + if result.filename: + errstr = result.filename + ': ' + errstr + sys.stderr.write("scons: *** %s\n" % errstr) + return result.status + else: + return result + + def File(self, name, *args, **kw): + """ + """ + s = self.subst(name) + if SCons.Util.is_Sequence(s): + result=[] + for e in s: + result.append(self.fs.File(e, *args, **kw)) + return result + return self.fs.File(s, *args, **kw) + + def FindFile(self, file, dirs): + file = self.subst(file) + nodes = self.arg2nodes(dirs, self.fs.Dir) + return SCons.Node.FS.find_file(file, tuple(nodes)) + + def Flatten(self, sequence): + return SCons.Util.flatten(sequence) + + def GetBuildPath(self, files): + result = list(map(str, self.arg2nodes(files, self.fs.Entry))) + if SCons.Util.is_List(files): + return result + else: + return result[0] + + def Glob(self, pattern, ondisk=True, source=False, strings=False): + return self.fs.Glob(self.subst(pattern), ondisk, source, strings) + + def Ignore(self, target, dependency): + """Ignore a dependency.""" + tlist = self.arg2nodes(target, self.fs.Entry) + dlist = self.arg2nodes(dependency, self.fs.Entry) + for t in tlist: + t.add_ignore(dlist) + return tlist + + def Literal(self, string): + return SCons.Subst.Literal(string) + + def Local(self, *targets): + ret = [] + for targ in targets: + if isinstance(targ, SCons.Node.Node): + targ.set_local() + ret.append(targ) + else: + for t in self.arg2nodes(targ, self.fs.Entry): + t.set_local() + ret.append(t) + return ret + + def Precious(self, *targets): + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_precious() + return tlist + + def Repository(self, *dirs, **kw): + dirs = self.arg2nodes(list(dirs), self.fs.Dir) + self.fs.Repository(*dirs, **kw) + + def Requires(self, target, prerequisite): + """Specify that 'prerequisite' must be built before 'target', + (but 'target' does not actually depend on 'prerequisite' + and need not be rebuilt if it changes).""" + tlist = self.arg2nodes(target, self.fs.Entry) + plist = self.arg2nodes(prerequisite, self.fs.Entry) + for t in tlist: + t.add_prerequisite(plist) + return tlist + + def Scanner(self, *args, **kw): + nargs = [] + for arg in args: + if SCons.Util.is_String(arg): + arg = self.subst(arg) + nargs.append(arg) + nkw = self.subst_kw(kw) + return SCons.Scanner.Base(*nargs, **nkw) + + def SConsignFile(self, name=".sconsign", dbm_module=None): + if name is not None: + name = self.subst(name) + if not os.path.isabs(name): + name = os.path.join(str(self.fs.SConstruct_dir), name) + if name: + name = os.path.normpath(name) + sconsign_dir = os.path.dirname(name) + if sconsign_dir and not os.path.exists(sconsign_dir): + self.Execute(SCons.Defaults.Mkdir(sconsign_dir)) + SCons.SConsign.File(name, dbm_module) + + def SideEffect(self, side_effect, target): + """Tell scons that side_effects are built as side + effects of building targets.""" + side_effects = self.arg2nodes(side_effect, self.fs.Entry) + targets = self.arg2nodes(target, self.fs.Entry) + + for side_effect in side_effects: + if side_effect.multiple_side_effect_has_builder(): + raise SCons.Errors.UserError("Multiple ways to build the same target were specified for: %s" % str(side_effect)) + side_effect.add_source(targets) + side_effect.side_effect = 1 + self.Precious(side_effect) + for target in targets: + target.side_effects.append(side_effect) + return side_effects + + def SourceCode(self, entry, builder): + """Arrange for a source code builder for (part of) a tree.""" + msg = """SourceCode() has been deprecated and there is no replacement. +\tIf you need this function, please contact dev@scons.tigris.org.""" + SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceCodeWarning, msg) + entries = self.arg2nodes(entry, self.fs.Entry) + for entry in entries: + entry.set_src_builder(builder) + return entries + + def SourceSignatures(self, type): + global _warn_source_signatures_deprecated + if _warn_source_signatures_deprecated: + msg = "The env.SourceSignatures() method is deprecated;\n" + \ + "\tconvert your build to use the env.Decider() method instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) + _warn_source_signatures_deprecated = False + type = self.subst(type) + self.src_sig_type = type + if type == 'MD5': + if not SCons.Util.md5: + raise UserError("MD5 signatures are not available in this version of Python.") + self.decide_source = self._changed_content + elif type == 'timestamp': + self.decide_source = self._changed_timestamp_match + else: + raise UserError("Unknown source signature type '%s'" % type) + + def Split(self, arg): + """This function converts a string or list into a list of strings + or Nodes. This makes things easier for users by allowing files to + be specified as a white-space separated list to be split. + The input rules are: + - A single string containing names separated by spaces. These will be + split apart at the spaces. + - A single Node instance + - A list containing either strings or Node instances. Any strings + in the list are not split at spaces. + In all cases, the function returns a list of Nodes and strings.""" + if SCons.Util.is_List(arg): + return list(map(self.subst, arg)) + elif SCons.Util.is_String(arg): + return self.subst(arg).split() + else: + return [self.subst(arg)] + + def TargetSignatures(self, type): + global _warn_target_signatures_deprecated + if _warn_target_signatures_deprecated: + msg = "The env.TargetSignatures() method is deprecated;\n" + \ + "\tconvert your build to use the env.Decider() method instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) + _warn_target_signatures_deprecated = False + type = self.subst(type) + self.tgt_sig_type = type + if type in ('MD5', 'content'): + if not SCons.Util.md5: + raise UserError("MD5 signatures are not available in this version of Python.") + self.decide_target = self._changed_content + elif type == 'timestamp': + self.decide_target = self._changed_timestamp_match + elif type == 'build': + self.decide_target = self._changed_build + elif type == 'source': + self.decide_target = self._changed_source + else: + raise UserError("Unknown target signature type '%s'"%type) + + def Value(self, value, built_value=None): + """ + """ + return SCons.Node.Python.Value(value, built_value) + + def VariantDir(self, variant_dir, src_dir, duplicate=1): + variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] + src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] + self.fs.VariantDir(variant_dir, src_dir, duplicate) + + def FindSourceFiles(self, node='.'): + """ returns a list of all source files. + """ + node = self.arg2nodes(node, self.fs.Entry)[0] + + sources = [] + def build_source(ss): + for s in ss: + if isinstance(s, SCons.Node.FS.Dir): + build_source(s.all_children()) + elif s.has_builder(): + build_source(s.sources) + elif isinstance(s.disambiguate(), SCons.Node.FS.File): + sources.append(s) + build_source(node.all_children()) + + # THIS CODE APPEARS TO HAVE NO EFFECT + # # get the final srcnode for all nodes, this means stripping any + # # attached build node by calling the srcnode function + # for file in sources: + # srcnode = file.srcnode() + # while srcnode != file.srcnode(): + # srcnode = file.srcnode() + + # remove duplicates + return list(set(sources)) + + def FindInstalledFiles(self): + """ returns the list of all targets of the Install and InstallAs Builder. + """ + from SCons.Tool import install + if install._UNIQUE_INSTALLED_FILES is None: + install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) + return install._UNIQUE_INSTALLED_FILES + +class OverrideEnvironment(Base): + """A proxy that overrides variables in a wrapped construction + environment by returning values from an overrides dictionary in + preference to values from the underlying subject environment. + + This is a lightweight (I hope) proxy that passes through most use of + attributes to the underlying Environment.Base class, but has just + enough additional methods defined to act like a real construction + environment with overridden values. It can wrap either a Base + construction environment, or another OverrideEnvironment, which + can in turn nest arbitrary OverrideEnvironments... + + Note that we do *not* call the underlying base class + (SubsitutionEnvironment) initialization, because we get most of those + from proxying the attributes of the subject construction environment. + But because we subclass SubstitutionEnvironment, this class also + has inherited arg2nodes() and subst*() methods; those methods can't + be proxied because they need *this* object's methods to fetch the + values from the overrides dictionary. + """ + + def __init__(self, subject, overrides={}): + if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment') + self.__dict__['__subject'] = subject + self.__dict__['overrides'] = overrides + + # Methods that make this class act like a proxy. + def __getattr__(self, name): + return getattr(self.__dict__['__subject'], name) + def __setattr__(self, name, value): + setattr(self.__dict__['__subject'], name, value) + + # Methods that make this class act like a dictionary. + def __getitem__(self, key): + try: + return self.__dict__['overrides'][key] + except KeyError: + return self.__dict__['__subject'].__getitem__(key) + def __setitem__(self, key, value): + if not is_valid_construction_var(key): + raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) + self.__dict__['overrides'][key] = value + def __delitem__(self, key): + try: + del self.__dict__['overrides'][key] + except KeyError: + deleted = 0 + else: + deleted = 1 + try: + result = self.__dict__['__subject'].__delitem__(key) + except KeyError: + if not deleted: + raise + result = None + return result + def get(self, key, default=None): + """Emulates the get() method of dictionaries.""" + try: + return self.__dict__['overrides'][key] + except KeyError: + return self.__dict__['__subject'].get(key, default) + def has_key(self, key): + try: + self.__dict__['overrides'][key] + return 1 + except KeyError: + return key in self.__dict__['__subject'] + def __contains__(self, key): + if self.__dict__['overrides'].__contains__(key): + return 1 + return self.__dict__['__subject'].__contains__(key) + def Dictionary(self): + """Emulates the items() method of dictionaries.""" + d = self.__dict__['__subject'].Dictionary().copy() + d.update(self.__dict__['overrides']) + return d + def items(self): + """Emulates the items() method of dictionaries.""" + return list(self.Dictionary().items()) + + # Overridden private construction environment methods. + def _update(self, dict): + """Update an environment's values directly, bypassing the normal + checks that occur when users try to set items. + """ + self.__dict__['overrides'].update(dict) + + def gvars(self): + return self.__dict__['__subject'].gvars() + + def lvars(self): + lvars = self.__dict__['__subject'].lvars() + lvars.update(self.__dict__['overrides']) + return lvars + + # Overridden public construction environment methods. + def Replace(self, **kw): + kw = copy_non_reserved_keywords(kw) + self.__dict__['overrides'].update(semi_deepcopy(kw)) + +# The entry point that will be used by the external world +# to refer to a construction environment. This allows the wrapper +# interface to extend a construction environment for its own purposes +# by subclassing SCons.Environment.Base and then assigning the +# class to SCons.Environment.Environment. + +Environment = Base + +# An entry point for returning a proxy subclass instance that overrides +# the subst*() methods so they don't actually perform construction +# variable substitution. This is specifically intended to be the shim +# layer in between global function calls (which don't want construction +# variable substitution) and the DefaultEnvironment() (which would +# substitute variables if left to its own devices).""" +# +# We have to wrap this in a function that allows us to delay definition of +# the class until it's necessary, so that when it subclasses Environment +# it will pick up whatever Environment subclass the wrapper interface +# might have assigned to SCons.Environment.Environment. + +def NoSubstitutionProxy(subject): + class _NoSubstitutionProxy(Environment): + def __init__(self, subject): + self.__dict__['__subject'] = subject + def __getattr__(self, name): + return getattr(self.__dict__['__subject'], name) + def __setattr__(self, name, value): + return setattr(self.__dict__['__subject'], name, value) + def 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/engine/SCons/Errors.py b/engine/SCons/Errors.py new file mode 100644 index 0000000..2501e36 --- /dev/null +++ b/engine/SCons/Errors.py @@ -0,0 +1,205 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +"""SCons.Errors + +This file contains the exception classes used to handle internal +and user errors in SCons. + +""" + +__revision__ = "src/engine/SCons/Errors.py 5357 2011/09/09 21:31:03 bdeegan" + +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/engine/SCons/Executor.py b/engine/SCons/Executor.py new file mode 100644 index 0000000..f343568 --- /dev/null +++ b/engine/SCons/Executor.py @@ -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 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 5357 2011/09/09 21:31:03 bdeegan" + +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/engine/SCons/Job.py b/engine/SCons/Job.py new file mode 100644 index 0000000..04bb4c4 --- /dev/null +++ b/engine/SCons/Job.py @@ -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 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 5357 2011/09/09 21:31:03 bdeegan" + +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 require adding a whole + bunch of try/finally block and except KeyboardInterrupt all + over the place. + + Note also that we have to be careful to handle the case when + SCons forks before executing another process. In that case, we + want the child to exit immediately. + """ + def handler(signum, stack, self=self, parentpid=os.getpid()): + if os.getpid() == parentpid: + self.job.taskmaster.stop() + self.job.interrupted.set() + else: + os._exit(2) + + self.old_sigint = signal.signal(signal.SIGINT, handler) + self.old_sigterm = signal.signal(signal.SIGTERM, handler) + try: + self.old_sighup = signal.signal(signal.SIGHUP, handler) + except AttributeError: + pass + + def _reset_sig_handler(self): + """Restore the signal handlers to their previous state (before the + call to _setup_sig_handler().""" + + signal.signal(signal.SIGINT, self.old_sigint) + signal.signal(signal.SIGTERM, self.old_sigterm) + try: + signal.signal(signal.SIGHUP, self.old_sighup) + except AttributeError: + pass + +class Serial(object): + """This class is used to execute tasks in series, and is more efficient + than Parallel, but is only appropriate for non-parallel builds. Only + one instance of this class should be in existence at a time. + + This class is not thread safe. + """ + + def __init__(self, taskmaster): + """Create a new serial job given a taskmaster. + + The taskmaster's next_task() method should return the next task + that needs to be executed, or None if there are no more tasks. The + taskmaster's executed() method will be called for each task when it + is successfully executed or failed() will be called if it failed to + execute (e.g. execute() raised an exception).""" + + self.taskmaster = taskmaster + self.interrupted = InterruptState() + + def start(self): + """Start the job. This will begin pulling tasks from the taskmaster + and executing them, and return when there are no more tasks. If a task + fails to execute (i.e. execute() raises an exception), then the job will + stop.""" + + while True: + task = self.taskmaster.next_task() + + if task is None: + break + + try: + task.prepare() + if task.needs_execute(): + task.execute() + except: + if self.interrupted(): + try: + raise SCons.Errors.BuildError( + task.targets[0], errstr=interrupt_msg) + except: + task.exception_set() + else: + task.exception_set() + + # Let the failed() callback function arrange for the + # build to stop if that's appropriate. + task.failed() + else: + task.executed() + + task.postprocess() + self.taskmaster.cleanup() + + +# Trap import failure so that everything in the Job module but the +# Parallel class (and its dependent classes) will work if the interpreter +# doesn't support threads. +try: + import queue + import threading +except ImportError: + pass +else: + class Worker(threading.Thread): + """A worker thread waits on a task to be posted to its request queue, + dequeues the task, executes it, and posts a tuple including the task + and a boolean indicating whether the task executed successfully. """ + + def __init__(self, requestQueue, resultsQueue, interrupted): + threading.Thread.__init__(self) + self.setDaemon(1) + self.requestQueue = requestQueue + self.resultsQueue = resultsQueue + self.interrupted = interrupted + self.start() + + def run(self): + while True: + task = self.requestQueue.get() + + if task is None: + # The "None" value is used as a sentinel by + # ThreadPool.cleanup(). This indicates that there + # are no more tasks, so we should quit. + break + + try: + if self.interrupted(): + raise SCons.Errors.BuildError( + task.targets[0], errstr=interrupt_msg) + task.execute() + except: + task.exception_set() + ok = False + else: + ok = True + + self.resultsQueue.put((task, ok)) + + class ThreadPool(object): + """This class is responsible for spawning and managing worker threads.""" + + def __init__(self, num, stack_size, interrupted): + """Create the request and reply queues, and 'num' worker threads. + + One must specify the stack size of the worker threads. The + stack size is specified in kilobytes. + """ + self.requestQueue = queue.Queue(0) + self.resultsQueue = queue.Queue(0) + + try: + prev_size = threading.stack_size(stack_size*1024) + except AttributeError, e: + # Only print a warning if the stack size has been + # explicitly set. + if not explicit_stack_size is None: + msg = "Setting stack size is unsupported by this version of Python:\n " + \ + e.args[0] + SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) + except ValueError, e: + msg = "Setting stack size failed:\n " + str(e) + SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) + + # Create worker threads + self.workers = [] + for _ in range(num): + worker = Worker(self.requestQueue, self.resultsQueue, interrupted) + self.workers.append(worker) + + if 'prev_size' in locals(): + threading.stack_size(prev_size) + + def put(self, task): + """Put task into request queue.""" + self.requestQueue.put(task) + + def get(self): + """Remove and return a result tuple from the results queue.""" + return self.resultsQueue.get() + + def preparation_failed(self, task): + self.resultsQueue.put((task, False)) + + def cleanup(self): + """ + Shuts down the thread pool, giving each worker thread a + chance to shut down gracefully. + """ + # For each worker thread, put a sentinel "None" value + # on the requestQueue (indicating that there's no work + # to be done) so that each worker thread will get one and + # terminate gracefully. + for _ in self.workers: + self.requestQueue.put(None) + + # Wait for all of the workers to terminate. + # + # If we don't do this, later Python versions (2.4, 2.5) often + # seem to raise exceptions during shutdown. This happens + # in requestQueue.get(), as an assertion failure that + # requestQueue.not_full is notified while not acquired, + # seemingly because the main thread has shut down (or is + # in the process of doing so) while the workers are still + # trying to pull sentinels off the requestQueue. + # + # Normally these terminations should happen fairly quickly, + # but we'll stick a one-second timeout on here just in case + # someone gets hung. + for worker in self.workers: + worker.join(1.0) + self.workers = [] + + class Parallel(object): + """This class is used to execute tasks in parallel, and is somewhat + less efficient than Serial, but is appropriate for parallel builds. + + This class is thread safe. + """ + + def __init__(self, taskmaster, num, stack_size): + """Create a new parallel job given a taskmaster. + + The taskmaster's next_task() method should return the next + task that needs to be executed, or None if there are no more + tasks. The taskmaster's executed() method will be called + for each task when it is successfully executed or failed() + will be called if the task failed to execute (i.e. execute() + raised an exception). + + Note: calls to taskmaster are serialized, but calls to + execute() on distinct tasks are not serialized, because + that is the whole point of parallel jobs: they can execute + multiple tasks simultaneously. """ + + self.taskmaster = taskmaster + self.interrupted = InterruptState() + self.tp = ThreadPool(num, stack_size, self.interrupted) + + self.maxjobs = num + + def start(self): + """Start the job. This will begin pulling tasks from the + taskmaster and executing them, and return when there are no + more tasks. If a task fails to execute (i.e. execute() raises + an exception), then the job will stop.""" + + jobs = 0 + + while True: + # Start up as many available tasks as we're + # allowed to. + while jobs < self.maxjobs: + task = self.taskmaster.next_task() + if task is None: + break + + try: + # prepare task for execution + task.prepare() + except: + task.exception_set() + task.failed() + task.postprocess() + else: + if task.needs_execute(): + # dispatch task + self.tp.put(task) + jobs = jobs + 1 + else: + task.executed() + task.postprocess() + + if not task and not jobs: break + + # Let any/all completed tasks finish up before we go + # back and put the next batch of tasks on the queue. + while True: + task, ok = self.tp.get() + jobs = jobs - 1 + + if ok: + task.executed() + else: + if self.interrupted(): + try: + raise SCons.Errors.BuildError( + task.targets[0], errstr=interrupt_msg) + except: + task.exception_set() + + # Let the failed() callback function arrange + # for the build to stop if that's appropriate. + task.failed() + + task.postprocess() + + if self.tp.resultsQueue.empty(): + break + + self.tp.cleanup() + self.taskmaster.cleanup() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Memoize.py b/engine/SCons/Memoize.py new file mode 100644 index 0000000..ea7d58f --- /dev/null +++ b/engine/SCons/Memoize.py @@ -0,0 +1,244 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Memoize.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Memoizer + +A metaclass implementation to count hits and misses of the computed +values that various methods cache in memory. + +Use of this modules assumes that wrapped methods be coded to cache their +values in a consistent way. Here is an example of wrapping a method +that returns a computed value, with no input parameters: + + memoizer_counters = [] # Memoization + + memoizer_counters.append(SCons.Memoize.CountValue('foo')) # Memoization + + def foo(self): + + try: # Memoization + return self._memo['foo'] # Memoization + except KeyError: # Memoization + pass # Memoization + + result = self.compute_foo_value() + + self._memo['foo'] = result # Memoization + + return result + +Here is an example of wrapping a method that will return different values +based on one or more input arguments: + + def _bar_key(self, argument): # Memoization + return argument # Memoization + + memoizer_counters.append(SCons.Memoize.CountDict('bar', _bar_key)) # Memoization + + def bar(self, argument): + + memo_key = argument # Memoization + try: # Memoization + memo_dict = self._memo['bar'] # Memoization + except KeyError: # Memoization + memo_dict = {} # Memoization + self._memo['dict'] = memo_dict # Memoization + else: # Memoization + try: # Memoization + return memo_dict[memo_key] # Memoization + except KeyError: # Memoization + pass # Memoization + + result = self.compute_bar_value(argument) + + memo_dict[memo_key] = result # Memoization + + return result + +At one point we avoided replicating this sort of logic in all the methods +by putting it right into this module, but we've moved away from that at +present (see the "Historical Note," below.). + +Deciding what to cache is tricky, because different configurations +can have radically different performance tradeoffs, and because the +tradeoffs involved are often so non-obvious. Consequently, deciding +whether or not to cache a given method will likely be more of an art than +a science, but should still be based on available data from this module. +Here are some VERY GENERAL guidelines about deciding whether or not to +cache return values from a method that's being called a lot: + + -- The first question to ask is, "Can we change the calling code + so this method isn't called so often?" Sometimes this can be + done by changing the algorithm. Sometimes the *caller* should + be memoized, not the method you're looking at. + + -- The memoized function should be timed with multiple configurations + to make sure it doesn't inadvertently slow down some other + configuration. + + -- When memoizing values based on a dictionary key composed of + input arguments, you don't need to use all of the arguments + if some of them don't affect the return values. + +Historical Note: The initial Memoizer implementation actually handled +the caching of values for the wrapped methods, based on a set of generic +algorithms for computing hashable values based on the method's arguments. +This collected caching logic nicely, but had two drawbacks: + + Running arguments through a generic key-conversion mechanism is slower + (and less flexible) than just coding these things directly. Since the + methods that need memoized values are generally performance-critical, + slowing them down in order to collect the logic isn't the right + tradeoff. + + Use of the memoizer really obscured what was being called, because + all the memoized methods were wrapped with re-used generic methods. + This made it more difficult, for example, to use the Python profiler + to figure out how to optimize the underlying methods. +""" + +import types + +# A flag controlling whether or not we actually use memoization. +use_memoizer = None + +CounterList = [] + +class Counter(object): + """ + Base class for counting memoization hits and misses. + + We expect that the metaclass initialization will have filled in + the .name attribute that represents the name of the function + being counted. + """ + def __init__(self, method_name): + """ + """ + self.method_name = method_name + self.hit = 0 + self.miss = 0 + CounterList.append(self) + def display(self): + fmt = " %7d hits %7d misses %s()" + print fmt % (self.hit, self.miss, self.name) + def __cmp__(self, other): + try: + return cmp(self.name, other.name) + except AttributeError: + return 0 + +class CountValue(Counter): + """ + A counter class for simple, atomic memoized values. + + A CountValue object should be instantiated in a class for each of + the class's methods that memoizes its return value by simply storing + the return value in its _memo dictionary. + + We expect that the metaclass initialization will fill in the + .underlying_method attribute with the method that we're wrapping. + We then call the underlying_method method after counting whether + its memoized value has already been set (a hit) or not (a miss). + """ + def __call__(self, *args, **kw): + obj = args[0] + if self.method_name in obj._memo: + self.hit = self.hit + 1 + else: + self.miss = self.miss + 1 + return self.underlying_method(*args, **kw) + +class CountDict(Counter): + """ + A counter class for memoized values stored in a dictionary, with + keys based on the method's input arguments. + + A CountDict object is instantiated in a class for each of the + class's methods that memoizes its return value in a dictionary, + indexed by some key that can be computed from one or more of + its input arguments. + + We expect that the metaclass initialization will fill in the + .underlying_method attribute with the method that we're wrapping. + We then call the underlying_method method after counting whether the + computed key value is already present in the memoization dictionary + (a hit) or not (a miss). + """ + def __init__(self, method_name, keymaker): + """ + """ + Counter.__init__(self, method_name) + self.keymaker = keymaker + def __call__(self, *args, **kw): + obj = args[0] + try: + memo_dict = obj._memo[self.method_name] + except KeyError: + self.miss = self.miss + 1 + else: + key = self.keymaker(*args, **kw) + if key in memo_dict: + self.hit = self.hit + 1 + else: + self.miss = self.miss + 1 + return self.underlying_method(*args, **kw) + +class Memoizer(object): + """Object which performs caching of method calls for its 'primary' + instance.""" + + def __init__(self): + pass + +def Dump(title=None): + if title: + print title + CounterList.sort() + for counter in CounterList: + counter.display() + +class Memoized_Metaclass(type): + def __init__(cls, name, bases, cls_dict): + super(Memoized_Metaclass, cls).__init__(name, bases, cls_dict) + + for counter in cls_dict.get('memoizer_counters', []): + method_name = counter.method_name + + counter.name = cls.__name__ + '.' + method_name + counter.underlying_method = cls_dict[method_name] + + replacement_method = types.MethodType(counter, None, cls) + setattr(cls, method_name, replacement_method) + +def EnableMemoization(): + global use_memoizer + use_memoizer = 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Node/Alias.py b/engine/SCons/Node/Alias.py new file mode 100644 index 0000000..9996e64 --- /dev/null +++ b/engine/SCons/Node/Alias.py @@ -0,0 +1,152 @@ + +"""scons.Node.Alias + +Alias nodes. + +This creates a hash of global Aliases (dummy targets). + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Node/Alias.py 5357 2011/09/09 21:31:03 bdeegan" + +import collections + +import SCons.Errors +import SCons.Node +import SCons.Util + +class AliasNameSpace(collections.UserDict): + def Alias(self, name, **kw): + if isinstance(name, SCons.Node.Alias.Alias): + return name + try: + a = self[name] + except KeyError: + a = SCons.Node.Alias.Alias(name, **kw) + self[name] = a + return a + + def lookup(self, name, **kw): + try: + return self[name] + except KeyError: + return None + +class AliasNodeInfo(SCons.Node.NodeInfoBase): + current_version_id = 1 + field_list = ['csig'] + def str_to_node(self, s): + return default_ans.Alias(s) + +class AliasBuildInfo(SCons.Node.BuildInfoBase): + current_version_id = 1 + +class Alias(SCons.Node.Node): + + NodeInfo = AliasNodeInfo + BuildInfo = AliasBuildInfo + + def __init__(self, name): + SCons.Node.Node.__init__(self) + self.name = name + + def str_for_display(self): + return '"' + self.__str__() + '"' + + def __str__(self): + return self.name + + def make_ready(self): + self.get_csig() + + really_build = SCons.Node.Node.build + is_up_to_date = SCons.Node.Node.children_are_up_to_date + + def is_under(self, dir): + # Make Alias nodes get built regardless of + # what directory scons was run from. Alias nodes + # are outside the filesystem: + return 1 + + def get_contents(self): + """The contents of an alias is the concatenation + of the content signatures of all its sources.""" + childsigs = [n.get_csig() for n in self.children()] + return ''.join(childsigs) + + def sconsign(self): + """An Alias is not recorded in .sconsign files""" + pass + + # + # + # + + def changed_since_last_build(self, target, prev_ni): + cur_csig = self.get_csig() + try: + return cur_csig != prev_ni.csig + except AttributeError: + return 1 + + def build(self): + """A "builder" for aliases.""" + pass + + def convert(self): + try: del self.builder + except AttributeError: pass + self.reset_executor() + self.build = self.really_build + + def get_csig(self): + """ + Generate a node's content signature, the digested signature + of its content. + + node - the node + cache - alternate node to use for the signature cache + returns - the content signature + """ + try: + return self.ninfo.csig + except AttributeError: + pass + + contents = self.get_contents() + csig = SCons.Util.MD5signature(contents) + self.get_ninfo().csig = csig + return csig + +default_ans = AliasNameSpace() + +SCons.Node.arg2nodes_lookups.append(default_ans.lookup) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Node/FS.py b/engine/SCons/Node/FS.py new file mode 100644 index 0000000..9c1b05a --- /dev/null +++ b/engine/SCons/Node/FS.py @@ -0,0 +1,3302 @@ +"""scons.Node.FS + +File system nodes. + +These Nodes represent the canonical external objects that people think +of when they think of building software: files and directories. + +This holds a "default_fs" variable that should be initialized with an FS +that can be used by scripts or modules looking for the canonical default. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Node/FS.py 5357 2011/09/09 21:31:03 bdeegan" + +import fnmatch +import os +import re +import shutil +import stat +import sys +import time +import codecs + +import SCons.Action +from SCons.Debug import logInstanceCreation +import SCons.Errors +import SCons.Memoize +import SCons.Node +import SCons.Node.Alias +import SCons.Subst +import SCons.Util +import SCons.Warnings + +from SCons.Debug import Trace + +do_store_info = True +print_duplicate = 0 + + +class EntryProxyAttributeError(AttributeError): + """ + An AttributeError subclass for recording and displaying the name + of the underlying Entry involved in an AttributeError exception. + """ + def __init__(self, entry_proxy, attribute): + AttributeError.__init__(self) + self.entry_proxy = entry_proxy + self.attribute = attribute + def __str__(self): + entry = self.entry_proxy.get() + fmt = "%s instance %s has no attribute %s" + return fmt % (entry.__class__.__name__, + repr(entry.name), + repr(self.attribute)) + +# The max_drift value: by default, use a cached signature value for +# any file that's been untouched for more than two days. +default_max_drift = 2*24*60*60 + +# +# We stringify these file system Nodes a lot. Turning a file system Node +# into a string is non-trivial, because the final string representation +# can depend on a lot of factors: whether it's a derived target or not, +# whether it's linked to a repository or source directory, and whether +# there's duplication going on. The normal technique for optimizing +# calculations like this is to memoize (cache) the string value, so you +# only have to do the calculation once. +# +# A number of the above factors, however, can be set after we've already +# been asked to return a string for a Node, because a Repository() or +# VariantDir() call or the like may not occur until later in SConscript +# files. So this variable controls whether we bother trying to save +# string values for Nodes. The wrapper interface can set this whenever +# they're done mucking with Repository and VariantDir and the other stuff, +# to let this module know it can start returning saved string values +# for Nodes. +# +Save_Strings = None + +def save_strings(val): + global Save_Strings + Save_Strings = val + +# +# Avoid unnecessary function calls by recording a Boolean value that +# tells us whether or not os.path.splitdrive() actually does anything +# on this system, and therefore whether we need to bother calling it +# when looking up path names in various methods below. +# + +do_splitdrive = None +_my_splitdrive =None + +def initialize_do_splitdrive(): + global do_splitdrive + global has_unc + drive, path = os.path.splitdrive('X:/foo') + has_unc = hasattr(os.path, 'splitunc') + + do_splitdrive = not not drive or has_unc + + global _my_splitdrive + if has_unc: + def splitdrive(p): + if p[1:2] == ':': + return p[:2], p[2:] + if p[0:2] == '//': + # Note that we leave a leading slash in the path + # because UNC paths are always absolute. + return '//', p[1:] + return '', p + else: + def splitdrive(p): + if p[1:2] == ':': + return p[:2], p[2:] + return '', p + _my_splitdrive = splitdrive + + # Keep some commonly used values in global variables to skip to + # module look-up costs. + global OS_SEP + global UNC_PREFIX + global os_sep_is_slash + + OS_SEP = os.sep + UNC_PREFIX = OS_SEP + OS_SEP + os_sep_is_slash = OS_SEP == '/' + +initialize_do_splitdrive() + +# Used to avoid invoking os.path.normpath if not necessary. +needs_normpath_check = re.compile( + r''' + # We need to renormalize the path if it contains any consecutive + # '/' characters. + .*// | + + # We need to renormalize the path if it contains a '..' directory. + # Note that we check for all the following cases: + # + # a) The path is a single '..' + # b) The path starts with '..'. E.g. '../' or '../moredirs' + # but we not match '..abc/'. + # c) The path ends with '..'. E.g. '/..' or 'dirs/..' + # d) The path contains a '..' in the middle. + # E.g. dirs/../moredirs + + (.*/)?\.\.(?:/|$) | + + # We need to renormalize the path if it contains a '.' + # directory, but NOT if it is a single '.' '/' characters. We + # do not want to match a single '.' because this case is checked + # for explicitely since this is common enough case. + # + # Note that we check for all the following cases: + # + # a) We don't match a single '.' + # b) We match if the path starts with '.'. E.g. './' or + # './moredirs' but we not match '.abc/'. + # c) We match if the path ends with '.'. E.g. '/.' or + # 'dirs/.' + # d) We match if the path contains a '.' in the middle. + # E.g. dirs/./moredirs + + \./|.*/\.(?:/|$) + + ''', + re.VERBOSE + ) +needs_normpath_match = needs_normpath_check.match + +# +# SCons.Action objects for interacting with the outside world. +# +# The Node.FS methods in this module should use these actions to +# create and/or remove files and directories; they should *not* use +# os.{link,symlink,unlink,mkdir}(), etc., directly. +# +# Using these SCons.Action objects ensures that descriptions of these +# external activities are properly displayed, that the displays are +# suppressed when the -s (silent) option is used, and (most importantly) +# the actions are disabled when the the -n option is used, in which case +# there should be *no* changes to the external file system(s)... +# + +if hasattr(os, 'link'): + def _hardlink_func(fs, src, dst): + # If the source is a symlink, we can't just hard-link to it + # because a relative symlink may point somewhere completely + # different. We must disambiguate the symlink and then + # hard-link the final destination file. + while fs.islink(src): + link = fs.readlink(src) + if not os.path.isabs(link): + src = link + else: + src = os.path.join(os.path.dirname(src), link) + fs.link(src, dst) +else: + _hardlink_func = None + +if hasattr(os, 'symlink'): + def _softlink_func(fs, src, dst): + fs.symlink(src, dst) +else: + _softlink_func = None + +def _copy_func(fs, src, dest): + shutil.copy2(src, dest) + st = fs.stat(src) + fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + + +Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy', + 'hard-copy', 'soft-copy', 'copy'] + +Link_Funcs = [] # contains the callables of the specified duplication style + +def set_duplicate(duplicate): + # Fill in the Link_Funcs list according to the argument + # (discarding those not available on the platform). + + # Set up the dictionary that maps the argument names to the + # underlying implementations. We do this inside this function, + # not in the top-level module code, so that we can remap os.link + # and os.symlink for testing purposes. + link_dict = { + 'hard' : _hardlink_func, + 'soft' : _softlink_func, + 'copy' : _copy_func + } + + if not duplicate in Valid_Duplicates: + raise SCons.Errors.InternalError("The argument of set_duplicate " + "should be in Valid_Duplicates") + global Link_Funcs + Link_Funcs = [] + for func in duplicate.split('-'): + if link_dict[func]: + Link_Funcs.append(link_dict[func]) + +def LinkFunc(target, source, env): + # Relative paths cause problems with symbolic links, so + # we use absolute paths, which may be a problem for people + # who want to move their soft-linked src-trees around. Those + # people should use the 'hard-copy' mode, softlinks cannot be + # used for that; at least I have no idea how ... + src = source[0].abspath + dest = target[0].abspath + dir, file = os.path.split(dest) + if dir and not target[0].fs.isdir(dir): + os.makedirs(dir) + if not Link_Funcs: + # Set a default order of link functions. + set_duplicate('hard-soft-copy') + fs = source[0].fs + # Now link the files with the previously specified order. + for func in Link_Funcs: + try: + func(fs, src, dest) + break + except (IOError, OSError): + # An OSError indicates something happened like a permissions + # problem or an attempt to symlink across file-system + # boundaries. An IOError indicates something like the file + # not existing. In either case, keeping trying additional + # functions in the list and only raise an error if the last + # one failed. + if func == Link_Funcs[-1]: + # exception of the last link method (copy) are fatal + raise + return 0 + +Link = SCons.Action.Action(LinkFunc, None) +def LocalString(target, source, env): + return 'Local copy of %s from %s' % (target[0], source[0]) + +LocalCopy = SCons.Action.Action(LinkFunc, LocalString) + +def UnlinkFunc(target, source, env): + t = target[0] + t.fs.unlink(t.abspath) + return 0 + +Unlink = SCons.Action.Action(UnlinkFunc, None) + +def MkdirFunc(target, source, env): + t = target[0] + if not t.exists(): + t.fs.mkdir(t.abspath) + return 0 + +Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) + +MkdirBuilder = None + +def get_MkdirBuilder(): + global MkdirBuilder + if MkdirBuilder is None: + import SCons.Builder + import SCons.Defaults + # "env" will get filled in by Executor.get_build_env() + # calling SCons.Defaults.DefaultEnvironment() when necessary. + MkdirBuilder = SCons.Builder.Builder(action = Mkdir, + env = None, + explain = None, + is_explicit = None, + target_scanner = SCons.Defaults.DirEntryScanner, + name = "MkdirBuilder") + return MkdirBuilder + +class _Null(object): + pass + +_null = _Null() + +DefaultSCCSBuilder = None +DefaultRCSBuilder = None + +def get_DefaultSCCSBuilder(): + global DefaultSCCSBuilder + if DefaultSCCSBuilder is None: + import SCons.Builder + # "env" will get filled in by Executor.get_build_env() + # calling SCons.Defaults.DefaultEnvironment() when necessary. + act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') + DefaultSCCSBuilder = SCons.Builder.Builder(action = act, + env = None, + name = "DefaultSCCSBuilder") + return DefaultSCCSBuilder + +def get_DefaultRCSBuilder(): + global DefaultRCSBuilder + if DefaultRCSBuilder is None: + import SCons.Builder + # "env" will get filled in by Executor.get_build_env() + # calling SCons.Defaults.DefaultEnvironment() when necessary. + act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') + DefaultRCSBuilder = SCons.Builder.Builder(action = act, + env = None, + name = "DefaultRCSBuilder") + return DefaultRCSBuilder + +# Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. +_is_cygwin = sys.platform == "cygwin" +if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin: + def _my_normcase(x): + return x +else: + def _my_normcase(x): + return x.upper() + + + +class DiskChecker(object): + def __init__(self, type, do, ignore): + self.type = type + self.do = do + self.ignore = ignore + self.func = do + def __call__(self, *args, **kw): + return self.func(*args, **kw) + def set(self, list): + if self.type in list: + self.func = self.do + else: + self.func = self.ignore + +def do_diskcheck_match(node, predicate, errorfmt): + result = predicate() + try: + # If calling the predicate() cached a None value from stat(), + # remove it so it doesn't interfere with later attempts to + # build this Node as we walk the DAG. (This isn't a great way + # to do this, we're reaching into an interface that doesn't + # really belong to us, but it's all about performance, so + # for now we'll just document the dependency...) + if node._memo['stat'] is None: + del node._memo['stat'] + except (AttributeError, KeyError): + pass + if result: + raise TypeError(errorfmt % node.abspath) + +def ignore_diskcheck_match(node, predicate, errorfmt): + pass + +def do_diskcheck_rcs(node, name): + try: + rcs_dir = node.rcs_dir + except AttributeError: + if node.entry_exists_on_disk('RCS'): + rcs_dir = node.Dir('RCS') + else: + rcs_dir = None + node.rcs_dir = rcs_dir + if rcs_dir: + return rcs_dir.entry_exists_on_disk(name+',v') + return None + +def ignore_diskcheck_rcs(node, name): + return None + +def do_diskcheck_sccs(node, name): + try: + sccs_dir = node.sccs_dir + except AttributeError: + if node.entry_exists_on_disk('SCCS'): + sccs_dir = node.Dir('SCCS') + else: + sccs_dir = None + node.sccs_dir = sccs_dir + if sccs_dir: + return sccs_dir.entry_exists_on_disk('s.'+name) + return None + +def ignore_diskcheck_sccs(node, name): + return None + +diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match) +diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs) +diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs) + +diskcheckers = [ + diskcheck_match, + diskcheck_rcs, + diskcheck_sccs, +] + +def set_diskcheck(list): + for dc in diskcheckers: + dc.set(list) + +def diskcheck_types(): + return [dc.type for dc in diskcheckers] + + + +class EntryProxy(SCons.Util.Proxy): + + __str__ = SCons.Util.Delegate('__str__') + + def __get_abspath(self): + entry = self.get() + return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(), + entry.name + "_abspath") + + def __get_filebase(self): + name = self.get().name + return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0], + name + "_filebase") + + def __get_suffix(self): + name = self.get().name + return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[1], + name + "_suffix") + + def __get_file(self): + name = self.get().name + return SCons.Subst.SpecialAttrWrapper(name, name + "_file") + + def __get_base_path(self): + """Return the file's directory and file name, with the + suffix stripped.""" + entry = self.get() + return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0], + entry.name + "_base") + + def __get_posix_path(self): + """Return the path with / as the path separator, + regardless of platform.""" + if os_sep_is_slash: + return self + else: + entry = self.get() + r = entry.get_path().replace(OS_SEP, '/') + return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix") + + def __get_windows_path(self): + """Return the path with \ as the path separator, + regardless of platform.""" + if OS_SEP == '\\': + return self + else: + entry = self.get() + r = entry.get_path().replace(OS_SEP, '\\') + return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_windows") + + def __get_srcnode(self): + return EntryProxy(self.get().srcnode()) + + def __get_srcdir(self): + """Returns the directory containing the source node linked to this + node via VariantDir(), or the directory of this node if not linked.""" + return EntryProxy(self.get().srcnode().dir) + + def __get_rsrcnode(self): + return EntryProxy(self.get().srcnode().rfile()) + + def __get_rsrcdir(self): + """Returns the directory containing the source node linked to this + node via VariantDir(), or the directory of this node if not linked.""" + return EntryProxy(self.get().srcnode().rfile().dir) + + def __get_dir(self): + return EntryProxy(self.get().dir) + + dictSpecialAttrs = { "base" : __get_base_path, + "posix" : __get_posix_path, + "windows" : __get_windows_path, + "win32" : __get_windows_path, + "srcpath" : __get_srcnode, + "srcdir" : __get_srcdir, + "dir" : __get_dir, + "abspath" : __get_abspath, + "filebase" : __get_filebase, + "suffix" : __get_suffix, + "file" : __get_file, + "rsrcpath" : __get_rsrcnode, + "rsrcdir" : __get_rsrcdir, + } + + def __getattr__(self, name): + # This is how we implement the "special" attributes + # such as base, posix, srcdir, etc. + try: + attr_function = self.dictSpecialAttrs[name] + except KeyError: + try: + attr = SCons.Util.Proxy.__getattr__(self, name) + except AttributeError, e: + # Raise our own AttributeError subclass with an + # overridden __str__() method that identifies the + # name of the entry that caused the exception. + raise EntryProxyAttributeError(self, name) + return attr + else: + return attr_function(self) + +class Base(SCons.Node.Node): + """A generic class for file system entries. This class is for + when we don't know yet whether the entry being looked up is a file + or a directory. Instances of this class can morph into either + Dir or File objects by a later, more precise lookup. + + Note: this class does not define __cmp__ and __hash__ for + efficiency reasons. SCons does a lot of comparing of + Node.FS.{Base,Entry,File,Dir} objects, so those operations must be + as fast as possible, which means we want to use Python's built-in + object identity comparisons. + """ + + memoizer_counters = [] + + def __init__(self, name, directory, fs): + """Initialize a generic Node.FS.Base object. + + Call the superclass initialization, take care of setting up + our relative and absolute paths, identify our parent + directory, and indicate that this node should use + signatures.""" + if __debug__: logInstanceCreation(self, 'Node.FS.Base') + SCons.Node.Node.__init__(self) + + # Filenames and paths are probably reused and are intern'ed to + # save some memory. + + #: Filename with extension as it was specified when the object was + #: created; to obtain filesystem path, use Python str() function + self.name = SCons.Util.silent_intern(name) + #: Cached filename extension + self.suffix = SCons.Util.silent_intern(SCons.Util.splitext(name)[1]) + self.fs = fs #: Reference to parent Node.FS object + + assert directory, "A directory must be provided" + + self.abspath = SCons.Util.silent_intern(directory.entry_abspath(name)) + self.labspath = SCons.Util.silent_intern(directory.entry_labspath(name)) + if directory.path == '.': + self.path = SCons.Util.silent_intern(name) + else: + self.path = SCons.Util.silent_intern(directory.entry_path(name)) + if directory.tpath == '.': + self.tpath = SCons.Util.silent_intern(name) + else: + self.tpath = SCons.Util.silent_intern(directory.entry_tpath(name)) + self.path_elements = directory.path_elements + [self] + + self.dir = directory + self.cwd = None # will hold the SConscript directory for target nodes + self.duplicate = directory.duplicate + + def str_for_display(self): + return '"' + self.__str__() + '"' + + def must_be_same(self, klass): + """ + This node, which already existed, is being looked up as the + specified klass. Raise an exception if it isn't. + """ + if isinstance(self, klass) or klass is Entry: + return + raise TypeError("Tried to lookup %s '%s' as a %s." %\ + (self.__class__.__name__, self.path, klass.__name__)) + + def get_dir(self): + return self.dir + + def get_suffix(self): + return self.suffix + + def rfile(self): + return self + + def __str__(self): + """A Node.FS.Base object's string representation is its path + name.""" + global Save_Strings + if Save_Strings: + return self._save_str() + return self._get_str() + + memoizer_counters.append(SCons.Memoize.CountValue('_save_str')) + + def _save_str(self): + try: + return self._memo['_save_str'] + except KeyError: + pass + result = sys.intern(self._get_str()) + self._memo['_save_str'] = result + return result + + def _get_str(self): + global Save_Strings + if self.duplicate or self.is_derived(): + return self.get_path() + srcnode = self.srcnode() + if srcnode.stat() is None and self.stat() is not None: + result = self.get_path() + else: + result = srcnode.get_path() + if not Save_Strings: + # We're not at the point where we're saving the string + # representations of FS Nodes (because we haven't finished + # reading the SConscript files and need to have str() return + # things relative to them). That also means we can't yet + # cache values returned (or not returned) by stat(), since + # Python code in the SConscript files might still create + # or otherwise affect the on-disk file. So get rid of the + # values that the underlying stat() method saved. + try: del self._memo['stat'] + except KeyError: pass + if self is not srcnode: + try: del srcnode._memo['stat'] + except KeyError: pass + return result + + rstr = __str__ + + memoizer_counters.append(SCons.Memoize.CountValue('stat')) + + def stat(self): + try: return self._memo['stat'] + except KeyError: pass + try: result = self.fs.stat(self.abspath) + except os.error: result = None + self._memo['stat'] = result + return result + + def exists(self): + return self.stat() is not None + + def rexists(self): + return self.rfile().exists() + + def getmtime(self): + st = self.stat() + if st: return st[stat.ST_MTIME] + else: return None + + def getsize(self): + st = self.stat() + if st: return st[stat.ST_SIZE] + else: return None + + def isdir(self): + st = self.stat() + return st is not None and stat.S_ISDIR(st[stat.ST_MODE]) + + def isfile(self): + st = self.stat() + return st is not None and stat.S_ISREG(st[stat.ST_MODE]) + + if hasattr(os, 'symlink'): + def islink(self): + try: st = self.fs.lstat(self.abspath) + except os.error: return 0 + return stat.S_ISLNK(st[stat.ST_MODE]) + else: + def islink(self): + return 0 # no symlinks + + def is_under(self, dir): + if self is dir: + return 1 + else: + return self.dir.is_under(dir) + + def set_local(self): + self._local = 1 + + def srcnode(self): + """If this node is in a build path, return the node + corresponding to its source file. Otherwise, return + ourself. + """ + srcdir_list = self.dir.srcdir_list() + if srcdir_list: + srcnode = srcdir_list[0].Entry(self.name) + srcnode.must_be_same(self.__class__) + return srcnode + return self + + def get_path(self, dir=None): + """Return path relative to the current working directory of the + Node.FS.Base object that owns us.""" + if not dir: + dir = self.fs.getcwd() + if self == dir: + return '.' + path_elems = self.path_elements + pathname = '' + try: i = path_elems.index(dir) + except ValueError: + for p in path_elems[:-1]: + pathname += p.dirname + else: + for p in path_elems[i+1:-1]: + pathname += p.dirname + return pathname + path_elems[-1].name + + def set_src_builder(self, builder): + """Set the source code builder for this node.""" + self.sbuilder = builder + if not self.has_builder(): + self.builder_set(builder) + + def src_builder(self): + """Fetch the source code builder for this node. + + If there isn't one, we cache the source code builder specified + for the directory (which in turn will cache the value from its + parent directory, and so on up to the file system root). + """ + try: + scb = self.sbuilder + except AttributeError: + scb = self.dir.src_builder() + self.sbuilder = scb + return scb + + def get_abspath(self): + """Get the absolute path of the file.""" + return self.abspath + + def for_signature(self): + # Return just our name. Even an absolute path would not work, + # because that can change thanks to symlinks or remapped network + # paths. + return self.name + + def get_subst_proxy(self): + try: + return self._proxy + except AttributeError: + ret = EntryProxy(self) + self._proxy = ret + return ret + + def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext): + """ + + Generates a target entry that corresponds to this entry (usually + a source file) with the specified prefix and suffix. + + Note that this method can be overridden dynamically for generated + files that need different behavior. See Tool/swig.py for + an example. + """ + return self.dir.Entry(prefix + splitext(self.name)[0] + suffix) + + def _Rfindalldirs_key(self, pathlist): + return pathlist + + memoizer_counters.append(SCons.Memoize.CountDict('Rfindalldirs', _Rfindalldirs_key)) + + def Rfindalldirs(self, pathlist): + """ + Return all of the directories for a given path list, including + corresponding "backing" directories in any repositories. + + The Node lookups are relative to this Node (typically a + directory), so memoizing result saves cycles from looking + up the same path for each target in a given directory. + """ + try: + memo_dict = self._memo['Rfindalldirs'] + except KeyError: + memo_dict = {} + self._memo['Rfindalldirs'] = memo_dict + else: + try: + return memo_dict[pathlist] + except KeyError: + pass + + create_dir_relative_to_self = self.Dir + result = [] + for path in pathlist: + if isinstance(path, SCons.Node.Node): + result.append(path) + else: + dir = create_dir_relative_to_self(path) + result.extend(dir.get_all_rdirs()) + + memo_dict[pathlist] = result + + return result + + def RDirs(self, pathlist): + """Search for a list of directories in the Repository list.""" + cwd = self.cwd or self.fs._cwd + return cwd.Rfindalldirs(pathlist) + + memoizer_counters.append(SCons.Memoize.CountValue('rentry')) + + def rentry(self): + try: + return self._memo['rentry'] + except KeyError: + pass + result = self + if not self.exists(): + norm_name = _my_normcase(self.name) + for dir in self.dir.get_all_rdirs(): + try: + node = dir.entries[norm_name] + except KeyError: + if dir.entry_exists_on_disk(self.name): + result = dir.Entry(self.name) + break + self._memo['rentry'] = result + return result + + def _glob1(self, pattern, ondisk=True, source=False, strings=False): + return [] + +class Entry(Base): + """This is the class for generic Node.FS entries--that is, things + that could be a File or a Dir, but we're just not sure yet. + Consequently, the methods in this class really exist just to + transform their associated object into the right class when the + time comes, and then call the same-named method in the transformed + class.""" + + def diskcheck_match(self): + pass + + def disambiguate(self, must_exist=None): + """ + """ + if self.isdir(): + self.__class__ = Dir + self._morph() + elif self.isfile(): + self.__class__ = File + self._morph() + self.clear() + else: + # There was nothing on-disk at this location, so look in + # the src directory. + # + # We can't just use self.srcnode() straight away because + # that would create an actual Node for this file in the src + # directory, and there might not be one. Instead, use the + # dir_on_disk() method to see if there's something on-disk + # with that name, in which case we can go ahead and call + # self.srcnode() to create the right type of entry. + srcdir = self.dir.srcnode() + if srcdir != self.dir and \ + srcdir.entry_exists_on_disk(self.name) and \ + self.srcnode().isdir(): + self.__class__ = Dir + self._morph() + elif must_exist: + msg = "No such file or directory: '%s'" % self.abspath + raise SCons.Errors.UserError(msg) + else: + self.__class__ = File + self._morph() + self.clear() + return self + + def rfile(self): + """We're a generic Entry, but the caller is actually looking for + a File at this point, so morph into one.""" + self.__class__ = File + self._morph() + self.clear() + return File.rfile(self) + + def scanner_key(self): + return self.get_suffix() + + def get_contents(self): + """Fetch the contents of the entry. Returns the exact binary + contents of the file.""" + try: + self = self.disambiguate(must_exist=1) + except SCons.Errors.UserError: + # There was nothing on disk with which to disambiguate + # this entry. Leave it as an Entry, but return a null + # string so calls to get_contents() in emitters and the + # like (e.g. in qt.py) don't have to disambiguate by hand + # or catch the exception. + return '' + else: + return self.get_contents() + + def get_text_contents(self): + """Fetch the decoded text contents of a Unicode encoded Entry. + + Since this should return the text contents from the file + system, we check to see into what sort of subclass we should + morph this Entry.""" + try: + self = self.disambiguate(must_exist=1) + except SCons.Errors.UserError: + # There was nothing on disk with which to disambiguate + # this entry. Leave it as an Entry, but return a null + # string so calls to get_text_contents() in emitters and + # the like (e.g. in qt.py) don't have to disambiguate by + # hand or catch the exception. + return '' + else: + return self.get_text_contents() + + def must_be_same(self, klass): + """Called to make sure a Node is a Dir. Since we're an + Entry, we can morph into one.""" + if self.__class__ is not klass: + self.__class__ = klass + self._morph() + self.clear() + + # The following methods can get called before the Taskmaster has + # had a chance to call disambiguate() directly to see if this Entry + # should really be a Dir or a File. We therefore use these to call + # disambiguate() transparently (from our caller's point of view). + # + # Right now, this minimal set of methods has been derived by just + # looking at some of the methods that will obviously be called early + # in any of the various Taskmasters' calling sequences, and then + # empirically figuring out which additional methods are necessary + # to make various tests pass. + + def exists(self): + """Return if the Entry exists. Check the file system to see + what we should turn into first. Assume a file if there's no + directory.""" + return self.disambiguate().exists() + + def rel_path(self, other): + d = self.disambiguate() + if d.__class__ is Entry: + raise Exception("rel_path() could not disambiguate File/Dir") + return d.rel_path(other) + + def new_ninfo(self): + return self.disambiguate().new_ninfo() + + def changed_since_last_build(self, target, prev_ni): + return self.disambiguate().changed_since_last_build(target, prev_ni) + + def _glob1(self, pattern, ondisk=True, source=False, strings=False): + return self.disambiguate()._glob1(pattern, ondisk, source, strings) + + def get_subst_proxy(self): + return self.disambiguate().get_subst_proxy() + +# This is for later so we can differentiate between Entry the class and Entry +# the method of the FS class. +_classEntry = Entry + + +class LocalFS(object): + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + # This class implements an abstraction layer for operations involving + # a local file system. Essentially, this wraps any function in + # the os, os.path or shutil modules that we use to actually go do + # anything with or to the local file system. + # + # Note that there's a very good chance we'll refactor this part of + # the architecture in some way as we really implement the interface(s) + # for remote file system Nodes. For example, the right architecture + # might be to have this be a subclass instead of a base class. + # Nevertheless, we're using this as a first step in that direction. + # + # We're not using chdir() yet because the calling subclass method + # needs to use os.chdir() directly to avoid recursion. Will we + # really need this one? + #def chdir(self, path): + # return os.chdir(path) + def chmod(self, path, mode): + return os.chmod(path, mode) + def copy(self, src, dst): + return shutil.copy(src, dst) + def copy2(self, src, dst): + return shutil.copy2(src, dst) + def exists(self, path): + return os.path.exists(path) + def getmtime(self, path): + return os.path.getmtime(path) + def getsize(self, path): + return os.path.getsize(path) + def isdir(self, path): + return os.path.isdir(path) + def isfile(self, path): + return os.path.isfile(path) + def link(self, src, dst): + return os.link(src, dst) + def lstat(self, path): + return os.lstat(path) + def listdir(self, path): + return os.listdir(path) + def makedirs(self, path): + return os.makedirs(path) + def mkdir(self, path): + return os.mkdir(path) + def rename(self, old, new): + return os.rename(old, new) + def stat(self, path): + return os.stat(path) + def symlink(self, src, dst): + return os.symlink(src, dst) + def open(self, path): + return open(path) + def unlink(self, path): + return os.unlink(path) + + if hasattr(os, 'symlink'): + def islink(self, path): + return os.path.islink(path) + else: + def islink(self, path): + return 0 # no symlinks + + if hasattr(os, 'readlink'): + def readlink(self, file): + return os.readlink(file) + else: + def readlink(self, file): + return '' + + +#class RemoteFS: +# # Skeleton for the obvious methods we might need from the +# # abstraction layer for a remote filesystem. +# def upload(self, local_src, remote_dst): +# pass +# def download(self, remote_src, local_dst): +# pass + + +class FS(LocalFS): + + memoizer_counters = [] + + def __init__(self, path = None): + """Initialize the Node.FS subsystem. + + The supplied path is the top of the source tree, where we + expect to find the top-level build file. If no path is + supplied, the current directory is the default. + + The path argument must be a valid absolute path. + """ + if __debug__: logInstanceCreation(self, 'Node.FS') + + self._memo = {} + + self.Root = {} + self.SConstruct_dir = None + self.max_drift = default_max_drift + + self.Top = None + if path is None: + self.pathTop = os.getcwd() + else: + self.pathTop = path + self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0]) + + self.Top = self.Dir(self.pathTop) + self.Top.path = '.' + self.Top.tpath = '.' + self._cwd = self.Top + + DirNodeInfo.fs = self + FileNodeInfo.fs = self + + def set_SConstruct_dir(self, dir): + self.SConstruct_dir = dir + + def get_max_drift(self): + return self.max_drift + + def set_max_drift(self, max_drift): + self.max_drift = max_drift + + def getcwd(self): + if hasattr(self, "_cwd"): + return self._cwd + else: + return "" + + def chdir(self, dir, change_os_dir=0): + """Change the current working directory for lookups. + If change_os_dir is true, we will also change the "real" cwd + to match. + """ + curr=self._cwd + try: + if dir is not None: + self._cwd = dir + if change_os_dir: + os.chdir(dir.abspath) + except OSError: + self._cwd = curr + raise + + def get_root(self, drive): + """ + Returns the root directory for the specified drive, creating + it if necessary. + """ + drive = _my_normcase(drive) + try: + return self.Root[drive] + except KeyError: + root = RootDir(drive, self) + self.Root[drive] = root + if not drive: + self.Root[self.defaultDrive] = root + elif drive == self.defaultDrive: + self.Root[''] = root + return root + + def _lookup(self, p, directory, fsclass, create=1): + """ + The generic entry point for Node lookup with user-supplied data. + + This translates arbitrary input into a canonical Node.FS object + of the specified fsclass. The general approach for strings is + to turn it into a fully normalized absolute path and then call + the root directory's lookup_abs() method for the heavy lifting. + + If the path name begins with '#', it is unconditionally + interpreted relative to the top-level directory of this FS. '#' + is treated as a synonym for the top-level SConstruct directory, + much like '~' is treated as a synonym for the user's home + directory in a UNIX shell. So both '#foo' and '#/foo' refer + to the 'foo' subdirectory underneath the top-level SConstruct + directory. + + If the path name is relative, then the path is looked up relative + to the specified directory, or the current directory (self._cwd, + typically the SConscript directory) if the specified directory + is None. + """ + if isinstance(p, Base): + # It's already a Node.FS object. Make sure it's the right + # class and return. + p.must_be_same(fsclass) + return p + # str(p) in case it's something like a proxy object + p = str(p) + + if not os_sep_is_slash: + p = p.replace(OS_SEP, '/') + + if p[0:1] == '#': + # There was an initial '#', so we strip it and override + # whatever directory they may have specified with the + # top-level SConstruct directory. + p = p[1:] + directory = self.Top + + # There might be a drive letter following the + # '#'. Although it is not described in the SCons man page, + # the regression test suite explicitly tests for that + # syntax. It seems to mean the following thing: + # + # Assuming the the SCons top dir is in C:/xxx/yyy, + # '#X:/toto' means X:/xxx/yyy/toto. + # + # i.e. it assumes that the X: drive has a directory + # structure similar to the one found on drive C:. + if do_splitdrive: + drive, p = _my_splitdrive(p) + if drive: + root = self.get_root(drive) + else: + root = directory.root + else: + root = directory.root + + # We can only strip trailing after splitting the drive + # since the drive might the UNC '//' prefix. + p = p.strip('/') + + needs_normpath = needs_normpath_match(p) + + # The path is relative to the top-level SCons directory. + if p in ('', '.'): + p = directory.labspath + else: + p = directory.labspath + '/' + p + else: + if do_splitdrive: + drive, p = _my_splitdrive(p) + if drive and not p: + # This causes a naked drive letter to be treated + # as a synonym for the root directory on that + # drive. + p = '/' + else: + drive = '' + + # We can only strip trailing '/' since the drive might the + # UNC '//' prefix. + if p != '/': + p = p.rstrip('/') + + needs_normpath = needs_normpath_match(p) + + if p[0:1] == '/': + # Absolute path + root = self.get_root(drive) + else: + # This is a relative lookup or to the current directory + # (the path name is not absolute). Add the string to the + # appropriate directory lookup path, after which the whole + # thing gets normalized. + if directory: + if not isinstance(directory, Dir): + directory = self.Dir(directory) + else: + directory = self._cwd + + if p in ('', '.'): + p = directory.labspath + else: + p = directory.labspath + '/' + p + + if drive: + root = self.get_root(drive) + else: + root = directory.root + + if needs_normpath is not None: + # Normalize a pathname. Will return the same result for + # equivalent paths. + # + # We take advantage of the fact that we have an absolute + # path here for sure. In addition, we know that the + # components of lookup path are separated by slashes at + # this point. Because of this, this code is about 2X + # faster than calling os.path.normpath() followed by + # replacing os.sep with '/' again. + ins = p.split('/')[1:] + outs = [] + for d in ins: + if d == '..': + try: + outs.pop() + except IndexError: + pass + elif d not in ('', '.'): + outs.append(d) + p = '/' + '/'.join(outs) + + return root._lookup_abs(p, fsclass, create) + + def Entry(self, name, directory = None, create = 1): + """Look up or create a generic Entry node with the specified name. + If the name is a relative path (begins with ./, ../, or a file + name), then it is looked up relative to the supplied directory + node, or to the top level directory of the FS (supplied at + construction time) if no directory is supplied. + """ + return self._lookup(name, directory, Entry, create) + + def File(self, name, directory = None, create = 1): + """Look up or create a File node with the specified name. If + the name is a relative path (begins with ./, ../, or a file name), + then it is looked up relative to the supplied directory node, + or to the top level directory of the FS (supplied at construction + time) if no directory is supplied. + + This method will raise TypeError if a directory is found at the + specified path. + """ + return self._lookup(name, directory, File, create) + + def Dir(self, name, directory = None, create = True): + """Look up or create a Dir node with the specified name. If + the name is a relative path (begins with ./, ../, or a file name), + then it is looked up relative to the supplied directory node, + or to the top level directory of the FS (supplied at construction + time) if no directory is supplied. + + This method will raise TypeError if a normal file is found at the + specified path. + """ + return self._lookup(name, directory, Dir, create) + + def VariantDir(self, variant_dir, src_dir, duplicate=1): + """Link the supplied variant directory to the source directory + for purposes of building files.""" + + if not isinstance(src_dir, SCons.Node.Node): + src_dir = self.Dir(src_dir) + if not isinstance(variant_dir, SCons.Node.Node): + variant_dir = self.Dir(variant_dir) + if src_dir.is_under(variant_dir): + raise SCons.Errors.UserError("Source directory cannot be under variant directory.") + if variant_dir.srcdir: + if variant_dir.srcdir == src_dir: + return # We already did this. + raise SCons.Errors.UserError("'%s' already has a source directory: '%s'."%(variant_dir, variant_dir.srcdir)) + variant_dir.link(src_dir, duplicate) + + def Repository(self, *dirs): + """Specify Repository directories to search.""" + for d in dirs: + if not isinstance(d, SCons.Node.Node): + d = self.Dir(d) + self.Top.addRepository(d) + + def variant_dir_target_climb(self, orig, dir, tail): + """Create targets in corresponding variant directories + + Climb the directory tree, and look up path names + relative to any linked variant directories we find. + + Even though this loops and walks up the tree, we don't memoize + the return value because this is really only used to process + the command-line targets. + """ + targets = [] + message = None + fmt = "building associated VariantDir targets: %s" + start_dir = dir + while dir: + for bd in dir.variant_dirs: + if start_dir.is_under(bd): + # If already in the build-dir location, don't reflect + return [orig], fmt % str(orig) + p = os.path.join(bd.path, *tail) + targets.append(self.Entry(p)) + tail = [dir.name] + tail + dir = dir.up() + if targets: + message = fmt % ' '.join(map(str, targets)) + return targets, message + + def Glob(self, pathname, ondisk=True, source=True, strings=False, cwd=None): + """ + Globs + + This is mainly a shim layer + """ + if cwd is None: + cwd = self.getcwd() + return cwd.glob(pathname, ondisk, source, strings) + +class DirNodeInfo(SCons.Node.NodeInfoBase): + # This should get reset by the FS initialization. + current_version_id = 1 + + fs = None + + def str_to_node(self, s): + top = self.fs.Top + root = top.root + if do_splitdrive: + drive, s = _my_splitdrive(s) + if drive: + root = self.fs.get_root(drive) + if not os.path.isabs(s): + s = top.labspath + '/' + s + return root._lookup_abs(s, Entry) + +class DirBuildInfo(SCons.Node.BuildInfoBase): + current_version_id = 1 + +glob_magic_check = re.compile('[*?[]') + +def has_glob_magic(s): + return glob_magic_check.search(s) is not None + +class Dir(Base): + """A class for directories in a file system. + """ + + memoizer_counters = [] + + NodeInfo = DirNodeInfo + BuildInfo = DirBuildInfo + + def __init__(self, name, directory, fs): + if __debug__: logInstanceCreation(self, 'Node.FS.Dir') + Base.__init__(self, name, directory, fs) + self._morph() + + def _morph(self): + """Turn a file system Node (either a freshly initialized directory + object or a separate Entry object) into a proper directory object. + + Set up this directory's entries and hook it into the file + system tree. Specify that directories (this Node) don't use + signatures for calculating whether they're current. + """ + + self.repositories = [] + self.srcdir = None + + self.entries = {} + self.entries['.'] = self + self.entries['..'] = self.dir + self.cwd = self + self.searched = 0 + self._sconsign = None + self.variant_dirs = [] + self.root = self.dir.root + + # For directories, we make a difference between the directory + # 'name' and the directory 'dirname'. The 'name' attribute is + # used when we need to print the 'name' of the directory or + # when we it is used as the last part of a path. The 'dirname' + # is used when the directory is not the last element of the + # path. The main reason for making that distinction is that + # for RoorDir's the dirname can not be easily inferred from + # the name. For example, we have to add a '/' after a drive + # letter but not after a UNC path prefix ('//'). + self.dirname = self.name + OS_SEP + + # Don't just reset the executor, replace its action list, + # because it might have some pre-or post-actions that need to + # be preserved. + # + # But don't reset the executor if there is a non-null executor + # attached already. The existing executor might have other + # targets, in which case replacing the action list with a + # Mkdir action is a big mistake. + if not hasattr(self, 'executor'): + self.builder = get_MkdirBuilder() + self.get_executor().set_action_list(self.builder.action) + else: + # Prepend MkdirBuilder action to existing action list + l = self.get_executor().action_list + a = get_MkdirBuilder().action + l.insert(0, a) + self.get_executor().set_action_list(l) + + def diskcheck_match(self): + diskcheck_match(self, self.isfile, + "File %s found where directory expected.") + + def __clearRepositoryCache(self, duplicate=None): + """Called when we change the repository(ies) for a directory. + This clears any cached information that is invalidated by changing + the repository.""" + + for node in self.entries.values(): + if node != self.dir: + if node != self and isinstance(node, Dir): + node.__clearRepositoryCache(duplicate) + else: + node.clear() + try: + del node._srcreps + except AttributeError: + pass + if duplicate is not None: + node.duplicate=duplicate + + def __resetDuplicate(self, node): + if node != self: + node.duplicate = node.get_dir().duplicate + + def Entry(self, name): + """ + Looks up or creates an entry node named 'name' relative to + this directory. + """ + return self.fs.Entry(name, self) + + def Dir(self, name, create=True): + """ + Looks up or creates a directory node named 'name' relative to + this directory. + """ + return self.fs.Dir(name, self, create) + + def File(self, name): + """ + Looks up or creates a file node named 'name' relative to + this directory. + """ + return self.fs.File(name, self) + + def link(self, srcdir, duplicate): + """Set this directory as the variant directory for the + supplied source directory.""" + self.srcdir = srcdir + self.duplicate = duplicate + self.__clearRepositoryCache(duplicate) + srcdir.variant_dirs.append(self) + + def getRepositories(self): + """Returns a list of repositories for this directory. + """ + if self.srcdir and not self.duplicate: + return self.srcdir.get_all_rdirs() + self.repositories + return self.repositories + + memoizer_counters.append(SCons.Memoize.CountValue('get_all_rdirs')) + + def get_all_rdirs(self): + try: + return list(self._memo['get_all_rdirs']) + except KeyError: + pass + + result = [self] + fname = '.' + dir = self + while dir: + for rep in dir.getRepositories(): + result.append(rep.Dir(fname)) + if fname == '.': + fname = dir.name + else: + fname = dir.name + OS_SEP + fname + dir = dir.up() + + self._memo['get_all_rdirs'] = list(result) + + return result + + def addRepository(self, dir): + if dir != self and not dir in self.repositories: + self.repositories.append(dir) + dir.tpath = '.' + self.__clearRepositoryCache() + + def up(self): + return self.dir + + def _rel_path_key(self, other): + return str(other) + + memoizer_counters.append(SCons.Memoize.CountDict('rel_path', _rel_path_key)) + + def rel_path(self, other): + """Return a path to "other" relative to this directory. + """ + + # This complicated and expensive method, which constructs relative + # paths between arbitrary Node.FS objects, is no longer used + # by SCons itself. It was introduced to store dependency paths + # in .sconsign files relative to the target, but that ended up + # being significantly inefficient. + # + # We're continuing to support the method because some SConstruct + # files out there started using it when it was available, and + # we're all about backwards compatibility.. + + try: + memo_dict = self._memo['rel_path'] + except KeyError: + memo_dict = {} + self._memo['rel_path'] = memo_dict + else: + try: + return memo_dict[other] + except KeyError: + pass + + if self is other: + result = '.' + + elif not other in self.path_elements: + try: + other_dir = other.get_dir() + except AttributeError: + result = str(other) + else: + if other_dir is None: + result = other.name + else: + dir_rel_path = self.rel_path(other_dir) + if dir_rel_path == '.': + result = other.name + else: + result = dir_rel_path + OS_SEP + other.name + else: + i = self.path_elements.index(other) + 1 + + path_elems = ['..'] * (len(self.path_elements) - i) \ + + [n.name for n in other.path_elements[i:]] + + result = OS_SEP.join(path_elems) + + memo_dict[other] = result + + return result + + def get_env_scanner(self, env, kw={}): + import SCons.Defaults + return SCons.Defaults.DirEntryScanner + + def get_target_scanner(self): + import SCons.Defaults + return SCons.Defaults.DirEntryScanner + + def get_found_includes(self, env, scanner, path): + """Return this directory's implicit dependencies. + + We don't bother caching the results because the scan typically + shouldn't be requested more than once (as opposed to scanning + .h file contents, which can be requested as many times as the + files is #included by other files). + """ + if not scanner: + return [] + # Clear cached info for this Dir. If we already visited this + # directory on our walk down the tree (because we didn't know at + # that point it was being used as the source for another Node) + # then we may have calculated build signature before realizing + # we had to scan the disk. Now that we have to, though, we need + # to invalidate the old calculated signature so that any node + # dependent on our directory structure gets one that includes + # info about everything on disk. + self.clear() + return scanner(self, env, path) + + # + # Taskmaster interface subsystem + # + + def prepare(self): + pass + + def build(self, **kw): + """A null "builder" for directories.""" + global MkdirBuilder + if self.builder is not MkdirBuilder: + SCons.Node.Node.build(self, **kw) + + # + # + # + + def _create(self): + """Create this directory, silently and without worrying about + whether the builder is the default or not.""" + listDirs = [] + parent = self + while parent: + if parent.exists(): + break + listDirs.append(parent) + p = parent.up() + if p is None: + # Don't use while: - else: for this condition because + # if so, then parent is None and has no .path attribute. + raise SCons.Errors.StopError(parent.path) + parent = p + listDirs.reverse() + for dirnode in listDirs: + try: + # Don't call dirnode.build(), call the base Node method + # directly because we definitely *must* create this + # directory. The dirnode.build() method will suppress + # the build if it's the default builder. + SCons.Node.Node.build(dirnode) + dirnode.get_executor().nullify() + # The build() action may or may not have actually + # created the directory, depending on whether the -n + # option was used or not. Delete the _exists and + # _rexists attributes so they can be reevaluated. + dirnode.clear() + except OSError: + pass + + def multiple_side_effect_has_builder(self): + global MkdirBuilder + return self.builder is not MkdirBuilder and self.has_builder() + + def alter_targets(self): + """Return any corresponding targets in a variant directory. + """ + return self.fs.variant_dir_target_climb(self, self, []) + + def scanner_key(self): + """A directory does not get scanned.""" + return None + + def get_text_contents(self): + """We already emit things in text, so just return the binary + version.""" + return self.get_contents() + + def get_contents(self): + """Return content signatures and names of all our children + separated by new-lines. Ensure that the nodes are sorted.""" + contents = [] + for node in sorted(self.children(), key=lambda t: t.name): + contents.append('%s %s\n' % (node.get_csig(), node.name)) + return ''.join(contents) + + def get_csig(self): + """Compute the content signature for Directory nodes. In + general, this is not needed and the content signature is not + stored in the DirNodeInfo. However, if get_contents on a Dir + node is called which has a child directory, the child + directory should return the hash of its contents.""" + contents = self.get_contents() + return SCons.Util.MD5signature(contents) + + def do_duplicate(self, src): + pass + + changed_since_last_build = SCons.Node.Node.state_has_changed + + def is_up_to_date(self): + """If any child is not up-to-date, then this directory isn't, + either.""" + if self.builder is not MkdirBuilder and not self.exists(): + return 0 + up_to_date = SCons.Node.up_to_date + for kid in self.children(): + if kid.get_state() > up_to_date: + return 0 + return 1 + + def rdir(self): + if not self.exists(): + norm_name = _my_normcase(self.name) + for dir in self.dir.get_all_rdirs(): + try: node = dir.entries[norm_name] + except KeyError: node = dir.dir_on_disk(self.name) + if node and node.exists() and \ + (isinstance(dir, Dir) or isinstance(dir, Entry)): + return node + return self + + def sconsign(self): + """Return the .sconsign file info for this directory, + creating it first if necessary.""" + if not self._sconsign: + import SCons.SConsign + self._sconsign = SCons.SConsign.ForDirectory(self) + return self._sconsign + + def srcnode(self): + """Dir has a special need for srcnode()...if we + have a srcdir attribute set, then that *is* our srcnode.""" + if self.srcdir: + return self.srcdir + return Base.srcnode(self) + + def get_timestamp(self): + """Return the latest timestamp from among our children""" + stamp = 0 + for kid in self.children(): + if kid.get_timestamp() > stamp: + stamp = kid.get_timestamp() + return stamp + + def entry_abspath(self, name): + return self.abspath + OS_SEP + name + + def entry_labspath(self, name): + return self.labspath + '/' + name + + def entry_path(self, name): + return self.path + OS_SEP + name + + def entry_tpath(self, name): + return self.tpath + OS_SEP + name + + def entry_exists_on_disk(self, name): + try: + d = self.on_disk_entries + except AttributeError: + d = {} + try: + entries = os.listdir(self.abspath) + except OSError: + pass + else: + for entry in map(_my_normcase, entries): + d[entry] = True + self.on_disk_entries = d + if sys.platform == 'win32': + name = _my_normcase(name) + result = d.get(name) + if result is None: + # Belt-and-suspenders for Windows: check directly for + # 8.3 file names that don't show up in os.listdir(). + result = os.path.exists(self.abspath + OS_SEP + name) + d[name] = result + return result + else: + return name in d + + memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list')) + + def srcdir_list(self): + try: + return self._memo['srcdir_list'] + except KeyError: + pass + + result = [] + + dirname = '.' + dir = self + while dir: + if dir.srcdir: + result.append(dir.srcdir.Dir(dirname)) + dirname = dir.name + OS_SEP + dirname + dir = dir.up() + + self._memo['srcdir_list'] = result + + return result + + def srcdir_duplicate(self, name): + for dir in self.srcdir_list(): + if self.is_under(dir): + # We shouldn't source from something in the build path; + # variant_dir is probably under src_dir, in which case + # we are reflecting. + break + if dir.entry_exists_on_disk(name): + srcnode = dir.Entry(name).disambiguate() + if self.duplicate: + node = self.Entry(name).disambiguate() + node.do_duplicate(srcnode) + return node + else: + return srcnode + return None + + def _srcdir_find_file_key(self, filename): + return filename + + memoizer_counters.append(SCons.Memoize.CountDict('srcdir_find_file', _srcdir_find_file_key)) + + def srcdir_find_file(self, filename): + try: + memo_dict = self._memo['srcdir_find_file'] + except KeyError: + memo_dict = {} + self._memo['srcdir_find_file'] = memo_dict + else: + try: + return memo_dict[filename] + except KeyError: + pass + + def func(node): + if (isinstance(node, File) or isinstance(node, Entry)) and \ + (node.is_derived() or node.exists()): + return node + return None + + norm_name = _my_normcase(filename) + + for rdir in self.get_all_rdirs(): + try: node = rdir.entries[norm_name] + except KeyError: node = rdir.file_on_disk(filename) + else: node = func(node) + if node: + result = (node, self) + memo_dict[filename] = result + return result + + for srcdir in self.srcdir_list(): + for rdir in srcdir.get_all_rdirs(): + try: node = rdir.entries[norm_name] + except KeyError: node = rdir.file_on_disk(filename) + else: node = func(node) + if node: + result = (File(filename, self, self.fs), srcdir) + memo_dict[filename] = result + return result + + result = (None, None) + memo_dict[filename] = result + return result + + def dir_on_disk(self, name): + if self.entry_exists_on_disk(name): + try: return self.Dir(name) + except TypeError: pass + node = self.srcdir_duplicate(name) + if isinstance(node, File): + return None + return node + + def file_on_disk(self, name): + if self.entry_exists_on_disk(name) or \ + diskcheck_rcs(self, name) or \ + diskcheck_sccs(self, name): + try: return self.File(name) + except TypeError: pass + node = self.srcdir_duplicate(name) + if isinstance(node, Dir): + return None + return node + + def walk(self, func, arg): + """ + Walk this directory tree by calling the specified function + for each directory in the tree. + + This behaves like the os.path.walk() function, but for in-memory + Node.FS.Dir objects. The function takes the same arguments as + the functions passed to os.path.walk(): + + func(arg, dirname, fnames) + + Except that "dirname" will actually be the directory *Node*, + not the string. The '.' and '..' entries are excluded from + fnames. The fnames list may be modified in-place to filter the + subdirectories visited or otherwise impose a specific order. + The "arg" argument is always passed to func() and may be used + in any way (or ignored, passing None is common). + """ + entries = self.entries + names = list(entries.keys()) + names.remove('.') + names.remove('..') + func(arg, self, names) + for dirname in [n for n in names if isinstance(entries[n], Dir)]: + entries[dirname].walk(func, arg) + + def glob(self, pathname, ondisk=True, source=False, strings=False): + """ + Returns a list of Nodes (or strings) matching a specified + pathname pattern. + + Pathname patterns follow UNIX shell semantics: * matches + any-length strings of any characters, ? matches any character, + and [] can enclose lists or ranges of characters. Matches do + not span directory separators. + + The matches take into account Repositories, returning local + Nodes if a corresponding entry exists in a Repository (either + an in-memory Node or something on disk). + + By defafult, the glob() function matches entries that exist + on-disk, in addition to in-memory Nodes. Setting the "ondisk" + argument to False (or some other non-true value) causes the glob() + function to only match in-memory Nodes. The default behavior is + to return both the on-disk and in-memory Nodes. + + The "source" argument, when true, specifies that corresponding + source Nodes must be returned if you're globbing in a build + directory (initialized with VariantDir()). The default behavior + is to return Nodes local to the VariantDir(). + + The "strings" argument, when true, returns the matches as strings, + not Nodes. The strings are path names relative to this directory. + + The underlying algorithm is adapted from the glob.glob() function + in the Python library (but heavily modified), and uses fnmatch() + under the covers. + """ + dirname, basename = os.path.split(pathname) + if not dirname: + return sorted(self._glob1(basename, ondisk, source, strings), + key=lambda t: str(t)) + if has_glob_magic(dirname): + list = self.glob(dirname, ondisk, source, strings=False) + else: + list = [self.Dir(dirname, create=True)] + result = [] + for dir in list: + r = dir._glob1(basename, ondisk, source, strings) + if strings: + r = [os.path.join(str(dir), x) for x in r] + result.extend(r) + return sorted(result, key=lambda a: str(a)) + + def _glob1(self, pattern, ondisk=True, source=False, strings=False): + """ + Globs for and returns a list of entry names matching a single + pattern in this directory. + + This searches any repositories and source directories for + corresponding entries and returns a Node (or string) relative + to the current directory if an entry is found anywhere. + + TODO: handle pattern with no wildcard + """ + search_dir_list = self.get_all_rdirs() + for srcdir in self.srcdir_list(): + search_dir_list.extend(srcdir.get_all_rdirs()) + + selfEntry = self.Entry + names = [] + for dir in search_dir_list: + # We use the .name attribute from the Node because the keys of + # the dir.entries dictionary are normalized (that is, all upper + # case) on case-insensitive systems like Windows. + node_names = [ v.name for k, v in dir.entries.items() + if k not in ('.', '..') ] + names.extend(node_names) + if not strings: + # Make sure the working directory (self) actually has + # entries for all Nodes in repositories or variant dirs. + for name in node_names: selfEntry(name) + if ondisk: + try: + disk_names = os.listdir(dir.abspath) + except os.error: + continue + names.extend(disk_names) + if not strings: + # We're going to return corresponding Nodes in + # the local directory, so we need to make sure + # those Nodes exist. We only want to create + # Nodes for the entries that will match the + # specified pattern, though, which means we + # need to filter the list here, even though + # the overall list will also be filtered later, + # after we exit this loop. + if pattern[0] != '.': + #disk_names = [ d for d in disk_names if d[0] != '.' ] + disk_names = [x for x in disk_names if x[0] != '.'] + disk_names = fnmatch.filter(disk_names, pattern) + dirEntry = dir.Entry + for name in disk_names: + # Add './' before disk filename so that '#' at + # beginning of filename isn't interpreted. + name = './' + name + node = dirEntry(name).disambiguate() + n = selfEntry(name) + if n.__class__ != node.__class__: + n.__class__ = node.__class__ + n._morph() + + names = set(names) + if pattern[0] != '.': + #names = [ n for n in names if n[0] != '.' ] + names = [x for x in names if x[0] != '.'] + names = fnmatch.filter(names, pattern) + + if strings: + return names + + #return [ self.entries[_my_normcase(n)] for n in names ] + return [self.entries[_my_normcase(n)] for n in names] + +class RootDir(Dir): + """A class for the root directory of a file system. + + This is the same as a Dir class, except that the path separator + ('/' or '\\') is actually part of the name, so we don't need to + add a separator when creating the path names of entries within + this directory. + """ + def __init__(self, drive, fs): + if __debug__: logInstanceCreation(self, 'Node.FS.RootDir') + # We're going to be our own parent directory (".." entry and .dir + # attribute) so we have to set up some values so Base.__init__() + # won't gag won't it calls some of our methods. + self.abspath = '' + self.labspath = '' + self.path = '' + self.tpath = '' + self.path_elements = [] + self.duplicate = 0 + self.root = self + + # Handle all the types of drives: + if drive == '': + # No drive, regular UNIX root or Windows default drive. + name = OS_SEP + dirname = OS_SEP + elif drive == '//': + # UNC path + name = UNC_PREFIX + dirname = UNC_PREFIX + else: + # Windows drive letter + name = drive + dirname = drive + OS_SEP + + Base.__init__(self, name, self, fs) + + # Now set our paths to what we really want them to be. The + # name should already contain any necessary separators, such + # as the initial drive letter (the name) plus the directory + # separator, except for the "lookup abspath," which does not + # have the drive letter. + self.abspath = dirname + self.labspath = '' + self.path = dirname + self.tpath = dirname + self._morph() + + # Must be reset after Dir._morph() is invoked... + self.dirname = dirname + + self._lookupDict = {} + + self._lookupDict[''] = self + self._lookupDict['/'] = self + + # The // entry is necessary because os.path.normpath() + # preserves double slashes at the beginning of a path on Posix + # platforms. + if not has_unc: + self._lookupDict['//'] = self + + def must_be_same(self, klass): + if klass is Dir: + return + Base.must_be_same(self, klass) + + def _lookup_abs(self, p, klass, create=1): + """ + Fast (?) lookup of a *normalized* absolute path. + + This method is intended for use by internal lookups with + already-normalized path data. For general-purpose lookups, + use the FS.Entry(), FS.Dir() or FS.File() methods. + + The caller is responsible for making sure we're passed a + normalized absolute path; we merely let Python's dictionary look + up and return the One True Node.FS object for the path. + + If a Node for the specified "p" doesn't already exist, and + "create" is specified, the Node may be created after recursive + invocation to find or create the parent directory or directories. + """ + k = _my_normcase(p) + try: + result = self._lookupDict[k] + except KeyError: + if not create: + msg = "No such file or directory: '%s' in '%s' (and create is False)" % (p, str(self)) + raise SCons.Errors.UserError(msg) + # There is no Node for this path name, and we're allowed + # to create it. + # (note: would like to use p.rsplit('/',1) here but + # that's not in python 2.3) + # e.g.: dir_name, file_name = p.rsplit('/',1) + last_slash = p.rindex('/') + if (last_slash >= 0): + dir_name = p[:last_slash] + file_name = p[last_slash+1:] + else: + dir_name = p # shouldn't happen, just in case + file_name = '' + + dir_node = self._lookup_abs(dir_name, Dir) + result = klass(file_name, dir_node, self.fs) + + # Double-check on disk (as configured) that the Node we + # created matches whatever is out there in the real world. + result.diskcheck_match() + + self._lookupDict[k] = result + dir_node.entries[_my_normcase(file_name)] = result + dir_node.implicit = None + else: + # There is already a Node for this path name. Allow it to + # complain if we were looking for an inappropriate type. + result.must_be_same(klass) + return result + + def __str__(self): + return self.abspath + + def entry_abspath(self, name): + return self.abspath + name + + def entry_labspath(self, name): + return '/' + name + + def entry_path(self, name): + return self.path + name + + def entry_tpath(self, name): + return self.tpath + name + + def is_under(self, dir): + if self is dir: + return 1 + else: + return 0 + + def up(self): + return None + + def get_dir(self): + return None + + def src_builder(self): + return _null + +class FileNodeInfo(SCons.Node.NodeInfoBase): + current_version_id = 1 + + field_list = ['csig', 'timestamp', 'size'] + + # This should get reset by the FS initialization. + fs = None + + def str_to_node(self, s): + top = self.fs.Top + root = top.root + if do_splitdrive: + drive, s = _my_splitdrive(s) + if drive: + root = self.fs.get_root(drive) + if not os.path.isabs(s): + s = top.labspath + '/' + s + return root._lookup_abs(s, Entry) + +class FileBuildInfo(SCons.Node.BuildInfoBase): + current_version_id = 1 + + def convert_to_sconsign(self): + """ + Converts this FileBuildInfo object for writing to a .sconsign file + + This replaces each Node in our various dependency lists with its + usual string representation: relative to the top-level SConstruct + directory, or an absolute path if it's outside. + """ + if os_sep_is_slash: + node_to_str = str + else: + def node_to_str(n): + try: + s = n.path + except AttributeError: + s = str(n) + else: + s = s.replace(OS_SEP, '/') + return s + for attr in ['bsources', 'bdepends', 'bimplicit']: + try: + val = getattr(self, attr) + except AttributeError: + pass + else: + setattr(self, attr, list(map(node_to_str, val))) + def convert_from_sconsign(self, dir, name): + """ + Converts a newly-read FileBuildInfo object for in-SCons use + + For normal up-to-date checking, we don't have any conversion to + perform--but we're leaving this method here to make that clear. + """ + pass + def prepare_dependencies(self): + """ + Prepares a FileBuildInfo object for explaining what changed + + The bsources, bdepends and bimplicit lists have all been + stored on disk as paths relative to the top-level SConstruct + directory. Convert the strings to actual Nodes (for use by the + --debug=explain code and --implicit-cache). + """ + attrs = [ + ('bsources', 'bsourcesigs'), + ('bdepends', 'bdependsigs'), + ('bimplicit', 'bimplicitsigs'), + ] + for (nattr, sattr) in attrs: + try: + strings = getattr(self, nattr) + nodeinfos = getattr(self, sattr) + except AttributeError: + continue + nodes = [] + for s, ni in zip(strings, nodeinfos): + if not isinstance(s, SCons.Node.Node): + s = ni.str_to_node(s) + nodes.append(s) + setattr(self, nattr, nodes) + def format(self, names=0): + result = [] + bkids = self.bsources + self.bdepends + self.bimplicit + bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs + for bkid, bkidsig in zip(bkids, bkidsigs): + result.append(str(bkid) + ': ' + + ' '.join(bkidsig.format(names=names))) + result.append('%s [%s]' % (self.bactsig, self.bact)) + return '\n'.join(result) + +class File(Base): + """A class for files in a file system. + """ + + memoizer_counters = [] + + NodeInfo = FileNodeInfo + BuildInfo = FileBuildInfo + + md5_chunksize = 64 + + def diskcheck_match(self): + diskcheck_match(self, self.isdir, + "Directory %s found where file expected.") + + def __init__(self, name, directory, fs): + if __debug__: logInstanceCreation(self, 'Node.FS.File') + Base.__init__(self, name, directory, fs) + self._morph() + + def Entry(self, name): + """Create an entry node named 'name' relative to + the directory of this file.""" + return self.dir.Entry(name) + + def Dir(self, name, create=True): + """Create a directory node named 'name' relative to + the directory of this file.""" + return self.dir.Dir(name, create=create) + + def Dirs(self, pathlist): + """Create a list of directories relative to the SConscript + directory of this file.""" + return [self.Dir(p) for p in pathlist] + + def File(self, name): + """Create a file node named 'name' relative to + the directory of this file.""" + return self.dir.File(name) + + #def generate_build_dict(self): + # """Return an appropriate dictionary of values for building + # this File.""" + # return {'Dir' : self.Dir, + # 'File' : self.File, + # 'RDirs' : self.RDirs} + + def _morph(self): + """Turn a file system node into a File object.""" + self.scanner_paths = {} + if not hasattr(self, '_local'): + self._local = 0 + + # If there was already a Builder set on this entry, then + # we need to make sure we call the target-decider function, + # not the source-decider. Reaching in and doing this by hand + # is a little bogus. We'd prefer to handle this by adding + # an Entry.builder_set() method that disambiguates like the + # other methods, but that starts running into problems with the + # fragile way we initialize Dir Nodes with their Mkdir builders, + # yet still allow them to be overridden by the user. Since it's + # not clear right now how to fix that, stick with what works + # until it becomes clear... + if self.has_builder(): + self.changed_since_last_build = self.decide_target + + def scanner_key(self): + return self.get_suffix() + + def get_contents(self): + if not self.rexists(): + return '' + fname = self.rfile().abspath + try: + contents = open(fname, "rb").read() + except EnvironmentError, e: + if not e.filename: + e.filename = fname + raise + return contents + + # This attempts to figure out what the encoding of the text is + # based upon the BOM bytes, and then decodes the contents so that + # it's a valid python string. + def get_text_contents(self): + contents = self.get_contents() + # The behavior of various decode() methods and functions + # w.r.t. the initial BOM bytes is different for different + # encodings and/or Python versions. ('utf-8' does not strip + # them, but has a 'utf-8-sig' which does; 'utf-16' seems to + # strip them; etc.) Just sidestep all the complication by + # explicitly stripping the BOM before we decode(). + if contents.startswith(codecs.BOM_UTF8): + return contents[len(codecs.BOM_UTF8):].decode('utf-8') + if contents.startswith(codecs.BOM_UTF16_LE): + return contents[len(codecs.BOM_UTF16_LE):].decode('utf-16-le') + if contents.startswith(codecs.BOM_UTF16_BE): + return contents[len(codecs.BOM_UTF16_BE):].decode('utf-16-be') + return contents + + def get_content_hash(self): + """ + Compute and return the MD5 hash for this file. + """ + if not self.rexists(): + return SCons.Util.MD5signature('') + fname = self.rfile().abspath + try: + cs = SCons.Util.MD5filesignature(fname, + chunksize=SCons.Node.FS.File.md5_chunksize*1024) + except EnvironmentError, e: + if not e.filename: + e.filename = fname + raise + return cs + + + memoizer_counters.append(SCons.Memoize.CountValue('get_size')) + + def get_size(self): + try: + return self._memo['get_size'] + except KeyError: + pass + + if self.rexists(): + size = self.rfile().getsize() + else: + size = 0 + + self._memo['get_size'] = size + + return size + + memoizer_counters.append(SCons.Memoize.CountValue('get_timestamp')) + + def get_timestamp(self): + try: + return self._memo['get_timestamp'] + except KeyError: + pass + + if self.rexists(): + timestamp = self.rfile().getmtime() + else: + timestamp = 0 + + self._memo['get_timestamp'] = timestamp + + return timestamp + + def store_info(self): + # Merge our build information into the already-stored entry. + # This accomodates "chained builds" where a file that's a target + # in one build (SConstruct file) is a source in a different build. + # See test/chained-build.py for the use case. + if do_store_info: + self.dir.sconsign().store_info(self.name, self) + + convert_copy_attrs = [ + 'bsources', + 'bimplicit', + 'bdepends', + 'bact', + 'bactsig', + 'ninfo', + ] + + + convert_sig_attrs = [ + 'bsourcesigs', + 'bimplicitsigs', + 'bdependsigs', + ] + + def convert_old_entry(self, old_entry): + # Convert a .sconsign entry from before the Big Signature + # Refactoring, doing what we can to convert its information + # to the new .sconsign entry format. + # + # The old format looked essentially like this: + # + # BuildInfo + # .ninfo (NodeInfo) + # .bsig + # .csig + # .timestamp + # .size + # .bsources + # .bsourcesigs ("signature" list) + # .bdepends + # .bdependsigs ("signature" list) + # .bimplicit + # .bimplicitsigs ("signature" list) + # .bact + # .bactsig + # + # The new format looks like this: + # + # .ninfo (NodeInfo) + # .bsig + # .csig + # .timestamp + # .size + # .binfo (BuildInfo) + # .bsources + # .bsourcesigs (NodeInfo list) + # .bsig + # .csig + # .timestamp + # .size + # .bdepends + # .bdependsigs (NodeInfo list) + # .bsig + # .csig + # .timestamp + # .size + # .bimplicit + # .bimplicitsigs (NodeInfo list) + # .bsig + # .csig + # .timestamp + # .size + # .bact + # .bactsig + # + # The basic idea of the new structure is that a NodeInfo always + # holds all available information about the state of a given Node + # at a certain point in time. The various .b*sigs lists can just + # be a list of pointers to the .ninfo attributes of the different + # dependent nodes, without any copying of information until it's + # time to pickle it for writing out to a .sconsign file. + # + # The complicating issue is that the *old* format only stored one + # "signature" per dependency, based on however the *last* build + # was configured. We don't know from just looking at it whether + # it was a build signature, a content signature, or a timestamp + # "signature". Since we no longer use build signatures, the + # best we can do is look at the length and if it's thirty two, + # assume that it was (or might have been) a content signature. + # If it was actually a build signature, then it will cause a + # rebuild anyway when it doesn't match the new content signature, + # but that's probably the best we can do. + import SCons.SConsign + new_entry = SCons.SConsign.SConsignEntry() + new_entry.binfo = self.new_binfo() + binfo = new_entry.binfo + for attr in self.convert_copy_attrs: + try: + value = getattr(old_entry, attr) + except AttributeError: + continue + setattr(binfo, attr, value) + delattr(old_entry, attr) + for attr in self.convert_sig_attrs: + try: + sig_list = getattr(old_entry, attr) + except AttributeError: + continue + value = [] + for sig in sig_list: + ninfo = self.new_ninfo() + if len(sig) == 32: + ninfo.csig = sig + else: + ninfo.timestamp = sig + value.append(ninfo) + setattr(binfo, attr, value) + delattr(old_entry, attr) + return new_entry + + memoizer_counters.append(SCons.Memoize.CountValue('get_stored_info')) + + def get_stored_info(self): + try: + return self._memo['get_stored_info'] + except KeyError: + pass + + try: + sconsign_entry = self.dir.sconsign().get_entry(self.name) + except (KeyError, EnvironmentError): + import SCons.SConsign + sconsign_entry = SCons.SConsign.SConsignEntry() + sconsign_entry.binfo = self.new_binfo() + sconsign_entry.ninfo = self.new_ninfo() + else: + if isinstance(sconsign_entry, FileBuildInfo): + # This is a .sconsign file from before the Big Signature + # Refactoring; convert it as best we can. + sconsign_entry = self.convert_old_entry(sconsign_entry) + try: + delattr(sconsign_entry.ninfo, 'bsig') + except AttributeError: + pass + + self._memo['get_stored_info'] = sconsign_entry + + return sconsign_entry + + def get_stored_implicit(self): + binfo = self.get_stored_info().binfo + binfo.prepare_dependencies() + try: return binfo.bimplicit + except AttributeError: return None + + def rel_path(self, other): + return self.dir.rel_path(other) + + def _get_found_includes_key(self, env, scanner, path): + return (id(env), id(scanner), path) + + memoizer_counters.append(SCons.Memoize.CountDict('get_found_includes', _get_found_includes_key)) + + def get_found_includes(self, env, scanner, path): + """Return the included implicit dependencies in this file. + Cache results so we only scan the file once per path + regardless of how many times this information is requested. + """ + memo_key = (id(env), id(scanner), path) + try: + memo_dict = self._memo['get_found_includes'] + except KeyError: + memo_dict = {} + self._memo['get_found_includes'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + + if scanner: + # result = [n.disambiguate() for n in scanner(self, env, path)] + result = scanner(self, env, path) + result = [N.disambiguate() for N in result] + else: + result = [] + + memo_dict[memo_key] = result + + return result + + def _createDir(self): + # ensure that the directories for this node are + # created. + self.dir._create() + + def push_to_cache(self): + """Try to push the node into a cache + """ + # This should get called before the Nodes' .built() method is + # called, which would clear the build signature if the file has + # a source scanner. + # + # We have to clear the local memoized values *before* we push + # the node to cache so that the memoization of the self.exists() + # return value doesn't interfere. + if self.nocache: + return + self.clear_memoized_values() + if self.exists(): + self.get_build_env().get_CacheDir().push(self) + + def retrieve_from_cache(self): + """Try to retrieve the node's content from a cache + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + + Returns true iff the node was successfully retrieved. + """ + if self.nocache: + return None + if not self.is_derived(): + return None + return self.get_build_env().get_CacheDir().retrieve(self) + + def visited(self): + if self.exists(): + self.get_build_env().get_CacheDir().push_if_forced(self) + + ninfo = self.get_ninfo() + + csig = self.get_max_drift_csig() + if csig: + ninfo.csig = csig + + ninfo.timestamp = self.get_timestamp() + ninfo.size = self.get_size() + + if not self.has_builder(): + # This is a source file, but it might have been a target file + # in another build that included more of the DAG. Copy + # any build information that's stored in the .sconsign file + # into our binfo object so it doesn't get lost. + old = self.get_stored_info() + self.get_binfo().__dict__.update(old.binfo.__dict__) + + self.store_info() + + def find_src_builder(self): + if self.rexists(): + return None + scb = self.dir.src_builder() + if scb is _null: + if diskcheck_sccs(self.dir, self.name): + scb = get_DefaultSCCSBuilder() + elif diskcheck_rcs(self.dir, self.name): + scb = get_DefaultRCSBuilder() + else: + scb = None + if scb is not None: + try: + b = self.builder + except AttributeError: + b = None + if b is None: + self.builder_set(scb) + return scb + + def has_src_builder(self): + """Return whether this Node has a source builder or not. + + If this Node doesn't have an explicit source code builder, this + is where we figure out, on the fly, if there's a transparent + source code builder for it. + + Note that if we found a source builder, we also set the + self.builder attribute, so that all of the methods that actually + *build* this file don't have to do anything different. + """ + try: + scb = self.sbuilder + except AttributeError: + scb = self.sbuilder = self.find_src_builder() + return scb is not None + + def alter_targets(self): + """Return any corresponding targets in a variant directory. + """ + if self.is_derived(): + return [], None + return self.fs.variant_dir_target_climb(self, self.dir, [self.name]) + + def _rmv_existing(self): + self.clear_memoized_values() + if print_duplicate: + print "dup: removing existing target %s"%self + e = Unlink(self, [], None) + if isinstance(e, SCons.Errors.BuildError): + raise e + + # + # Taskmaster interface subsystem + # + + def make_ready(self): + self.has_src_builder() + self.get_binfo() + + def prepare(self): + """Prepare for this file to be created.""" + SCons.Node.Node.prepare(self) + + if self.get_state() != SCons.Node.up_to_date: + if self.exists(): + if self.is_derived() and not self.precious: + self._rmv_existing() + else: + try: + self._createDir() + except SCons.Errors.StopError, drive: + desc = "No drive `%s' for target `%s'." % (drive, self) + raise SCons.Errors.StopError(desc) + + # + # + # + + def remove(self): + """Remove this file.""" + if self.exists() or self.islink(): + self.fs.unlink(self.path) + return 1 + return None + + def do_duplicate(self, src): + self._createDir() + if print_duplicate: + print "dup: relinking variant '%s' from '%s'"%(self, src) + Unlink(self, None, None) + e = Link(self, src, None) + if isinstance(e, SCons.Errors.BuildError): + desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr) + raise SCons.Errors.StopError(desc) + self.linked = 1 + # The Link() action may or may not have actually + # created the file, depending on whether the -n + # option was used or not. Delete the _exists and + # _rexists attributes so they can be reevaluated. + self.clear() + + memoizer_counters.append(SCons.Memoize.CountValue('exists')) + + def exists(self): + try: + return self._memo['exists'] + except KeyError: + pass + # Duplicate from source path if we are set up to do this. + if self.duplicate and not self.is_derived() and not self.linked: + src = self.srcnode() + if src is not self: + # At this point, src is meant to be copied in a variant directory. + src = src.rfile() + if src.abspath != self.abspath: + if src.exists(): + self.do_duplicate(src) + # Can't return 1 here because the duplication might + # not actually occur if the -n option is being used. + else: + # The source file does not exist. Make sure no old + # copy remains in the variant directory. + if print_duplicate: + print "dup: no src for %s, unlinking old variant copy"%self + if Base.exists(self) or self.islink(): + self.fs.unlink(self.path) + # Return None explicitly because the Base.exists() call + # above will have cached its value if the file existed. + self._memo['exists'] = None + return None + result = Base.exists(self) + self._memo['exists'] = result + return result + + # + # SIGNATURE SUBSYSTEM + # + + def get_max_drift_csig(self): + """ + Returns the content signature currently stored for this node + if it's been unmodified longer than the max_drift value, or the + max_drift value is 0. Returns None otherwise. + """ + old = self.get_stored_info() + mtime = self.get_timestamp() + + max_drift = self.fs.max_drift + if max_drift > 0: + if (time.time() - mtime) > max_drift: + try: + n = old.ninfo + if n.timestamp and n.csig and n.timestamp == mtime: + return n.csig + except AttributeError: + pass + elif max_drift == 0: + try: + return old.ninfo.csig + except AttributeError: + pass + + return None + + def get_csig(self): + """ + Generate a node's content signature, the digested signature + of its content. + + node - the node + cache - alternate node to use for the signature cache + returns - the content signature + """ + ninfo = self.get_ninfo() + try: + return ninfo.csig + except AttributeError: + pass + + csig = self.get_max_drift_csig() + if csig is None: + + try: + if self.get_size() < SCons.Node.FS.File.md5_chunksize: + contents = self.get_contents() + else: + csig = self.get_content_hash() + except IOError: + # This can happen if there's actually a directory on-disk, + # which can be the case if they've disabled disk checks, + # or if an action with a File target actually happens to + # create a same-named directory by mistake. + csig = '' + else: + if not csig: + csig = SCons.Util.MD5signature(contents) + + ninfo.csig = csig + + return csig + + # + # DECISION SUBSYSTEM + # + + def builder_set(self, builder): + SCons.Node.Node.builder_set(self, builder) + self.changed_since_last_build = self.decide_target + + def changed_content(self, target, prev_ni): + cur_csig = self.get_csig() + try: + return cur_csig != prev_ni.csig + except AttributeError: + return 1 + + def changed_state(self, target, prev_ni): + return self.state != SCons.Node.up_to_date + + def changed_timestamp_then_content(self, target, prev_ni): + if not self.changed_timestamp_match(target, prev_ni): + try: + self.get_ninfo().csig = prev_ni.csig + except AttributeError: + pass + return False + return self.changed_content(target, prev_ni) + + def changed_timestamp_newer(self, target, prev_ni): + try: + return self.get_timestamp() > target.get_timestamp() + except AttributeError: + return 1 + + def changed_timestamp_match(self, target, prev_ni): + try: + return self.get_timestamp() != prev_ni.timestamp + except AttributeError: + return 1 + + def decide_source(self, target, prev_ni): + return target.get_build_env().decide_source(self, target, prev_ni) + + def decide_target(self, target, prev_ni): + return target.get_build_env().decide_target(self, target, prev_ni) + + # Initialize this Node's decider function to decide_source() because + # every file is a source file until it has a Builder attached... + changed_since_last_build = decide_source + + def is_up_to_date(self): + T = 0 + if T: Trace('is_up_to_date(%s):' % self) + if not self.exists(): + if T: Trace(' not self.exists():') + # The file doesn't exist locally... + r = self.rfile() + if r != self: + # ...but there is one in a Repository... + if not self.changed(r): + if T: Trace(' changed(%s):' % r) + # ...and it's even up-to-date... + if self._local: + # ...and they'd like a local copy. + e = LocalCopy(self, r, None) + if isinstance(e, SCons.Errors.BuildError): + raise + self.store_info() + if T: Trace(' 1\n') + return 1 + self.changed() + if T: Trace(' None\n') + return None + else: + r = self.changed() + if T: Trace(' self.exists(): %s\n' % r) + return not r + + memoizer_counters.append(SCons.Memoize.CountValue('rfile')) + + def rfile(self): + try: + return self._memo['rfile'] + except KeyError: + pass + result = self + if not self.exists(): + norm_name = _my_normcase(self.name) + for dir in self.dir.get_all_rdirs(): + try: node = dir.entries[norm_name] + except KeyError: node = dir.file_on_disk(self.name) + if node and node.exists() and \ + (isinstance(node, File) or isinstance(node, Entry) \ + or not node.is_derived()): + result = node + # Copy over our local attributes to the repository + # Node so we identify shared object files in the + # repository and don't assume they're static. + # + # This isn't perfect; the attribute would ideally + # be attached to the object in the repository in + # case it was built statically in the repository + # and we changed it to shared locally, but that's + # rarely the case and would only occur if you + # intentionally used the same suffix for both + # shared and static objects anyway. So this + # should work well in practice. + result.attributes = self.attributes + break + self._memo['rfile'] = result + return result + + def rstr(self): + return str(self.rfile()) + + def get_cachedir_csig(self): + """ + Fetch a Node's content signature for purposes of computing + another Node's cachesig. + + This is a wrapper around the normal get_csig() method that handles + the somewhat obscure case of using CacheDir with the -n option. + Any files that don't exist would normally be "built" by fetching + them from the cache, but the normal get_csig() method will try + to open up the local file, which doesn't exist because the -n + option meant we didn't actually pull the file from cachedir. + But since the file *does* actually exist in the cachedir, we + can use its contents for the csig. + """ + try: + return self.cachedir_csig + except AttributeError: + pass + + cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self) + if not self.exists() and cachefile and os.path.exists(cachefile): + self.cachedir_csig = SCons.Util.MD5filesignature(cachefile, \ + SCons.Node.FS.File.md5_chunksize * 1024) + else: + self.cachedir_csig = self.get_csig() + return self.cachedir_csig + + def get_cachedir_bsig(self): + try: + return self.cachesig + except AttributeError: + pass + + # Add the path to the cache signature, because multiple + # targets built by the same action will all have the same + # build signature, and we have to differentiate them somehow. + children = self.children() + executor = self.get_executor() + # sigs = [n.get_cachedir_csig() for n in children] + sigs = [n.get_cachedir_csig() for n in children] + sigs.append(SCons.Util.MD5signature(executor.get_contents())) + sigs.append(self.path) + result = self.cachesig = SCons.Util.MD5collect(sigs) + return result + + +default_fs = None + +def get_default_fs(): + global default_fs + if not default_fs: + default_fs = FS() + return default_fs + +class FileFinder(object): + """ + """ + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + def __init__(self): + self._memo = {} + + def filedir_lookup(self, p, fd=None): + """ + A helper method for find_file() that looks up a directory for + a file we're trying to find. This only creates the Dir Node if + it exists on-disk, since if the directory doesn't exist we know + we won't find any files in it... :-) + + It would be more compact to just use this as a nested function + with a default keyword argument (see the commented-out version + below), but that doesn't work unless you have nested scopes, + so we define it here just so this work under Python 1.5.2. + """ + if fd is None: + fd = self.default_filedir + dir, name = os.path.split(fd) + drive, d = _my_splitdrive(dir) + if not name and d[:1] in ('/', OS_SEP): + #return p.fs.get_root(drive).dir_on_disk(name) + return p.fs.get_root(drive) + if dir: + p = self.filedir_lookup(p, dir) + if not p: + return None + norm_name = _my_normcase(name) + try: + node = p.entries[norm_name] + except KeyError: + return p.dir_on_disk(name) + if isinstance(node, Dir): + return node + if isinstance(node, Entry): + node.must_be_same(Dir) + return node + return None + + def _find_file_key(self, filename, paths, verbose=None): + return (filename, paths) + + memoizer_counters.append(SCons.Memoize.CountDict('find_file', _find_file_key)) + + def find_file(self, filename, paths, verbose=None): + """ + find_file(str, [Dir()]) -> [nodes] + + filename - a filename to find + paths - a list of directory path *nodes* to search in. Can be + represented as a list, a tuple, or a callable that is + called with no arguments and returns the list or tuple. + + returns - the node created from the found file. + + Find a node corresponding to either a derived file or a file + that exists already. + + Only the first file found is returned, and none is returned + if no file is found. + """ + memo_key = self._find_file_key(filename, paths) + try: + memo_dict = self._memo['find_file'] + except KeyError: + memo_dict = {} + self._memo['find_file'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + + if verbose and not callable(verbose): + if not SCons.Util.is_String(verbose): + verbose = "find_file" + _verbose = u' %s: ' % verbose + verbose = lambda s: sys.stdout.write(_verbose + s) + + filedir, filename = os.path.split(filename) + if filedir: + # More compact code that we can't use until we drop + # support for Python 1.5.2: + # + #def filedir_lookup(p, fd=filedir): + # """ + # A helper function that looks up a directory for a file + # we're trying to find. This only creates the Dir Node + # if it exists on-disk, since if the directory doesn't + # exist we know we won't find any files in it... :-) + # """ + # dir, name = os.path.split(fd) + # if dir: + # p = filedir_lookup(p, dir) + # if not p: + # return None + # norm_name = _my_normcase(name) + # try: + # node = p.entries[norm_name] + # except KeyError: + # return p.dir_on_disk(name) + # if isinstance(node, Dir): + # return node + # if isinstance(node, Entry): + # node.must_be_same(Dir) + # return node + # if isinstance(node, Dir) or isinstance(node, Entry): + # return node + # return None + #paths = [_f for _f in map(filedir_lookup, paths) if _f] + + self.default_filedir = filedir + paths = [_f for _f in map(self.filedir_lookup, paths) if _f] + + result = None + for dir in paths: + if verbose: + verbose("looking for '%s' in '%s' ...\n" % (filename, dir)) + node, d = dir.srcdir_find_file(filename) + if node: + if verbose: + verbose("... FOUND '%s' in '%s'\n" % (filename, d)) + result = node + break + + memo_dict[memo_key] = result + + return result + +find_file = FileFinder().find_file + + +def invalidate_node_memos(targets): + """ + Invalidate the memoized values of all Nodes (files or directories) + that are associated with the given entries. Has been added to + clear the cache of nodes affected by a direct execution of an + action (e.g. Delete/Copy/Chmod). Existing Node caches become + inconsistent if the action is run through Execute(). The argument + `targets` can be a single Node object or filename, or a sequence + of Nodes/filenames. + """ + from traceback import extract_stack + + # First check if the cache really needs to be flushed. Only + # actions run in the SConscript with Execute() seem to be + # affected. XXX The way to check if Execute() is in the stacktrace + # is a very dirty hack and should be replaced by a more sensible + # solution. + for f in extract_stack(): + if f[2] == 'Execute' and f[0][-14:] == 'Environment.py': + break + else: + # Dont have to invalidate, so return + return + + if not SCons.Util.is_List(targets): + targets = [targets] + + for entry in targets: + # If the target is a Node object, clear the cache. If it is a + # filename, look up potentially existing Node object first. + try: + entry.clear_memoized_values() + except AttributeError: + # Not a Node object, try to look up Node by filename. XXX + # This creates Node objects even for those filenames which + # do not correspond to an existing Node object. + node = get_default_fs().Entry(entry) + if node: + node.clear_memoized_values() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Node/Python.py b/engine/SCons/Node/Python.py new file mode 100644 index 0000000..b97f9d4 --- /dev/null +++ b/engine/SCons/Node/Python.py @@ -0,0 +1,128 @@ +"""scons.Node.Python + +Python nodes. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Node/Python.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Node + +class ValueNodeInfo(SCons.Node.NodeInfoBase): + current_version_id = 1 + + field_list = ['csig'] + + def str_to_node(self, s): + return Value(s) + +class ValueBuildInfo(SCons.Node.BuildInfoBase): + current_version_id = 1 + +class Value(SCons.Node.Node): + """A class for Python variables, typically passed on the command line + or generated by a script, but not from a file or some other source. + """ + + NodeInfo = ValueNodeInfo + BuildInfo = ValueBuildInfo + + def __init__(self, value, built_value=None): + SCons.Node.Node.__init__(self) + self.value = value + if built_value is not None: + self.built_value = built_value + + def str_for_display(self): + return repr(self.value) + + def __str__(self): + return str(self.value) + + def make_ready(self): + self.get_csig() + + def build(self, **kw): + if not hasattr(self, 'built_value'): + SCons.Node.Node.build(self, **kw) + + is_up_to_date = SCons.Node.Node.children_are_up_to_date + + def is_under(self, dir): + # Make Value nodes get built regardless of + # what directory scons was run from. Value nodes + # are outside the filesystem: + return 1 + + def write(self, built_value): + """Set the value of the node.""" + self.built_value = built_value + + def read(self): + """Return the value. If necessary, the value is built.""" + self.build() + if not hasattr(self, 'built_value'): + self.built_value = self.value + return self.built_value + + def get_text_contents(self): + """By the assumption that the node.built_value is a + deterministic product of the sources, the contents of a Value + are the concatenation of all the contents of its sources. As + the value need not be built when get_contents() is called, we + cannot use the actual node.built_value.""" + ###TODO: something reasonable about universal newlines + contents = str(self.value) + for kid in self.children(None): + contents = contents + kid.get_contents() + return contents + + get_contents = get_text_contents ###TODO should return 'bytes' value + + def changed_since_last_build(self, target, prev_ni): + cur_csig = self.get_csig() + try: + return cur_csig != prev_ni.csig + except AttributeError: + return 1 + + def get_csig(self, calc=None): + """Because we're a Python value node and don't have a real + timestamp, we get to ignore the calculator and just use the + value contents.""" + try: + return self.ninfo.csig + except AttributeError: + pass + contents = self.get_contents() + self.get_ninfo().csig = contents + return contents + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Node/__init__.py b/engine/SCons/Node/__init__.py new file mode 100644 index 0000000..a832a5f --- /dev/null +++ b/engine/SCons/Node/__init__.py @@ -0,0 +1,1329 @@ +"""SCons.Node + +The Node package for the SCons software construction utility. + +This is, in many ways, the heart of SCons. + +A Node is where we encapsulate all of the dependency information about +any thing that SCons can build, or about any thing which SCons can use +to build some other thing. The canonical "thing," of course, is a file, +but a Node can also represent something remote (like a web page) or +something completely abstract (like an Alias). + +Each specific type of "thing" is specifically represented by a subclass +of the Node base class: Node.FS.File for files, Node.Alias for aliases, +etc. Dependency information is kept here in the base class, and +information specific to files/aliases/etc. is in the subclass. The +goal, if we've done this correctly, is that any type of "thing" should +be able to depend on any other type of "thing." + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Node/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +import collections +import copy +from itertools import chain + +from SCons.Debug import logInstanceCreation +import SCons.Executor +import SCons.Memoize +import SCons.Util + +from SCons.Debug import Trace + +def classname(obj): + return str(obj.__class__).split('.')[-1] + +# Node states +# +# These are in "priority" order, so that the maximum value for any +# child/dependency of a node represents the state of that node if +# it has no builder of its own. The canonical example is a file +# system directory, which is only up to date if all of its children +# were up to date. +no_state = 0 +pending = 1 +executing = 2 +up_to_date = 3 +executed = 4 +failed = 5 + +StateString = { + 0 : "no_state", + 1 : "pending", + 2 : "executing", + 3 : "up_to_date", + 4 : "executed", + 5 : "failed", +} + +# controls whether implicit dependencies are cached: +implicit_cache = 0 + +# controls whether implicit dep changes are ignored: +implicit_deps_unchanged = 0 + +# controls whether the cached implicit deps are ignored: +implicit_deps_changed = 0 + +# A variable that can be set to an interface-specific function be called +# to annotate a Node with information about its creation. +def do_nothing(node): pass + +Annotate = do_nothing + +# Classes for signature info for Nodes. + +class NodeInfoBase(object): + """ + The generic base class for signature information for a Node. + + Node subclasses should subclass NodeInfoBase to provide their own + logic for dealing with their own Node-specific signature information. + """ + current_version_id = 1 + def __init__(self, node=None): + # Create an object attribute from the class attribute so it ends up + # in the pickled data in the .sconsign file. + self._version_id = self.current_version_id + def update(self, node): + try: + field_list = self.field_list + except AttributeError: + return + for f in field_list: + try: + delattr(self, f) + except AttributeError: + pass + try: + func = getattr(node, 'get_' + f) + except AttributeError: + pass + else: + setattr(self, f, func()) + def convert(self, node, val): + pass + def merge(self, other): + self.__dict__.update(other.__dict__) + def format(self, field_list=None, names=0): + if field_list is None: + try: + field_list = self.field_list + except AttributeError: + field_list = sorted(self.__dict__.keys()) + fields = [] + for field in field_list: + try: + f = getattr(self, field) + except AttributeError: + f = None + f = str(f) + if names: + f = field + ': ' + f + fields.append(f) + return fields + +class BuildInfoBase(object): + """ + The generic base class for build information for a Node. + + This is what gets stored in a .sconsign file for each target file. + It contains a NodeInfo instance for this node (signature information + that's specific to the type of Node) and direct attributes for the + generic build stuff we have to track: sources, explicit dependencies, + implicit dependencies, and action information. + """ + current_version_id = 1 + def __init__(self, node=None): + # Create an object attribute from the class attribute so it ends up + # in the pickled data in the .sconsign file. + self._version_id = self.current_version_id + self.bsourcesigs = [] + self.bdependsigs = [] + self.bimplicitsigs = [] + self.bactsig = None + def merge(self, other): + self.__dict__.update(other.__dict__) + +class Node(object): + """The base Node class, for entities that we know how to + build, or use to build other Nodes. + """ + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + class Attrs(object): + pass + + def __init__(self): + if __debug__: logInstanceCreation(self, 'Node.Node') + # Note that we no longer explicitly initialize a self.builder + # attribute to None here. That's because the self.builder + # attribute may be created on-the-fly later by a subclass (the + # canonical example being a builder to fetch a file from a + # source code system like CVS or Subversion). + + # Each list of children that we maintain is accompanied by a + # dictionary used to look up quickly whether a node is already + # present in the list. Empirical tests showed that it was + # fastest to maintain them as side-by-side Node attributes in + # this way, instead of wrapping up each list+dictionary pair in + # a class. (Of course, we could always still do that in the + # future if we had a good reason to...). + self.sources = [] # source files used to build node + self.sources_set = set() + self._specific_sources = False + self.depends = [] # explicit dependencies (from Depends) + self.depends_set = set() + self.ignore = [] # dependencies to ignore + self.ignore_set = set() + self.prerequisites = SCons.Util.UniqueList() + self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) + self.waiting_parents = set() + self.waiting_s_e = set() + self.ref_count = 0 + self.wkids = None # Kids yet to walk, when it's an array + + self.env = None + self.state = no_state + self.precious = None + self.noclean = 0 + self.nocache = 0 + self.always_build = None + self.includes = None + self.attributes = self.Attrs() # Generic place to stick information about the Node. + self.side_effect = 0 # true iff this node is a side effect + self.side_effects = [] # the side effects of building this target + self.linked = 0 # is this node linked to the variant directory? + + self.clear_memoized_values() + + # Let the interface in which the build engine is embedded + # annotate this Node with its own info (like a description of + # what line in what file created the node, for example). + Annotate(self) + + def disambiguate(self, must_exist=None): + return self + + def get_suffix(self): + return '' + + memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) + + def get_build_env(self): + """Fetch the appropriate Environment to build this node. + """ + try: + return self._memo['get_build_env'] + except KeyError: + pass + result = self.get_executor().get_build_env() + self._memo['get_build_env'] = result + return result + + def get_build_scanner_path(self, scanner): + """Fetch the appropriate scanner path for this node.""" + return self.get_executor().get_build_scanner_path(scanner) + + def set_executor(self, executor): + """Set the action executor for this node.""" + self.executor = executor + + def get_executor(self, create=1): + """Fetch the action executor for this node. Create one if + there isn't already one, and requested to do so.""" + try: + executor = self.executor + except AttributeError: + if not create: + raise + try: + act = self.builder.action + except AttributeError: + executor = SCons.Executor.Null(targets=[self]) + else: + executor = SCons.Executor.Executor(act, + self.env or self.builder.env, + [self.builder.overrides], + [self], + self.sources) + self.executor = executor + return executor + + def executor_cleanup(self): + """Let the executor clean up any cached information.""" + try: + executor = self.get_executor(create=None) + except AttributeError: + pass + else: + executor.cleanup() + + def reset_executor(self): + "Remove cached executor; forces recompute when needed." + try: + delattr(self, 'executor') + except AttributeError: + pass + + def push_to_cache(self): + """Try to push a node into a cache + """ + pass + + def retrieve_from_cache(self): + """Try to retrieve the node's content from a cache + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + + Returns true iff the node was successfully retrieved. + """ + return 0 + + # + # Taskmaster interface subsystem + # + + def make_ready(self): + """Get a Node ready for evaluation. + + This is called before the Taskmaster decides if the Node is + up-to-date or not. Overriding this method allows for a Node + subclass to be disambiguated if necessary, or for an implicit + source builder to be attached. + """ + pass + + def prepare(self): + """Prepare for this Node to be built. + + This is called after the Taskmaster has decided that the Node + is out-of-date and must be rebuilt, but before actually calling + the method to build the Node. + + This default implementation checks that explicit or implicit + dependencies either exist or are derived, and initializes the + BuildInfo structure that will hold the information about how + this node is, uh, built. + + (The existence of source files is checked separately by the + Executor, which aggregates checks for all of the targets built + by a specific action.) + + Overriding this method allows for for a Node subclass to remove + the underlying file from the file system. Note that subclass + methods should call this base class method to get the child + check and the BuildInfo structure. + """ + for d in self.depends: + if d.missing(): + msg = "Explicit dependency `%s' not found, needed by target `%s'." + raise SCons.Errors.StopError(msg % (d, self)) + if self.implicit is not None: + for i in self.implicit: + if i.missing(): + msg = "Implicit dependency `%s' not found, needed by target `%s'." + raise SCons.Errors.StopError(msg % (i, self)) + self.binfo = self.get_binfo() + + def build(self, **kw): + """Actually build the node. + + This is called by the Taskmaster after it's decided that the + Node is out-of-date and must be rebuilt, and after the prepare() + method has gotten everything, uh, prepared. + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff + in built(). + + """ + try: + self.get_executor()(self, **kw) + except SCons.Errors.BuildError, e: + e.node = self + raise + + def built(self): + """Called just after this node is successfully built.""" + + # Clear the implicit dependency caches of any Nodes + # waiting for this Node to be built. + for parent in self.waiting_parents: + parent.implicit = None + + self.clear() + + self.ninfo.update(self) + + def visited(self): + """Called just after this node has been visited (with or + without a build).""" + try: + binfo = self.binfo + except AttributeError: + # Apparently this node doesn't need build info, so + # don't bother calculating or storing it. + pass + else: + self.ninfo.update(self) + self.store_info() + + # + # + # + + def add_to_waiting_s_e(self, node): + self.waiting_s_e.add(node) + + def add_to_waiting_parents(self, node): + """ + Returns the number of nodes added to our waiting parents list: + 1 if we add a unique waiting parent, 0 if not. (Note that the + returned values are intended to be used to increment a reference + count, so don't think you can "clean up" this function by using + True and False instead...) + """ + wp = self.waiting_parents + if node in wp: + return 0 + wp.add(node) + return 1 + + def postprocess(self): + """Clean up anything we don't need to hang onto after we've + been built.""" + self.executor_cleanup() + self.waiting_parents = set() + + def clear(self): + """Completely clear a Node of all its cached state (so that it + can be re-evaluated by interfaces that do continuous integration + builds). + """ + # The del_binfo() call here isn't necessary for normal execution, + # but is for interactive mode, where we might rebuild the same + # target and need to start from scratch. + self.del_binfo() + self.clear_memoized_values() + self.ninfo = self.new_ninfo() + self.executor_cleanup() + try: + delattr(self, '_calculated_sig') + except AttributeError: + pass + self.includes = None + + def clear_memoized_values(self): + self._memo = {} + + def builder_set(self, builder): + self.builder = builder + try: + del self.executor + except AttributeError: + pass + + def has_builder(self): + """Return whether this Node has a builder or not. + + In Boolean tests, this turns out to be a *lot* more efficient + than simply examining the builder attribute directly ("if + node.builder: ..."). When the builder attribute is examined + directly, it ends up calling __getattr__ for both the __len__ + and __nonzero__ attributes on instances of our Builder Proxy + class(es), generating a bazillion extra calls and slowing + things down immensely. + """ + try: + b = self.builder + except AttributeError: + # There was no explicit builder for this Node, so initialize + # the self.builder attribute to None now. + b = self.builder = None + return b is not None + + def set_explicit(self, is_explicit): + self.is_explicit = is_explicit + + def has_explicit_builder(self): + """Return whether this Node has an explicit builder + + This allows an internal Builder created by SCons to be marked + non-explicit, so that it can be overridden by an explicit + builder that the user supplies (the canonical example being + directories).""" + try: + return self.is_explicit + except AttributeError: + self.is_explicit = None + return self.is_explicit + + def get_builder(self, default_builder=None): + """Return the set builder, or a specified default value""" + try: + return self.builder + except AttributeError: + return default_builder + + multiple_side_effect_has_builder = has_builder + + def is_derived(self): + """ + Returns true iff this node is derived (i.e. built). + + This should return true only for nodes whose path should be in + the variant directory when duplicate=0 and should contribute their build + signatures when they are used as source files to other derived files. For + example: source with source builders are not derived in this sense, + and hence should not return true. + """ + return self.has_builder() or self.side_effect + + def alter_targets(self): + """Return a list of alternate targets for this Node. + """ + return [], None + + def get_found_includes(self, env, scanner, path): + """Return the scanned include lines (implicit dependencies) + found in this node. + + The default is no implicit dependencies. We expect this method + to be overridden by any subclass that can be scanned for + implicit dependencies. + """ + return [] + + def get_implicit_deps(self, env, scanner, path): + """Return a list of implicit dependencies for this node. + + This method exists to handle recursive invocation of the scanner + on the implicit dependencies returned by the scanner, if the + scanner's recursive flag says that we should. + """ + if not scanner: + return [] + + # Give the scanner a chance to select a more specific scanner + # for this Node. + #scanner = scanner.select(self) + + nodes = [self] + seen = {} + seen[self] = 1 + deps = [] + while nodes: + n = nodes.pop(0) + d = [x for x in n.get_found_includes(env, scanner, path) if x not in seen] + if d: + deps.extend(d) + for n in d: + seen[n] = 1 + nodes.extend(scanner.recurse_nodes(d)) + + return deps + + def get_env_scanner(self, env, kw={}): + return env.get_scanner(self.scanner_key()) + + def get_target_scanner(self): + return self.builder.target_scanner + + def get_source_scanner(self, node): + """Fetch the source scanner for the specified node + + NOTE: "self" is the target being built, "node" is + the source file for which we want to fetch the scanner. + + Implies self.has_builder() is true; again, expect to only be + called from locations where this is already verified. + + This function may be called very often; it attempts to cache + the scanner found to improve performance. + """ + scanner = None + try: + scanner = self.builder.source_scanner + except AttributeError: + pass + if not scanner: + # The builder didn't have an explicit scanner, so go look up + # a scanner from env['SCANNERS'] based on the node's scanner + # key (usually the file extension). + scanner = self.get_env_scanner(self.get_build_env()) + if scanner: + scanner = scanner.select(node) + return scanner + + def add_to_implicit(self, deps): + if not hasattr(self, 'implicit') or self.implicit is None: + self.implicit = [] + self.implicit_set = set() + self._children_reset() + self._add_child(self.implicit, self.implicit_set, deps) + + def scan(self): + """Scan this node's dependents for implicit dependencies.""" + # Don't bother scanning non-derived files, because we don't + # care what their dependencies are. + # Don't scan again, if we already have scanned. + if self.implicit is not None: + return + self.implicit = [] + self.implicit_set = set() + self._children_reset() + if not self.has_builder(): + return + + build_env = self.get_build_env() + executor = self.get_executor() + + # Here's where we implement --implicit-cache. + if implicit_cache and not implicit_deps_changed: + implicit = self.get_stored_implicit() + if implicit is not None: + # We now add the implicit dependencies returned from the + # stored .sconsign entry to have already been converted + # to Nodes for us. (We used to run them through a + # source_factory function here.) + + # 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. + for tgt in executor.get_all_targets(): + tgt.add_to_implicit(implicit) + + if implicit_deps_unchanged or self.is_up_to_date(): + return + # one of this node's sources has changed, + # so we must recalculate the implicit deps for all targets + for tgt in executor.get_all_targets(): + tgt.implicit = [] + tgt.implicit_set = set() + + # Have the executor scan the sources. + executor.scan_sources(self.builder.source_scanner) + + # If there's a target scanner, have the executor scan the target + # node itself and associated targets that might be built. + scanner = self.get_target_scanner() + if scanner: + executor.scan_targets(scanner) + + def scanner_key(self): + return None + + def select_scanner(self, scanner): + """Selects a scanner for this Node. + + This is a separate method so it can be overridden by Node + subclasses (specifically, Node.FS.Dir) that *must* use their + own Scanner and don't select one the Scanner.Selector that's + configured for the target. + """ + return scanner.select(self) + + def env_set(self, env, safe=0): + if safe and self.env: + return + self.env = env + + # + # SIGNATURE SUBSYSTEM + # + + NodeInfo = NodeInfoBase + BuildInfo = BuildInfoBase + + def new_ninfo(self): + ninfo = self.NodeInfo(self) + return ninfo + + def get_ninfo(self): + try: + return self.ninfo + except AttributeError: + self.ninfo = self.new_ninfo() + return self.ninfo + + def new_binfo(self): + binfo = self.BuildInfo(self) + return binfo + + def get_binfo(self): + """ + Fetch a node's build information. + + node - the node whose sources will be collected + cache - alternate node to use for the signature cache + returns - the build signature + + This no longer handles the recursive descent of the + node's children's signatures. We expect that they're + already built and updated by someone else, if that's + what's wanted. + """ + try: + return self.binfo + except AttributeError: + pass + + binfo = self.new_binfo() + self.binfo = binfo + + executor = self.get_executor() + ignore_set = self.ignore_set + + if self.has_builder(): + binfo.bact = str(executor) + binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) + + if self._specific_sources: + sources = [] + for s in self.sources: + if s not in ignore_set: + sources.append(s) + else: + sources = executor.get_unignored_sources(self, self.ignore) + seen = set() + bsources = [] + bsourcesigs = [] + for s in sources: + if not s in seen: + seen.add(s) + bsources.append(s) + bsourcesigs.append(s.get_ninfo()) + binfo.bsources = bsources + binfo.bsourcesigs = bsourcesigs + + depends = self.depends + dependsigs = [] + for d in depends: + if d not in ignore_set: + dependsigs.append(d.get_ninfo()) + binfo.bdepends = depends + binfo.bdependsigs = dependsigs + + implicit = self.implicit or [] + implicitsigs = [] + for i in implicit: + if i not in ignore_set: + implicitsigs.append(i.get_ninfo()) + binfo.bimplicit = implicit + binfo.bimplicitsigs = implicitsigs + + return binfo + + def del_binfo(self): + """Delete the build info from this node.""" + try: + delattr(self, 'binfo') + except AttributeError: + pass + + def get_csig(self): + try: + return self.ninfo.csig + except AttributeError: + ninfo = self.get_ninfo() + ninfo.csig = SCons.Util.MD5signature(self.get_contents()) + return self.ninfo.csig + + def get_cachedir_csig(self): + return self.get_csig() + + def store_info(self): + """Make the build signature permanent (that is, store it in the + .sconsign file or equivalent).""" + pass + + def do_not_store_info(self): + pass + + def get_stored_info(self): + return None + + def get_stored_implicit(self): + """Fetch the stored implicit dependencies""" + return None + + # + # + # + + def set_precious(self, precious = 1): + """Set the Node's precious value.""" + self.precious = precious + + def set_noclean(self, noclean = 1): + """Set the Node's noclean value.""" + # Make sure noclean is an integer so the --debug=stree + # output in Util.py can use it as an index. + self.noclean = noclean and 1 or 0 + + def set_nocache(self, nocache = 1): + """Set the Node's nocache value.""" + # Make sure nocache is an integer so the --debug=stree + # output in Util.py can use it as an index. + self.nocache = nocache and 1 or 0 + + def set_always_build(self, always_build = 1): + """Set the Node's always_build value.""" + self.always_build = always_build + + def exists(self): + """Does this node exists?""" + # All node exist by default: + return 1 + + def rexists(self): + """Does this node exist locally or in a repositiory?""" + # There are no repositories by default: + return self.exists() + + def missing(self): + return not self.is_derived() and \ + not self.linked and \ + not self.rexists() + + def remove(self): + """Remove this Node: no-op by default.""" + return None + + def add_dependency(self, depend): + """Adds dependencies.""" + try: + self._add_child(self.depends, self.depends_set, depend) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = list(map(str, e)) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def add_prerequisite(self, prerequisite): + """Adds prerequisites""" + self.prerequisites.extend(prerequisite) + self._children_reset() + + def add_ignore(self, depend): + """Adds dependencies to ignore.""" + try: + self._add_child(self.ignore, self.ignore_set, depend) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = list(map(str, e)) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def add_source(self, source): + """Adds sources.""" + if self._specific_sources: + return + try: + self._add_child(self.sources, self.sources_set, source) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = list(map(str, e)) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def _add_child(self, collection, set, child): + """Adds 'child' to 'collection', first checking 'set' to see if it's + already present.""" + #if type(child) is not type([]): + # child = [child] + #for c in child: + # if not isinstance(c, Node): + # raise TypeError, c + added = None + for c in child: + if c not in set: + set.add(c) + collection.append(c) + added = 1 + if added: + self._children_reset() + + def set_specific_source(self, source): + self.add_source(source) + self._specific_sources = True + + def add_wkid(self, wkid): + """Add a node to the list of kids waiting to be evaluated""" + if self.wkids is not None: + self.wkids.append(wkid) + + def _children_reset(self): + self.clear_memoized_values() + # We need to let the Executor clear out any calculated + # build info that it's cached so we can re-calculate it. + self.executor_cleanup() + + memoizer_counters.append(SCons.Memoize.CountValue('_children_get')) + + def _children_get(self): + try: + return self._memo['children_get'] + except KeyError: + pass + + # The return list may contain duplicate Nodes, especially in + # source trees where there are a lot of repeated #includes + # of a tangle of .h files. Profiling shows, however, that + # eliminating the duplicates with a brute-force approach that + # preserves the order (that is, something like: + # + # u = [] + # for n in list: + # if n not in u: + # u.append(n)" + # + # takes more cycles than just letting the underlying methods + # hand back cached values if a Node's information is requested + # multiple times. (Other methods of removing duplicates, like + # using dictionary keys, lose the order, and the only ordered + # dictionary patterns I found all ended up using "not in" + # internally anyway...) + if self.ignore_set: + if self.implicit is None: + iter = chain(self.sources,self.depends) + else: + iter = chain(self.sources, self.depends, self.implicit) + + children = [] + for i in iter: + if i not in self.ignore_set: + children.append(i) + else: + if self.implicit is None: + children = self.sources + self.depends + else: + children = self.sources + self.depends + self.implicit + + self._memo['children_get'] = children + return children + + def all_children(self, scan=1): + """Return a list of all the node's direct children.""" + if scan: + self.scan() + + # The return list may contain duplicate Nodes, especially in + # source trees where there are a lot of repeated #includes + # of a tangle of .h files. Profiling shows, however, that + # eliminating the duplicates with a brute-force approach that + # preserves the order (that is, something like: + # + # u = [] + # for n in list: + # if n not in u: + # u.append(n)" + # + # takes more cycles than just letting the underlying methods + # hand back cached values if a Node's information is requested + # multiple times. (Other methods of removing duplicates, like + # using dictionary keys, lose the order, and the only ordered + # dictionary patterns I found all ended up using "not in" + # internally anyway...) + if self.implicit is None: + return self.sources + self.depends + else: + return self.sources + self.depends + self.implicit + + def children(self, scan=1): + """Return a list of the node's direct children, minus those + that are ignored by this node.""" + if scan: + self.scan() + return self._children_get() + + def set_state(self, state): + self.state = state + + def get_state(self): + return self.state + + def state_has_changed(self, target, prev_ni): + return (self.state != SCons.Node.up_to_date) + + def get_env(self): + env = self.env + if not env: + import SCons.Defaults + env = SCons.Defaults.DefaultEnvironment() + return env + + def changed_since_last_build(self, target, prev_ni): + """ + + Must be overridden in a specific subclass to return True if this + Node (a dependency) has changed since the last time it was used + to build the specified target. prev_ni is this Node's state (for + example, its file timestamp, length, maybe content signature) + as of the last time the target was built. + + Note that this method is called through the dependency, not the + target, because a dependency Node must be able to use its own + logic to decide if it changed. For example, File Nodes need to + obey if we're configured to use timestamps, but Python Value Nodes + never use timestamps and always use the content. If this method + were called through the target, then each Node's implementation + of this method would have to have more complicated logic to + handle all the different Node types on which it might depend. + """ + raise NotImplementedError + + def Decider(self, function): + SCons.Util.AddMethod(self, function, 'changed_since_last_build') + + def changed(self, node=None): + """ + Returns if the node is up-to-date with respect to the BuildInfo + stored last time it was built. The default behavior is to compare + it against our own previously stored BuildInfo, but the stored + BuildInfo from another Node (typically one in a Repository) + can be used instead. + + Note that we now *always* check every dependency. We used to + short-circuit the check by returning as soon as we detected + any difference, but we now rely on checking every dependency + to make sure that any necessary Node information (for example, + the content signature of an #included .h file) is updated. + """ + t = 0 + if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) + if node is None: + node = self + + result = False + + bi = node.get_stored_info().binfo + then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs + children = self.children() + + diff = len(children) - len(then) + if diff: + # The old and new dependency lists are different lengths. + # This always indicates that the Node must be rebuilt. + # We also extend the old dependency list with enough None + # entries to equal the new dependency list, for the benefit + # of the loop below that updates node information. + then.extend([None] * diff) + if t: Trace(': old %s new %s' % (len(then), len(children))) + result = True + + for child, prev_ni in zip(children, then): + if child.changed_since_last_build(self, prev_ni): + if t: Trace(': %s changed' % child) + result = True + + contents = self.get_executor().get_contents() + if self.has_builder(): + import SCons.Util + newsig = SCons.Util.MD5signature(contents) + if bi.bactsig != newsig: + if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) + result = True + + if not result: + if t: Trace(': up to date') + + if t: Trace('\n') + + return result + + def is_up_to_date(self): + """Default check for whether the Node is current: unknown Node + subtypes are always out of date, so they will always get built.""" + return None + + def children_are_up_to_date(self): + """Alternate check for whether the Node is current: If all of + our children were up-to-date, then this Node was up-to-date, too. + + The SCons.Node.Alias and SCons.Node.Python.Value subclasses + rebind their current() method to this method.""" + # Allow the children to calculate their signatures. + self.binfo = self.get_binfo() + if self.always_build: + return None + state = 0 + for kid in self.children(None): + s = kid.get_state() + if s and (not state or s > state): + state = s + return (state == 0 or state == SCons.Node.up_to_date) + + def is_literal(self): + """Always pass the string representation of a Node to + the command interpreter literally.""" + return 1 + + def render_include_tree(self): + """ + Return a text representation, suitable for displaying to the + user, of the include tree for the sources of this node. + """ + if self.is_derived() and self.env: + env = self.get_build_env() + for s in self.sources: + scanner = self.get_source_scanner(s) + if scanner: + path = self.get_build_scanner_path(scanner) + else: + path = None + def f(node, env=env, scanner=scanner, path=path): + return node.get_found_includes(env, scanner, path) + return SCons.Util.render_tree(s, f, 1) + else: + return None + + def get_abspath(self): + """ + Return an absolute path to the Node. This will return simply + str(Node) by default, but for Node types that have a concept of + relative path, this might return something different. + """ + return str(self) + + def for_signature(self): + """ + Return a string representation of the Node that will always + be the same for this particular Node, no matter what. This + is by contrast to the __str__() method, which might, for + instance, return a relative path for a file Node. The purpose + of this method is to generate a value to be used in signature + calculation for the command line used to build a target, and + we use this method instead of str() to avoid unnecessary + rebuilds. This method does not need to return something that + would actually work in a command line; it can return any kind of + nonsense, so long as it does not change. + """ + return str(self) + + def get_string(self, for_signature): + """This is a convenience function designed primarily to be + used in command generators (i.e., CommandGeneratorActions or + Environment variables that are callable), which are called + with a for_signature argument that is nonzero if the command + generator is being called to generate a signature for the + command line, which determines if we should rebuild or not. + + Such command generators should use this method in preference + to str(Node) when converting a Node to a string, passing + in the for_signature parameter, such that we will call + Node.for_signature() or str(Node) properly, depending on whether + we are calculating a signature or actually constructing a + command line.""" + if for_signature: + return self.for_signature() + return str(self) + + def get_subst_proxy(self): + """ + This method is expected to return an object that will function + exactly like this Node, except that it implements any additional + special features that we would like to be in effect for + Environment variable substitution. The principle use is that + some Nodes would like to implement a __getattr__() method, + but putting that in the Node type itself has a tendency to kill + performance. We instead put it in a proxy and return it from + this method. It is legal for this method to return self + if no new functionality is needed for Environment substitution. + """ + return self + + def explain(self): + if not self.exists(): + return "building `%s' because it doesn't exist\n" % self + + if self.always_build: + return "rebuilding `%s' because AlwaysBuild() is specified\n" % self + + old = self.get_stored_info() + if old is None: + return None + + old = old.binfo + old.prepare_dependencies() + + try: + old_bkids = old.bsources + old.bdepends + old.bimplicit + old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs + except AttributeError: + return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self + + new = self.get_binfo() + + new_bkids = new.bsources + new.bdepends + new.bimplicit + new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs + + osig = dict(zip(old_bkids, old_bkidsigs)) + nsig = dict(zip(new_bkids, new_bkidsigs)) + + # The sources and dependencies we'll want to report are all stored + # as relative paths to this target's directory, but we want to + # report them relative to the top-level SConstruct directory, + # so we only print them after running them through this lambda + # to turn them into the right relative Node and then return + # its string. + def stringify( s, E=self.dir.Entry ) : + if hasattr( s, 'dir' ) : + return str(E(s)) + return str(s) + + lines = [] + + removed = [x for x in old_bkids if not x in new_bkids] + if removed: + removed = list(map(stringify, removed)) + fmt = "`%s' is no longer a dependency\n" + lines.extend([fmt % s for s in removed]) + + for k in new_bkids: + if not k in old_bkids: + lines.append("`%s' is a new dependency\n" % stringify(k)) + elif k.changed_since_last_build(self, osig[k]): + lines.append("`%s' changed\n" % stringify(k)) + + if len(lines) == 0 and old_bkids != new_bkids: + lines.append("the dependency order changed:\n" + + "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + + "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) + + if len(lines) == 0: + def fmt_with_title(title, strlines): + lines = strlines.split('\n') + sep = '\n' + ' '*(15 + len(title)) + return ' '*15 + title + sep.join(lines) + '\n' + if old.bactsig != new.bactsig: + if old.bact == new.bact: + lines.append("the contents of the build action changed\n" + + fmt_with_title('action: ', new.bact)) + else: + lines.append("the build action changed:\n" + + fmt_with_title('old: ', old.bact) + + fmt_with_title('new: ', new.bact)) + + if len(lines) == 0: + return "rebuilding `%s' for unknown reasons\n" % self + + preamble = "rebuilding `%s' because" % self + if len(lines) == 1: + return "%s %s" % (preamble, lines[0]) + else: + lines = ["%s:\n" % preamble] + lines + return ( ' '*11).join(lines) + +class NodeList(collections.UserList): + def __str__(self): + return str(list(map(str, self.data))) + +def get_children(node, parent): return node.children() +def ignore_cycle(node, stack): pass +def do_nothing(node, parent): pass + +class Walker(object): + """An iterator for walking a Node tree. + + This is depth-first, children are visited before the parent. + The Walker object can be initialized with any node, and + returns the next node on the descent with each get_next() call. + 'kids_func' is an optional function that will be called to + get the children of a node instead of calling 'children'. + 'cycle_func' is an optional function that will be called + when a cycle is detected. + + This class does not get caught in node cycles caused, for example, + by C header file include loops. + """ + def __init__(self, node, kids_func=get_children, + cycle_func=ignore_cycle, + eval_func=do_nothing): + self.kids_func = kids_func + self.cycle_func = cycle_func + self.eval_func = eval_func + node.wkids = copy.copy(kids_func(node, None)) + self.stack = [node] + self.history = {} # used to efficiently detect and avoid cycles + self.history[node] = None + + def get_next(self): + """Return the next node for this walk of the tree. + + This function is intentionally iterative, not recursive, + to sidestep any issues of stack size limitations. + """ + + while self.stack: + if self.stack[-1].wkids: + node = self.stack[-1].wkids.pop(0) + if not self.stack[-1].wkids: + self.stack[-1].wkids = None + if node in self.history: + self.cycle_func(node, self.stack) + else: + node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) + self.stack.append(node) + self.history[node] = None + else: + node = self.stack.pop() + del self.history[node] + if node: + if self.stack: + parent = self.stack[-1] + else: + parent = None + self.eval_func(node, parent) + return node + return None + + def is_done(self): + return not self.stack + + +arg2nodes_lookups = [] + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Options/BoolOption.py b/engine/SCons/Options/BoolOption.py new file mode 100644 index 0000000..2b86533 --- /dev/null +++ b/engine/SCons/Options/BoolOption.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Options/BoolOption.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +def BoolOption(*args, **kw): + global warned + if not warned: + msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + return SCons.Variables.BoolVariable(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Options/EnumOption.py b/engine/SCons/Options/EnumOption.py new file mode 100644 index 0000000..5296209 --- /dev/null +++ b/engine/SCons/Options/EnumOption.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Options/EnumOption.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +def EnumOption(*args, **kw): + global warned + if not warned: + msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + return SCons.Variables.EnumVariable(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Options/ListOption.py b/engine/SCons/Options/ListOption.py new file mode 100644 index 0000000..5672689 --- /dev/null +++ b/engine/SCons/Options/ListOption.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Options/ListOption.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +def ListOption(*args, **kw): + global warned + if not warned: + msg = "The ListOption() function is deprecated; use the ListVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + return SCons.Variables.ListVariable(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Options/PackageOption.py b/engine/SCons/Options/PackageOption.py new file mode 100644 index 0000000..930c18f --- /dev/null +++ b/engine/SCons/Options/PackageOption.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Options/PackageOption.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +def PackageOption(*args, **kw): + global warned + if not warned: + msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + return SCons.Variables.PackageVariable(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Options/PathOption.py b/engine/SCons/Options/PathOption.py new file mode 100644 index 0000000..ebf0f24 --- /dev/null +++ b/engine/SCons/Options/PathOption.py @@ -0,0 +1,76 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Options/PathOption.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +class _PathOptionClass(object): + def warn(self): + global warned + if not warned: + msg = "The PathOption() function is deprecated; use the PathVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + + def __call__(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable(*args, **kw) + + def PathAccept(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathAccept(*args, **kw) + + def PathIsDir(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathIsDir(*args, **kw) + + def PathIsDirCreate(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathIsDirCreate(*args, **kw) + + def PathIsFile(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathIsFile(*args, **kw) + + def PathExists(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathExists(*args, **kw) + +PathOption = _PathOptionClass() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Options/__init__.py b/engine/SCons/Options/__init__.py new file mode 100644 index 0000000..b1428e9 --- /dev/null +++ b/engine/SCons/Options/__init__.py @@ -0,0 +1,67 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Options/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +from BoolOption import BoolOption # okay +from EnumOption import EnumOption # okay +from ListOption import ListOption # naja +from PackageOption import PackageOption # naja +from PathOption import PathOption # okay + +warned = False + +class Options(SCons.Variables.Variables): + def __init__(self, *args, **kw): + global warned + if not warned: + msg = "The Options class is deprecated; use the Variables class instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + SCons.Variables.Variables.__init__(self, *args, **kw) + + def AddOptions(self, *args, **kw): + return SCons.Variables.Variables.AddVariables(self, *args, **kw) + + def UnknownOptions(self, *args, **kw): + return SCons.Variables.Variables.UnknownVariables(self, *args, **kw) + + def FormatOptionHelpText(self, *args, **kw): + return SCons.Variables.Variables.FormatVariableHelpText(self, *args, + **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/PathList.py b/engine/SCons/PathList.py new file mode 100644 index 0000000..0ba07f5 --- /dev/null +++ b/engine/SCons/PathList.py @@ -0,0 +1,231 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/PathList.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """SCons.PathList + +A module for handling lists of directory paths (the sort of things +that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and +efficiency as we can while still keeping the evaluation delayed so that we +Do the Right Thing (almost) regardless of how the variable is specified. + +""" + +import os + +import SCons.Memoize +import SCons.Node +import SCons.Util + +# +# Variables to specify the different types of entries in a PathList object: +# + +TYPE_STRING_NO_SUBST = 0 # string with no '$' +TYPE_STRING_SUBST = 1 # string containing '$' +TYPE_OBJECT = 2 # other object + +def node_conv(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: + if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ): + result = obj + else: + result = str(obj) + else: + result = get() + return result + +class _PathList(object): + """ + An actual PathList object. + """ + def __init__(self, pathlist): + """ + Initializes a PathList object, canonicalizing the input and + pre-processing it for quicker substitution later. + + The stored representation of the PathList is a list of tuples + containing (type, value), where the "type" is one of the TYPE_* + variables defined above. We distinguish between: + + strings that contain no '$' and therefore need no + delayed-evaluation string substitution (we expect that there + will be many of these and that we therefore get a pretty + big win from avoiding string substitution) + + strings that contain '$' and therefore need substitution + (the hard case is things like '${TARGET.dir}/include', + which require re-evaluation for every target + source) + + other objects (which may be something like an EntryProxy + that needs a method called to return a Node) + + Pre-identifying the type of each element in the PathList up-front + and storing the type in the list of tuples is intended to reduce + the amount of calculation when we actually do the substitution + over and over for each target. + """ + if SCons.Util.is_String(pathlist): + pathlist = pathlist.split(os.pathsep) + elif not SCons.Util.is_Sequence(pathlist): + pathlist = [pathlist] + + pl = [] + for p in pathlist: + try: + index = p.find('$') + except (AttributeError, TypeError): + type = TYPE_OBJECT + else: + if index == -1: + type = TYPE_STRING_NO_SUBST + else: + type = TYPE_STRING_SUBST + pl.append((type, p)) + + self.pathlist = tuple(pl) + + def __len__(self): return len(self.pathlist) + + def __getitem__(self, i): return self.pathlist[i] + + def subst_path(self, env, target, source): + """ + Performs construction variable substitution on a pre-digested + PathList for a specific target and source. + """ + result = [] + for type, value in self.pathlist: + if type == TYPE_STRING_SUBST: + value = env.subst(value, target=target, source=source, + conv=node_conv) + if SCons.Util.is_Sequence(value): + result.extend(value) + continue + + elif type == TYPE_OBJECT: + value = node_conv(value) + if value: + result.append(value) + return tuple(result) + + +class PathListCache(object): + """ + A class to handle caching of PathList lookups. + + This class gets instantiated once and then deleted from the namespace, + so it's used as a Singleton (although we don't enforce that in the + usual Pythonic ways). We could have just made the cache a dictionary + in the module namespace, but putting it in this class allows us to + use the same Memoizer pattern that we use elsewhere to count cache + hits and misses, which is very valuable. + + Lookup keys in the cache are computed by the _PathList_key() method. + Cache lookup should be quick, so we don't spend cycles canonicalizing + all forms of the same lookup key. For example, 'x:y' and ['x', + 'y'] logically represent the same list, but we don't bother to + split string representations and treat those two equivalently. + (Note, however, that we do, treat lists and tuples the same.) + + The main type of duplication we're trying to catch will come from + looking up the same path list from two different clones of the + same construction environment. That is, given + + env2 = env1.Clone() + + both env1 and env2 will have the same CPPPATH value, and we can + cheaply avoid re-parsing both values of CPPPATH by using the + common value from this cache. + """ + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + def __init__(self): + self._memo = {} + + def _PathList_key(self, pathlist): + """ + Returns the key for memoization of PathLists. + + Note that we want this to be pretty quick, so we don't completely + canonicalize all forms of the same list. For example, + 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically + represent the same list if you're executing from $ROOT, but + we're not going to bother splitting strings into path elements, + or massaging strings into Nodes, to identify that equivalence. + We just want to eliminate obvious redundancy from the normal + case of re-using exactly the same cloned value for a path. + """ + if SCons.Util.is_Sequence(pathlist): + pathlist = tuple(SCons.Util.flatten(pathlist)) + return pathlist + + memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key)) + + def PathList(self, pathlist): + """ + Returns the cached _PathList object for the specified pathlist, + creating and caching a new object as necessary. + """ + pathlist = self._PathList_key(pathlist) + try: + memo_dict = self._memo['PathList'] + except KeyError: + memo_dict = {} + self._memo['PathList'] = memo_dict + else: + try: + return memo_dict[pathlist] + except KeyError: + pass + + result = _PathList(pathlist) + + memo_dict[pathlist] = result + + return result + +PathList = PathListCache().PathList + + +del PathListCache + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/__init__.py b/engine/SCons/Platform/__init__.py new file mode 100644 index 0000000..eb0108e --- /dev/null +++ b/engine/SCons/Platform/__init__.py @@ -0,0 +1,241 @@ +"""SCons.Platform + +SCons platform selection. + +This looks for modules that define a callable object that can modify a +construction environment as appropriate for a given platform. + +Note that we take a more simplistic view of "platform" than Python does. +We're looking for a single string that determines a set of +tool-independent variables with which to initialize a construction +environment. Consequently, we'll examine both sys.platform and os.name +(and anything else that might come in to play) in order to return some +specification which is unique enough for our purposes. + +Note that because this subsysem just *selects* a callable that can +modify a construction environment, it's possible for people to define +their own "platform specification" in an arbitrary callable function. +No one needs to use or tie in to this subsystem in order to roll +their own platform definition. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.compat + +import imp +import os +import sys +import tempfile + +import SCons.Errors +import SCons.Subst +import SCons.Tool + +def platform_default(): + """Return the platform string for our execution environment. + + The returned value should map to one of the SCons/Platform/*.py + files. Since we're architecture independent, though, we don't + care about the machine architecture. + """ + osname = os.name + if osname == 'java': + osname = os._osType + if osname == 'posix': + if sys.platform == 'cygwin': + return 'cygwin' + elif sys.platform.find('irix') != -1: + return 'irix' + elif sys.platform.find('sunos') != -1: + return 'sunos' + elif sys.platform.find('hp-ux') != -1: + return 'hpux' + elif sys.platform.find('aix') != -1: + return 'aix' + elif sys.platform.find('darwin') != -1: + return 'darwin' + else: + return 'posix' + elif os.name == 'os2': + return 'os2' + else: + return sys.platform + +def platform_module(name = platform_default()): + """Return the imported module for the platform. + + This looks for a module name that matches the specified argument. + If the name is unspecified, we fetch the appropriate default for + our execution environment. + """ + full_name = 'SCons.Platform.' + name + if full_name not in sys.modules: + if os.name == 'java': + eval(full_name) + else: + try: + file, path, desc = imp.find_module(name, + sys.modules['SCons.Platform'].__path__) + try: + mod = imp.load_module(full_name, file, path, desc) + finally: + if file: + file.close() + except ImportError: + try: + import zipimport + importer = zipimport.zipimporter( sys.modules['SCons.Platform'].__path__[0] ) + mod = importer.load_module(full_name) + except ImportError: + raise SCons.Errors.UserError("No platform named '%s'" % name) + setattr(SCons.Platform, name, mod) + return sys.modules[full_name] + +def DefaultToolList(platform, env): + """Select a default tool list for the specified platform. + """ + return SCons.Tool.tool_list(platform, env) + +class PlatformSpec(object): + def __init__(self, name, generate): + self.name = name + self.generate = generate + + def __call__(self, *args, **kw): + return self.generate(*args, **kw) + + def __str__(self): + return self.name + +class TempFileMunge(object): + """A callable class. You can set an Environment variable to this, + then call it with a string argument, then it will perform temporary + file substitution on it. This is used to circumvent the long command + line limitation. + + Example usage: + env["TEMPFILE"] = TempFileMunge + env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}" + + By default, the name of the temporary file used begins with a + prefix of '@'. This may be configred for other tool chains by + setting '$TEMPFILEPREFIX'. + + env["TEMPFILEPREFIX"] = '-@' # diab compiler + env["TEMPFILEPREFIX"] = '-via' # arm tool chain + """ + def __init__(self, cmd): + self.cmd = cmd + + def __call__(self, target, source, env, for_signature): + if for_signature: + # If we're being called for signature calculation, it's + # because we're being called by the string expansion in + # Subst.py, which has the logic to strip any $( $) that + # may be in the command line we squirreled away. So we + # just return the raw command line and let the upper + # string substitution layers do their thing. + return self.cmd + + # Now we're actually being called because someone is actually + # going to try to execute the command, so we have to do our + # own expansion. + cmd = env.subst_list(self.cmd, SCons.Subst.SUBST_CMD, target, source)[0] + try: + maxline = int(env.subst('$MAXLINELENGTH')) + except ValueError: + maxline = 2048 + + length = 0 + for c in cmd: + length += len(c) + if length <= maxline: + return self.cmd + + # We do a normpath because mktemp() has what appears to be + # a bug in Windows that will use a forward slash as a path + # delimiter. Windows's link mistakes that for a command line + # switch and barfs. + # + # We use the .lnk suffix for the benefit of the Phar Lap + # linkloc linker, which likes to append an .lnk suffix if + # none is given. + (fd, tmp) = tempfile.mkstemp('.lnk', text=True) + native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp)) + + if env['SHELL'] and env['SHELL'] == 'sh': + # The sh shell will try to escape the backslashes in the + # path, so unescape them. + native_tmp = native_tmp.replace('\\', r'\\\\') + # In Cygwin, we want to use rm to delete the temporary + # file, because del does not exist in the sh shell. + rm = env.Detect('rm') or 'del' + else: + # Don't use 'rm' if the shell is not sh, because rm won't + # work with the Windows shells (cmd.exe or command.com) or + # Windows path names. + rm = 'del' + + prefix = env.subst('$TEMPFILEPREFIX') + if not prefix: + prefix = '@' + + args = list(map(SCons.Subst.quote_spaces, cmd[1:])) + os.write(fd, " ".join(args) + "\n") + os.close(fd) + # XXX Using the SCons.Action.print_actions value directly + # like this is bogus, but expedient. This class should + # really be rewritten as an Action that defines the + # __call__() and strfunction() methods and lets the + # normal action-execution logic handle whether or not to + # print/execute the action. The problem, though, is all + # of that is decided before we execute this method as + # part of expanding the $TEMPFILE construction variable. + # Consequently, refactoring this will have to wait until + # we get more flexible with allowing Actions to exist + # independently and get strung together arbitrarily like + # Ant tasks. In the meantime, it's going to be more + # user-friendly to not let obsession with architectural + # purity get in the way of just being helpful, so we'll + # reach into SCons.Action directly. + if SCons.Action.print_actions: + print("Using tempfile "+native_tmp+" for command line:\n"+ + str(cmd[0]) + " " + " ".join(args)) + return [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ] + +def Platform(name = platform_default()): + """Select a canned Platform specification. + """ + module = platform_module(name) + spec = PlatformSpec(name, module.generate) + return spec + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/aix.py b/engine/SCons/Platform/aix.py new file mode 100644 index 0000000..c48aaff --- /dev/null +++ b/engine/SCons/Platform/aix.py @@ -0,0 +1,69 @@ +"""engine.SCons.Platform.aix + +Platform-specific initialization for IBM AIX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/aix.py 5357 2011/09/09 21:31:03 bdeegan" + +import os + +import posix + +def get_xlc(env, xlc=None, xlc_r=None, packages=[]): + # Use the AIX package installer tool lslpp to figure out where a + # given xl* compiler is installed and what version it is. + xlcPath = None + xlcVersion = None + + if xlc is None: + xlc = env.get('CC', 'xlc') + if xlc_r is None: + xlc_r = xlc + '_r' + for package in packages: + cmd = "lslpp -fc " + package + " 2>/dev/null | egrep '" + xlc + "([^-_a-zA-Z0-9].*)?$'" + line = os.popen(cmd).readline() + if line: + v, p = line.split(':')[1:3] + xlcVersion = v.split()[1] + xlcPath = p.split()[0] + xlcPath = xlcPath[:xlcPath.rindex('/')] + break + return (xlcPath, xlc, xlc_r, xlcVersion) + +def generate(env): + posix.generate(env) + #Based on AIX 5.2: ARG_MAX=24576 - 3000 for environment expansion + env['MAXLINELENGTH'] = 21576 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/cygwin.py b/engine/SCons/Platform/cygwin.py new file mode 100644 index 0000000..c04f50a --- /dev/null +++ b/engine/SCons/Platform/cygwin.py @@ -0,0 +1,55 @@ +"""SCons.Platform.cygwin + +Platform-specific initialization for Cygwin systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/cygwin.py 5357 2011/09/09 21:31:03 bdeegan" + +import posix +from SCons.Platform import TempFileMunge + +def generate(env): + posix.generate(env) + + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['TEMPFILE'] = TempFileMunge + env['TEMPFILEPREFIX'] = '@' + env['MAXLINELENGTH'] = 2048 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/darwin.py b/engine/SCons/Platform/darwin.py new file mode 100644 index 0000000..bed6fce --- /dev/null +++ b/engine/SCons/Platform/darwin.py @@ -0,0 +1,70 @@ +"""engine.SCons.Platform.darwin + +Platform-specific initialization for Mac OS X systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/darwin.py 5357 2011/09/09 21:31:03 bdeegan" + +import posix +import os + +def generate(env): + posix.generate(env) + env['SHLIBSUFFIX'] = '.dylib' + # put macports paths at front to override Apple's versions, fink path is after + # For now let people who want Macports or Fink tools specify it! + # env['ENV']['PATH'] = '/opt/local/bin:/opt/local/sbin:' + env['ENV']['PATH'] + ':/sw/bin' + + # Store extra system paths in env['ENV']['PATHOSX'] + + filelist = ['/etc/paths',] + # make sure this works on Macs with Tiger or earlier + try: + dirlist = os.listdir('/etc/paths.d') + except: + dirlist = [] + + for file in dirlist: + filelist.append('/etc/paths.d/'+file) + + for file in filelist: + if os.path.isfile(file): + f = open(file, 'r') + lines = f.readlines() + for line in lines: + if line: + env.AppendENVPath('PATHOSX', line.strip('\n')) + f.close() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/hpux.py b/engine/SCons/Platform/hpux.py new file mode 100644 index 0000000..fbacc04 --- /dev/null +++ b/engine/SCons/Platform/hpux.py @@ -0,0 +1,46 @@ +"""engine.SCons.Platform.hpux + +Platform-specific initialization for HP-UX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/hpux.py 5357 2011/09/09 21:31:03 bdeegan" + +import posix + +def generate(env): + posix.generate(env) + #Based on HP-UX11i: ARG_MAX=2048000 - 3000 for environment expansion + env['MAXLINELENGTH'] = 2045000 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/irix.py b/engine/SCons/Platform/irix.py new file mode 100644 index 0000000..2c3bc15 --- /dev/null +++ b/engine/SCons/Platform/irix.py @@ -0,0 +1,44 @@ +"""SCons.Platform.irix + +Platform-specific initialization for SGI IRIX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/irix.py 5357 2011/09/09 21:31:03 bdeegan" + +import posix + +def generate(env): + posix.generate(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/os2.py b/engine/SCons/Platform/os2.py new file mode 100644 index 0000000..9c76419 --- /dev/null +++ b/engine/SCons/Platform/os2.py @@ -0,0 +1,58 @@ +"""SCons.Platform.os2 + +Platform-specific initialization for OS/2 systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/os2.py 5357 2011/09/09 21:31:03 bdeegan" +import win32 + +def generate(env): + if 'ENV' not in env: + env['ENV'] = {} + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.obj' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = '$LIBPREFIX' + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['HOST_OS'] = 'os2' + env['HOST_ARCH'] = win32.get_architecture().arch + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/posix.py b/engine/SCons/Platform/posix.py new file mode 100644 index 0000000..6d99fdb --- /dev/null +++ b/engine/SCons/Platform/posix.py @@ -0,0 +1,263 @@ +"""SCons.Platform.posix + +Platform-specific initialization for POSIX (Linux, UNIX, etc.) systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/posix.py 5357 2011/09/09 21:31:03 bdeegan" + +import errno +import os +import os.path +import subprocess +import sys +import select + +import SCons.Util +from SCons.Platform import TempFileMunge + +exitvalmap = { + 2 : 127, + 13 : 126, +} + +def escape(arg): + "escape shell special characters" + slash = '\\' + special = '"$()' + + arg = arg.replace(slash, slash+slash) + for c in special: + arg = arg.replace(c, slash+c) + + return '"' + arg + '"' + +def exec_system(l, env): + stat = os.system(' '.join(l)) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def exec_spawnvpe(l, env): + stat = os.spawnvpe(os.P_WAIT, l[0], l, env) + # os.spawnvpe() returns the actual exit code, not the encoding + # returned by os.waitpid() or os.system(). + return stat + +def exec_fork(l, env): + pid = os.fork() + if not pid: + # Child process. + exitval = 127 + try: + os.execvpe(l[0], l, env) + except OSError, e: + exitval = exitvalmap.get(e[0], e[0]) + sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) + os._exit(exitval) + else: + # Parent process. + pid, stat = os.waitpid(pid, 0) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def _get_env_command(sh, escape, cmd, args, env): + s = ' '.join(args) + if env: + l = ['env', '-'] + \ + [escape(t[0])+'='+escape(t[1]) for t in env.items()] + \ + [sh, '-c', escape(s)] + s = ' '.join(l) + return s + +def env_spawn(sh, escape, cmd, args, env): + return exec_system([_get_env_command( sh, escape, cmd, args, env)], env) + +def spawnvpe_spawn(sh, escape, cmd, args, env): + return exec_spawnvpe([sh, '-c', ' '.join(args)], env) + +def fork_spawn(sh, escape, cmd, args, env): + return exec_fork([sh, '-c', ' '.join(args)], env) + +def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr): + stdout_eof = stderr_eof = 0 + while not (stdout_eof and stderr_eof): + try: + (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], []) + if cmd_stdout in i: + str = cmd_stdout.read() + if len(str) == 0: + stdout_eof = 1 + elif stdout is not None: + stdout.write(str) + if cmd_stderr in i: + str = cmd_stderr.read() + if len(str) == 0: + #sys.__stderr__.write( "stderr_eof=1\n" ) + stderr_eof = 1 + else: + #sys.__stderr__.write( "str(stderr) = %s\n" % str ) + stderr.write(str) + except select.error, (_errno, _strerror): + if _errno != errno.EINTR: + raise + +def exec_popen3(l, env, stdout, stderr): + proc = subprocess.Popen(' '.join(l), + stdout=stdout, + stderr=stderr, + shell=True) + stat = proc.wait() + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def exec_piped_fork(l, env, stdout, stderr): + # spawn using fork / exec and providing a pipe for the command's + # stdout / stderr stream + if stdout != stderr: + (rFdOut, wFdOut) = os.pipe() + (rFdErr, wFdErr) = os.pipe() + else: + (rFdOut, wFdOut) = os.pipe() + rFdErr = rFdOut + wFdErr = wFdOut + # do the fork + pid = os.fork() + if not pid: + # Child process + os.close( rFdOut ) + if rFdOut != rFdErr: + os.close( rFdErr ) + os.dup2( wFdOut, 1 ) # is there some symbolic way to do that ? + os.dup2( wFdErr, 2 ) + os.close( wFdOut ) + if stdout != stderr: + os.close( wFdErr ) + exitval = 127 + try: + os.execvpe(l[0], l, env) + except OSError, e: + exitval = exitvalmap.get(e[0], e[0]) + stderr.write("scons: %s: %s\n" % (l[0], e[1])) + os._exit(exitval) + else: + # Parent process + pid, stat = os.waitpid(pid, 0) + os.close( wFdOut ) + if stdout != stderr: + os.close( wFdErr ) + childOut = os.fdopen( rFdOut ) + if stdout != stderr: + childErr = os.fdopen( rFdErr ) + else: + childErr = childOut + process_cmd_output(childOut, childErr, stdout, stderr) + os.close( rFdOut ) + if stdout != stderr: + os.close( rFdErr ) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr): + # spawn using Popen3 combined with the env command + # the command name and the command's stdout is written to stdout + # the command's stderr is written to stderr + return exec_popen3([_get_env_command(sh, escape, cmd, args, env)], + env, stdout, stderr) + +def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr): + # spawn using fork / exec and providing a pipe for the command's + # stdout / stderr stream + return exec_piped_fork([sh, '-c', ' '.join(args)], + env, stdout, stderr) + + + +def generate(env): + # If os.spawnvpe() exists, we use it to spawn commands. Otherwise + # if the env utility exists, we use os.system() to spawn commands, + # finally we fall back on os.fork()/os.exec(). + # + # os.spawnvpe() is prefered because it is the most efficient. But + # for Python versions without it, os.system() is prefered because it + # is claimed that it works better with threads (i.e. -j) and is more + # efficient than forking Python. + # + # NB: Other people on the scons-users mailing list have claimed that + # os.fork()/os.exec() works better than os.system(). There may just + # not be a default that works best for all users. + + if 'spawnvpe' in os.__dict__: + spawn = spawnvpe_spawn + elif env.Detect('env'): + spawn = env_spawn + else: + spawn = fork_spawn + + if env.Detect('env'): + pspawn = piped_env_spawn + else: + pspawn = piped_fork_spawn + + if 'ENV' not in env: + env['ENV'] = {} + env['ENV']['PATH'] = '/usr/local/bin:/opt/bin:/bin:/usr/bin' + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.o' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + env['SHLIBPREFIX'] = '$LIBPREFIX' + env['SHLIBSUFFIX'] = '.so' + env['LIBPREFIXES'] = [ '$LIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['PSPAWN'] = pspawn + env['SPAWN'] = spawn + env['SHELL'] = 'sh' + env['ESCAPE'] = escape + env['TEMPFILE'] = TempFileMunge + env['TEMPFILEPREFIX'] = '@' + #Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion + #Note: specific platforms might rise or lower this value + env['MAXLINELENGTH'] = 128072 + + # This platform supports RPATH specifications. + env['__RPATH'] = '$_RPATH' + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/sunos.py b/engine/SCons/Platform/sunos.py new file mode 100644 index 0000000..2344906 --- /dev/null +++ b/engine/SCons/Platform/sunos.py @@ -0,0 +1,50 @@ +"""engine.SCons.Platform.sunos + +Platform-specific initialization for Sun systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/sunos.py 5357 2011/09/09 21:31:03 bdeegan" + +import posix + +def generate(env): + posix.generate(env) + # Based on sunSparc 8:32bit + # ARG_MAX=1048320 - 3000 for environment expansion + env['MAXLINELENGTH'] = 1045320 + env['PKGINFO'] = 'pkginfo' + env['PKGCHK'] = '/usr/sbin/pkgchk' + env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin' + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Platform/win32.py b/engine/SCons/Platform/win32.py new file mode 100644 index 0000000..fec9b4c --- /dev/null +++ b/engine/SCons/Platform/win32.py @@ -0,0 +1,385 @@ +"""SCons.Platform.win32 + +Platform-specific initialization for Win32 systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Platform/win32.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path +import sys +import tempfile + +from SCons.Platform.posix import exitvalmap +from SCons.Platform import TempFileMunge +import SCons.Util + +try: + import msvcrt + import win32api + import win32con + + msvcrt.get_osfhandle + win32api.SetHandleInformation + win32con.HANDLE_FLAG_INHERIT +except ImportError: + parallel_msg = \ + "you do not seem to have the pywin32 extensions installed;\n" + \ + "\tparallel (-j) builds may not work reliably with open Python files." +except AttributeError: + parallel_msg = \ + "your pywin32 extensions do not support file handle operations;\n" + \ + "\tparallel (-j) builds may not work reliably with open Python files." +else: + parallel_msg = None + + import builtins + + _builtin_file = builtins.file + _builtin_open = builtins.open + + def _scons_file(*args, **kw): + fp = _builtin_file(*args, **kw) + win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()), + win32con.HANDLE_FLAG_INHERIT, + 0) + return fp + + def _scons_open(*args, **kw): + fp = _builtin_open(*args, **kw) + win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()), + win32con.HANDLE_FLAG_INHERIT, + 0) + return fp + + builtins.file = _scons_file + builtins.open = _scons_open + + + +# The upshot of all this is that, if you are using Python 1.5.2, +# you had better have cmd or command.com in your PATH when you run +# scons. + +def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): + # There is no direct way to do that in python. What we do + # here should work for most cases: + # In case stdout (stderr) is not redirected to a file, + # we redirect it into a temporary file tmpFileStdout + # (tmpFileStderr) and copy the contents of this file + # to stdout (stderr) given in the argument + if not sh: + sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") + return 127 + else: + # one temporary file for stdout and stderr + tmpFileStdout = os.path.normpath(tempfile.mktemp()) + tmpFileStderr = os.path.normpath(tempfile.mktemp()) + + # check if output is redirected + stdoutRedirected = 0 + stderrRedirected = 0 + for arg in args: + # are there more possibilities to redirect stdout ? + if (arg.find( ">", 0, 1 ) != -1 or + arg.find( "1>", 0, 2 ) != -1): + stdoutRedirected = 1 + # are there more possibilities to redirect stderr ? + if arg.find( "2>", 0, 2 ) != -1: + stderrRedirected = 1 + + # redirect output of non-redirected streams to our tempfiles + if stdoutRedirected == 0: + args.append(">" + str(tmpFileStdout)) + if stderrRedirected == 0: + args.append("2>" + str(tmpFileStderr)) + + # actually do the spawn + try: + args = [sh, '/C', escape(' '.join(args)) ] + ret = os.spawnve(os.P_WAIT, sh, args, env) + except OSError, e: + # catch any error + try: + ret = exitvalmap[e[0]] + except KeyError: + sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1])) + if stderr is not None: + stderr.write("scons: %s: %s\n" % (cmd, e[1])) + # copy child output from tempfiles to our streams + # and do clean up stuff + if stdout is not None and stdoutRedirected == 0: + try: + stdout.write(open( tmpFileStdout, "r" ).read()) + os.remove( tmpFileStdout ) + except (IOError, OSError): + pass + + if stderr is not None and stderrRedirected == 0: + try: + stderr.write(open( tmpFileStderr, "r" ).read()) + os.remove( tmpFileStderr ) + except (IOError, OSError): + pass + return ret + +def exec_spawn(l, env): + try: + result = os.spawnve(os.P_WAIT, l[0], l, env) + except OSError, e: + try: + result = exitvalmap[e[0]] + sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) + except KeyError: + result = 127 + if len(l) > 2: + if len(l[2]) < 1000: + command = ' '.join(l[0:3]) + else: + command = l[0] + else: + command = l[0] + sys.stderr.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e[0], command, e[1])) + return result + +def spawn(sh, escape, cmd, args, env): + if not sh: + sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") + return 127 + return exec_spawn([sh, '/C', escape(' '.join(args))], env) + +# Windows does not allow special characters in file names anyway, so no +# need for a complex escape function, we will just quote the arg, except +# that "cmd /c" requires that if an argument ends with a backslash it +# needs to be escaped so as not to interfere with closing double quote +# that we add. +def escape(x): + if x[-1] == '\\': + x = x + '\\' + return '"' + x + '"' + +# Get the windows system directory name +_system_root = None + +def get_system_root(): + global _system_root + if _system_root is not None: + return _system_root + + # A resonable default if we can't read the registry + val = os.environ.get('SystemRoot', "C:\\WINDOWS") + + if SCons.Util.can_read_reg: + try: + # Look for Windows NT system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows NT\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + except SCons.Util.RegError: + try: + # Okay, try the Windows 9x system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + except KeyboardInterrupt: + raise + except: + pass + _system_root = val + return val + +# Get the location of the program files directory +def get_program_files_dir(): + # Now see if we can look in the registry... + val = '' + if SCons.Util.can_read_reg: + try: + # Look for Windows Program Files directory + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir') + except SCons.Util.RegError: + val = '' + pass + + if val == '': + # A reasonable default if we can't read the registry + # (Actually, it's pretty reasonable even if we can :-) + val = os.path.join(os.path.dirname(get_system_root()),"Program Files") + + return val + + + +# Determine which windows CPU were running on. +class ArchDefinition(object): + """ + A class for defining architecture-specific settings and logic. + """ + def __init__(self, arch, synonyms=[]): + self.arch = arch + self.synonyms = synonyms + +SupportedArchitectureList = [ + ArchDefinition( + 'x86', + ['i386', 'i486', 'i586', 'i686'], + ), + + ArchDefinition( + 'x86_64', + ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], + ), + + ArchDefinition( + 'ia64', + ['IA64'], + ), +] + +SupportedArchitectureMap = {} +for a in SupportedArchitectureList: + SupportedArchitectureMap[a.arch] = a + for s in a.synonyms: + SupportedArchitectureMap[s] = a + +def get_architecture(arch=None): + """Returns the definition for the specified architecture string. + + If no string is specified, the system default is returned (as defined + by the PROCESSOR_ARCHITEW6432 or PROCESSOR_ARCHITECTURE environment + variables). + """ + if arch is None: + arch = os.environ.get('PROCESSOR_ARCHITEW6432') + if not arch: + arch = os.environ.get('PROCESSOR_ARCHITECTURE') + return SupportedArchitectureMap.get(arch, ArchDefinition('', [''])) + +def generate(env): + # Attempt to find cmd.exe (for WinNT/2k/XP) or + # command.com for Win9x + cmd_interp = '' + # First see if we can look in the registry... + if SCons.Util.can_read_reg: + try: + # Look for Windows NT system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows NT\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + cmd_interp = os.path.join(val, 'System32\\cmd.exe') + except SCons.Util.RegError: + try: + # Okay, try the Windows 9x system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + cmd_interp = os.path.join(val, 'command.com') + except KeyboardInterrupt: + raise + except: + pass + + # For the special case of not having access to the registry, we + # use a temporary path and pathext to attempt to find the command + # interpreter. If we fail, we try to find the interpreter through + # the env's PATH. The problem with that is that it might not + # contain an ENV and a PATH. + if not cmd_interp: + systemroot = get_system_root() + tmp_path = systemroot + os.pathsep + \ + os.path.join(systemroot,'System32') + tmp_pathext = '.com;.exe;.bat;.cmd' + if 'PATHEXT' in os.environ: + tmp_pathext = os.environ['PATHEXT'] + cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) + if not cmd_interp: + cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) + + if not cmd_interp: + cmd_interp = env.Detect('cmd') + if not cmd_interp: + cmd_interp = env.Detect('command') + + + if 'ENV' not in env: + env['ENV'] = {} + + # Import things from the external environment to the construction + # environment's ENV. This is a potential slippery slope, because we + # *don't* want to make builds dependent on the user's environment by + # default. We're doing this for SystemRoot, though, because it's + # needed for anything that uses sockets, and seldom changes, and + # for SystemDrive because it's related. + # + # Weigh the impact carefully before adding other variables to this list. + import_env = [ 'SystemDrive', 'SystemRoot', 'TEMP', 'TMP' ] + for var in import_env: + v = os.environ.get(var) + if v: + env['ENV'][var] = v + + if 'COMSPEC' not in env['ENV']: + v = os.environ.get("COMSPEC") + if v: + env['ENV']['COMSPEC'] = v + + env.AppendENVPath('PATH', get_system_root() + '\System32') + + env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD' + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.obj' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] + env['PSPAWN'] = piped_spawn + env['SPAWN'] = spawn + env['SHELL'] = cmd_interp + env['TEMPFILE'] = TempFileMunge + env['TEMPFILEPREFIX'] = '@' + env['MAXLINELENGTH'] = 2048 + env['ESCAPE'] = escape + + env['HOST_OS'] = 'win32' + env['HOST_ARCH'] = get_architecture().arch + + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/SConf.py b/engine/SCons/SConf.py new file mode 100644 index 0000000..11d6245 --- /dev/null +++ b/engine/SCons/SConf.py @@ -0,0 +1,1030 @@ +"""SCons.SConf + +Autoconf-like configuration support. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/SConf.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.compat + +import io +import os +import re +import sys +import traceback + +import SCons.Action +import SCons.Builder +import SCons.Errors +import SCons.Job +import SCons.Node.FS +import SCons.Taskmaster +import SCons.Util +import SCons.Warnings +import SCons.Conftest + +from SCons.Debug import Trace + +# Turn off the Conftest error logging +SCons.Conftest.LogInputFiles = 0 +SCons.Conftest.LogErrorMessages = 0 + +# Set +build_type = None +build_types = ['clean', 'help'] + +def SetBuildType(type): + global build_type + build_type = type + +# to be set, if we are in dry-run mode +dryrun = 0 + +AUTO=0 # use SCons dependency scanning for up-to-date checks +FORCE=1 # force all tests to be rebuilt +CACHE=2 # force all tests to be taken from cache (raise an error, if necessary) +cache_mode = AUTO + +def SetCacheMode(mode): + """Set the Configure cache mode. mode must be one of "auto", "force", + or "cache".""" + global cache_mode + if mode == "auto": + cache_mode = AUTO + elif mode == "force": + cache_mode = FORCE + elif mode == "cache": + cache_mode = CACHE + else: + raise ValueError("SCons.SConf.SetCacheMode: Unknown mode " + mode) + +progress_display = SCons.Util.display # will be overwritten by SCons.Script +def SetProgressDisplay(display): + """Set the progress display to use (called from SCons.Script)""" + global progress_display + progress_display = display + +SConfFS = None + +_ac_build_counter = 0 # incremented, whenever TryBuild is called +_ac_config_logs = {} # all config.log files created in this build +_ac_config_hs = {} # all config.h files created in this build +sconf_global = None # current sconf object + +def _createConfigH(target, source, env): + t = open(str(target[0]), "w") + defname = re.sub('[^A-Za-z0-9_]', '_', str(target[0]).upper()) + t.write("""#ifndef %(DEFNAME)s_SEEN +#define %(DEFNAME)s_SEEN + +""" % {'DEFNAME' : defname}) + t.write(source[0].get_contents()) + t.write(""" +#endif /* %(DEFNAME)s_SEEN */ +""" % {'DEFNAME' : defname}) + t.close() + +def _stringConfigH(target, source, env): + return "scons: Configure: creating " + str(target[0]) + +def CreateConfigHBuilder(env): + """Called just before the building targets phase begins.""" + if len(_ac_config_hs) == 0: + return + action = SCons.Action.Action(_createConfigH, + _stringConfigH) + sconfigHBld = SCons.Builder.Builder(action=action) + env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} ) + for k in _ac_config_hs.keys(): + env.SConfigHBuilder(k, env.Value(_ac_config_hs[k])) + +class SConfWarning(SCons.Warnings.Warning): + pass +SCons.Warnings.enableWarningClass(SConfWarning) + +# some error definitions +class SConfError(SCons.Errors.UserError): + def __init__(self,msg): + SCons.Errors.UserError.__init__(self,msg) + +class ConfigureDryRunError(SConfError): + """Raised when a file or directory needs to be updated during a Configure + process, but the user requested a dry-run""" + def __init__(self,target): + if not isinstance(target, SCons.Node.FS.File): + msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target) + else: + msg = 'Cannot update configure test "%s" within a dry-run.' % str(target) + SConfError.__init__(self,msg) + +class ConfigureCacheError(SConfError): + """Raised when a use explicitely requested the cache feature, but the test + is run the first time.""" + def __init__(self,target): + SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target)) + +# define actions for building text files +def _createSource( target, source, env ): + fd = open(str(target[0]), "w") + fd.write(source[0].get_contents()) + fd.close() +def _stringSource( target, source, env ): + return (str(target[0]) + ' <-\n |' + + source[0].get_contents().replace( '\n', "\n |" ) ) + +class SConfBuildInfo(SCons.Node.FS.FileBuildInfo): + """ + Special build info for targets of configure tests. Additional members + are result (did the builder succeed last time?) and string, which + contains messages of the original build phase. + """ + result = None # -> 0/None -> no error, != 0 error + string = None # the stdout / stderr output when building the target + + def set_build_result(self, result, string): + self.result = result + self.string = string + + +class Streamer(object): + """ + 'Sniffer' for a file-like writable object. Similar to the unix tool tee. + """ + def __init__(self, orig): + self.orig = orig + self.s = io.StringIO() + + def write(self, str): + if self.orig: + self.orig.write(str) + self.s.write(str) + + def writelines(self, lines): + for l in lines: + self.write(l + '\n') + + def getvalue(self): + """ + Return everything written to orig since the Streamer was created. + """ + return self.s.getvalue() + + def flush(self): + if self.orig: + self.orig.flush() + self.s.flush() + + +class SConfBuildTask(SCons.Taskmaster.AlwaysTask): + """ + This is almost the same as SCons.Script.BuildTask. Handles SConfErrors + correctly and knows about the current cache_mode. + """ + def display(self, message): + if sconf_global.logstream: + sconf_global.logstream.write("scons: Configure: " + message + "\n") + + def display_cached_string(self, bi): + """ + Logs the original builder messages, given the SConfBuildInfo instance + bi. + """ + if not isinstance(bi, SConfBuildInfo): + SCons.Warnings.warn(SConfWarning, + "The stored build information has an unexpected class: %s" % bi.__class__) + else: + self.display("The original builder output was:\n" + + (" |" + str(bi.string)).replace("\n", "\n |")) + + def failed(self): + # check, if the reason was a ConfigureDryRunError or a + # ConfigureCacheError and if yes, reraise the exception + exc_type = self.exc_info()[0] + if issubclass(exc_type, SConfError): + raise + elif issubclass(exc_type, SCons.Errors.BuildError): + # we ignore Build Errors (occurs, when a test doesn't pass) + # Clear the exception to prevent the contained traceback + # to build a reference cycle. + self.exc_clear() + else: + self.display('Caught exception while building "%s":\n' % + self.targets[0]) + try: + excepthook = sys.excepthook + except AttributeError: + # Earlier versions of Python don't have sys.excepthook... + def excepthook(type, value, tb): + traceback.print_tb(tb) + print type, value + excepthook(*self.exc_info()) + return SCons.Taskmaster.Task.failed(self) + + def collect_node_states(self): + # returns (is_up_to_date, cached_error, cachable) + # where is_up_to_date is 1, if the node(s) are up_to_date + # cached_error is 1, if the node(s) are up_to_date, but the + # build will fail + # cachable is 0, if some nodes are not in our cache + T = 0 + changed = False + cached_error = False + cachable = True + for t in self.targets: + if T: Trace('%s' % (t)) + bi = t.get_stored_info().binfo + if isinstance(bi, SConfBuildInfo): + if T: Trace(': SConfBuildInfo') + if cache_mode == CACHE: + t.set_state(SCons.Node.up_to_date) + if T: Trace(': set_state(up_to-date)') + else: + if T: Trace(': get_state() %s' % t.get_state()) + if T: Trace(': changed() %s' % t.changed()) + if (t.get_state() != SCons.Node.up_to_date and t.changed()): + changed = True + if T: Trace(': changed %s' % changed) + cached_error = cached_error or bi.result + else: + if T: Trace(': else') + # the node hasn't been built in a SConf context or doesn't + # exist + cachable = False + changed = ( t.get_state() != SCons.Node.up_to_date ) + if T: Trace(': changed %s' % changed) + if T: Trace('\n') + return (not changed, cached_error, cachable) + + def execute(self): + if not self.targets[0].has_builder(): + return + + sconf = sconf_global + + is_up_to_date, cached_error, cachable = self.collect_node_states() + + if cache_mode == CACHE and not cachable: + raise ConfigureCacheError(self.targets[0]) + elif cache_mode == FORCE: + is_up_to_date = 0 + + if cached_error and is_up_to_date: + self.display("Building \"%s\" failed in a previous run and all " + "its sources are up to date." % str(self.targets[0])) + binfo = self.targets[0].get_stored_info().binfo + self.display_cached_string(binfo) + raise SCons.Errors.BuildError # will be 'caught' in self.failed + elif is_up_to_date: + self.display("\"%s\" is up to date." % str(self.targets[0])) + binfo = self.targets[0].get_stored_info().binfo + self.display_cached_string(binfo) + elif dryrun: + raise ConfigureDryRunError(self.targets[0]) + else: + # note stdout and stderr are the same here + s = sys.stdout = sys.stderr = Streamer(sys.stdout) + try: + env = self.targets[0].get_build_env() + if cache_mode == FORCE: + # Set up the Decider() to force rebuilds by saying + # that every source has changed. Note that we still + # call the environment's underlying source decider so + # that the correct .sconsign info will get calculated + # and keep the build state consistent. + def force_build(dependency, target, prev_ni, + env_decider=env.decide_source): + env_decider(dependency, target, prev_ni) + return True + if env.decide_source.func_code is not force_build.func_code: + env.Decider(force_build) + env['PSTDOUT'] = env['PSTDERR'] = s + try: + sconf.cached = 0 + self.targets[0].build() + finally: + sys.stdout = sys.stderr = env['PSTDOUT'] = \ + env['PSTDERR'] = sconf.logstream + except KeyboardInterrupt: + raise + except SystemExit: + exc_value = sys.exc_info()[1] + raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code) + except Exception, e: + for t in self.targets: + binfo = t.get_binfo() + binfo.__class__ = SConfBuildInfo + binfo.set_build_result(1, s.getvalue()) + sconsign_entry = SCons.SConsign.SConsignEntry() + sconsign_entry.binfo = binfo + #sconsign_entry.ninfo = self.get_ninfo() + # We'd like to do this as follows: + # t.store_info(binfo) + # However, we need to store it as an SConfBuildInfo + # object, and store_info() will turn it into a + # regular FileNodeInfo if the target is itself a + # regular File. + sconsign = t.dir.sconsign() + sconsign.set_entry(t.name, sconsign_entry) + sconsign.merge() + raise e + else: + for t in self.targets: + binfo = t.get_binfo() + binfo.__class__ = SConfBuildInfo + binfo.set_build_result(0, s.getvalue()) + sconsign_entry = SCons.SConsign.SConsignEntry() + sconsign_entry.binfo = binfo + #sconsign_entry.ninfo = self.get_ninfo() + # We'd like to do this as follows: + # t.store_info(binfo) + # However, we need to store it as an SConfBuildInfo + # object, and store_info() will turn it into a + # regular FileNodeInfo if the target is itself a + # regular File. + sconsign = t.dir.sconsign() + sconsign.set_entry(t.name, sconsign_entry) + sconsign.merge() + +class SConfBase(object): + """This is simply a class to represent a configure context. After + creating a SConf object, you can call any tests. After finished with your + tests, be sure to call the Finish() method, which returns the modified + environment. + Some words about caching: In most cases, it is not necessary to cache + Test results explicitely. Instead, we use the scons dependency checking + mechanism. For example, if one wants to compile a test program + (SConf.TryLink), the compiler is only called, if the program dependencies + have changed. However, if the program could not be compiled in a former + SConf run, we need to explicitely cache this error. + """ + + def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR', + log_file='$CONFIGURELOG', config_h = None, _depth = 0): + """Constructor. Pass additional tests in the custom_tests-dictinary, + e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest + defines a custom test. + Note also the conf_dir and log_file arguments (you may want to + build tests in the VariantDir, not in the SourceDir) + """ + global SConfFS + if not SConfFS: + SConfFS = SCons.Node.FS.default_fs or \ + SCons.Node.FS.FS(env.fs.pathTop) + if sconf_global is not None: + raise SCons.Errors.UserError + self.env = env + if log_file is not None: + log_file = SConfFS.File(env.subst(log_file)) + self.logfile = log_file + self.logstream = None + self.lastTarget = None + self.depth = _depth + self.cached = 0 # will be set, if all test results are cached + + # add default tests + default_tests = { + 'CheckCC' : CheckCC, + 'CheckCXX' : CheckCXX, + 'CheckSHCC' : CheckSHCC, + 'CheckSHCXX' : CheckSHCXX, + 'CheckFunc' : CheckFunc, + 'CheckType' : CheckType, + 'CheckTypeSize' : CheckTypeSize, + 'CheckDeclaration' : CheckDeclaration, + 'CheckHeader' : CheckHeader, + 'CheckCHeader' : CheckCHeader, + 'CheckCXXHeader' : CheckCXXHeader, + 'CheckLib' : CheckLib, + 'CheckLibWithHeader' : CheckLibWithHeader, + } + self.AddTests(default_tests) + self.AddTests(custom_tests) + self.confdir = SConfFS.Dir(env.subst(conf_dir)) + if config_h is not None: + config_h = SConfFS.File(config_h) + self.config_h = config_h + self._startup() + + def Finish(self): + """Call this method after finished with your tests: + env = sconf.Finish() + """ + self._shutdown() + return self.env + + def Define(self, name, value = None, comment = None): + """ + Define a pre processor symbol name, with the optional given value in the + current config header. + + If value is None (default), then #define name is written. If value is not + none, then #define name value is written. + + comment is a string which will be put as a C comment in the + header, to explain the meaning of the value (appropriate C comments /* and + */ will be put automatically.""" + lines = [] + if comment: + comment_str = "/* %s */" % comment + lines.append(comment_str) + + if value is not None: + define_str = "#define %s %s" % (name, value) + else: + define_str = "#define %s" % name + lines.append(define_str) + lines.append('') + + self.config_h_text = self.config_h_text + '\n'.join(lines) + + def BuildNodes(self, nodes): + """ + Tries to build the given nodes immediately. Returns 1 on success, + 0 on error. + """ + if self.logstream is not None: + # override stdout / stderr to write in log file + oldStdout = sys.stdout + sys.stdout = self.logstream + oldStderr = sys.stderr + sys.stderr = self.logstream + + # the engine assumes the current path is the SConstruct directory ... + old_fs_dir = SConfFS.getcwd() + old_os_dir = os.getcwd() + SConfFS.chdir(SConfFS.Top, change_os_dir=1) + + # Because we take responsibility here for writing out our + # own .sconsign info (see SConfBuildTask.execute(), above), + # we override the store_info() method with a null place-holder + # so we really control how it gets written. + for n in nodes: + n.store_info = n.do_not_store_info + + ret = 1 + + try: + # ToDo: use user options for calc + save_max_drift = SConfFS.get_max_drift() + SConfFS.set_max_drift(0) + tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask) + # we don't want to build tests in parallel + jobs = SCons.Job.Jobs(1, tm ) + jobs.run() + for n in nodes: + state = n.get_state() + if (state != SCons.Node.executed and + state != SCons.Node.up_to_date): + # the node could not be built. we return 0 in this case + ret = 0 + finally: + SConfFS.set_max_drift(save_max_drift) + os.chdir(old_os_dir) + SConfFS.chdir(old_fs_dir, change_os_dir=0) + if self.logstream is not None: + # restore stdout / stderr + sys.stdout = oldStdout + sys.stderr = oldStderr + return ret + + def pspawn_wrapper(self, sh, escape, cmd, args, env): + """Wrapper function for handling piped spawns. + + This looks to the calling interface (in Action.py) like a "normal" + spawn, but associates the call with the PSPAWN variable from + the construction environment and with the streams to which we + want the output logged. This gets slid into the construction + environment as the SPAWN variable so Action.py doesn't have to + know or care whether it's spawning a piped command or not. + """ + return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream) + + + def TryBuild(self, builder, text = None, extension = ""): + """Low level TryBuild implementation. Normally you don't need to + call that - you can use TryCompile / TryLink / TryRun instead + """ + global _ac_build_counter + + # Make sure we have a PSPAWN value, and save the current + # SPAWN value. + try: + self.pspawn = self.env['PSPAWN'] + except KeyError: + raise SCons.Errors.UserError('Missing PSPAWN construction variable.') + try: + save_spawn = self.env['SPAWN'] + except KeyError: + raise SCons.Errors.UserError('Missing SPAWN construction variable.') + + nodesToBeBuilt = [] + + f = "conftest_" + str(_ac_build_counter) + pref = self.env.subst( builder.builder.prefix ) + suff = self.env.subst( builder.builder.suffix ) + target = self.confdir.File(pref + f + suff) + + try: + # Slide our wrapper into the construction environment as + # the SPAWN function. + self.env['SPAWN'] = self.pspawn_wrapper + sourcetext = self.env.Value(text) + + if text is not None: + textFile = self.confdir.File(f + extension) + textFileNode = self.env.SConfSourceBuilder(target=textFile, + source=sourcetext) + nodesToBeBuilt.extend(textFileNode) + source = textFileNode + else: + source = None + + nodes = builder(target = target, source = source) + if not SCons.Util.is_List(nodes): + nodes = [nodes] + nodesToBeBuilt.extend(nodes) + result = self.BuildNodes(nodesToBeBuilt) + + finally: + self.env['SPAWN'] = save_spawn + + _ac_build_counter = _ac_build_counter + 1 + if result: + self.lastTarget = nodes[0] + else: + self.lastTarget = None + + return result + + def TryAction(self, action, text = None, extension = ""): + """Tries to execute the given action with optional source file + contents and optional source file extension , + Returns the status (0 : failed, 1 : ok) and the contents of the + output file. + """ + builder = SCons.Builder.Builder(action=action) + self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} ) + ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) + del self.env['BUILDERS']['SConfActionBuilder'] + if ok: + outputStr = self.lastTarget.get_contents() + return (1, outputStr) + return (0, "") + + def TryCompile( self, text, extension): + """Compiles the program given in text to an env.Object, using extension + as file extension (e.g. '.c'). Returns 1, if compilation was + successful, 0 otherwise. The target is saved in self.lastTarget (for + further processing). + """ + return self.TryBuild(self.env.Object, text, extension) + + def TryLink( self, text, extension ): + """Compiles the program given in text to an executable env.Program, + using extension as file extension (e.g. '.c'). Returns 1, if + compilation was successful, 0 otherwise. The target is saved in + self.lastTarget (for further processing). + """ + return self.TryBuild(self.env.Program, text, extension ) + + def TryRun(self, text, extension ): + """Compiles and runs the program given in text, using extension + as file extension (e.g. '.c'). Returns (1, outputStr) on success, + (0, '') otherwise. The target (a file containing the program's stdout) + is saved in self.lastTarget (for further processing). + """ + ok = self.TryLink(text, extension) + if( ok ): + prog = self.lastTarget + pname = prog.path + output = self.confdir.File(os.path.basename(pname)+'.out') + node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ]) + ok = self.BuildNodes(node) + if ok: + outputStr = output.get_contents() + return( 1, outputStr) + return (0, "") + + class TestWrapper(object): + """A wrapper around Tests (to ensure sanity)""" + def __init__(self, test, sconf): + self.test = test + self.sconf = sconf + def __call__(self, *args, **kw): + if not self.sconf.active: + raise SCons.Errors.UserError + context = CheckContext(self.sconf) + ret = self.test(context, *args, **kw) + if self.sconf.config_h is not None: + self.sconf.config_h_text = self.sconf.config_h_text + context.config_h + context.Result("error: no result") + return ret + + def AddTest(self, test_name, test_instance): + """Adds test_class to this SConf instance. It can be called with + self.test_name(...)""" + setattr(self, test_name, SConfBase.TestWrapper(test_instance, self)) + + def AddTests(self, tests): + """Adds all the tests given in the tests dictionary to this SConf + instance + """ + for name in tests.keys(): + self.AddTest(name, tests[name]) + + def _createDir( self, node ): + dirName = str(node) + if dryrun: + if not os.path.isdir( dirName ): + raise ConfigureDryRunError(dirName) + else: + if not os.path.isdir( dirName ): + os.makedirs( dirName ) + node._exists = 1 + + def _startup(self): + """Private method. Set up logstream, and set the environment + variables necessary for a piped build + """ + global _ac_config_logs + global sconf_global + global SConfFS + + self.lastEnvFs = self.env.fs + self.env.fs = SConfFS + self._createDir(self.confdir) + self.confdir.up().add_ignore( [self.confdir] ) + + if self.logfile is not None and not dryrun: + # truncate logfile, if SConf.Configure is called for the first time + # in a build + if self.logfile in _ac_config_logs: + log_mode = "a" + else: + _ac_config_logs[self.logfile] = None + log_mode = "w" + fp = open(str(self.logfile), log_mode) + self.logstream = SCons.Util.Unbuffered(fp) + # logfile may stay in a build directory, so we tell + # the build system not to override it with a eventually + # existing file with the same name in the source directory + self.logfile.dir.add_ignore( [self.logfile] ) + + tb = traceback.extract_stack()[-3-self.depth] + old_fs_dir = SConfFS.getcwd() + SConfFS.chdir(SConfFS.Top, change_os_dir=0) + self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' % + (tb[0], tb[1], str(self.confdir)) ) + SConfFS.chdir(old_fs_dir) + else: + self.logstream = None + # we use a special builder to create source files from TEXT + action = SCons.Action.Action(_createSource, + _stringSource) + sconfSrcBld = SCons.Builder.Builder(action=action) + self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} ) + self.config_h_text = _ac_config_hs.get(self.config_h, "") + self.active = 1 + # only one SConf instance should be active at a time ... + sconf_global = self + + def _shutdown(self): + """Private method. Reset to non-piped spawn""" + global sconf_global, _ac_config_hs + + if not self.active: + raise SCons.Errors.UserError("Finish may be called only once!") + if self.logstream is not None and not dryrun: + self.logstream.write("\n") + self.logstream.close() + self.logstream = None + # remove the SConfSourceBuilder from the environment + blds = self.env['BUILDERS'] + del blds['SConfSourceBuilder'] + self.env.Replace( BUILDERS=blds ) + self.active = 0 + sconf_global = None + if not self.config_h is None: + _ac_config_hs[self.config_h] = self.config_h_text + self.env.fs = self.lastEnvFs + +class CheckContext(object): + """Provides a context for configure tests. Defines how a test writes to the + screen and log file. + + A typical test is just a callable with an instance of CheckContext as + first argument: + + def CheckCustom(context, ...) + context.Message('Checking my weird test ... ') + ret = myWeirdTestFunction(...) + context.Result(ret) + + Often, myWeirdTestFunction will be one of + context.TryCompile/context.TryLink/context.TryRun. The results of + those are cached, for they are only rebuild, if the dependencies have + changed. + """ + + def __init__(self, sconf): + """Constructor. Pass the corresponding SConf instance.""" + self.sconf = sconf + self.did_show_result = 0 + + # for Conftest.py: + self.vardict = {} + self.havedict = {} + self.headerfilename = None + self.config_h = "" # config_h text will be stored here + # we don't regenerate the config.h file after each test. That means, + # that tests won't be able to include the config.h file, and so + # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major + # issue, though. If it turns out, that we need to include config.h + # in tests, we must ensure, that the dependencies are worked out + # correctly. Note that we can't use Conftest.py's support for config.h, + # cause we will need to specify a builder for the config.h file ... + + def Message(self, text): + """Inform about what we are doing right now, e.g. + 'Checking for SOMETHING ... ' + """ + self.Display(text) + self.sconf.cached = 1 + self.did_show_result = 0 + + def Result(self, res): + """Inform about the result of the test. res may be an integer or a + string. In case of an integer, the written text will be 'yes' or 'no'. + The result is only displayed when self.did_show_result is not set. + """ + if isinstance(res, (int, bool)): + if res: + text = "yes" + else: + text = "no" + elif isinstance(res, str): + text = res + else: + raise TypeError("Expected string, int or bool, got " + str(type(res))) + + if self.did_show_result == 0: + # Didn't show result yet, do it now. + self.Display(text + "\n") + self.did_show_result = 1 + + def TryBuild(self, *args, **kw): + return self.sconf.TryBuild(*args, **kw) + + def TryAction(self, *args, **kw): + return self.sconf.TryAction(*args, **kw) + + def TryCompile(self, *args, **kw): + return self.sconf.TryCompile(*args, **kw) + + def TryLink(self, *args, **kw): + return self.sconf.TryLink(*args, **kw) + + def TryRun(self, *args, **kw): + return self.sconf.TryRun(*args, **kw) + + def __getattr__( self, attr ): + if( attr == 'env' ): + return self.sconf.env + elif( attr == 'lastTarget' ): + return self.sconf.lastTarget + else: + raise AttributeError("CheckContext instance has no attribute '%s'" % attr) + + #### Stuff used by Conftest.py (look there for explanations). + + def BuildProg(self, text, ext): + self.sconf.cached = 1 + # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. + return not self.TryBuild(self.env.Program, text, ext) + + def CompileProg(self, text, ext): + self.sconf.cached = 1 + # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. + return not self.TryBuild(self.env.Object, text, ext) + + def CompileSharedObject(self, text, ext): + self.sconf.cached = 1 + # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc. + return not self.TryBuild(self.env.SharedObject, text, ext) + + def RunProg(self, text, ext): + self.sconf.cached = 1 + # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. + st, out = self.TryRun(text, ext) + return not st, out + + def AppendLIBS(self, lib_name_list): + oldLIBS = self.env.get( 'LIBS', [] ) + self.env.Append(LIBS = lib_name_list) + return oldLIBS + + def PrependLIBS(self, lib_name_list): + oldLIBS = self.env.get( 'LIBS', [] ) + self.env.Prepend(LIBS = lib_name_list) + return oldLIBS + + def SetLIBS(self, val): + oldLIBS = self.env.get( 'LIBS', [] ) + self.env.Replace(LIBS = val) + return oldLIBS + + def Display(self, msg): + if self.sconf.cached: + # We assume that Display is called twice for each test here + # once for the Checking for ... message and once for the result. + # The self.sconf.cached flag can only be set between those calls + msg = "(cached) " + msg + self.sconf.cached = 0 + progress_display(msg, append_newline=0) + self.Log("scons: Configure: " + msg + "\n") + + def Log(self, msg): + if self.sconf.logstream is not None: + self.sconf.logstream.write(msg) + + #### End of stuff used by Conftest.py. + + +def SConf(*args, **kw): + if kw.get(build_type, True): + kw['_depth'] = kw.get('_depth', 0) + 1 + for bt in build_types: + try: + del kw[bt] + except KeyError: + pass + return SConfBase(*args, **kw) + else: + return SCons.Util.Null() + + +def CheckFunc(context, function_name, header = None, language = None): + res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language) + context.did_show_result = 1 + return not res + +def CheckType(context, type_name, includes = "", language = None): + res = SCons.Conftest.CheckType(context, type_name, + header = includes, language = language) + context.did_show_result = 1 + return not res + +def CheckTypeSize(context, type_name, includes = "", language = None, expect = None): + res = SCons.Conftest.CheckTypeSize(context, type_name, + header = includes, language = language, + expect = expect) + context.did_show_result = 1 + return res + +def CheckDeclaration(context, declaration, includes = "", language = None): + res = SCons.Conftest.CheckDeclaration(context, declaration, + includes = includes, + language = language) + context.did_show_result = 1 + return not res + +def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'): + # used by CheckHeader and CheckLibWithHeader to produce C - #include + # statements from the specified header (list) + if not SCons.Util.is_List(headers): + headers = [headers] + l = [] + if leaveLast: + lastHeader = headers[-1] + headers = headers[:-1] + else: + lastHeader = None + for s in headers: + l.append("#include %s%s%s\n" + % (include_quotes[0], s, include_quotes[1])) + return ''.join(l), lastHeader + +def CheckHeader(context, header, include_quotes = '<>', language = None): + """ + A test for a C or C++ header file. + """ + prog_prefix, hdr_to_check = \ + createIncludesFromHeaders(header, 1, include_quotes) + res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix, + language = language, + include_quotes = include_quotes) + context.did_show_result = 1 + return not res + +def CheckCC(context): + res = SCons.Conftest.CheckCC(context) + context.did_show_result = 1 + return not res + +def CheckCXX(context): + res = SCons.Conftest.CheckCXX(context) + context.did_show_result = 1 + return not res + +def CheckSHCC(context): + res = SCons.Conftest.CheckSHCC(context) + context.did_show_result = 1 + return not res + +def CheckSHCXX(context): + res = SCons.Conftest.CheckSHCXX(context) + context.did_show_result = 1 + return not res + +# Bram: Make this function obsolete? CheckHeader() is more generic. + +def CheckCHeader(context, header, include_quotes = '""'): + """ + A test for a C header file. + """ + return CheckHeader(context, header, include_quotes, language = "C") + + +# Bram: Make this function obsolete? CheckHeader() is more generic. + +def CheckCXXHeader(context, header, include_quotes = '""'): + """ + A test for a C++ header file. + """ + return CheckHeader(context, header, include_quotes, language = "C++") + + +def CheckLib(context, library = None, symbol = "main", + header = None, language = None, autoadd = 1): + """ + A test for a library. See also CheckLibWithHeader. + Note that library may also be None to test whether the given symbol + compiles without flags. + """ + + if library == []: + library = [None] + + if not SCons.Util.is_List(library): + library = [library] + + # ToDo: accept path for the library + res = SCons.Conftest.CheckLib(context, library, symbol, header = header, + language = language, autoadd = autoadd) + context.did_show_result = 1 + return not res + +# XXX +# Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H. + +def CheckLibWithHeader(context, libs, header, language, + call = None, autoadd = 1): + # ToDo: accept path for library. Support system header files. + """ + Another (more sophisticated) test for a library. + Checks, if library and header is available for language (may be 'C' + or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'. + As in CheckLib, we support library=None, to test if the call compiles + without extra link flags. + """ + prog_prefix, dummy = \ + createIncludesFromHeaders(header, 0) + if libs == []: + libs = [None] + + if not SCons.Util.is_List(libs): + libs = [libs] + + res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix, + call = call, language = language, autoadd = autoadd) + context.did_show_result = 1 + return not res + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/SConsign.py b/engine/SCons/SConsign.py new file mode 100644 index 0000000..bbe475b --- /dev/null +++ b/engine/SCons/SConsign.py @@ -0,0 +1,389 @@ +"""SCons.SConsign + +Writing and reading information to the .sconsign file or files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/SConsign.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.compat + +import os +# compat layer imports "cPickle" for us if it's available. +import pickle + +import SCons.dblite +import SCons.Warnings + +def corrupt_dblite_warning(filename): + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, + "Ignoring corrupt .sconsign file: %s"%filename) + +SCons.dblite.ignore_corrupt_dbfiles = 1 +SCons.dblite.corruption_warning = corrupt_dblite_warning + +#XXX Get rid of the global array so this becomes re-entrant. +sig_files = [] + +# Info for the database SConsign implementation (now the default): +# "DataBase" is a dictionary that maps top-level SConstruct directories +# to open database handles. +# "DB_Module" is the Python database module to create the handles. +# "DB_Name" is the base name of the database file (minus any +# extension the underlying DB module will add). +DataBase = {} +DB_Module = SCons.dblite +DB_Name = ".sconsign" +DB_sync_list = [] + +def Get_DataBase(dir): + global DataBase, DB_Module, DB_Name + top = dir.fs.Top + if not os.path.isabs(DB_Name) and top.repositories: + mode = "c" + for d in [top] + top.repositories: + if dir.is_under(d): + try: + return DataBase[d], mode + except KeyError: + path = d.entry_abspath(DB_Name) + try: db = DataBase[d] = DB_Module.open(path, mode) + except (IOError, OSError): pass + else: + if mode != "r": + DB_sync_list.append(db) + return db, mode + mode = "r" + try: + return DataBase[top], "c" + except KeyError: + db = DataBase[top] = DB_Module.open(DB_Name, "c") + DB_sync_list.append(db) + return db, "c" + except TypeError: + print "DataBase =", DataBase + raise + +def Reset(): + """Reset global state. Used by unit tests that end up using + SConsign multiple times to get a clean slate for each test.""" + global sig_files, DB_sync_list + sig_files = [] + DB_sync_list = [] + +normcase = os.path.normcase + +def write(): + global sig_files + for sig_file in sig_files: + sig_file.write(sync=0) + for db in DB_sync_list: + try: + syncmethod = db.sync + except AttributeError: + pass # Not all dbm modules have sync() methods. + else: + syncmethod() + try: + closemethod = db.close + except AttributeError: + pass # Not all dbm modules have close() methods. + else: + closemethod() + +class SConsignEntry(object): + """ + Wrapper class for the generic entry in a .sconsign file. + The Node subclass populates it with attributes as it pleases. + + XXX As coded below, we do expect a '.binfo' attribute to be added, + but we'll probably generalize this in the next refactorings. + """ + current_version_id = 1 + def __init__(self): + # Create an object attribute from the class attribute so it ends up + # in the pickled data in the .sconsign file. + _version_id = self.current_version_id + def convert_to_sconsign(self): + self.binfo.convert_to_sconsign() + def convert_from_sconsign(self, dir, name): + self.binfo.convert_from_sconsign(dir, name) + +class Base(object): + """ + This is the controlling class for the signatures for the collection of + entries associated with a specific directory. The actual directory + association will be maintained by a subclass that is specific to + the underlying storage method. This class provides a common set of + methods for fetching and storing the individual bits of information + that make up signature entry. + """ + def __init__(self): + self.entries = {} + self.dirty = False + self.to_be_merged = {} + + def get_entry(self, filename): + """ + Fetch the specified entry attribute. + """ + return self.entries[filename] + + def set_entry(self, filename, obj): + """ + Set the entry. + """ + self.entries[filename] = obj + self.dirty = True + + def do_not_set_entry(self, filename, obj): + pass + + def store_info(self, filename, node): + entry = node.get_stored_info() + entry.binfo.merge(node.get_binfo()) + self.to_be_merged[filename] = node + self.dirty = True + + def do_not_store_info(self, filename, node): + pass + + def merge(self): + for key, node in self.to_be_merged.items(): + entry = node.get_stored_info() + try: + ninfo = entry.ninfo + except AttributeError: + # This happens with SConf Nodes, because the configuration + # subsystem takes direct control over how the build decision + # is made and its information stored. + pass + else: + ninfo.merge(node.get_ninfo()) + self.entries[key] = entry + self.to_be_merged = {} + +class DB(Base): + """ + A Base subclass that reads and writes signature information + from a global .sconsign.db* file--the actual file suffix is + determined by the database module. + """ + def __init__(self, dir): + Base.__init__(self) + + self.dir = dir + + db, mode = Get_DataBase(dir) + + # Read using the path relative to the top of the Repository + # (self.dir.tpath) from which we're fetching the signature + # information. + path = normcase(dir.tpath) + try: + rawentries = db[path] + except KeyError: + pass + else: + try: + self.entries = pickle.loads(rawentries) + if not isinstance(self.entries, dict): + self.entries = {} + raise TypeError + except KeyboardInterrupt: + raise + except Exception, e: + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, + "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e)) + for key, entry in self.entries.items(): + entry.convert_from_sconsign(dir, key) + + if mode == "r": + # This directory is actually under a repository, which means + # likely they're reaching in directly for a dependency on + # a file there. Don't actually set any entry info, so we + # won't try to write to that .sconsign.dblite file. + self.set_entry = self.do_not_set_entry + self.store_info = self.do_not_store_info + + global sig_files + sig_files.append(self) + + def write(self, sync=1): + if not self.dirty: + return + + self.merge() + + db, mode = Get_DataBase(self.dir) + + # Write using the path relative to the top of the SConstruct + # directory (self.dir.path), not relative to the top of + # the Repository; we only write to our own .sconsign file, + # not to .sconsign files in Repositories. + path = normcase(self.dir.path) + for key, entry in self.entries.items(): + entry.convert_to_sconsign() + db[path] = pickle.dumps(self.entries, 1) + + if sync: + try: + syncmethod = db.sync + except AttributeError: + # Not all anydbm modules have sync() methods. + pass + else: + syncmethod() + +class Dir(Base): + def __init__(self, fp=None, dir=None): + """ + fp - file pointer to read entries from + """ + Base.__init__(self) + + if not fp: + return + + self.entries = pickle.load(fp) + if not isinstance(self.entries, dict): + self.entries = {} + raise TypeError + + if dir: + for key, entry in self.entries.items(): + entry.convert_from_sconsign(dir, key) + +class DirFile(Dir): + """ + Encapsulates reading and writing a per-directory .sconsign file. + """ + def __init__(self, dir): + """ + dir - the directory for the file + """ + + self.dir = dir + self.sconsign = os.path.join(dir.path, '.sconsign') + + try: + fp = open(self.sconsign, 'rb') + except IOError: + fp = None + + try: + Dir.__init__(self, fp, dir) + except KeyboardInterrupt: + raise + except: + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, + "Ignoring corrupt .sconsign file: %s"%self.sconsign) + + global sig_files + sig_files.append(self) + + def write(self, sync=1): + """ + Write the .sconsign file to disk. + + Try to write to a temporary file first, and rename it if we + succeed. If we can't write to the temporary file, it's + probably because the directory isn't writable (and if so, + how did we build anything in this directory, anyway?), so + try to write directly to the .sconsign file as a backup. + If we can't rename, try to copy the temporary contents back + to the .sconsign file. Either way, always try to remove + the temporary file at the end. + """ + if not self.dirty: + return + + self.merge() + + temp = os.path.join(self.dir.path, '.scons%d' % os.getpid()) + try: + file = open(temp, 'wb') + fname = temp + except IOError: + try: + file = open(self.sconsign, 'wb') + fname = self.sconsign + except IOError: + return + for key, entry in self.entries.items(): + entry.convert_to_sconsign() + pickle.dump(self.entries, file, 1) + file.close() + if fname != self.sconsign: + try: + mode = os.stat(self.sconsign)[0] + os.chmod(self.sconsign, 0666) + os.unlink(self.sconsign) + except (IOError, OSError): + # Try to carry on in the face of either OSError + # (things like permission issues) or IOError (disk + # or network issues). If there's a really dangerous + # issue, it should get re-raised by the calls below. + pass + try: + os.rename(fname, self.sconsign) + except OSError: + # An OSError failure to rename may indicate something + # like the directory has no write permission, but + # the .sconsign file itself might still be writable, + # so try writing on top of it directly. An IOError + # here, or in any of the following calls, would get + # raised, indicating something like a potentially + # serious disk or network issue. + open(self.sconsign, 'wb').write(open(fname, 'rb').read()) + os.chmod(self.sconsign, mode) + try: + os.unlink(temp) + except (IOError, OSError): + pass + +ForDirectory = DB + +def File(name, dbm_module=None): + """ + Arrange for all signatures to be stored in a global .sconsign.db* + file. + """ + global ForDirectory, DB_Name, DB_Module + if name is None: + ForDirectory = DirFile + DB_Module = None + else: + ForDirectory = DB + DB_Name = name + if not dbm_module is None: + DB_Module = dbm_module + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/C.py b/engine/SCons/Scanner/C.py new file mode 100644 index 0000000..0b07c13 --- /dev/null +++ b/engine/SCons/Scanner/C.py @@ -0,0 +1,132 @@ +"""SCons.Scanner.C + +This module implements the depenency scanner for C/C++ code. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Scanner/C.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Node.FS +import SCons.Scanner +import SCons.Util + +import SCons.cpp + +class SConsCPPScanner(SCons.cpp.PreProcessor): + """ + SCons-specific subclass of the cpp.py module's processing. + + We subclass this so that: 1) we can deal with files represented + by Nodes, not strings; 2) we can keep track of the files that are + missing. + """ + def __init__(self, *args, **kw): + SCons.cpp.PreProcessor.__init__(self, *args, **kw) + self.missing = [] + def initialize_result(self, fname): + self.result = SCons.Util.UniqueList([fname]) + def finalize_result(self, fname): + return self.result[1:] + def find_include_file(self, t): + keyword, quote, fname = t + result = SCons.Node.FS.find_file(fname, self.searchpath[quote]) + if not result: + self.missing.append((fname, self.current_file)) + return result + def read_file(self, file): + try: + fp = open(str(file.rfile())) + except EnvironmentError, e: + self.missing.append((file, self.current_file)) + return '' + else: + return fp.read() + +def dictify_CPPDEFINES(env): + cppdefines = env.get('CPPDEFINES', {}) + if cppdefines is None: + return {} + if SCons.Util.is_Sequence(cppdefines): + result = {} + for c in cppdefines: + if SCons.Util.is_Sequence(c): + result[c[0]] = c[1] + else: + result[c] = None + return result + if not SCons.Util.is_Dict(cppdefines): + return {cppdefines : None} + return cppdefines + +class SConsCPPScannerWrapper(object): + """ + The SCons wrapper around a cpp.py scanner. + + This is the actual glue between the calling conventions of generic + SCons scanners, and the (subclass of) cpp.py class that knows how + to look for #include lines with reasonably real C-preprocessor-like + evaluation of #if/#ifdef/#else/#elif lines. + """ + def __init__(self, name, variable): + self.name = name + self.path = SCons.Scanner.FindPathDirs(variable) + def __call__(self, node, env, path = ()): + cpp = SConsCPPScanner(current = node.get_dir(), + cpppath = path, + dict = dictify_CPPDEFINES(env)) + result = cpp(node) + for included, includer in cpp.missing: + fmt = "No dependency generated for file: %s (included from: %s) -- file not found" + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + fmt % (included, includer)) + return result + + def recurse_nodes(self, nodes): + return nodes + def select(self, node): + return self + +def CScanner(): + """Return a prototype Scanner instance for scanning source files + that use the C pre-processor""" + + # Here's how we would (or might) use the CPP scanner code above that + # knows how to evaluate #if/#ifdef/#else/#elif lines when searching + # for #includes. This is commented out for now until we add the + # right configurability to let users pick between the scanners. + #return SConsCPPScannerWrapper("CScanner", "CPPPATH") + + cs = SCons.Scanner.ClassicCPP("CScanner", + "$CPPSUFFIXES", + "CPPPATH", + '^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")') + return cs + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/D.py b/engine/SCons/Scanner/D.py new file mode 100644 index 0000000..b4b26ba --- /dev/null +++ b/engine/SCons/Scanner/D.py @@ -0,0 +1,73 @@ +"""SCons.Scanner.D + +Scanner for the Digital Mars "D" programming language. + +Coded by Andy Friesen +17 Nov 2003 + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Scanner/D.py 5357 2011/09/09 21:31:03 bdeegan" + +import re + +import SCons.Scanner + +def DScanner(): + """Return a prototype Scanner instance for scanning D source files""" + ds = D() + return ds + +class D(SCons.Scanner.Classic): + def __init__ (self): + SCons.Scanner.Classic.__init__ (self, + name = "DScanner", + suffixes = '$DSUFFIXES', + path_variable = 'DPATH', + regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;') + + self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M) + + def find_include(self, include, source_dir, path): + # translate dots (package separators) to slashes + inc = include.replace('.', '/') + + i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path) + if i is None: + i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path) + return i, include + + def find_include_names(self, node): + includes = [] + for i in self.cre.findall(node.get_text_contents()): + includes = includes + self.cre2.findall(i) + return includes + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/Dir.py b/engine/SCons/Scanner/Dir.py new file mode 100644 index 0000000..aa1239f --- /dev/null +++ b/engine/SCons/Scanner/Dir.py @@ -0,0 +1,109 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Scanner/Dir.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Node.FS +import SCons.Scanner + +def only_dirs(nodes): + is_Dir = lambda n: isinstance(n.disambiguate(), SCons.Node.FS.Dir) + return list(filter(is_Dir, nodes)) + +def DirScanner(**kw): + """Return a prototype Scanner instance for scanning + directories for on-disk files""" + kw['node_factory'] = SCons.Node.FS.Entry + kw['recursive'] = only_dirs + return SCons.Scanner.Base(scan_on_disk, "DirScanner", **kw) + +def DirEntryScanner(**kw): + """Return a prototype Scanner instance for "scanning" + directory Nodes for their in-memory entries""" + kw['node_factory'] = SCons.Node.FS.Entry + kw['recursive'] = None + return SCons.Scanner.Base(scan_in_memory, "DirEntryScanner", **kw) + +skip_entry = {} + +skip_entry_list = [ + '.', + '..', + '.sconsign', + # Used by the native dblite.py module. + '.sconsign.dblite', + # Used by dbm and dumbdbm. + '.sconsign.dir', + # Used by dbm. + '.sconsign.pag', + # Used by dumbdbm. + '.sconsign.dat', + '.sconsign.bak', + # Used by some dbm emulations using Berkeley DB. + '.sconsign.db', +] + +for skip in skip_entry_list: + skip_entry[skip] = 1 + skip_entry[SCons.Node.FS._my_normcase(skip)] = 1 + +do_not_scan = lambda k: k not in skip_entry + +def scan_on_disk(node, env, path=()): + """ + Scans a directory for on-disk files and directories therein. + + Looking up the entries will add these to the in-memory Node tree + representation of the file system, so all we have to do is just + that and then call the in-memory scanning function. + """ + try: + flist = node.fs.listdir(node.abspath) + except (IOError, OSError): + return [] + e = node.Entry + for f in filter(do_not_scan, flist): + # Add ./ to the beginning of the file name so if it begins with a + # '#' we don't look it up relative to the top-level directory. + e('./' + f) + return scan_in_memory(node, env, path) + +def scan_in_memory(node, env, path=()): + """ + "Scans" a Node.FS.Dir for its in-memory entries. + """ + try: + entries = node.entries + except AttributeError: + # It's not a Node.FS.Dir (or doesn't look enough like one for + # our purposes), which can happen if a target list containing + # mixed Node types (Dirs and Files, for example) has a Dir as + # the first entry. + return [] + entry_list = sorted(filter(do_not_scan, list(entries.keys()))) + return [entries[n] for n in entry_list] + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/Fortran.py b/engine/SCons/Scanner/Fortran.py new file mode 100644 index 0000000..13f670d --- /dev/null +++ b/engine/SCons/Scanner/Fortran.py @@ -0,0 +1,316 @@ +"""SCons.Scanner.Fortran + +This module implements the dependency scanner for Fortran code. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Scanner/Fortran.py 5357 2011/09/09 21:31:03 bdeegan" + +import re + +import SCons.Node +import SCons.Node.FS +import SCons.Scanner +import SCons.Util +import SCons.Warnings + +class F90Scanner(SCons.Scanner.Classic): + """ + A Classic Scanner subclass for Fortran source files which takes + into account both USE and INCLUDE statements. This scanner will + work for both F77 and F90 (and beyond) compilers. + + Currently, this scanner assumes that the include files do not contain + USE statements. To enable the ability to deal with USE statements + in include files, add logic right after the module names are found + to loop over each include file, search for and locate each USE + statement, and append each module name to the list of dependencies. + Caching the search results in a common dictionary somewhere so that + the same include file is not searched multiple times would be a + smart thing to do. + """ + + def __init__(self, name, suffixes, path_variable, + use_regex, incl_regex, def_regex, *args, **kw): + + self.cre_use = re.compile(use_regex, re.M) + self.cre_incl = re.compile(incl_regex, re.M) + self.cre_def = re.compile(def_regex, re.M) + + def _scan(node, env, path, self=self): + node = node.rfile() + + if not node.exists(): + return [] + + return self.scan(node, env, path) + + kw['function'] = _scan + kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable) + kw['recursive'] = 1 + kw['skeys'] = suffixes + kw['name'] = name + + SCons.Scanner.Current.__init__(self, *args, **kw) + + def scan(self, node, env, path=()): + + # cache the includes list in node so we only scan it once: + if node.includes != None: + mods_and_includes = node.includes + else: + # retrieve all included filenames + includes = self.cre_incl.findall(node.get_text_contents()) + # retrieve all USE'd module names + modules = self.cre_use.findall(node.get_text_contents()) + # retrieve all defined module names + defmodules = self.cre_def.findall(node.get_text_contents()) + + # Remove all USE'd module names that are defined in the same file + # (case-insensitively) + d = {} + for m in defmodules: + d[m.lower()] = 1 + modules = [m for m in modules if m.lower() not in d] + + # Convert module name to a .mod filename + suffix = env.subst('$FORTRANMODSUFFIX') + modules = [x.lower() + suffix for x in modules] + # Remove unique items from the list + mods_and_includes = SCons.Util.unique(includes+modules) + node.includes = mods_and_includes + + # This is a hand-coded DSU (decorate-sort-undecorate, or + # Schwartzian transform) pattern. The sort key is the raw name + # of the file as specifed on the USE or INCLUDE line, which lets + # us keep the sort order constant regardless of whether the file + # is actually found in a Repository or locally. + nodes = [] + source_dir = node.get_dir() + if callable(path): + path = path() + for dep in mods_and_includes: + n, i = self.find_include(dep, source_dir, path) + + if n is None: + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node)) + else: + sortkey = self.sort_key(dep) + nodes.append((sortkey, n)) + + return [pair[1] for pair in sorted(nodes)] + +def FortranScan(path_variable="FORTRANPATH"): + """Return a prototype Scanner instance for scanning source files + for Fortran USE & INCLUDE statements""" + +# The USE statement regex matches the following: +# +# USE module_name +# USE :: module_name +# USE, INTRINSIC :: module_name +# USE, NON_INTRINSIC :: module_name +# +# Limitations +# +# -- While the regex can handle multiple USE statements on one line, +# it cannot properly handle them if they are commented out. +# In either of the following cases: +# +# ! USE mod_a ; USE mod_b [entire line is commented out] +# USE mod_a ! ; USE mod_b [in-line comment of second USE statement] +# +# the second module name (mod_b) will be picked up as a dependency +# even though it should be ignored. The only way I can see +# to rectify this would be to modify the scanner to eliminate +# the call to re.findall, read in the contents of the file, +# treating the comment character as an end-of-line character +# in addition to the normal linefeed, loop over each line, +# weeding out the comments, and looking for the USE statements. +# One advantage to this is that the regex passed to the scanner +# would no longer need to match a semicolon. +# +# -- I question whether or not we need to detect dependencies to +# INTRINSIC modules because these are built-in to the compiler. +# If we consider them a dependency, will SCons look for them, not +# find them, and kill the build? Or will we there be standard +# compiler-specific directories we will need to point to so the +# compiler and SCons can locate the proper object and mod files? + +# Here is a breakdown of the regex: +# +# (?i) : regex is case insensitive +# ^ : start of line +# (?: : group a collection of regex symbols without saving the match as a "group" +# ^|; : matches either the start of the line or a semicolon - semicolon +# ) : end the unsaved grouping +# \s* : any amount of white space +# USE : match the string USE, case insensitive +# (?: : group a collection of regex symbols without saving the match as a "group" +# \s+| : match one or more whitespace OR .... (the next entire grouped set of regex symbols) +# (?: : group a collection of regex symbols without saving the match as a "group" +# (?: : establish another unsaved grouping of regex symbols +# \s* : any amount of white space +# , : match a comma +# \s* : any amount of white space +# (?:NON_)? : optionally match the prefix NON_, case insensitive +# INTRINSIC : match the string INTRINSIC, case insensitive +# )? : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression +# \s* : any amount of white space +# :: : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute +# ) : end the unsaved grouping +# ) : end the unsaved grouping +# \s* : match any amount of white space +# (\w+) : match the module name that is being USE'd +# +# + use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" + + +# The INCLUDE statement regex matches the following: +# +# INCLUDE 'some_Text' +# INCLUDE "some_Text" +# INCLUDE "some_Text" ; INCLUDE "some_Text" +# INCLUDE kind_"some_Text" +# INCLUDE kind_'some_Text" +# +# where some_Text can include any alphanumeric and/or special character +# as defined by the Fortran 2003 standard. +# +# Limitations: +# +# -- The Fortran standard dictates that a " or ' in the INCLUDE'd +# string must be represented as a "" or '', if the quotes that wrap +# the entire string are either a ' or ", respectively. While the +# regular expression below can detect the ' or " characters just fine, +# the scanning logic, presently is unable to detect them and reduce +# them to a single instance. This probably isn't an issue since, +# in practice, ' or " are not generally used in filenames. +# +# -- This regex will not properly deal with multiple INCLUDE statements +# when the entire line has been commented out, ala +# +# ! INCLUDE 'some_file' ; INCLUDE 'some_file' +# +# In such cases, it will properly ignore the first INCLUDE file, +# but will actually still pick up the second. Interestingly enough, +# the regex will properly deal with these cases: +# +# INCLUDE 'some_file' +# INCLUDE 'some_file' !; INCLUDE 'some_file' +# +# To get around the above limitation, the FORTRAN programmer could +# simply comment each INCLUDE statement separately, like this +# +# ! INCLUDE 'some_file' !; INCLUDE 'some_file' +# +# The way I see it, the only way to get around this limitation would +# be to modify the scanning logic to replace the calls to re.findall +# with a custom loop that processes each line separately, throwing +# away fully commented out lines before attempting to match against +# the INCLUDE syntax. +# +# Here is a breakdown of the regex: +# +# (?i) : regex is case insensitive +# (?: : begin a non-saving group that matches the following: +# ^ : either the start of the line +# | : or +# ['">]\s*; : a semicolon that follows a single quote, +# double quote or greater than symbol (with any +# amount of whitespace in between). This will +# allow the regex to match multiple INCLUDE +# statements per line (although it also requires +# the positive lookahead assertion that is +# used below). It will even properly deal with +# (i.e. ignore) cases in which the additional +# INCLUDES are part of an in-line comment, ala +# " INCLUDE 'someFile' ! ; INCLUDE 'someFile2' " +# ) : end of non-saving group +# \s* : any amount of white space +# INCLUDE : match the string INCLUDE, case insensitive +# \s+ : match one or more white space characters +# (?\w+_)? : match the optional "kind-param _" prefix allowed by the standard +# [<"'] : match the include delimiter - an apostrophe, double quote, or less than symbol +# (.+?) : match one or more characters that make up +# the included path and file name and save it +# in a group. The Fortran standard allows for +# any non-control character to be used. The dot +# operator will pick up any character, including +# control codes, but I can't conceive of anyone +# putting control codes in their file names. +# The question mark indicates it is non-greedy so +# that regex will match only up to the next quote, +# double quote, or greater than symbol +# (?=["'>]) : positive lookahead assertion to match the include +# delimiter - an apostrophe, double quote, or +# greater than symbol. This level of complexity +# is required so that the include delimiter is +# not consumed by the match, thus allowing the +# sub-regex discussed above to uniquely match a +# set of semicolon-separated INCLUDE statements +# (as allowed by the F2003 standard) + + include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" + +# The MODULE statement regex finds module definitions by matching +# the following: +# +# MODULE module_name +# +# but *not* the following: +# +# MODULE PROCEDURE procedure_name +# +# Here is a breakdown of the regex: +# +# (?i) : regex is case insensitive +# ^\s* : any amount of white space +# MODULE : match the string MODULE, case insensitive +# \s+ : match one or more white space characters +# (?!PROCEDURE) : but *don't* match if the next word matches +# PROCEDURE (negative lookahead assertion), +# case insensitive +# (\w+) : match one or more alphanumeric characters +# that make up the defined module name and +# save it in a group + + def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" + + scanner = F90Scanner("FortranScan", + "$FORTRANSUFFIXES", + path_variable, + use_regex, + include_regex, + def_regex) + return scanner + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/IDL.py b/engine/SCons/Scanner/IDL.py new file mode 100644 index 0000000..206a106 --- /dev/null +++ b/engine/SCons/Scanner/IDL.py @@ -0,0 +1,48 @@ +"""SCons.Scanner.IDL + +This module implements the depenency scanner for IDL (Interface +Definition Language) files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Scanner/IDL.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Node.FS +import SCons.Scanner + +def IDLScan(): + """Return a prototype Scanner instance for scanning IDL source files""" + cs = SCons.Scanner.ClassicCPP("IDLScan", + "$IDLSUFFIXES", + "CPPPATH", + '^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")') + return cs + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/LaTeX.py b/engine/SCons/Scanner/LaTeX.py new file mode 100644 index 0000000..913e64b --- /dev/null +++ b/engine/SCons/Scanner/LaTeX.py @@ -0,0 +1,387 @@ +"""SCons.Scanner.LaTeX + +This module implements the dependency scanner for LaTeX code. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Scanner/LaTeX.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path +import re + +import SCons.Scanner +import SCons.Util + +# list of graphics file extensions for TeX and LaTeX +TexGraphics = ['.eps', '.ps'] +LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] + +# Used as a return value of modify_env_var if the variable is not set. +class _Null(object): + pass +_null = _Null + +# The user specifies the paths in env[variable], similar to other builders. +# They may be relative and must be converted to absolute, as expected +# by LaTeX and Co. The environment may already have some paths in +# env['ENV'][var]. These paths are honored, but the env[var] paths have +# higher precedence. All changes are un-done on exit. +def modify_env_var(env, var, abspath): + try: + save = env['ENV'][var] + except KeyError: + save = _null + env.PrependENVPath(var, abspath) + try: + if SCons.Util.is_List(env[var]): + env.PrependENVPath(var, [os.path.abspath(str(p)) for p in env[var]]) + else: + # Split at os.pathsep to convert into absolute path + env.PrependENVPath(var, [os.path.abspath(p) for p in str(env[var]).split(os.pathsep)]) + except KeyError: + pass + + # Convert into a string explicitly to append ":" (without which it won't search system + # paths as well). The problem is that env.AppendENVPath(var, ":") + # does not work, refuses to append ":" (os.pathsep). + + if SCons.Util.is_List(env['ENV'][var]): + env['ENV'][var] = os.pathsep.join(env['ENV'][var]) + # Append the trailing os.pathsep character here to catch the case with no env[var] + env['ENV'][var] = env['ENV'][var] + os.pathsep + + return save + +class FindENVPathDirs(object): + """A class to bind a specific *PATH variable name to a function that + will return all of the *path directories.""" + def __init__(self, variable): + self.variable = variable + def __call__(self, env, dir=None, target=None, source=None, argument=None): + import SCons.PathList + try: + path = env['ENV'][self.variable] + except KeyError: + return () + + dir = dir or env.fs._cwd + path = SCons.PathList.PathList(path).subst_path(env, target, source) + return tuple(dir.Rfindalldirs(path)) + + + +def LaTeXScanner(): + """Return a prototype Scanner instance for scanning LaTeX source files + when built with latex. + """ + ds = LaTeX(name = "LaTeXScanner", + suffixes = '$LATEXSUFFIXES', + # in the search order, see below in LaTeX class docstring + graphics_extensions = TexGraphics, + recursive = 0) + return ds + +def PDFLaTeXScanner(): + """Return a prototype Scanner instance for scanning LaTeX source files + when built with pdflatex. + """ + ds = LaTeX(name = "PDFLaTeXScanner", + suffixes = '$LATEXSUFFIXES', + # in the search order, see below in LaTeX class docstring + graphics_extensions = LatexGraphics, + recursive = 0) + return ds + +class LaTeX(SCons.Scanner.Base): + """Class for scanning LaTeX files for included files. + + Unlike most scanners, which use regular expressions that just + return the included file name, this returns a tuple consisting + of the keyword for the inclusion ("include", "includegraphics", + "input", or "bibliography"), and then the file name itself. + Based on a quick look at LaTeX documentation, it seems that we + should append .tex suffix for the "include" keywords, append .tex if + there is no extension for the "input" keyword, and need to add .bib + for the "bibliography" keyword that does not accept extensions by itself. + + Finally, if there is no extension for an "includegraphics" keyword + latex will append .ps or .eps to find the file, while pdftex may use .pdf, + .jpg, .tif, .mps, or .png. + + The actual subset and search order may be altered by + DeclareGraphicsExtensions command. This complication is ignored. + The default order corresponds to experimentation with teTeX + $ latex --version + pdfeTeX 3.141592-1.21a-2.2 (Web2C 7.5.4) + kpathsea version 3.5.4 + The order is: + ['.eps', '.ps'] for latex + ['.png', '.pdf', '.jpg', '.tif']. + + Another difference is that the search path is determined by the type + of the file being searched: + env['TEXINPUTS'] for "input" and "include" keywords + env['TEXINPUTS'] for "includegraphics" keyword + env['TEXINPUTS'] for "lstinputlisting" keyword + env['BIBINPUTS'] for "bibliography" keyword + env['BSTINPUTS'] for "bibliographystyle" keyword + env['INDEXSTYLE'] for "makeindex" keyword, no scanning support needed + just allows user to set it if needed. + + FIXME: also look for the class or style in document[class|style]{} + FIXME: also look for the argument of bibliographystyle{} + """ + keyword_paths = {'include': 'TEXINPUTS', + 'input': 'TEXINPUTS', + 'includegraphics': 'TEXINPUTS', + 'bibliography': 'BIBINPUTS', + 'bibliographystyle': 'BSTINPUTS', + 'makeindex': 'INDEXSTYLE', + 'usepackage': 'TEXINPUTS', + 'lstinputlisting': 'TEXINPUTS'} + env_variables = SCons.Util.unique(list(keyword_paths.values())) + + def __init__(self, name, suffixes, graphics_extensions, *args, **kw): + + # We have to include \n with the % we exclude from the first part + # part of the regex because the expression is compiled with re.M. + # Without the \n, the ^ could match the beginning of a *previous* + # line followed by one or more newline characters (i.e. blank + # lines), interfering with a match on the next line. + # add option for whitespace before the '[options]' or the '{filename}' + regex = r'^[^%\n]*\\(include|includegraphics(?:\s*\[[^\]]+\])?|lstinputlisting(?:\[[^\]]+\])?|input|bibliography|usepackage)\s*{([^}]*)}' + self.cre = re.compile(regex, re.M) + self.comment_re = re.compile(r'^((?:(?:\\%)|[^%\n])*)(.*)$', re.M) + + self.graphics_extensions = graphics_extensions + + def _scan(node, env, path=(), self=self): + node = node.rfile() + if not node.exists(): + return [] + return self.scan_recurse(node, path) + + class FindMultiPathDirs(object): + """The stock FindPathDirs function has the wrong granularity: + it is called once per target, while we need the path that depends + on what kind of included files is being searched. This wrapper + hides multiple instances of FindPathDirs, one per the LaTeX path + variable in the environment. When invoked, the function calculates + and returns all the required paths as a dictionary (converted into + a tuple to become hashable). Then the scan function converts it + back and uses a dictionary of tuples rather than a single tuple + of paths. + """ + def __init__(self, dictionary): + self.dictionary = {} + for k,n in dictionary.items(): + self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n), + FindENVPathDirs(n) ) + + def __call__(self, env, dir=None, target=None, source=None, + argument=None): + di = {} + for k,(c,cENV) in self.dictionary.items(): + di[k] = ( c(env, dir=None, target=None, source=None, + argument=None) , + cENV(env, dir=None, target=None, source=None, + argument=None) ) + # To prevent "dict is not hashable error" + return tuple(di.items()) + + class LaTeXScanCheck(object): + """Skip all but LaTeX source files, i.e., do not scan *.eps, + *.pdf, *.jpg, etc. + """ + def __init__(self, suffixes): + self.suffixes = suffixes + def __call__(self, node, env): + current = not node.has_builder() or node.is_up_to_date() + scannable = node.get_suffix() in env.subst_list(self.suffixes)[0] + # Returning false means that the file is not scanned. + return scannable and current + + kw['function'] = _scan + kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) + kw['recursive'] = 0 + kw['skeys'] = suffixes + kw['scan_check'] = LaTeXScanCheck(suffixes) + kw['name'] = name + + SCons.Scanner.Base.__init__(self, *args, **kw) + + def _latex_names(self, include): + filename = include[1] + if include[0] == 'input': + base, ext = os.path.splitext( filename ) + if ext == "": + return [filename + '.tex'] + if (include[0] == 'include'): + return [filename + '.tex'] + if include[0] == 'bibliography': + base, ext = os.path.splitext( filename ) + if ext == "": + return [filename + '.bib'] + if include[0] == 'usepackage': + base, ext = os.path.splitext( filename ) + if ext == "": + return [filename + '.sty'] + if include[0] == 'includegraphics': + base, ext = os.path.splitext( filename ) + if ext == "": + #return [filename+e for e in self.graphics_extensions + TexGraphics] + # use the line above to find dependencies for the PDF builder + # when only an .eps figure is present. Since it will be found + # if the user tells scons how to make the pdf figure, leave + # it out for now. + return [filename+e for e in self.graphics_extensions] + return [filename] + + def sort_key(self, include): + return SCons.Node.FS._my_normcase(str(include)) + + def find_include(self, include, source_dir, path): + try: + sub_path = path[include[0]] + except (IndexError, KeyError): + sub_path = () + try_names = self._latex_names(include) + for n in try_names: + # see if we find it using the path in env[var] + i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[0]) + if i: + return i, include + # see if we find it using the path in env['ENV'][var] + i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[1]) + if i: + return i, include + return i, include + + def canonical_text(self, text): + """Standardize an input TeX-file contents. + + Currently: + * removes comments, unwrapping comment-wrapped lines. + """ + out = [] + line_continues_a_comment = False + for line in text.splitlines(): + line,comment = self.comment_re.findall(line)[0] + if line_continues_a_comment == True: + out[-1] = out[-1] + line.lstrip() + else: + out.append(line) + line_continues_a_comment = len(comment) > 0 + return '\n'.join(out).rstrip()+'\n' + + def scan(self, node): + # Modify the default scan function to allow for the regular + # expression to return a comma separated list of file names + # as can be the case with the bibliography keyword. + + # Cache the includes list in node so we only scan it once: + # path_dict = dict(list(path)) + # add option for whitespace (\s) before the '[' + noopt_cre = re.compile('\s*\[.*$') + if node.includes != None: + includes = node.includes + else: + text = self.canonical_text(node.get_text_contents()) + includes = self.cre.findall(text) + # 1. Split comma-separated lines, e.g. + # ('bibliography', 'phys,comp') + # should become two entries + # ('bibliography', 'phys') + # ('bibliography', 'comp') + # 2. Remove the options, e.g., such as + # ('includegraphics[clip,width=0.7\\linewidth]', 'picture.eps') + # should become + # ('includegraphics', 'picture.eps') + split_includes = [] + for include in includes: + inc_type = noopt_cre.sub('', include[0]) + inc_list = include[1].split(',') + for j in range(len(inc_list)): + split_includes.append( (inc_type, inc_list[j]) ) + # + includes = split_includes + node.includes = includes + + return includes + + def scan_recurse(self, node, path=()): + """ do a recursive scan of the top level target file + This lets us search for included files based on the + directory of the main file just as latex does""" + + path_dict = dict(list(path)) + + queue = [] + queue.extend( self.scan(node) ) + seen = {} + + # This is a hand-coded DSU (decorate-sort-undecorate, or + # Schwartzian transform) pattern. The sort key is the raw name + # of the file as specifed on the \include, \input, etc. line. + # TODO: what about the comment in the original Classic scanner: + # """which lets + # us keep the sort order constant regardless of whether the file + # is actually found in a Repository or locally.""" + nodes = [] + source_dir = node.get_dir() + #for include in includes: + while queue: + + include = queue.pop() + try: + if seen[include[1]] == 1: + continue + except KeyError: + seen[include[1]] = 1 + + # + # Handle multiple filenames in include[1] + # + n, i = self.find_include(include, source_dir, path_dict) + if n is None: + # Do not bother with 'usepackage' warnings, as they most + # likely refer to system-level files + if include[0] != 'usepackage': + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) + else: + sortkey = self.sort_key(n) + nodes.append((sortkey, n)) + # recurse down + queue.extend( self.scan(n) ) + + return [pair[1] for pair in sorted(nodes)] + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/Prog.py b/engine/SCons/Scanner/Prog.py new file mode 100644 index 0000000..0df80f8 --- /dev/null +++ b/engine/SCons/Scanner/Prog.py @@ -0,0 +1,101 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Scanner/Prog.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Node +import SCons.Node.FS +import SCons.Scanner +import SCons.Util + +# global, set by --debug=findlibs +print_find_libs = None + +def ProgramScanner(**kw): + """Return a prototype Scanner instance for scanning executable + files for static-lib dependencies""" + kw['path_function'] = SCons.Scanner.FindPathDirs('LIBPATH') + ps = SCons.Scanner.Base(scan, "ProgramScanner", **kw) + return ps + +def scan(node, env, libpath = ()): + """ + This scanner scans program files for static-library + dependencies. It will search the LIBPATH environment variable + for libraries specified in the LIBS variable, returning any + files it finds as dependencies. + """ + try: + libs = env['LIBS'] + except KeyError: + # There are no LIBS in this environment, so just return a null list: + return [] + if SCons.Util.is_String(libs): + libs = libs.split() + else: + libs = SCons.Util.flatten(libs) + + try: + prefix = env['LIBPREFIXES'] + if not SCons.Util.is_List(prefix): + prefix = [ prefix ] + except KeyError: + prefix = [ '' ] + + try: + suffix = env['LIBSUFFIXES'] + if not SCons.Util.is_List(suffix): + suffix = [ suffix ] + except KeyError: + suffix = [ '' ] + + pairs = [] + for suf in map(env.subst, suffix): + for pref in map(env.subst, prefix): + pairs.append((pref, suf)) + + result = [] + + if callable(libpath): + libpath = libpath() + + find_file = SCons.Node.FS.find_file + adjustixes = SCons.Util.adjustixes + for lib in libs: + if SCons.Util.is_String(lib): + lib = env.subst(lib) + for pref, suf in pairs: + l = adjustixes(lib, pref, suf) + l = find_file(l, libpath, verbose=print_find_libs) + if l: + result.append(l) + else: + result.append(lib) + + return result + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/RC.py b/engine/SCons/Scanner/RC.py new file mode 100644 index 0000000..a650581 --- /dev/null +++ b/engine/SCons/Scanner/RC.py @@ -0,0 +1,55 @@ +"""SCons.Scanner.RC + +This module implements the depenency scanner for RC (Interface +Definition Language) files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Scanner/RC.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Node.FS +import SCons.Scanner +import re + +def RCScan(): + """Return a prototype Scanner instance for scanning RC source files""" + + res_re= r'^(?:\s*#\s*(?:include)|' \ + '.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \ + '\s*.*?)' \ + '\s*(<|"| )([^>"\s]+)(?:[>"\s])*$' + resScanner = SCons.Scanner.ClassicCPP( "ResourceScanner", + "$RCSUFFIXES", + "CPPPATH", + res_re ) + + return resScanner + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Scanner/__init__.py b/engine/SCons/Scanner/__init__.py new file mode 100644 index 0000000..d2d7a62 --- /dev/null +++ b/engine/SCons/Scanner/__init__.py @@ -0,0 +1,413 @@ +"""SCons.Scanner + +The Scanner package for the SCons software construction utility. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Scanner/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +import re + +import SCons.Node.FS +import SCons.Util + + +class _Null(object): + pass + +# This is used instead of None as a default argument value so None can be +# used as an actual argument value. +_null = _Null + +def Scanner(function, *args, **kw): + """ + Public interface factory function for creating different types + of Scanners based on the different types of "functions" that may + be supplied. + + TODO: Deprecate this some day. We've moved the functionality + inside the Base class and really don't need this factory function + any more. It was, however, used by some of our Tool modules, so + the call probably ended up in various people's custom modules + patterned on SCons code. + """ + if SCons.Util.is_Dict(function): + return Selector(function, *args, **kw) + else: + return Base(function, *args, **kw) + + + +class FindPathDirs(object): + """A class to bind a specific *PATH variable name to a function that + will return all of the *path directories.""" + def __init__(self, variable): + self.variable = variable + def __call__(self, env, dir=None, target=None, source=None, argument=None): + import SCons.PathList + try: + path = env[self.variable] + except KeyError: + return () + + dir = dir or env.fs._cwd + path = SCons.PathList.PathList(path).subst_path(env, target, source) + return tuple(dir.Rfindalldirs(path)) + + + +class Base(object): + """ + The base class for dependency scanners. This implements + straightforward, single-pass scanning of a single file. + """ + + def __init__(self, + function, + name = "NONE", + argument = _null, + skeys = _null, + path_function = None, + # Node.FS.Base so that, by default, it's okay for a + # scanner to return a Dir, File or Entry. + node_class = SCons.Node.FS.Base, + node_factory = None, + scan_check = None, + recursive = None): + """ + Construct a new scanner object given a scanner function. + + 'function' - a scanner function taking two or three + arguments and returning a list of strings. + + 'name' - a name for identifying this scanner object. + + 'argument' - an optional argument that, if specified, will be + passed to both the scanner function and the path_function. + + 'skeys' - an optional list argument that can be used to determine + which scanner should be used for a given Node. In the case of File + nodes, for example, the 'skeys' would be file suffixes. + + 'path_function' - a function that takes four or five arguments + (a construction environment, Node for the directory containing + the SConscript file that defined the primary target, list of + target nodes, list of source nodes, and optional argument for + this instance) and returns a tuple of the directories that can + be searched for implicit dependency files. May also return a + callable() which is called with no args and returns the tuple + (supporting Bindable class). + + 'node_class' - the class of Nodes which this scan will return. + If node_class is None, then this scanner will not enforce any + Node conversion and will return the raw results from the + underlying scanner function. + + 'node_factory' - the factory function to be called to translate + the raw results returned by the scanner function into the + expected node_class objects. + + 'scan_check' - a function to be called to first check whether + this node really needs to be scanned. + + 'recursive' - specifies that this scanner should be invoked + recursively on all of the implicit dependencies it returns + (the canonical example being #include lines in C source files). + May be a callable, which will be called to filter the list + of nodes found to select a subset for recursive scanning + (the canonical example being only recursively scanning + subdirectories within a directory). + + The scanner function's first argument will be a Node that should + be scanned for dependencies, the second argument will be an + Environment object, the third argument will be the tuple of paths + returned by the path_function, and the fourth argument will be + the value passed into 'argument', and the returned list should + contain the Nodes for all the direct dependencies of the file. + + Examples: + + s = Scanner(my_scanner_function) + + s = Scanner(function = my_scanner_function) + + s = Scanner(function = my_scanner_function, argument = 'foo') + + """ + + # Note: this class could easily work with scanner functions that take + # something other than a filename as an argument (e.g. a database + # node) and a dependencies list that aren't file names. All that + # would need to be changed is the documentation. + + self.function = function + self.path_function = path_function + self.name = name + self.argument = argument + + if skeys is _null: + if SCons.Util.is_Dict(function): + skeys = list(function.keys()) + else: + skeys = [] + self.skeys = skeys + + self.node_class = node_class + self.node_factory = node_factory + self.scan_check = scan_check + if callable(recursive): + self.recurse_nodes = recursive + elif recursive: + self.recurse_nodes = self._recurse_all_nodes + else: + self.recurse_nodes = self._recurse_no_nodes + + def path(self, env, dir=None, target=None, source=None): + if not self.path_function: + return () + if not self.argument is _null: + return self.path_function(env, dir, target, source, self.argument) + else: + return self.path_function(env, dir, target, source) + + def __call__(self, node, env, path = ()): + """ + This method scans a single object. 'node' is the node + that will be passed to the scanner function, and 'env' is the + environment that will be passed to the scanner function. A list of + direct dependency nodes for the specified node will be returned. + """ + if self.scan_check and not self.scan_check(node, env): + return [] + + self = self.select(node) + + if not self.argument is _null: + list = self.function(node, env, path, self.argument) + else: + list = self.function(node, env, path) + + kw = {} + if hasattr(node, 'dir'): + kw['directory'] = node.dir + node_factory = env.get_factory(self.node_factory) + nodes = [] + for l in list: + if self.node_class and not isinstance(l, self.node_class): + l = node_factory(l, **kw) + nodes.append(l) + return nodes + + def __cmp__(self, other): + try: + return cmp(self.__dict__, other.__dict__) + except AttributeError: + # other probably doesn't have a __dict__ + return cmp(self.__dict__, other) + + def __hash__(self): + return id(self) + + def __str__(self): + return self.name + + def add_skey(self, skey): + """Add a skey to the list of skeys""" + self.skeys.append(skey) + + def get_skeys(self, env=None): + if env and SCons.Util.is_String(self.skeys): + return env.subst_list(self.skeys)[0] + return self.skeys + + def select(self, node): + if SCons.Util.is_Dict(self.function): + key = node.scanner_key() + try: + return self.function[key] + except KeyError: + return None + else: + return self + + def _recurse_all_nodes(self, nodes): + return nodes + + def _recurse_no_nodes(self, nodes): + return [] + + recurse_nodes = _recurse_no_nodes + + def add_scanner(self, skey, scanner): + self.function[skey] = scanner + self.add_skey(skey) + + +class Selector(Base): + """ + A class for selecting a more specific scanner based on the + scanner_key() (suffix) for a specific Node. + + TODO: This functionality has been moved into the inner workings of + the Base class, and this class will be deprecated at some point. + (It was never exposed directly as part of the public interface, + although it is used by the Scanner() factory function that was + used by various Tool modules and therefore was likely a template + for custom modules that may be out there.) + """ + def __init__(self, dict, *args, **kw): + Base.__init__(self, None, *args, **kw) + self.dict = dict + self.skeys = list(dict.keys()) + + def __call__(self, node, env, path = ()): + return self.select(node)(node, env, path) + + def select(self, node): + try: + return self.dict[node.scanner_key()] + except KeyError: + return None + + def add_scanner(self, skey, scanner): + self.dict[skey] = scanner + self.add_skey(skey) + + +class Current(Base): + """ + A class for scanning files that are source files (have no builder) + or are derived files and are current (which implies that they exist, + either locally or in a repository). + """ + + def __init__(self, *args, **kw): + def current_check(node, env): + return not node.has_builder() or node.is_up_to_date() + kw['scan_check'] = current_check + Base.__init__(self, *args, **kw) + +class Classic(Current): + """ + A Scanner subclass to contain the common logic for classic CPP-style + include scanning, but which can be customized to use different + regular expressions to find the includes. + + Note that in order for this to work "out of the box" (without + overriding the find_include() and sort_key() methods), the regular + expression passed to the constructor must return the name of the + include file in group 0. + """ + + def __init__(self, name, suffixes, path_variable, regex, *args, **kw): + + self.cre = re.compile(regex, re.M) + + def _scan(node, env, path=(), self=self): + node = node.rfile() + if not node.exists(): + return [] + return self.scan(node, path) + + kw['function'] = _scan + kw['path_function'] = FindPathDirs(path_variable) + kw['recursive'] = 1 + kw['skeys'] = suffixes + kw['name'] = name + + Current.__init__(self, *args, **kw) + + def find_include(self, include, source_dir, path): + n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path)) + return n, include + + def sort_key(self, include): + return SCons.Node.FS._my_normcase(include) + + def find_include_names(self, node): + return self.cre.findall(node.get_text_contents()) + + def scan(self, node, path=()): + + # cache the includes list in node so we only scan it once: + if node.includes is not None: + includes = node.includes + else: + includes = self.find_include_names (node) + # Intern the names of the include files. Saves some memory + # if the same header is included many times. + node.includes = list(map(SCons.Util.silent_intern, includes)) + + # This is a hand-coded DSU (decorate-sort-undecorate, or + # Schwartzian transform) pattern. The sort key is the raw name + # of the file as specifed on the #include line (including the + # " or <, since that may affect what file is found), which lets + # us keep the sort order constant regardless of whether the file + # is actually found in a Repository or locally. + nodes = [] + source_dir = node.get_dir() + if callable(path): + path = path() + for include in includes: + n, i = self.find_include(include, source_dir, path) + + if n is None: + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) + else: + nodes.append((self.sort_key(include), n)) + + return [pair[1] for pair in sorted(nodes)] + +class ClassicCPP(Classic): + """ + A Classic Scanner subclass which takes into account the type of + bracketing used to include the file, and uses classic CPP rules + for searching for the files based on the bracketing. + + Note that in order for this to work, the regular expression passed + to the constructor must return the leading bracket in group 0, and + the contained filename in group 1. + """ + def find_include(self, include, source_dir, path): + if include[0] == '"': + paths = (source_dir,) + tuple(path) + else: + paths = tuple(path) + (source_dir,) + + n = SCons.Node.FS.find_file(include[1], paths) + + i = SCons.Util.silent_intern(include[1]) + return n, i + + def sort_key(self, include): + return SCons.Node.FS._my_normcase(' '.join(include)) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Script/Interactive.py b/engine/SCons/Script/Interactive.py new file mode 100644 index 0000000..f2151fa --- /dev/null +++ b/engine/SCons/Script/Interactive.py @@ -0,0 +1,384 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Script/Interactive.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """ +SCons interactive mode +""" + +# TODO: +# +# This has the potential to grow into something with a really big life +# of its own, which might or might not be a good thing. Nevertheless, +# here are some enhancements that will probably be requested some day +# and are worth keeping in mind (assuming this takes off): +# +# - A command to re-read / re-load the SConscript files. This may +# involve allowing people to specify command-line options (e.g. -f, +# -I, --no-site-dir) that affect how the SConscript files are read. +# +# - Additional command-line options on the "build" command. +# +# Of the supported options that seemed to make sense (after a quick +# pass through the list), the ones that seemed likely enough to be +# used are listed in the man page and have explicit test scripts. +# +# These had code changed in Script/Main.py to support them, but didn't +# seem likely to be used regularly, so had no test scripts added: +# +# build --diskcheck=* +# build --implicit-cache=* +# build --implicit-deps-changed=* +# build --implicit-deps-unchanged=* +# +# These look like they should "just work" with no changes to the +# existing code, but like those above, look unlikely to be used and +# therefore had no test scripts added: +# +# build --random +# +# These I'm not sure about. They might be useful for individual +# "build" commands, and may even work, but they seem unlikely enough +# that we'll wait until they're requested before spending any time on +# writing test scripts for them, or investigating whether they work. +# +# build -q [??? is there a useful analog to the exit status?] +# build --duplicate= +# build --profile= +# build --max-drift= +# build --warn=* +# build --Y +# +# - Most of the SCons command-line options that the "build" command +# supports should be settable as default options that apply to all +# subsequent "build" commands. Maybe a "set {option}" command that +# maps to "SetOption('{option}')". +# +# - Need something in the 'help' command that prints the -h output. +# +# - A command to run the configure subsystem separately (must see how +# this interacts with the new automake model). +# +# - Command-line completion of target names; maybe even of SCons options? +# Completion is something that's supported by the Python cmd module, +# so this should be doable without too much trouble. +# + +import cmd +import copy +import os +import re +import shlex +import sys + +try: + import readline +except ImportError: + pass + +class SConsInteractiveCmd(cmd.Cmd): + """\ + build [TARGETS] Build the specified TARGETS and their dependencies. + 'b' is a synonym. + clean [TARGETS] Clean (remove) the specified TARGETS and their + dependencies. 'c' is a synonym. + exit Exit SCons interactive mode. + help [COMMAND] Prints help for the specified COMMAND. 'h' and + '?' are synonyms. + shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' + are synonyms. + version Prints SCons version information. + """ + + synonyms = { + 'b' : 'build', + 'c' : 'clean', + 'h' : 'help', + 'scons' : 'build', + 'sh' : 'shell', + } + + def __init__(self, **kw): + cmd.Cmd.__init__(self) + for key, val in kw.items(): + setattr(self, key, val) + + if sys.platform == 'win32': + self.shell_variable = 'COMSPEC' + else: + self.shell_variable = 'SHELL' + + def default(self, argv): + print "*** Unknown command: %s" % argv[0] + + def onecmd(self, line): + line = line.strip() + if not line: + print self.lastcmd + return self.emptyline() + self.lastcmd = line + if line[0] == '!': + line = 'shell ' + line[1:] + elif line[0] == '?': + line = 'help ' + line[1:] + if os.sep == '\\': + line = line.replace('\\', '\\\\') + argv = shlex.split(line) + argv[0] = self.synonyms.get(argv[0], argv[0]) + if not argv[0]: + return self.default(line) + else: + try: + func = getattr(self, 'do_' + argv[0]) + except AttributeError: + return self.default(argv) + return func(argv) + + def do_build(self, argv): + """\ + build [TARGETS] Build the specified TARGETS and their + dependencies. 'b' is a synonym. + """ + import SCons.Node + import SCons.SConsign + import SCons.Script.Main + + options = copy.deepcopy(self.options) + + options, targets = self.parser.parse_args(argv[1:], values=options) + + SCons.Script.COMMAND_LINE_TARGETS = targets + + if targets: + SCons.Script.BUILD_TARGETS = targets + else: + # If the user didn't specify any targets on the command line, + # use the list of default targets. + SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default + + nodes = SCons.Script.Main._build_targets(self.fs, + options, + targets, + self.target_top) + + if not nodes: + return + + # Call each of the Node's alter_targets() methods, which may + # provide additional targets that ended up as part of the build + # (the canonical example being a VariantDir() when we're building + # from a source directory) and which we therefore need their + # state cleared, too. + x = [] + for n in nodes: + x.extend(n.alter_targets()[0]) + nodes.extend(x) + + # Clean up so that we can perform the next build correctly. + # + # We do this by walking over all the children of the targets, + # and clearing their state. + # + # We currently have to re-scan each node to find their + # children, because built nodes have already been partially + # cleared and don't remember their children. (In scons + # 0.96.1 and earlier, this wasn't the case, and we didn't + # have to re-scan the nodes.) + # + # Because we have to re-scan each node, we can't clear the + # nodes as we walk over them, because we may end up rescanning + # a cleared node as we scan a later node. Therefore, only + # store the list of nodes that need to be cleared as we walk + # the tree, and clear them in a separate pass. + # + # XXX: Someone more familiar with the inner workings of scons + # may be able to point out a more efficient way to do this. + + SCons.Script.Main.progress_display("scons: Clearing cached node information ...") + + seen_nodes = {} + + def get_unseen_children(node, parent, seen_nodes=seen_nodes): + def is_unseen(node, seen_nodes=seen_nodes): + return node not in seen_nodes + return list(filter(is_unseen, node.children(scan=1))) + + def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes): + seen_nodes[node] = 1 + + # If this file is in a VariantDir and has a + # corresponding source file in the source tree, remember the + # node in the source tree, too. This is needed in + # particular to clear cached implicit dependencies on the + # source file, since the scanner will scan it if the + # VariantDir was created with duplicate=0. + try: + rfile_method = node.rfile + except AttributeError: + return + else: + rfile = rfile_method() + if rfile != node: + seen_nodes[rfile] = 1 + + for node in nodes: + walker = SCons.Node.Walker(node, + kids_func=get_unseen_children, + eval_func=add_to_seen_nodes) + n = walker.get_next() + while n: + n = walker.get_next() + + for node in seen_nodes.keys(): + # Call node.clear() to clear most of the state + node.clear() + # node.clear() doesn't reset node.state, so call + # node.set_state() to reset it manually + node.set_state(SCons.Node.no_state) + node.implicit = None + + # Debug: Uncomment to verify that all Taskmaster reference + # counts have been reset to zero. + #if node.ref_count != 0: + # from SCons.Debug import Trace + # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count)) + + SCons.SConsign.Reset() + SCons.Script.Main.progress_display("scons: done clearing node information.") + + def do_clean(self, argv): + """\ + clean [TARGETS] Clean (remove) the specified TARGETS + and their dependencies. 'c' is a synonym. + """ + return self.do_build(['build', '--clean'] + argv[1:]) + + def do_EOF(self, argv): + print + self.do_exit(argv) + + def _do_one_help(self, arg): + try: + # If help_() exists, then call it. + func = getattr(self, 'help_' + arg) + except AttributeError: + try: + func = getattr(self, 'do_' + arg) + except AttributeError: + doc = None + else: + doc = self._doc_to_help(func) + if doc: + sys.stdout.write(doc + '\n') + sys.stdout.flush() + else: + doc = self.strip_initial_spaces(func()) + if doc: + sys.stdout.write(doc + '\n') + sys.stdout.flush() + + def _doc_to_help(self, obj): + doc = obj.__doc__ + if doc is None: + return '' + return self._strip_initial_spaces(doc) + + def _strip_initial_spaces(self, s): + #lines = s.split('\n') + lines = s.split('\n') + spaces = re.match(' *', lines[0]).group(0) + #def strip_spaces(l): + # if l.startswith(spaces): + # l = l[len(spaces):] + # return l + #return '\n'.join([ strip_spaces(l) for l in lines ]) + def strip_spaces(l, spaces=spaces): + if l[:len(spaces)] == spaces: + l = l[len(spaces):] + return l + lines = list(map(strip_spaces, lines)) + return '\n'.join(lines) + + def do_exit(self, argv): + """\ + exit Exit SCons interactive mode. + """ + sys.exit(0) + + def do_help(self, argv): + """\ + help [COMMAND] Prints help for the specified COMMAND. 'h' + and '?' are synonyms. + """ + if argv[1:]: + for arg in argv[1:]: + if self._do_one_help(arg): + break + else: + # If bare 'help' is called, print this class's doc + # string (if it has one). + doc = self._doc_to_help(self.__class__) + if doc: + sys.stdout.write(doc + '\n') + sys.stdout.flush() + + def do_shell(self, argv): + """\ + shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and + '!' are synonyms. + """ + import subprocess + argv = argv[1:] + if not argv: + argv = os.environ[self.shell_variable] + try: + # Per "[Python-Dev] subprocess insufficiently platform-independent?" + # http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+ + # Doing the right thing with an argument list currently + # requires different shell= values on Windows and Linux. + p = subprocess.Popen(argv, shell=(sys.platform=='win32')) + except EnvironmentError, e: + sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror)) + else: + p.wait() + + def do_version(self, argv): + """\ + version Prints SCons version information. + """ + sys.stdout.write(self.parser.version + '\n') + +def interact(fs, parser, options, targets, target_top): + c = SConsInteractiveCmd(prompt = 'scons>>> ', + fs = fs, + parser = parser, + options = options, + targets = targets, + target_top = target_top) + c.cmdloop() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Script/Main.py b/engine/SCons/Script/Main.py new file mode 100644 index 0000000..2f00e5c --- /dev/null +++ b/engine/SCons/Script/Main.py @@ -0,0 +1,1405 @@ +"""SCons.Script + +This file implements the main() function used by the scons script. + +Architecturally, this *is* the scons script, and will likely only be +called from the external "scons" wrapper. Consequently, anything here +should not be, or be considered, part of the build engine. If it's +something that we expect other software to want to use, it should go in +some other module. If it's specific to the "scons" script invocation, +it goes here. +""" + +unsupported_python_version = (2, 3, 0) +deprecated_python_version = (2, 4, 0) + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Script/Main.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.compat + +import os +import sys +import time +import traceback + +# Strip the script directory from sys.path() so on case-insensitive +# (Windows) systems Python doesn't think that the "scons" script is the +# "SCons" package. Replace it with our own version directory so, if +# if they're there, we pick up the right version of the build engine +# modules. +#sys.path = [os.path.join(sys.prefix, +# 'lib', +# 'scons-%d' % SCons.__version__)] + sys.path[1:] + +import SCons.CacheDir +import SCons.Debug +import SCons.Defaults +import SCons.Environment +import SCons.Errors +import SCons.Job +import SCons.Node +import SCons.Node.FS +import SCons.Platform +import SCons.SConf +import SCons.Script +import SCons.Taskmaster +import SCons.Util +import SCons.Warnings + +import SCons.Script.Interactive + +def fetch_win32_parallel_msg(): + # A subsidiary function that exists solely to isolate this import + # so we don't have to pull it in on all platforms, and so that an + # in-line "import" statement in the _main() function below doesn't + # cause warnings about local names shadowing use of the 'SCons' + # globl in nest scopes and UnboundLocalErrors and the like in some + # versions (2.1) of Python. + import SCons.Platform.win32 + return SCons.Platform.win32.parallel_msg + +# + +class SConsPrintHelpException(Exception): + pass + +display = SCons.Util.display +progress_display = SCons.Util.DisplayEngine() + +first_command_start = None +last_command_end = None + +class Progressor(object): + prev = '' + count = 0 + target_string = '$TARGET' + + def __init__(self, obj, interval=1, file=None, overwrite=False): + if file is None: + file = sys.stdout + + self.obj = obj + self.file = file + self.interval = interval + self.overwrite = overwrite + + if callable(obj): + self.func = obj + elif SCons.Util.is_List(obj): + self.func = self.spinner + elif obj.find(self.target_string) != -1: + self.func = self.replace_string + else: + self.func = self.string + + def write(self, s): + self.file.write(s) + self.file.flush() + self.prev = s + + def erase_previous(self): + if self.prev: + length = len(self.prev) + if self.prev[-1] in ('\n', '\r'): + length = length - 1 + self.write(' ' * length + '\r') + self.prev = '' + + def spinner(self, node): + self.write(self.obj[self.count % len(self.obj)]) + + def string(self, node): + self.write(self.obj) + + def replace_string(self, node): + self.write(self.obj.replace(self.target_string, str(node))) + + def __call__(self, node): + self.count = self.count + 1 + if (self.count % self.interval) == 0: + if self.overwrite: + self.erase_previous() + self.func(node) + +ProgressObject = SCons.Util.Null() + +def Progress(*args, **kw): + global ProgressObject + ProgressObject = Progressor(*args, **kw) + +# Task control. +# + +_BuildFailures = [] + +def GetBuildFailures(): + return _BuildFailures + +class BuildTask(SCons.Taskmaster.OutOfDateTask): + """An SCons build task.""" + progress = ProgressObject + + def display(self, message): + display('scons: ' + message) + + def prepare(self): + self.progress(self.targets[0]) + return SCons.Taskmaster.OutOfDateTask.prepare(self) + + def needs_execute(self): + if SCons.Taskmaster.OutOfDateTask.needs_execute(self): + return True + if self.top and self.targets[0].has_builder(): + display("scons: `%s' is up to date." % str(self.node)) + return False + + def execute(self): + if print_time: + start_time = time.time() + global first_command_start + if first_command_start is None: + first_command_start = start_time + SCons.Taskmaster.OutOfDateTask.execute(self) + if print_time: + global cumulative_command_time + global last_command_end + finish_time = time.time() + last_command_end = finish_time + cumulative_command_time = cumulative_command_time+finish_time-start_time + sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time)) + + def do_failed(self, status=2): + _BuildFailures.append(self.exception[1]) + global exit_status + global this_build_status + if self.options.ignore_errors: + SCons.Taskmaster.OutOfDateTask.executed(self) + elif self.options.keep_going: + SCons.Taskmaster.OutOfDateTask.fail_continue(self) + exit_status = status + this_build_status = status + else: + SCons.Taskmaster.OutOfDateTask.fail_stop(self) + exit_status = status + this_build_status = status + + def executed(self): + t = self.targets[0] + if self.top and not t.has_builder() and not t.side_effect: + if not t.exists(): + if t.__class__.__name__ in ('File', 'Dir', 'Entry'): + errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.abspath) + else: # Alias or Python or ... + errstr="Do not know how to make %s target `%s'." % (t.__class__.__name__, t) + sys.stderr.write("scons: *** " + errstr) + if not self.options.keep_going: + sys.stderr.write(" Stop.") + sys.stderr.write("\n") + try: + raise SCons.Errors.BuildError(t, errstr) + except KeyboardInterrupt: + raise + except: + self.exception_set() + self.do_failed() + else: + print "scons: Nothing to be done for `%s'." % t + SCons.Taskmaster.OutOfDateTask.executed(self) + else: + SCons.Taskmaster.OutOfDateTask.executed(self) + + def failed(self): + # Handle the failure of a build task. The primary purpose here + # is to display the various types of Errors and Exceptions + # appropriately. + exc_info = self.exc_info() + try: + t, e, tb = exc_info + except ValueError: + t, e = exc_info + tb = None + + if t is None: + # The Taskmaster didn't record an exception for this Task; + # see if the sys module has one. + try: + t, e, tb = sys.exc_info()[:] + except ValueError: + t, e = exc_info + tb = None + + # Deprecated string exceptions will have their string stored + # in the first entry of the tuple. + if e is None: + e = t + + buildError = SCons.Errors.convert_to_BuildError(e) + if not buildError.node: + buildError.node = self.node + + node = buildError.node + if not SCons.Util.is_List(node): + node = [ node ] + nodename = ', '.join(map(str, node)) + + errfmt = "scons: *** [%s] %s\n" + sys.stderr.write(errfmt % (nodename, buildError)) + + if (buildError.exc_info[2] and buildError.exc_info[1] and + not isinstance( + buildError.exc_info[1], + (EnvironmentError, SCons.Errors.StopError, + SCons.Errors.UserError))): + type, value, trace = buildError.exc_info + traceback.print_exception(type, value, trace) + elif tb and print_stacktrace: + sys.stderr.write("scons: internal stack trace:\n") + traceback.print_tb(tb, file=sys.stderr) + + self.exception = (e, buildError, tb) # type, value, traceback + self.do_failed(buildError.exitstatus) + + self.exc_clear() + + def postprocess(self): + if self.top: + t = self.targets[0] + for tp in self.options.tree_printers: + tp.display(t) + if self.options.debug_includes: + tree = t.render_include_tree() + if tree: + print + print tree + SCons.Taskmaster.OutOfDateTask.postprocess(self) + + def make_ready(self): + """Make a task ready for execution""" + SCons.Taskmaster.OutOfDateTask.make_ready(self) + if self.out_of_date and self.options.debug_explain: + explanation = self.out_of_date[0].explain() + if explanation: + sys.stdout.write("scons: " + explanation) + +class CleanTask(SCons.Taskmaster.AlwaysTask): + """An SCons clean task.""" + def fs_delete(self, path, pathstr, remove=1): + try: + if os.path.lexists(path): + if os.path.isfile(path) or os.path.islink(path): + if remove: os.unlink(path) + display("Removed " + pathstr) + elif os.path.isdir(path) and not os.path.islink(path): + # delete everything in the dir + for e in sorted(os.listdir(path)): + p = os.path.join(path, e) + s = os.path.join(pathstr, e) + if os.path.isfile(p): + if remove: os.unlink(p) + display("Removed " + s) + else: + self.fs_delete(p, s, remove) + # then delete dir itself + if remove: os.rmdir(path) + display("Removed directory " + pathstr) + else: + errstr = "Path '%s' exists but isn't a file or directory." + raise SCons.Errors.UserError(errstr % (pathstr)) + except SCons.Errors.UserError, e: + print e + except (IOError, OSError), e: + print "scons: Could not remove '%s':" % pathstr, e.strerror + + def show(self): + target = self.targets[0] + if (target.has_builder() or target.side_effect) and not target.noclean: + for t in self.targets: + if not t.isdir(): + display("Removed " + str(t)) + if target in SCons.Environment.CleanTargets: + files = SCons.Environment.CleanTargets[target] + for f in files: + self.fs_delete(f.abspath, str(f), 0) + + def remove(self): + target = self.targets[0] + if (target.has_builder() or target.side_effect) and not target.noclean: + for t in self.targets: + try: + removed = t.remove() + except OSError, e: + # An OSError may indicate something like a permissions + # issue, an IOError would indicate something like + # the file not existing. In either case, print a + # message and keep going to try to remove as many + # targets aa possible. + print "scons: Could not remove '%s':" % str(t), e.strerror + else: + if removed: + display("Removed " + str(t)) + if target in SCons.Environment.CleanTargets: + files = SCons.Environment.CleanTargets[target] + for f in files: + self.fs_delete(f.abspath, str(f)) + + execute = remove + + # We want the Taskmaster to update the Node states (and therefore + # handle reference counts, etc.), but we don't want to call + # back to the Node's post-build methods, which would do things + # we don't want, like store .sconsign information. + executed = SCons.Taskmaster.Task.executed_without_callbacks + + # Have the taskmaster arrange to "execute" all of the targets, because + # we'll figure out ourselves (in remove() or show() above) whether + # anything really needs to be done. + make_ready = SCons.Taskmaster.Task.make_ready_all + + def prepare(self): + pass + +class QuestionTask(SCons.Taskmaster.AlwaysTask): + """An SCons task for the -q (question) option.""" + def prepare(self): + pass + + def execute(self): + if self.targets[0].get_state() != SCons.Node.up_to_date or \ + (self.top and not self.targets[0].exists()): + global exit_status + global this_build_status + exit_status = 1 + this_build_status = 1 + self.tm.stop() + + def executed(self): + pass + + +class TreePrinter(object): + def __init__(self, derived=False, prune=False, status=False): + self.derived = derived + self.prune = prune + self.status = status + def get_all_children(self, node): + return node.all_children() + def get_derived_children(self, node): + children = node.all_children(None) + return [x for x in children if x.has_builder()] + def display(self, t): + if self.derived: + func = self.get_derived_children + else: + func = self.get_all_children + s = self.status and 2 or 0 + SCons.Util.print_tree(t, func, prune=self.prune, showtags=s) + + +def python_version_string(): + return sys.version.split()[0] + +def python_version_unsupported(version=sys.version_info): + return version < unsupported_python_version + +def python_version_deprecated(version=sys.version_info): + return version < deprecated_python_version + + +# Global variables + +print_objects = 0 +print_memoizer = 0 +print_stacktrace = 0 +print_time = 0 +sconscript_time = 0 +cumulative_command_time = 0 +exit_status = 0 # final exit status, assume success by default +this_build_status = 0 # "exit status" of an individual build +num_jobs = None +delayed_warnings = [] + +class FakeOptionParser(object): + """ + A do-nothing option parser, used for the initial OptionsParser variable. + + During normal SCons operation, the OptionsParser is created right + away by the main() function. Certain tests scripts however, can + introspect on different Tool modules, the initialization of which + can try to add a new, local option to an otherwise uninitialized + OptionsParser object. This allows that introspection to happen + without blowing up. + + """ + class FakeOptionValues(object): + def __getattr__(self, attr): + return None + values = FakeOptionValues() + def add_local_option(self, *args, **kw): + pass + +OptionsParser = FakeOptionParser() + +def AddOption(*args, **kw): + if 'default' not in kw: + kw['default'] = None + result = OptionsParser.add_local_option(*args, **kw) + return result + +def GetOption(name): + return getattr(OptionsParser.values, name) + +def SetOption(name, value): + return OptionsParser.values.set_option(name, value) + +# +class Stats(object): + def __init__(self): + self.stats = [] + self.labels = [] + self.append = self.do_nothing + self.print_stats = self.do_nothing + def enable(self, outfp): + self.outfp = outfp + self.append = self.do_append + self.print_stats = self.do_print + def do_nothing(self, *args, **kw): + pass + +class CountStats(Stats): + def do_append(self, label): + self.labels.append(label) + self.stats.append(SCons.Debug.fetchLoggedInstances()) + def do_print(self): + stats_table = {} + for s in self.stats: + for n in [t[0] for t in s]: + stats_table[n] = [0, 0, 0, 0] + i = 0 + for s in self.stats: + for n, c in s: + stats_table[n][i] = c + i = i + 1 + self.outfp.write("Object counts:\n") + pre = [" "] + post = [" %s\n"] + l = len(self.stats) + fmt1 = ''.join(pre + [' %7s']*l + post) + fmt2 = ''.join(pre + [' %7d']*l + post) + labels = self.labels[:l] + labels.append(("", "Class")) + self.outfp.write(fmt1 % tuple([x[0] for x in labels])) + self.outfp.write(fmt1 % tuple([x[1] for x in labels])) + for k in sorted(stats_table.keys()): + r = stats_table[k][:l] + [k] + self.outfp.write(fmt2 % tuple(r)) + +count_stats = CountStats() + +class MemStats(Stats): + def do_append(self, label): + self.labels.append(label) + self.stats.append(SCons.Debug.memory()) + def do_print(self): + fmt = 'Memory %-32s %12d\n' + for label, stats in zip(self.labels, self.stats): + self.outfp.write(fmt % (label, stats)) + +memory_stats = MemStats() + +# utility functions + +def _scons_syntax_error(e): + """Handle syntax errors. Print out a message and show where the error + occurred. + """ + etype, value, tb = sys.exc_info() + lines = traceback.format_exception_only(etype, value) + for line in lines: + sys.stderr.write(line+'\n') + sys.exit(2) + +def find_deepest_user_frame(tb): + """ + Find the deepest stack frame that is not part of SCons. + + Input is a "pre-processed" stack trace in the form + returned by traceback.extract_tb() or traceback.extract_stack() + """ + + tb.reverse() + + # find the deepest traceback frame that is not part + # of SCons: + for frame in tb: + filename = frame[0] + if filename.find(os.sep+'SCons'+os.sep) == -1: + return frame + return tb[0] + +def _scons_user_error(e): + """Handle user errors. Print out a message and a description of the + error, along with the line number and routine where it occured. + The file and line number will be the deepest stack frame that is + not part of SCons itself. + """ + global print_stacktrace + etype, value, tb = sys.exc_info() + if print_stacktrace: + traceback.print_exception(etype, value, tb) + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) + sys.stderr.write("\nscons: *** %s\n" % value) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + sys.exit(2) + +def _scons_user_warning(e): + """Handle user warnings. Print out a message and a description of + the warning, along with the line number and routine where it occured. + The file and line number will be the deepest stack frame that is + not part of SCons itself. + """ + etype, value, tb = sys.exc_info() + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) + sys.stderr.write("\nscons: warning: %s\n" % e) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + +def _scons_internal_warning(e): + """Slightly different from _scons_user_warning in that we use the + *current call stack* rather than sys.exc_info() to get our stack trace. + This is used by the warnings framework to print warnings.""" + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) + sys.stderr.write("\nscons: warning: %s\n" % e.args[0]) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + +def _scons_internal_error(): + """Handle all errors but user errors. Print out a message telling + the user what to do in this case and print a normal trace. + """ + print 'internal error' + traceback.print_exc() + sys.exit(2) + +def _SConstruct_exists(dirname='', repositories=[], filelist=None): + """This function checks that an SConstruct file exists in a directory. + If so, it returns the path of the file. By default, it checks the + current directory. + """ + if not filelist: + filelist = ['SConstruct', 'Sconstruct', 'sconstruct'] + for file in filelist: + sfile = os.path.join(dirname, file) + if os.path.isfile(sfile): + return sfile + if not os.path.isabs(sfile): + for rep in repositories: + if os.path.isfile(os.path.join(rep, sfile)): + return sfile + return None + +def _set_debug_values(options): + global print_memoizer, print_objects, print_stacktrace, print_time + + debug_values = options.debug + + if "count" in debug_values: + # All of the object counts are within "if __debug__:" blocks, + # which get stripped when running optimized (with python -O or + # from compiled *.pyo files). Provide a warning if __debug__ is + # stripped, so it doesn't just look like --debug=count is broken. + enable_count = False + if __debug__: enable_count = True + if enable_count: + count_stats.enable(sys.stdout) + else: + msg = "--debug=count is not supported when running SCons\n" + \ + "\twith the python -O option or optimized (.pyo) modules." + SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg) + if "dtree" in debug_values: + options.tree_printers.append(TreePrinter(derived=True)) + options.debug_explain = ("explain" in debug_values) + if "findlibs" in debug_values: + SCons.Scanner.Prog.print_find_libs = "findlibs" + options.debug_includes = ("includes" in debug_values) + print_memoizer = ("memoizer" in debug_values) + if "memory" in debug_values: + memory_stats.enable(sys.stdout) + print_objects = ("objects" in debug_values) + if "presub" in debug_values: + SCons.Action.print_actions_presub = 1 + if "stacktrace" in debug_values: + print_stacktrace = 1 + if "stree" in debug_values: + options.tree_printers.append(TreePrinter(status=True)) + if "time" in debug_values: + print_time = 1 + if "tree" in debug_values: + options.tree_printers.append(TreePrinter()) + if "prepare" in debug_values: + SCons.Taskmaster.print_prepare = 1 + if "duplicate" in debug_values: + SCons.Node.FS.print_duplicate = 1 + +def _create_path(plist): + path = '.' + for d in plist: + if os.path.isabs(d): + path = d + else: + path = path + '/' + d + return path + +def _load_site_scons_dir(topdir, site_dir_name=None): + """Load the site_scons dir under topdir. + Prepends site_scons to sys.path, imports site_scons/site_init.py, + and prepends site_scons/site_tools to default toolpath.""" + if site_dir_name: + err_if_not_found = True # user specified: err if missing + else: + site_dir_name = "site_scons" + err_if_not_found = False + + site_dir = os.path.join(topdir, site_dir_name) + if not os.path.exists(site_dir): + if err_if_not_found: + raise SCons.Errors.UserError("site dir %s not found."%site_dir) + return + + site_init_filename = "site_init.py" + site_init_modname = "site_init" + site_tools_dirname = "site_tools" + # prepend to sys.path + sys.path = [os.path.abspath(site_dir)] + sys.path + site_init_file = os.path.join(site_dir, site_init_filename) + site_tools_dir = os.path.join(site_dir, site_tools_dirname) + if os.path.exists(site_init_file): + import imp, re + # TODO(2.4): turn this into try:-except:-finally: + try: + try: + fp, pathname, description = imp.find_module(site_init_modname, + [site_dir]) + # Load the file into SCons.Script namespace. This is + # opaque and clever; m is the module object for the + # SCons.Script module, and the exec ... in call executes a + # file (or string containing code) in the context of the + # module's dictionary, so anything that code defines ends + # up adding to that module. This is really short, but all + # the error checking makes it longer. + try: + m = sys.modules['SCons.Script'] + except Exception, e: + fmt = 'cannot import site_init.py: missing SCons.Script module %s' + raise SCons.Errors.InternalError(fmt % repr(e)) + try: + sfx = description[0] + modname = os.path.basename(pathname)[:-len(sfx)] + site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} + re_special = re.compile("__[^_]+__") + for k in m.__dict__.keys(): + if not re_special.match(k): + site_m[k] = m.__dict__[k] + + # This is the magic. + exec fp in site_m + except KeyboardInterrupt: + raise + except Exception, e: + fmt = '*** Error loading site_init file %s:\n' + sys.stderr.write(fmt % repr(site_init_file)) + raise + else: + for k in site_m: + if not re_special.match(k): + m.__dict__[k] = site_m[k] + except KeyboardInterrupt: + raise + except ImportError, e: + fmt = '*** cannot import site init file %s:\n' + sys.stderr.write(fmt % repr(site_init_file)) + raise + finally: + if fp: + fp.close() + if os.path.exists(site_tools_dir): + # prepend to DefaultToolpath + SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir)) + +def _load_all_site_scons_dirs(topdir, verbose=None): + """Load all of the predefined site_scons dir. + Order is significant; we load them in order from most generic + (machine-wide) to most specific (topdir). + The verbose argument is only for testing. + """ + platform = SCons.Platform.platform_default() + + def homedir(d): + return os.path.expanduser('~/'+d) + + if platform == 'win32' or platform == 'cygwin': + # Note we use $ here instead of %...% because older + # pythons (prior to 2.6?) didn't expand %...% on Windows. + # This set of dirs should work on XP, Vista, 7 and later. + sysdirs=[ + os.path.expandvars('$ALLUSERSPROFILE\\Application Data\\scons'), + os.path.expandvars('$USERPROFILE\\Local Settings\\Application Data\\scons')] + appdatadir = os.path.expandvars('$APPDATA\\scons') + if appdatadir not in sysdirs: + sysdirs.append(appdatadir) + sysdirs.append(homedir('.scons')) + + elif platform == 'darwin': # MacOS X + sysdirs=['/Library/Application Support/SCons', + '/opt/local/share/scons', # (for MacPorts) + '/sw/share/scons', # (for Fink) + homedir('Library/Application Support/SCons'), + homedir('.scons')] + elif platform == 'sunos': # Solaris + sysdirs=['/opt/sfw/scons', + '/usr/share/scons', + homedir('.scons')] + else: # Linux, HPUX, etc. + # assume posix-like, i.e. platform == 'posix' + sysdirs=['/usr/share/scons', + homedir('.scons')] + + dirs=sysdirs + [topdir] + for d in dirs: + if verbose: # this is used by unit tests. + print "Loading site dir ", d + _load_site_scons_dir(d) + +def test_load_all_site_scons_dirs(d): + _load_all_site_scons_dirs(d, True) + +def version_string(label, module): + version = module.__version__ + build = module.__build__ + if build: + if build[0] != '.': + build = '.' + build + version = version + build + fmt = "\t%s: v%s, %s, by %s on %s\n" + return fmt % (label, + version, + module.__date__, + module.__developer__, + module.__buildsys__) + +def path_string(label, module): + path = module.__path__ + return "\t%s path: %s\n"%(label,path) + +def _main(parser): + global exit_status + global this_build_status + + options = parser.values + + # Here's where everything really happens. + + # First order of business: set up default warnings and then + # handle the user's warning options, so that we can issue (or + # suppress) appropriate warnings about anything that might happen, + # as configured by the user. + + default_warnings = [ SCons.Warnings.WarningOnByDefault, + SCons.Warnings.DeprecatedWarning, + ] + + for warning in default_warnings: + SCons.Warnings.enableWarningClass(warning) + SCons.Warnings._warningOut = _scons_internal_warning + SCons.Warnings.process_warn_strings(options.warn) + + # Now that we have the warnings configuration set up, we can actually + # issue (or suppress) any warnings about warning-worthy things that + # occurred while the command-line options were getting parsed. + try: + dw = options.delayed_warnings + except AttributeError: + pass + else: + delayed_warnings.extend(dw) + for warning_type, message in delayed_warnings: + SCons.Warnings.warn(warning_type, message) + + if options.diskcheck: + SCons.Node.FS.set_diskcheck(options.diskcheck) + + # Next, we want to create the FS object that represents the outside + # world's file system, as that's central to a lot of initialization. + # To do this, however, we need to be in the directory from which we + # want to start everything, which means first handling any relevant + # options that might cause us to chdir somewhere (-C, -D, -U, -u). + if options.directory: + script_dir = os.path.abspath(_create_path(options.directory)) + else: + script_dir = os.getcwd() + + target_top = None + if options.climb_up: + target_top = '.' # directory to prepend to targets + while script_dir and not _SConstruct_exists(script_dir, + options.repository, + options.file): + script_dir, last_part = os.path.split(script_dir) + if last_part: + target_top = os.path.join(last_part, target_top) + else: + script_dir = '' + + if script_dir and script_dir != os.getcwd(): + display("scons: Entering directory `%s'" % script_dir) + try: + os.chdir(script_dir) + except OSError: + sys.stderr.write("Could not change directory to %s\n" % script_dir) + + # Now that we're in the top-level SConstruct directory, go ahead + # and initialize the FS object that represents the file system, + # and make it the build engine default. + fs = SCons.Node.FS.get_default_fs() + + for rep in options.repository: + fs.Repository(rep) + + # Now that we have the FS object, the next order of business is to + # check for an SConstruct file (or other specified config file). + # If there isn't one, we can bail before doing any more work. + scripts = [] + if options.file: + scripts.extend(options.file) + if not scripts: + sfile = _SConstruct_exists(repositories=options.repository, + filelist=options.file) + if sfile: + scripts.append(sfile) + + if not scripts: + if options.help: + # There's no SConstruct, but they specified -h. + # Give them the options usage now, before we fail + # trying to read a non-existent SConstruct file. + raise SConsPrintHelpException + raise SCons.Errors.UserError("No SConstruct file found.") + + if scripts[0] == "-": + d = fs.getcwd() + else: + d = fs.File(scripts[0]).dir + fs.set_SConstruct_dir(d) + + _set_debug_values(options) + SCons.Node.implicit_cache = options.implicit_cache + SCons.Node.implicit_deps_changed = options.implicit_deps_changed + SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged + + if options.no_exec: + SCons.SConf.dryrun = 1 + SCons.Action.execute_actions = None + if options.question: + SCons.SConf.dryrun = 1 + if options.clean: + SCons.SConf.SetBuildType('clean') + if options.help: + SCons.SConf.SetBuildType('help') + SCons.SConf.SetCacheMode(options.config) + SCons.SConf.SetProgressDisplay(progress_display) + + if options.no_progress or options.silent: + progress_display.set_mode(0) + + if options.site_dir: + _load_site_scons_dir(d.path, options.site_dir) + elif not options.no_site_dir: + _load_all_site_scons_dirs(d.path) + + if options.include_dir: + sys.path = options.include_dir + sys.path + + # That should cover (most of) the options. Next, set up the variables + # that hold command-line arguments, so the SConscript files that we + # read and execute have access to them. + targets = [] + xmit_args = [] + for a in parser.largs: + if a[:1] == '-': + continue + if '=' in a: + xmit_args.append(a) + else: + targets.append(a) + SCons.Script._Add_Targets(targets + parser.rargs) + SCons.Script._Add_Arguments(xmit_args) + + # If stdout is not a tty, replace it with a wrapper object to call flush + # after every write. + # + # Tty devices automatically flush after every newline, so the replacement + # isn't necessary. Furthermore, if we replace sys.stdout, the readline + # module will no longer work. This affects the behavior during + # --interactive mode. --interactive should only be used when stdin and + # stdout refer to a tty. + if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): + sys.stdout = SCons.Util.Unbuffered(sys.stdout) + if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty(): + sys.stderr = SCons.Util.Unbuffered(sys.stderr) + + memory_stats.append('before reading SConscript files:') + count_stats.append(('pre-', 'read')) + + # And here's where we (finally) read the SConscript files. + + progress_display("scons: Reading SConscript files ...") + + start_time = time.time() + try: + for script in scripts: + SCons.Script._SConscript._SConscript(fs, script) + except SCons.Errors.StopError, e: + # We had problems reading an SConscript file, such as it + # couldn't be copied in to the VariantDir. Since we're just + # reading SConscript files and haven't started building + # things yet, stop regardless of whether they used -i or -k + # or anything else. + sys.stderr.write("scons: *** %s Stop.\n" % e) + exit_status = 2 + sys.exit(exit_status) + global sconscript_time + sconscript_time = time.time() - start_time + + progress_display("scons: done reading SConscript files.") + + memory_stats.append('after reading SConscript files:') + count_stats.append(('post-', 'read')) + + # Re-{enable,disable} warnings in case they disabled some in + # the SConscript file. + # + # We delay enabling the PythonVersionWarning class until here so that, + # if they explicity disabled it in either in the command line or in + # $SCONSFLAGS, or in the SConscript file, then the search through + # the list of deprecated warning classes will find that disabling + # first and not issue the warning. + #SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning) + SCons.Warnings.process_warn_strings(options.warn) + + # Now that we've read the SConscript files, we can check for the + # warning about deprecated Python versions--delayed until here + # in case they disabled the warning in the SConscript files. + if python_version_deprecated(): + msg = "Support for pre-2.4 Python (%s) is deprecated.\n" + \ + " If this will cause hardship, contact dev@scons.tigris.org." + SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning, + msg % python_version_string()) + + if not options.help: + SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) + + # Now re-parse the command-line options (any to the left of a '--' + # argument, that is) with any user-defined command-line options that + # the SConscript files may have added to the parser object. This will + # emit the appropriate error message and exit if any unknown option + # was specified on the command line. + + parser.preserve_unknown_options = False + parser.parse_args(parser.largs, options) + + if options.help: + help_text = SCons.Script.help_text + if help_text is None: + # They specified -h, but there was no Help() inside the + # SConscript files. Give them the options usage. + raise SConsPrintHelpException + else: + print help_text + print "Use scons -H for help about command-line options." + exit_status = 0 + return + + # Change directory to the top-level SConstruct directory, then tell + # the Node.FS subsystem that we're all done reading the SConscript + # files and calling Repository() and VariantDir() and changing + # directories and the like, so it can go ahead and start memoizing + # the string values of file system nodes. + + fs.chdir(fs.Top) + + SCons.Node.FS.save_strings(1) + + # Now that we've read the SConscripts we can set the options + # that are SConscript settable: + SCons.Node.implicit_cache = options.implicit_cache + SCons.Node.FS.set_duplicate(options.duplicate) + fs.set_max_drift(options.max_drift) + + SCons.Job.explicit_stack_size = options.stack_size + + if options.md5_chunksize: + SCons.Node.FS.File.md5_chunksize = options.md5_chunksize + + platform = SCons.Platform.platform_module() + + if options.interactive: + SCons.Script.Interactive.interact(fs, OptionsParser, options, + targets, target_top) + + else: + + # Build the targets + nodes = _build_targets(fs, options, targets, target_top) + if not nodes: + exit_status = 2 + +def _build_targets(fs, options, targets, target_top): + + global this_build_status + this_build_status = 0 + + progress_display.set_mode(not (options.no_progress or options.silent)) + display.set_mode(not options.silent) + SCons.Action.print_actions = not options.silent + SCons.Action.execute_actions = not options.no_exec + SCons.Node.FS.do_store_info = not options.no_exec + SCons.SConf.dryrun = options.no_exec + + if options.diskcheck: + SCons.Node.FS.set_diskcheck(options.diskcheck) + + SCons.CacheDir.cache_enabled = not options.cache_disable + SCons.CacheDir.cache_debug = options.cache_debug + SCons.CacheDir.cache_force = options.cache_force + SCons.CacheDir.cache_show = options.cache_show + + if options.no_exec: + CleanTask.execute = CleanTask.show + else: + CleanTask.execute = CleanTask.remove + + lookup_top = None + if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default: + # They specified targets on the command line or modified + # BUILD_TARGETS in the SConscript file(s), so if they used -u, + # -U or -D, we have to look up targets relative to the top, + # but we build whatever they specified. + if target_top: + lookup_top = fs.Dir(target_top) + target_top = None + + targets = SCons.Script.BUILD_TARGETS + else: + # There are no targets specified on the command line, + # so if they used -u, -U or -D, we may have to restrict + # what actually gets built. + d = None + if target_top: + if options.climb_up == 1: + # -u, local directory and below + target_top = fs.Dir(target_top) + lookup_top = target_top + elif options.climb_up == 2: + # -D, all Default() targets + target_top = None + lookup_top = None + elif options.climb_up == 3: + # -U, local SConscript Default() targets + target_top = fs.Dir(target_top) + def check_dir(x, target_top=target_top): + if hasattr(x, 'cwd') and not x.cwd is None: + cwd = x.cwd.srcnode() + return cwd == target_top + else: + # x doesn't have a cwd, so it's either not a target, + # or not a file, so go ahead and keep it as a default + # target and let the engine sort it out: + return 1 + d = list(filter(check_dir, SCons.Script.DEFAULT_TARGETS)) + SCons.Script.DEFAULT_TARGETS[:] = d + target_top = None + lookup_top = None + + targets = SCons.Script._Get_Default_Targets(d, fs) + + if not targets: + sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") + return None + + def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): + if isinstance(x, SCons.Node.Node): + node = x + else: + node = None + # Why would ltop be None? Unfortunately this happens. + if ltop is None: ltop = '' + # Curdir becomes important when SCons is called with -u, -C, + # or similar option that changes directory, and so the paths + # of targets given on the command line need to be adjusted. + curdir = os.path.join(os.getcwd(), str(ltop)) + for lookup in SCons.Node.arg2nodes_lookups: + node = lookup(x, curdir=curdir) + if node is not None: + break + if node is None: + node = fs.Entry(x, directory=ltop, create=1) + if ttop and not node.is_under(ttop): + if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): + node = ttop + else: + node = None + return node + + nodes = [_f for _f in map(Entry, targets) if _f] + + task_class = BuildTask # default action is to build targets + opening_message = "Building targets ..." + closing_message = "done building targets." + if options.keep_going: + failure_message = "done building targets (errors occurred during build)." + else: + failure_message = "building terminated because of errors." + if options.question: + task_class = QuestionTask + try: + if options.clean: + task_class = CleanTask + opening_message = "Cleaning targets ..." + closing_message = "done cleaning targets." + if options.keep_going: + failure_message = "done cleaning targets (errors occurred during clean)." + else: + failure_message = "cleaning terminated because of errors." + except AttributeError: + pass + + task_class.progress = ProgressObject + + if options.random: + def order(dependencies): + """Randomize the dependencies.""" + import random + # This is cribbed from the implementation of + # random.shuffle() in Python 2.X. + d = dependencies + for i in range(len(d)-1, 0, -1): + j = int(random.random() * (i+1)) + d[i], d[j] = d[j], d[i] + return d + else: + def order(dependencies): + """Leave the order of dependencies alone.""" + return dependencies + + if options.taskmastertrace_file == '-': + tmtrace = sys.stdout + elif options.taskmastertrace_file: + tmtrace = open(options.taskmastertrace_file, 'wb') + else: + tmtrace = None + taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace) + + # Let the BuildTask objects get at the options to respond to the + # various print_* settings, tree_printer list, etc. + BuildTask.options = options + + global num_jobs + num_jobs = options.num_jobs + jobs = SCons.Job.Jobs(num_jobs, taskmaster) + if num_jobs > 1: + msg = None + if jobs.num_jobs == 1: + msg = "parallel builds are unsupported by this version of Python;\n" + \ + "\tignoring -j or num_jobs option.\n" + elif sys.platform == 'win32': + msg = fetch_win32_parallel_msg() + if msg: + SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) + + memory_stats.append('before building targets:') + count_stats.append(('pre-', 'build')) + + def jobs_postfunc( + jobs=jobs, + options=options, + closing_message=closing_message, + failure_message=failure_message + ): + if jobs.were_interrupted(): + if not options.no_progress and not options.silent: + sys.stderr.write("scons: Build interrupted.\n") + global exit_status + global this_build_status + exit_status = 2 + this_build_status = 2 + + if this_build_status: + progress_display("scons: " + failure_message) + else: + progress_display("scons: " + closing_message) + if not options.no_exec: + if jobs.were_interrupted(): + progress_display("scons: writing .sconsign file.") + SCons.SConsign.write() + + progress_display("scons: " + opening_message) + jobs.run(postfunc = jobs_postfunc) + + memory_stats.append('after building targets:') + count_stats.append(('post-', 'build')) + + return nodes + +def _exec_main(parser, values): + sconsflags = os.environ.get('SCONSFLAGS', '') + all_args = sconsflags.split() + sys.argv[1:] + + options, args = parser.parse_args(all_args, values) + + if isinstance(options.debug, list) and "pdb" in options.debug: + import pdb + pdb.Pdb().runcall(_main, parser) + elif options.profile_file: + # compat layer imports "cProfile" for us if it's available. + from profile import Profile + + # Some versions of Python 2.4 shipped a profiler that had the + # wrong 'c_exception' entry in its dispatch table. Make sure + # we have the right one. (This may put an unnecessary entry + # in the table in earlier versions of Python, but its presence + # shouldn't hurt anything). + try: + dispatch = Profile.dispatch + except AttributeError: + pass + else: + dispatch['c_exception'] = Profile.trace_dispatch_return + + prof = Profile() + try: + prof.runcall(_main, parser) + except SConsPrintHelpException, e: + prof.dump_stats(options.profile_file) + raise e + except SystemExit: + pass + prof.dump_stats(options.profile_file) + else: + _main(parser) + +def main(): + global OptionsParser + global exit_status + global first_command_start + + # Check up front for a Python version we do not support. We + # delay the check for deprecated Python versions until later, + # after the SConscript files have been read, in case they + # disable that warning. + if python_version_unsupported(): + msg = "scons: *** SCons version %s does not run under Python version %s.\n" + sys.stderr.write(msg % (SCons.__version__, python_version_string())) + sys.exit(1) + + parts = ["SCons by Steven Knight et al.:\n"] + try: + import __main__ + parts.append(version_string("script", __main__)) + except (ImportError, AttributeError): + # On Windows there is no scons.py, so there is no + # __main__.__version__, hence there is no script version. + pass + parts.append(version_string("engine", SCons)) + parts.append(path_string("engine", SCons)) + parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation") + version = ''.join(parts) + + import SConsOptions + parser = SConsOptions.Parser(version) + values = SConsOptions.SConsValues(parser.get_default_values()) + + OptionsParser = parser + + try: + _exec_main(parser, values) + except SystemExit, s: + if s: + exit_status = s + except KeyboardInterrupt: + print("scons: Build interrupted.") + sys.exit(2) + except SyntaxError, e: + _scons_syntax_error(e) + except SCons.Errors.InternalError: + _scons_internal_error() + except SCons.Errors.UserError, e: + _scons_user_error(e) + except SConsPrintHelpException: + parser.print_help() + exit_status = 0 + except SCons.Errors.BuildError, e: + exit_status = e.exitstatus + except: + # An exception here is likely a builtin Python exception Python + # code in an SConscript file. Show them precisely what the + # problem was and where it happened. + SCons.Script._SConscript.SConscript_exception() + sys.exit(2) + + memory_stats.print_stats() + count_stats.print_stats() + + if print_objects: + SCons.Debug.listLoggedInstances('*') + #SCons.Debug.dumpLoggedInstances('*') + + if print_memoizer: + SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:") + + # Dump any development debug info that may have been enabled. + # These are purely for internal debugging during development, so + # there's no need to control them with --debug= options; they're + # controlled by changing the source code. + SCons.Debug.dump_caller_counts() + SCons.Taskmaster.dump_stats() + + if print_time: + total_time = time.time() - SCons.Script.start_time + if num_jobs == 1: + ct = cumulative_command_time + else: + if last_command_end is None or first_command_start is None: + ct = 0.0 + else: + ct = last_command_end - first_command_start + scons_time = total_time - sconscript_time - ct + print "Total build time: %f seconds"%total_time + print "Total SConscript file execution time: %f seconds"%sconscript_time + print "Total SCons execution time: %f seconds"%scons_time + print "Total command execution time: %f seconds"%ct + + sys.exit(exit_status) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Script/SConsOptions.py b/engine/SCons/Script/SConsOptions.py new file mode 100644 index 0000000..79d9927 --- /dev/null +++ b/engine/SCons/Script/SConsOptions.py @@ -0,0 +1,939 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Script/SConsOptions.py 5357 2011/09/09 21:31:03 bdeegan" + +import optparse +import re +import sys +import textwrap + +no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') + +try: + from gettext import gettext +except ImportError: + def gettext(message): + return message +_ = gettext + +import SCons.Node.FS +import SCons.Warnings + +OptionValueError = optparse.OptionValueError +SUPPRESS_HELP = optparse.SUPPRESS_HELP + +diskcheck_all = SCons.Node.FS.diskcheck_types() + +def diskcheck_convert(value): + if value is None: + return [] + if not SCons.Util.is_List(value): + value = value.split(',') + result = [] + for v in value: + v = v.lower() + if v == 'all': + result = diskcheck_all + elif v == 'none': + result = [] + elif v in diskcheck_all: + result.append(v) + else: + raise ValueError(v) + return result + +class SConsValues(optparse.Values): + """ + Holder class for uniform access to SCons options, regardless + of whether or not they can be set on the command line or in the + SConscript files (using the SetOption() function). + + A SCons option value can originate three different ways: + + 1) set on the command line; + 2) set in an SConscript file; + 3) the default setting (from the the op.add_option() + calls in the Parser() function, below). + + The command line always overrides a value set in a SConscript file, + which in turn always overrides default settings. Because we want + to support user-specified options in the SConscript file itself, + though, we may not know about all of the options when the command + line is first parsed, so we can't make all the necessary precedence + decisions at the time the option is configured. + + The solution implemented in this class is to keep these different sets + of settings separate (command line, SConscript file, and default) + and to override the __getattr__() method to check them in turn. + This should allow the rest of the code to just fetch values as + attributes of an instance of this class, without having to worry + about where they came from. + + Note that not all command line options are settable from SConscript + files, and the ones that are must be explicitly added to the + "settable" list in this class, and optionally validated and coerced + in the set_option() method. + """ + + def __init__(self, defaults): + self.__dict__['__defaults__'] = defaults + self.__dict__['__SConscript_settings__'] = {} + + def __getattr__(self, attr): + """ + Fetches an options value, checking first for explicit settings + from the command line (which are direct attributes), then the + SConscript file settings, then the default values. + """ + try: + return self.__dict__[attr] + except KeyError: + try: + return self.__dict__['__SConscript_settings__'][attr] + except KeyError: + return getattr(self.__dict__['__defaults__'], attr) + + settable = [ + 'clean', + 'diskcheck', + 'duplicate', + 'help', + 'implicit_cache', + 'max_drift', + 'md5_chunksize', + 'no_exec', + 'num_jobs', + 'random', + 'stack_size', + 'warn', + ] + + def set_option(self, name, value): + """ + Sets an option from an SConscript file. + """ + if not name in self.settable: + raise SCons.Errors.UserError("This option is not settable from a SConscript file: %s"%name) + + if name == 'num_jobs': + try: + value = int(value) + if value < 1: + raise ValueError + except ValueError: + raise SCons.Errors.UserError("A positive integer is required: %s"%repr(value)) + elif name == 'max_drift': + try: + value = int(value) + except ValueError: + raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) + elif name == 'duplicate': + try: + value = str(value) + except ValueError: + raise SCons.Errors.UserError("A string is required: %s"%repr(value)) + if not value in SCons.Node.FS.Valid_Duplicates: + raise SCons.Errors.UserError("Not a valid duplication style: %s" % value) + # Set the duplicate style right away so it can affect linking + # of SConscript files. + SCons.Node.FS.set_duplicate(value) + elif name == 'diskcheck': + try: + value = diskcheck_convert(value) + except ValueError, v: + raise SCons.Errors.UserError("Not a valid diskcheck value: %s"%v) + if 'diskcheck' not in self.__dict__: + # No --diskcheck= option was specified on the command line. + # Set this right away so it can affect the rest of the + # file/Node lookups while processing the SConscript files. + SCons.Node.FS.set_diskcheck(value) + elif name == 'stack_size': + try: + value = int(value) + except ValueError: + raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) + elif name == 'md5_chunksize': + try: + value = int(value) + except ValueError: + raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) + elif name == 'warn': + if SCons.Util.is_String(value): + value = [value] + value = self.__SConscript_settings__.get(name, []) + value + SCons.Warnings.process_warn_strings(value) + + self.__SConscript_settings__[name] = value + +class SConsOption(optparse.Option): + def convert_value(self, opt, value): + if value is not None: + if self.nargs in (1, '?'): + return self.check_value(opt, value) + else: + return tuple([self.check_value(opt, v) for v in value]) + + def process(self, opt, value, values, parser): + + # First, convert the value(s) to the right type. Howl if any + # value(s) are bogus. + value = self.convert_value(opt, value) + + # And then take whatever action is expected of us. + # This is a separate method to make life easier for + # subclasses to add new actions. + return self.take_action( + self.action, self.dest, opt, value, values, parser) + + def _check_nargs_optional(self): + if self.nargs == '?' and self._short_opts: + fmt = "option %s: nargs='?' is incompatible with short options" + raise SCons.Errors.UserError(fmt % self._short_opts[0]) + + try: + _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS + + _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS + + except AttributeError: + # optparse.Option had no CONST_ACTIONS before Python 2.5. + + _orig_CONST_ACTIONS = ("store_const",) + + def _check_const(self): + if self.action not in self.CONST_ACTIONS and self.const is not None: + raise OptionError( + "'const' must not be supplied for action %r" % self.action, + self) + + # optparse.Option collects its list of unbound check functions + # up front. This sucks because it means we can't just override + # the _check_const() function like a normal method, we have to + # actually replace it in the list. This seems to be the most + # straightforward way to do that. + + _orig_CHECK_METHODS = [optparse.Option._check_action, + optparse.Option._check_type, + optparse.Option._check_choice, + optparse.Option._check_dest, + _check_const, + optparse.Option._check_nargs, + optparse.Option._check_callback] + + CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional] + + CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS + +class SConsOptionGroup(optparse.OptionGroup): + """ + A subclass for SCons-specific option groups. + + The only difference between this and the base class is that we print + the group's help text flush left, underneath their own title but + lined up with the normal "SCons Options". + """ + def format_help(self, formatter): + """ + Format an option group's help text, outdenting the title so it's + flush with the "SCons Options" title we print at the top. + """ + formatter.dedent() + result = formatter.format_heading(self.title) + formatter.indent() + result = result + optparse.OptionContainer.format_help(self, formatter) + return result + +class SConsOptionParser(optparse.OptionParser): + preserve_unknown_options = False + + def error(self, msg): + self.print_usage(sys.stderr) + sys.stderr.write("SCons error: %s\n" % msg) + sys.exit(2) + + def _process_long_opt(self, rargs, values): + """ + SCons-specific processing of long options. + + This is copied directly from the normal + optparse._process_long_opt() method, except that, if configured + to do so, we catch the exception thrown when an unknown option + is encountered and just stick it back on the "leftover" arguments + for later (re-)processing. + """ + arg = rargs.pop(0) + + # Value explicitly attached to arg? Pretend it's the next + # argument. + if "=" in arg: + (opt, next_arg) = arg.split("=", 1) + rargs.insert(0, next_arg) + had_explicit_value = True + else: + opt = arg + had_explicit_value = False + + try: + opt = self._match_long_opt(opt) + except optparse.BadOptionError: + if self.preserve_unknown_options: + # SCons-specific: if requested, add unknown options to + # the "leftover arguments" list for later processing. + self.largs.append(arg) + if had_explicit_value: + # The unknown option will be re-processed later, + # so undo the insertion of the explicit value. + rargs.pop(0) + return + raise + + option = self._long_opt[opt] + if option.takes_value(): + nargs = option.nargs + if nargs == '?': + if had_explicit_value: + value = rargs.pop(0) + else: + value = option.const + elif len(rargs) < nargs: + if nargs == 1: + self.error(_("%s option requires an argument") % opt) + else: + self.error(_("%s option requires %d arguments") + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + elif had_explicit_value: + self.error(_("%s option does not take a value") % opt) + + else: + value = None + + option.process(opt, value, values, self) + + def add_local_option(self, *args, **kw): + """ + Adds a local option to the parser. + + This is initiated by a SetOption() call to add a user-defined + command-line option. We add the option to a separate option + group for the local options, creating the group if necessary. + """ + try: + group = self.local_option_group + except AttributeError: + group = SConsOptionGroup(self, 'Local Options') + group = self.add_option_group(group) + self.local_option_group = group + + result = group.add_option(*args, **kw) + + if result: + # The option was added succesfully. We now have to add the + # default value to our object that holds the default values + # (so that an attempt to fetch the option's attribute will + # yield the default value when not overridden) and then + # we re-parse the leftover command-line options, so that + # any value overridden on the command line is immediately + # available if the user turns around and does a GetOption() + # right away. + setattr(self.values.__defaults__, result.dest, result.default) + self.parse_args(self.largs, self.values) + + return result + +class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): + def format_usage(self, usage): + return "usage: %s\n" % usage + + def format_heading(self, heading): + """ + This translates any heading of "options" or "Options" into + "SCons Options." Unfortunately, we have to do this here, + because those titles are hard-coded in the optparse calls. + """ + if heading == 'options': + # The versions of optparse.py shipped with Pythons 2.3 and + # 2.4 pass this in uncapitalized; override that so we get + # consistent output on all versions. + heading = "Options" + if heading == 'Options': + heading = "SCons Options" + return optparse.IndentedHelpFormatter.format_heading(self, heading) + + def format_option(self, option): + """ + A copy of the normal optparse.IndentedHelpFormatter.format_option() + method. This has been snarfed so we can modify text wrapping to + out liking: + + -- add our own regular expression that doesn't break on hyphens + (so things like --no-print-directory don't get broken); + + -- wrap the list of options themselves when it's too long + (the wrapper.fill(opts) call below); + + -- set the subsequent_indent when wrapping the help_text. + """ + # The help for each option consists of two parts: + # * the opt strings and metavars + # eg. ("-x", or "-fFILENAME, --file=FILENAME") + # * the user-supplied help string + # eg. ("turn on expert mode", "read data from FILENAME") + # + # If possible, we write both of these on the same line: + # -x turn on expert mode + # + # But if the opt string list is too long, we put the help + # string on a second line, indented to the same column it would + # start in if it fit on the first line. + # -fFILENAME, --file=FILENAME + # read data from FILENAME + result = [] + + try: + opts = self.option_strings[option] + except AttributeError: + # The Python 2.3 version of optparse attaches this to + # to the option argument, not to this object. + opts = option.option_strings + + opt_width = self.help_position - self.current_indent - 2 + if len(opts) > opt_width: + wrapper = textwrap.TextWrapper(width=self.width, + initial_indent = ' ', + subsequent_indent = ' ') + wrapper.wordsep_re = no_hyphen_re + opts = wrapper.fill(opts) + '\n' + indent_first = self.help_position + else: # start help on same line as opts + opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) + indent_first = 0 + result.append(opts) + if option.help: + + try: + expand_default = self.expand_default + except AttributeError: + # The HelpFormatter base class in the Python 2.3 version + # of optparse has no expand_default() method. + help_text = option.help + else: + help_text = expand_default(option) + + # SCons: indent every line of the help text but the first. + wrapper = textwrap.TextWrapper(width=self.help_width, + subsequent_indent = ' ') + wrapper.wordsep_re = no_hyphen_re + help_lines = wrapper.wrap(help_text) + result.append("%*s%s\n" % (indent_first, "", help_lines[0])) + for line in help_lines[1:]: + result.append("%*s%s\n" % (self.help_position, "", line)) + elif opts[-1] != "\n": + result.append("\n") + return "".join(result) + + # For consistent help output across Python versions, we provide a + # subclass copy of format_option_strings() and these two variables. + # This is necessary (?) for Python2.3, which otherwise concatenates + # a short option with its metavar. + _short_opt_fmt = "%s %s" + _long_opt_fmt = "%s=%s" + + def format_option_strings(self, option): + """Return a comma-separated list of option strings & metavariables.""" + if option.takes_value(): + metavar = option.metavar or option.dest.upper() + short_opts = [] + for sopt in option._short_opts: + short_opts.append(self._short_opt_fmt % (sopt, metavar)) + long_opts = [] + for lopt in option._long_opts: + long_opts.append(self._long_opt_fmt % (lopt, metavar)) + else: + short_opts = option._short_opts + long_opts = option._long_opts + + if self.short_first: + opts = short_opts + long_opts + else: + opts = long_opts + short_opts + + return ", ".join(opts) + +def Parser(version): + """ + Returns an options parser object initialized with the standard + SCons options. + """ + + formatter = SConsIndentedHelpFormatter(max_help_position=30) + + op = SConsOptionParser(option_class=SConsOption, + add_help_option=False, + formatter=formatter, + usage="usage: scons [OPTION] [TARGET] ...",) + + op.preserve_unknown_options = True + op.version = version + + # Add the options to the parser we just created. + # + # These are in the order we want them to show up in the -H help + # text, basically alphabetical. Each op.add_option() call below + # should have a consistent format: + # + # op.add_option("-L", "--long-option-name", + # nargs=1, type="string", + # dest="long_option_name", default='foo', + # action="callback", callback=opt_long_option, + # help="help text goes here", + # metavar="VAR") + # + # Even though the optparse module constructs reasonable default + # destination names from the long option names, we're going to be + # explicit about each one for easier readability and so this code + # will at least show up when grepping the source for option attribute + # names, or otherwise browsing the source code. + + # options ignored for compatibility + def opt_ignore(option, opt, value, parser): + sys.stderr.write("Warning: ignoring %s option\n" % opt) + op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w", + "--environment-overrides", + "--no-keep-going", + "--no-print-directory", + "--print-directory", + "--stop", + "--touch", + action="callback", callback=opt_ignore, + help="Ignored for compatibility.") + + op.add_option('-c', '--clean', '--remove', + dest="clean", default=False, + action="store_true", + help="Remove specified targets and dependencies.") + + op.add_option('-C', '--directory', + nargs=1, type="string", + dest="directory", default=[], + action="append", + help="Change to DIR before doing anything.", + metavar="DIR") + + op.add_option('--cache-debug', + nargs=1, + dest="cache_debug", default=None, + action="store", + help="Print CacheDir debug info to FILE.", + metavar="FILE") + + op.add_option('--cache-disable', '--no-cache', + dest='cache_disable', default=False, + action="store_true", + help="Do not retrieve built targets from CacheDir.") + + op.add_option('--cache-force', '--cache-populate', + dest='cache_force', default=False, + action="store_true", + help="Copy already-built targets into the CacheDir.") + + op.add_option('--cache-show', + dest='cache_show', default=False, + action="store_true", + help="Print build actions for files from CacheDir.") + + config_options = ["auto", "force" ,"cache"] + + def opt_config(option, opt, value, parser, c_options=config_options): + if not value in c_options: + raise OptionValueError("Warning: %s is not a valid config type" % value) + setattr(parser.values, option.dest, value) + opt_config_help = "Controls Configure subsystem: %s." \ + % ", ".join(config_options) + op.add_option('--config', + nargs=1, type="string", + dest="config", default="auto", + action="callback", callback=opt_config, + help = opt_config_help, + metavar="MODE") + + op.add_option('-D', + dest="climb_up", default=None, + action="store_const", const=2, + help="Search up directory tree for SConstruct, " + "build all Default() targets.") + + deprecated_debug_options = { + "dtree" : '; please use --tree=derived instead', + "nomemoizer" : ' and has no effect', + "stree" : '; please use --tree=all,status instead', + "tree" : '; please use --tree=all instead', + } + + debug_options = ["count", "duplicate", "explain", "findlibs", + "includes", "memoizer", "memory", "objects", + "pdb", "prepare", "presub", "stacktrace", + "time"] + list(deprecated_debug_options.keys()) + + def opt_debug(option, opt, value, parser, + debug_options=debug_options, + deprecated_debug_options=deprecated_debug_options): + if value in debug_options: + parser.values.debug.append(value) + if value in deprecated_debug_options.keys(): + try: + parser.values.delayed_warnings + except AttributeError: + parser.values.delayed_warnings = [] + msg = deprecated_debug_options[value] + w = "The --debug=%s option is deprecated%s." % (value, msg) + t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w) + parser.values.delayed_warnings.append(t) + else: + raise OptionValueError("Warning: %s is not a valid debug type" % value) + opt_debug_help = "Print various types of debugging information: %s." \ + % ", ".join(debug_options) + op.add_option('--debug', + nargs=1, type="string", + dest="debug", default=[], + action="callback", callback=opt_debug, + help=opt_debug_help, + metavar="TYPE") + + def opt_diskcheck(option, opt, value, parser): + try: + diskcheck_value = diskcheck_convert(value) + except ValueError, e: + raise OptionValueError("Warning: `%s' is not a valid diskcheck type" % e) + setattr(parser.values, option.dest, diskcheck_value) + + op.add_option('--diskcheck', + nargs=1, type="string", + dest='diskcheck', default=None, + action="callback", callback=opt_diskcheck, + help="Enable specific on-disk checks.", + metavar="TYPE") + + def opt_duplicate(option, opt, value, parser): + if not value in SCons.Node.FS.Valid_Duplicates: + raise OptionValueError("`%s' is not a valid duplication style." % value) + setattr(parser.values, option.dest, value) + # Set the duplicate style right away so it can affect linking + # of SConscript files. + SCons.Node.FS.set_duplicate(value) + + opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \ + + ", ".join(SCons.Node.FS.Valid_Duplicates) + + op.add_option('--duplicate', + nargs=1, type="string", + dest="duplicate", default='hard-soft-copy', + action="callback", callback=opt_duplicate, + help=opt_duplicate_help) + + op.add_option('-f', '--file', '--makefile', '--sconstruct', + nargs=1, type="string", + dest="file", default=[], + action="append", + help="Read FILE as the top-level SConstruct file.") + + op.add_option('-h', '--help', + dest="help", default=False, + action="store_true", + help="Print defined help message, or this one.") + + op.add_option("-H", "--help-options", + action="help", + help="Print this message and exit.") + + op.add_option('-i', '--ignore-errors', + dest='ignore_errors', default=False, + action="store_true", + help="Ignore errors from build actions.") + + op.add_option('-I', '--include-dir', + nargs=1, + dest='include_dir', default=[], + action="append", + help="Search DIR for imported Python modules.", + metavar="DIR") + + op.add_option('--implicit-cache', + dest='implicit_cache', default=False, + action="store_true", + help="Cache implicit dependencies") + + def opt_implicit_deps(option, opt, value, parser): + setattr(parser.values, 'implicit_cache', True) + setattr(parser.values, option.dest, True) + + op.add_option('--implicit-deps-changed', + dest="implicit_deps_changed", default=False, + action="callback", callback=opt_implicit_deps, + help="Ignore cached implicit dependencies.") + + op.add_option('--implicit-deps-unchanged', + dest="implicit_deps_unchanged", default=False, + action="callback", callback=opt_implicit_deps, + help="Ignore changes in implicit dependencies.") + + op.add_option('--interact', '--interactive', + dest='interactive', default=False, + action="store_true", + help="Run in interactive mode.") + + op.add_option('-j', '--jobs', + nargs=1, type="int", + dest="num_jobs", default=1, + action="store", + help="Allow N jobs at once.", + metavar="N") + + op.add_option('-k', '--keep-going', + dest='keep_going', default=False, + action="store_true", + help="Keep going when a target can't be made.") + + op.add_option('--max-drift', + nargs=1, type="int", + dest='max_drift', default=SCons.Node.FS.default_max_drift, + action="store", + help="Set maximum system clock drift to N seconds.", + metavar="N") + + op.add_option('--md5-chunksize', + nargs=1, type="int", + dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize, + action="store", + help="Set chunk-size for MD5 signature computation to N kilobytes.", + metavar="N") + + op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon', + dest='no_exec', default=False, + action="store_true", + help="Don't build; just print commands.") + + op.add_option('--no-site-dir', + dest='no_site_dir', default=False, + action="store_true", + help="Don't search or use the usual site_scons dir.") + + op.add_option('--profile', + nargs=1, + dest="profile_file", default=None, + action="store", + help="Profile SCons and put results in FILE.", + metavar="FILE") + + op.add_option('-q', '--question', + dest="question", default=False, + action="store_true", + help="Don't build; exit status says if up to date.") + + op.add_option('-Q', + dest='no_progress', default=False, + action="store_true", + help="Suppress \"Reading/Building\" progress messages.") + + op.add_option('--random', + dest="random", default=False, + action="store_true", + help="Build dependencies in random order.") + + op.add_option('-s', '--silent', '--quiet', + dest="silent", default=False, + action="store_true", + help="Don't print commands.") + + op.add_option('--site-dir', + nargs=1, + dest='site_dir', default=None, + action="store", + help="Use DIR instead of the usual site_scons dir.", + metavar="DIR") + + op.add_option('--stack-size', + nargs=1, type="int", + dest='stack_size', + action="store", + help="Set the stack size of the threads used to run jobs to N kilobytes.", + metavar="N") + + op.add_option('--taskmastertrace', + nargs=1, + dest="taskmastertrace_file", default=None, + action="store", + help="Trace Node evaluation to FILE.", + metavar="FILE") + + tree_options = ["all", "derived", "prune", "status"] + + def opt_tree(option, opt, value, parser, tree_options=tree_options): + import Main + tp = Main.TreePrinter() + for o in value.split(','): + if o == 'all': + tp.derived = False + elif o == 'derived': + tp.derived = True + elif o == 'prune': + tp.prune = True + elif o == 'status': + tp.status = True + else: + raise OptionValueError("Warning: %s is not a valid --tree option" % o) + parser.values.tree_printers.append(tp) + + opt_tree_help = "Print a dependency tree in various formats: %s." \ + % ", ".join(tree_options) + + op.add_option('--tree', + nargs=1, type="string", + dest="tree_printers", default=[], + action="callback", callback=opt_tree, + help=opt_tree_help, + metavar="OPTIONS") + + op.add_option('-u', '--up', '--search-up', + dest="climb_up", default=0, + action="store_const", const=1, + help="Search up directory tree for SConstruct, " + "build targets at or below current directory.") + + op.add_option('-U', + dest="climb_up", default=0, + action="store_const", const=3, + help="Search up directory tree for SConstruct, " + "build Default() targets from local SConscript.") + + def opt_version(option, opt, value, parser): + sys.stdout.write(parser.version + '\n') + sys.exit(0) + op.add_option("-v", "--version", + action="callback", callback=opt_version, + help="Print the SCons version number and exit.") + + def opt_warn(option, opt, value, parser, tree_options=tree_options): + if SCons.Util.is_String(value): + value = value.split(',') + parser.values.warn.extend(value) + + op.add_option('--warn', '--warning', + nargs=1, type="string", + dest="warn", default=[], + action="callback", callback=opt_warn, + help="Enable or disable warnings.", + metavar="WARNING-SPEC") + + op.add_option('-Y', '--repository', '--srcdir', + nargs=1, + dest="repository", default=[], + action="append", + help="Search REPOSITORY for source and target files.") + + # Options from Make and Cons classic that we do not yet support, + # but which we may support someday and whose (potential) meanings + # we don't want to change. These all get a "the -X option is not + # yet implemented" message and don't show up in the help output. + + def opt_not_yet(option, opt, value, parser): + msg = "Warning: the %s option is not yet implemented\n" % opt + sys.stderr.write(msg) + + op.add_option('-l', '--load-average', '--max-load', + nargs=1, type="float", + dest="load_average", default=0, + action="callback", callback=opt_not_yet, + # action="store", + # help="Don't start multiple jobs unless load is below " + # "LOAD-AVERAGE." + help=SUPPRESS_HELP) + op.add_option('--list-actions', + dest="list_actions", + action="callback", callback=opt_not_yet, + # help="Don't build; list files and build actions." + help=SUPPRESS_HELP) + op.add_option('--list-derived', + dest="list_derived", + action="callback", callback=opt_not_yet, + # help="Don't build; list files that would be built." + help=SUPPRESS_HELP) + op.add_option('--list-where', + dest="list_where", + action="callback", callback=opt_not_yet, + # help="Don't build; list files and where defined." + help=SUPPRESS_HELP) + op.add_option('-o', '--old-file', '--assume-old', + nargs=1, type="string", + dest="old_file", default=[], + action="callback", callback=opt_not_yet, + # action="append", + # help = "Consider FILE to be old; don't rebuild it." + help=SUPPRESS_HELP) + op.add_option('--override', + nargs=1, type="string", + action="callback", callback=opt_not_yet, + dest="override", + # help="Override variables as specified in FILE." + help=SUPPRESS_HELP) + op.add_option('-p', + action="callback", callback=opt_not_yet, + dest="p", + # help="Print internal environments/objects." + help=SUPPRESS_HELP) + op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables', + action="callback", callback=opt_not_yet, + dest="no_builtin_rules", + # help="Clear default environments and variables." + help=SUPPRESS_HELP) + op.add_option('--write-filenames', + nargs=1, type="string", + dest="write_filenames", + action="callback", callback=opt_not_yet, + # help="Write all filenames examined into FILE." + help=SUPPRESS_HELP) + op.add_option('-W', '--new-file', '--assume-new', '--what-if', + nargs=1, type="string", + dest="new_file", + action="callback", callback=opt_not_yet, + # help="Consider FILE to be changed." + help=SUPPRESS_HELP) + op.add_option('--warn-undefined-variables', + dest="warn_undefined_variables", + action="callback", callback=opt_not_yet, + # help="Warn when an undefined variable is referenced." + help=SUPPRESS_HELP) + + return op + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Script/SConscript.py b/engine/SCons/Script/SConscript.py new file mode 100644 index 0000000..ee5e290 --- /dev/null +++ b/engine/SCons/Script/SConscript.py @@ -0,0 +1,640 @@ +"""SCons.Script.SConscript + +This module defines the Python API provided to SConscript and SConstruct +files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import division + +__revision__ = "src/engine/SCons/Script/SConscript.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Environment +import SCons.Errors +import SCons.Node +import SCons.Node.Alias +import SCons.Node.FS +import SCons.Platform +import SCons.SConf +import SCons.Script.Main +import SCons.Tool +import SCons.Util + +import collections +import os +import os.path +import re +import sys +import traceback + +# The following variables used to live in this module. Some +# SConscript files out there may have referred to them directly as +# SCons.Script.SConscript.*. This is now supported by some special +# handling towards the bottom of the SConscript.__init__.py module. +#Arguments = {} +#ArgList = [] +#BuildTargets = TargetList() +#CommandLineTargets = [] +#DefaultTargets = [] + +class SConscriptReturn(Exception): + pass + +launch_dir = os.path.abspath(os.curdir) + +GlobalDict = None + +# global exports set by Export(): +global_exports = {} + +# chdir flag +sconscript_chdir = 1 + +def get_calling_namespaces(): + """Return the locals and globals for the function that called + into this module in the current call stack.""" + 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 + + # Find the first frame that *isn't* from this file. This means + # that we expect all of the SCons frames that implement an Export() + # or SConscript() call to be in this file, so that we can identify + # the first non-Script.SConscript frame as the user's local calling + # environment, and the locals and globals dictionaries from that + # frame as the calling namespaces. See the comment below preceding + # the DefaultEnvironmentCall block for even more explanation. + while frame.f_globals.get("__name__") == __name__: + frame = frame.f_back + + return frame.f_locals, frame.f_globals + + +def compute_exports(exports): + """Compute a dictionary of exports given one of the parameters + to the Export() function or the exports argument to SConscript().""" + + loc, glob = get_calling_namespaces() + + retval = {} + try: + for export in exports: + if SCons.Util.is_Dict(export): + retval.update(export) + else: + try: + retval[export] = loc[export] + except KeyError: + retval[export] = glob[export] + except KeyError, x: + raise SCons.Errors.UserError("Export of non-existent variable '%s'"%x) + + return retval + +class Frame(object): + """A frame on the SConstruct/SConscript call stack""" + def __init__(self, fs, exports, sconscript): + self.globals = BuildDefaultGlobals() + self.retval = None + self.prev_dir = fs.getcwd() + self.exports = compute_exports(exports) # exports from the calling SConscript + # make sure the sconscript attr is a Node. + if isinstance(sconscript, SCons.Node.Node): + self.sconscript = sconscript + elif sconscript == '-': + self.sconscript = None + else: + self.sconscript = fs.File(str(sconscript)) + +# the SConstruct/SConscript call stack: +call_stack = [] + +# For documentation on the methods in this file, see the scons man-page + +def Return(*vars, **kw): + retval = [] + try: + fvars = SCons.Util.flatten(vars) + for var in fvars: + for v in var.split(): + retval.append(call_stack[-1].globals[v]) + except KeyError, x: + raise SCons.Errors.UserError("Return of non-existent variable '%s'"%x) + + if len(retval) == 1: + call_stack[-1].retval = retval[0] + else: + call_stack[-1].retval = tuple(retval) + + stop = kw.get('stop', True) + + if stop: + raise SConscriptReturn + + +stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) + +def _SConscript(fs, *files, **kw): + top = fs.Top + sd = fs.SConstruct_dir.rdir() + exports = kw.get('exports', []) + + # evaluate each SConscript file + results = [] + for fn in files: + call_stack.append(Frame(fs, exports, fn)) + old_sys_path = sys.path + try: + SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1 + if fn == "-": + exec sys.stdin in call_stack[-1].globals + else: + if isinstance(fn, SCons.Node.Node): + f = fn + else: + f = fs.File(str(fn)) + _file_ = None + + # Change directory to the top of the source + # tree to make sure the os's cwd and the cwd of + # fs match so we can open the SConscript. + fs.chdir(top, change_os_dir=1) + if f.rexists(): + actual = f.rfile() + _file_ = open(actual.get_abspath(), "r") + elif f.srcnode().rexists(): + actual = f.srcnode().rfile() + _file_ = open(actual.get_abspath(), "r") + elif f.has_src_builder(): + # The SConscript file apparently exists in a source + # code management system. Build it, but then clear + # the builder so that it doesn't get built *again* + # during the actual build phase. + f.build() + f.built() + f.builder_set(None) + if f.exists(): + _file_ = open(f.get_abspath(), "r") + if _file_: + # Chdir to the SConscript directory. Use a path + # name relative to the SConstruct file so that if + # we're using the -f option, we're essentially + # creating a parallel SConscript directory structure + # in our local directory tree. + # + # XXX This is broken for multiple-repository cases + # where the SConstruct and SConscript files might be + # in different Repositories. For now, cross that + # bridge when someone comes to it. + try: + src_dir = kw['src_dir'] + except KeyError: + ldir = fs.Dir(f.dir.get_path(sd)) + else: + ldir = fs.Dir(src_dir) + if not ldir.is_under(f.dir): + # They specified a source directory, but + # it's above the SConscript directory. + # Do the sensible thing and just use the + # SConcript directory. + ldir = fs.Dir(f.dir.get_path(sd)) + try: + fs.chdir(ldir, change_os_dir=sconscript_chdir) + except OSError: + # There was no local directory, so we should be + # able to chdir to the Repository directory. + # Note that we do this directly, not through + # fs.chdir(), because we still need to + # interpret the stuff within the SConscript file + # relative to where we are logically. + fs.chdir(ldir, change_os_dir=0) + os.chdir(actual.dir.get_abspath()) + + # Append the SConscript directory to the beginning + # of sys.path so Python modules in the SConscript + # directory can be easily imported. + sys.path = [ f.dir.get_abspath() ] + sys.path + + # This is the magic line that actually reads up + # and executes the stuff in the SConscript file. + # The locals for this frame contain the special + # bottom-of-the-stack marker so that any + # exceptions that occur when processing this + # SConscript can base the printed frames at this + # level and not show SCons internals as well. + call_stack[-1].globals.update({stack_bottom:1}) + old_file = call_stack[-1].globals.get('__file__') + try: + del call_stack[-1].globals['__file__'] + except KeyError: + pass + try: + try: + exec _file_ in call_stack[-1].globals + except SConscriptReturn: + pass + finally: + if old_file is not None: + call_stack[-1].globals.update({__file__:old_file}) + else: + SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, + "Ignoring missing SConscript '%s'" % f.path) + + finally: + SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1 + sys.path = old_sys_path + frame = call_stack.pop() + try: + fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) + except OSError: + # There was no local directory, so chdir to the + # Repository directory. Like above, we do this + # directly. + fs.chdir(frame.prev_dir, change_os_dir=0) + rdir = frame.prev_dir.rdir() + rdir._create() # Make sure there's a directory there. + try: + os.chdir(rdir.get_abspath()) + except OSError, e: + # We still couldn't chdir there, so raise the error, + # but only if actions are being executed. + # + # If the -n option was used, the directory would *not* + # have been created and we should just carry on and + # let things muddle through. This isn't guaranteed + # to work if the SConscript files are reading things + # from disk (for example), but it should work well + # enough for most configurations. + if SCons.Action.execute_actions: + raise e + + results.append(frame.retval) + + # if we only have one script, don't return a tuple + if len(results) == 1: + return results[0] + else: + return tuple(results) + +def SConscript_exception(file=sys.stderr): + """Print an exception stack trace just for the SConscript file(s). + This will show users who have Python errors where the problem is, + without cluttering the output with all of the internal calls leading + up to where we exec the SConscript.""" + exc_type, exc_value, exc_tb = sys.exc_info() + tb = exc_tb + while tb and stack_bottom not in tb.tb_frame.f_locals: + tb = tb.tb_next + if not tb: + # We did not find our exec statement, so this was actually a bug + # in SCons itself. Show the whole stack. + tb = exc_tb + stack = traceback.extract_tb(tb) + try: + type = exc_type.__name__ + except AttributeError: + type = str(exc_type) + if type[:11] == "exceptions.": + type = type[11:] + file.write('%s: %s:\n' % (type, exc_value)) + for fname, line, func, text in stack: + file.write(' File "%s", line %d:\n' % (fname, line)) + file.write(' %s\n' % text) + +def annotate(node): + """Annotate a node with the stack frame describing the + SConscript file and line number that created it.""" + tb = sys.exc_info()[2] + while tb and stack_bottom not in tb.tb_frame.f_locals: + tb = tb.tb_next + if not tb: + # We did not find any exec of an SConscript file: what?! + raise SCons.Errors.InternalError("could not find SConscript stack frame") + node.creator = traceback.extract_stack(tb)[0] + +# The following line would cause each Node to be annotated using the +# above function. Unfortunately, this is a *huge* performance hit, so +# leave this disabled until we find a more efficient mechanism. +#SCons.Node.Annotate = annotate + +class SConsEnvironment(SCons.Environment.Base): + """An Environment subclass that contains all of the methods that + are particular to the wrapper SCons interface and which aren't + (or shouldn't be) part of the build engine itself. + + Note that not all of the methods of this class have corresponding + global functions, there are some private methods. + """ + + # + # Private methods of an SConsEnvironment. + # + def _exceeds_version(self, major, minor, v_major, v_minor): + """Return 1 if 'major' and 'minor' are greater than the version + in 'v_major' and 'v_minor', and 0 otherwise.""" + return (major > v_major or (major == v_major and minor > v_minor)) + + def _get_major_minor_revision(self, version_string): + """Split a version string into major, minor and (optionally) + revision parts. + + This is complicated by the fact that a version string can be + something like 3.2b1.""" + version = version_string.split(' ')[0].split('.') + v_major = int(version[0]) + v_minor = int(re.match('\d+', version[1]).group()) + if len(version) >= 3: + v_revision = int(re.match('\d+', version[2]).group()) + else: + v_revision = 0 + return v_major, v_minor, v_revision + + def _get_SConscript_filenames(self, ls, kw): + """ + Convert the parameters passed to SConscript() calls into a list + of files and export variables. If the parameters are invalid, + throws SCons.Errors.UserError. Returns a tuple (l, e) where l + is a list of SConscript filenames and e is a list of exports. + """ + exports = [] + + if len(ls) == 0: + try: + dirs = kw["dirs"] + except KeyError: + raise SCons.Errors.UserError("Invalid SConscript usage - no parameters") + + if not SCons.Util.is_List(dirs): + dirs = [ dirs ] + dirs = list(map(str, dirs)) + + name = kw.get('name', 'SConscript') + + files = [os.path.join(n, name) for n in dirs] + + elif len(ls) == 1: + + files = ls[0] + + elif len(ls) == 2: + + files = ls[0] + exports = self.Split(ls[1]) + + else: + + raise SCons.Errors.UserError("Invalid SConscript() usage - too many arguments") + + if not SCons.Util.is_List(files): + files = [ files ] + + if kw.get('exports'): + exports.extend(self.Split(kw['exports'])) + + variant_dir = kw.get('variant_dir') or kw.get('build_dir') + if variant_dir: + if len(files) != 1: + raise SCons.Errors.UserError("Invalid SConscript() usage - can only specify one SConscript with a variant_dir") + duplicate = kw.get('duplicate', 1) + src_dir = kw.get('src_dir') + if not src_dir: + src_dir, fname = os.path.split(str(files[0])) + files = [os.path.join(str(variant_dir), fname)] + else: + if not isinstance(src_dir, SCons.Node.Node): + src_dir = self.fs.Dir(src_dir) + fn = files[0] + if not isinstance(fn, SCons.Node.Node): + fn = self.fs.File(fn) + if fn.is_under(src_dir): + # Get path relative to the source directory. + fname = fn.get_path(src_dir) + files = [os.path.join(str(variant_dir), fname)] + else: + files = [fn.abspath] + kw['src_dir'] = variant_dir + self.fs.VariantDir(variant_dir, src_dir, duplicate) + + return (files, exports) + + # + # Public methods of an SConsEnvironment. These get + # entry points in the global name space so they can be called + # as global functions. + # + + def Configure(self, *args, **kw): + if not SCons.Script.sconscript_reading: + raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") + kw['_depth'] = kw.get('_depth', 0) + 1 + return SCons.Environment.Base.Configure(self, *args, **kw) + + def Default(self, *targets): + SCons.Script._Set_Default_Targets(self, targets) + + def EnsureSConsVersion(self, major, minor, revision=0): + """Exit abnormally if the SCons version is not late enough.""" + scons_ver = self._get_major_minor_revision(SCons.__version__) + if scons_ver < (major, minor, revision): + if revision: + scons_ver_string = '%d.%d.%d' % (major, minor, revision) + else: + scons_ver_string = '%d.%d' % (major, minor) + print "SCons %s or greater required, but you have SCons %s" % \ + (scons_ver_string, SCons.__version__) + sys.exit(2) + + def EnsurePythonVersion(self, major, minor): + """Exit abnormally if the Python version is not late enough.""" + try: + v_major, v_minor, v_micro, release, serial = sys.version_info + python_ver = (v_major, v_minor) + except AttributeError: + python_ver = self._get_major_minor_revision(sys.version)[:2] + if python_ver < (major, minor): + v = sys.version.split(" ", 1)[0] + print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v) + sys.exit(2) + + def Exit(self, value=0): + sys.exit(value) + + def Export(self, *vars, **kw): + for var in vars: + global_exports.update(compute_exports(self.Split(var))) + global_exports.update(kw) + + def GetLaunchDir(self): + global launch_dir + return launch_dir + + def GetOption(self, name): + name = self.subst(name) + return SCons.Script.Main.GetOption(name) + + def Help(self, text): + text = self.subst(text, raw=1) + SCons.Script.HelpFunction(text) + + def Import(self, *vars): + try: + frame = call_stack[-1] + globals = frame.globals + exports = frame.exports + for var in vars: + var = self.Split(var) + for v in var: + if v == '*': + globals.update(global_exports) + globals.update(exports) + else: + if v in exports: + globals[v] = exports[v] + else: + globals[v] = global_exports[v] + except KeyError,x: + raise SCons.Errors.UserError("Import of non-existent variable '%s'"%x) + + def SConscript(self, *ls, **kw): + if 'build_dir' in kw: + msg = """The build_dir keyword has been deprecated; use the variant_dir keyword instead.""" + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) + def subst_element(x, subst=self.subst): + if SCons.Util.is_List(x): + x = list(map(subst, x)) + else: + x = subst(x) + return x + ls = list(map(subst_element, ls)) + subst_kw = {} + for key, val in kw.items(): + if SCons.Util.is_String(val): + val = self.subst(val) + elif SCons.Util.is_List(val): + result = [] + for v in val: + if SCons.Util.is_String(v): + v = self.subst(v) + result.append(v) + val = result + subst_kw[key] = val + + files, exports = self._get_SConscript_filenames(ls, subst_kw) + subst_kw['exports'] = exports + return _SConscript(self.fs, *files, **subst_kw) + + def SConscriptChdir(self, flag): + global sconscript_chdir + sconscript_chdir = flag + + def SetOption(self, name, value): + name = self.subst(name) + SCons.Script.Main.SetOption(name, value) + +# +# +# +SCons.Environment.Environment = SConsEnvironment + +def Configure(*args, **kw): + if not SCons.Script.sconscript_reading: + raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") + kw['_depth'] = 1 + return SCons.SConf.SConf(*args, **kw) + +# It's very important that the DefaultEnvironmentCall() class stay in this +# file, with the get_calling_namespaces() function, the compute_exports() +# function, the Frame class and the SConsEnvironment.Export() method. +# These things make up the calling stack leading up to the actual global +# Export() or SConscript() call that the user issued. We want to allow +# users to export local variables that they define, like so: +# +# def func(): +# x = 1 +# Export('x') +# +# To support this, the get_calling_namespaces() function assumes that +# the *first* stack frame that's not from this file is the local frame +# for the Export() or SConscript() call. + +_DefaultEnvironmentProxy = None + +def get_DefaultEnvironmentProxy(): + global _DefaultEnvironmentProxy + if not _DefaultEnvironmentProxy: + default_env = SCons.Defaults.DefaultEnvironment() + _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env) + return _DefaultEnvironmentProxy + +class DefaultEnvironmentCall(object): + """A class that implements "global function" calls of + Environment methods by fetching the specified method from the + DefaultEnvironment's class. Note that this uses an intermediate + proxy class instead of calling the DefaultEnvironment method + directly so that the proxy can override the subst() method and + thereby prevent expansion of construction variables (since from + the user's point of view this was called as a global function, + with no associated construction environment).""" + def __init__(self, method_name, subst=0): + self.method_name = method_name + if subst: + self.factory = SCons.Defaults.DefaultEnvironment + else: + self.factory = get_DefaultEnvironmentProxy + def __call__(self, *args, **kw): + env = self.factory() + method = getattr(env, self.method_name) + return method(*args, **kw) + + +def BuildDefaultGlobals(): + """ + Create a dictionary containing all the default globals for + SConstruct and SConscript files. + """ + + global GlobalDict + if GlobalDict is None: + GlobalDict = {} + + import SCons.Script + d = SCons.Script.__dict__ + def not_a_module(m, d=d, mtype=type(SCons.Script)): + return not isinstance(d[m], mtype) + for m in filter(not_a_module, dir(SCons.Script)): + GlobalDict[m] = d[m] + + return GlobalDict.copy() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Script/__init__.py b/engine/SCons/Script/__init__.py new file mode 100644 index 0000000..f2503ae --- /dev/null +++ b/engine/SCons/Script/__init__.py @@ -0,0 +1,412 @@ +"""SCons.Script + +This file implements the main() function used by the scons script. + +Architecturally, this *is* the scons script, and will likely only be +called from the external "scons" wrapper. Consequently, anything here +should not be, or be considered, part of the build engine. If it's +something that we expect other software to want to use, it should go in +some other module. If it's specific to the "scons" script invocation, +it goes here. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Script/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +import time +start_time = time.time() + +import collections +import os +import sys + +# Special chicken-and-egg handling of the "--debug=memoizer" flag: +# +# SCons.Memoize contains a metaclass implementation that affects how +# the other classes are instantiated. The Memoizer may add shim methods +# to classes that have methods that cache computed values in order to +# count and report the hits and misses. +# +# If we wait to enable the Memoization until after we've parsed the +# command line options normally, it will be too late, because the Memoizer +# will have already analyzed the classes that it's Memoizing and decided +# to not add the shims. So we use a special-case, up-front check for +# the "--debug=memoizer" flag and enable Memoizer before we import any +# of the other modules that use it. + +_args = sys.argv + os.environ.get('SCONSFLAGS', '').split() +if "--debug=memoizer" in _args: + import SCons.Memoize + import SCons.Warnings + try: + SCons.Memoize.EnableMemoization() + except SCons.Warnings.Warning: + # Some warning was thrown. Arrange for it to be displayed + # or not after warnings are configured. + import Main + exc_type, exc_value, tb = sys.exc_info() + Main.delayed_warnings.append((exc_type, exc_value)) +del _args + +import SCons.Action +import SCons.Builder +import SCons.Environment +import SCons.Node.FS +import SCons.Options +import SCons.Platform +import SCons.Scanner +import SCons.SConf +import SCons.Subst +import SCons.Tool +import SCons.Util +import SCons.Variables +import SCons.Defaults + +import Main + +main = Main.main + +# The following are global class definitions and variables that used to +# live directly in this module back before 0.96.90, when it contained +# a lot of code. Some SConscript files in widely-distributed packages +# (Blender is the specific example) actually reached into SCons.Script +# directly to use some of these. Rather than break those SConscript +# files, we're going to propagate these names into the SCons.Script +# namespace here. +# +# Some of these are commented out because it's *really* unlikely anyone +# used them, but we're going to leave the comment here to try to make +# it obvious what to do if the situation arises. +BuildTask = Main.BuildTask +CleanTask = Main.CleanTask +QuestionTask = Main.QuestionTask +#PrintHelp = Main.PrintHelp +#SConscriptSettableOptions = Main.SConscriptSettableOptions + +AddOption = Main.AddOption +GetOption = Main.GetOption +SetOption = Main.SetOption +Progress = Main.Progress +GetBuildFailures = Main.GetBuildFailures + +#keep_going_on_error = Main.keep_going_on_error +#print_dtree = Main.print_dtree +#print_explanations = Main.print_explanations +#print_includes = Main.print_includes +#print_objects = Main.print_objects +#print_time = Main.print_time +#print_tree = Main.print_tree +#memory_stats = Main.memory_stats +#ignore_errors = Main.ignore_errors +#sconscript_time = Main.sconscript_time +#command_time = Main.command_time +#exit_status = Main.exit_status +#profiling = Main.profiling +#repositories = Main.repositories + +# +import SConscript +_SConscript = SConscript + +call_stack = _SConscript.call_stack + +# +Action = SCons.Action.Action +AddMethod = SCons.Util.AddMethod +AllowSubstExceptions = SCons.Subst.SetAllowableExceptions +Builder = SCons.Builder.Builder +Configure = _SConscript.Configure +Environment = SCons.Environment.Environment +#OptParser = SCons.SConsOptions.OptParser +FindPathDirs = SCons.Scanner.FindPathDirs +Platform = SCons.Platform.Platform +Return = _SConscript.Return +Scanner = SCons.Scanner.Base +Tool = SCons.Tool.Tool +WhereIs = SCons.Util.WhereIs + +# +BoolVariable = SCons.Variables.BoolVariable +EnumVariable = SCons.Variables.EnumVariable +ListVariable = SCons.Variables.ListVariable +PackageVariable = SCons.Variables.PackageVariable +PathVariable = SCons.Variables.PathVariable + +# Deprecated names that will go away some day. +BoolOption = SCons.Options.BoolOption +EnumOption = SCons.Options.EnumOption +ListOption = SCons.Options.ListOption +PackageOption = SCons.Options.PackageOption +PathOption = SCons.Options.PathOption + +# Action factories. +Chmod = SCons.Defaults.Chmod +Copy = SCons.Defaults.Copy +Delete = SCons.Defaults.Delete +Mkdir = SCons.Defaults.Mkdir +Move = SCons.Defaults.Move +Touch = SCons.Defaults.Touch + +# Pre-made, public scanners. +CScanner = SCons.Tool.CScanner +DScanner = SCons.Tool.DScanner +DirScanner = SCons.Defaults.DirScanner +ProgramScanner = SCons.Tool.ProgramScanner +SourceFileScanner = SCons.Tool.SourceFileScanner + +# Functions we might still convert to Environment methods. +CScan = SCons.Defaults.CScan +DefaultEnvironment = SCons.Defaults.DefaultEnvironment + +# Other variables we provide. +class TargetList(collections.UserList): + def _do_nothing(self, *args, **kw): + pass + def _add_Default(self, list): + self.extend(list) + def _clear(self): + del self[:] + +ARGUMENTS = {} +ARGLIST = [] +BUILD_TARGETS = TargetList() +COMMAND_LINE_TARGETS = [] +DEFAULT_TARGETS = [] + +# BUILD_TARGETS can be modified in the SConscript files. If so, we +# want to treat the modified BUILD_TARGETS list as if they specified +# targets on the command line. To do that, though, we need to know if +# BUILD_TARGETS was modified through "official" APIs or by hand. We do +# this by updating two lists in parallel, the documented BUILD_TARGETS +# list, above, and this internal _build_plus_default targets list which +# should only have "official" API changes. Then Script/Main.py can +# compare these two afterwards to figure out if the user added their +# own targets to BUILD_TARGETS. +_build_plus_default = TargetList() + +def _Add_Arguments(alist): + for arg in alist: + a, b = arg.split('=', 1) + ARGUMENTS[a] = b + ARGLIST.append((a, b)) + +def _Add_Targets(tlist): + if tlist: + COMMAND_LINE_TARGETS.extend(tlist) + BUILD_TARGETS.extend(tlist) + BUILD_TARGETS._add_Default = BUILD_TARGETS._do_nothing + BUILD_TARGETS._clear = BUILD_TARGETS._do_nothing + _build_plus_default.extend(tlist) + _build_plus_default._add_Default = _build_plus_default._do_nothing + _build_plus_default._clear = _build_plus_default._do_nothing + +def _Set_Default_Targets_Has_Been_Called(d, fs): + return DEFAULT_TARGETS + +def _Set_Default_Targets_Has_Not_Been_Called(d, fs): + if d is None: + d = [fs.Dir('.')] + return d + +_Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called + +def _Set_Default_Targets(env, tlist): + global DEFAULT_TARGETS + global _Get_Default_Targets + _Get_Default_Targets = _Set_Default_Targets_Has_Been_Called + for t in tlist: + if t is None: + # Delete the elements from the list in-place, don't + # reassign an empty list to DEFAULT_TARGETS, so that the + # variables will still point to the same object we point to. + del DEFAULT_TARGETS[:] + BUILD_TARGETS._clear() + _build_plus_default._clear() + elif isinstance(t, SCons.Node.Node): + DEFAULT_TARGETS.append(t) + BUILD_TARGETS._add_Default([t]) + _build_plus_default._add_Default([t]) + else: + nodes = env.arg2nodes(t, env.fs.Entry) + DEFAULT_TARGETS.extend(nodes) + BUILD_TARGETS._add_Default(nodes) + _build_plus_default._add_Default(nodes) + +# +help_text = None + +def HelpFunction(text): + global help_text + if SCons.Script.help_text is None: + SCons.Script.help_text = text + else: + help_text = help_text + text + +# +# Will be non-zero if we are reading an SConscript file. +sconscript_reading = 0 + +# +def Variables(files=[], args=ARGUMENTS): + return SCons.Variables.Variables(files, args) + +def Options(files=[], args=ARGUMENTS): + return SCons.Options.Options(files, args) + +# The list of global functions to add to the SConscript name space +# that end up calling corresponding methods or Builders in the +# DefaultEnvironment(). +GlobalDefaultEnvironmentFunctions = [ + # Methods from the SConsEnvironment class, above. + 'Default', + 'EnsurePythonVersion', + 'EnsureSConsVersion', + 'Exit', + 'Export', + 'GetLaunchDir', + 'Help', + 'Import', + #'SConscript', is handled separately, below. + 'SConscriptChdir', + + # Methods from the Environment.Base class. + 'AddPostAction', + 'AddPreAction', + 'Alias', + 'AlwaysBuild', + 'BuildDir', + 'CacheDir', + 'Clean', + #The Command() method is handled separately, below. + 'Decider', + 'Depends', + 'Dir', + 'NoClean', + 'NoCache', + 'Entry', + 'Execute', + 'File', + 'FindFile', + 'FindInstalledFiles', + 'FindSourceFiles', + 'Flatten', + 'GetBuildPath', + 'Glob', + 'Ignore', + 'Install', + 'InstallAs', + 'Literal', + 'Local', + 'ParseDepends', + 'Precious', + 'Repository', + 'Requires', + 'SConsignFile', + 'SideEffect', + 'SourceCode', + 'SourceSignatures', + 'Split', + 'Tag', + 'TargetSignatures', + 'Value', + 'VariantDir', +] + +GlobalDefaultBuilders = [ + # Supported builders. + 'CFile', + 'CXXFile', + 'DVI', + 'Jar', + 'Java', + 'JavaH', + 'Library', + 'M4', + 'MSVSProject', + 'Object', + 'PCH', + 'PDF', + 'PostScript', + 'Program', + 'RES', + 'RMIC', + 'SharedLibrary', + 'SharedObject', + 'StaticLibrary', + 'StaticObject', + 'Tar', + 'TypeLibrary', + 'Zip', + 'Package', +] + +for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: + exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)) +del name + +# There are a handful of variables that used to live in the +# Script/SConscript.py module that some SConscript files out there were +# accessing directly as SCons.Script.SConscript.*. The problem is that +# "SConscript" in this namespace is no longer a module, it's a global +# function call--or more precisely, an object that implements a global +# function call through the default Environment. Nevertheless, we can +# maintain backwards compatibility for SConscripts that were reaching in +# this way by hanging some attributes off the "SConscript" object here. +SConscript = _SConscript.DefaultEnvironmentCall('SConscript') + +# Make SConscript look enough like the module it used to be so +# that pychecker doesn't barf. +SConscript.__name__ = 'SConscript' + +SConscript.Arguments = ARGUMENTS +SConscript.ArgList = ARGLIST +SConscript.BuildTargets = BUILD_TARGETS +SConscript.CommandLineTargets = COMMAND_LINE_TARGETS +SConscript.DefaultTargets = DEFAULT_TARGETS + +# The global Command() function must be handled differently than the +# global functions for other construction environment methods because +# we want people to be able to use Actions that must expand $TARGET +# and $SOURCE later, when (and if) the Action is invoked to build +# the target(s). We do this with the subst=1 argument, which creates +# a DefaultEnvironmentCall instance that wraps up a normal default +# construction environment that performs variable substitution, not a +# proxy that doesn't. +# +# There's a flaw here, though, because any other $-variables on a command +# line will *also* be expanded, each to a null string, but that should +# only be a problem in the unusual case where someone was passing a '$' +# on a command line and *expected* the $ to get through to the shell +# because they were calling Command() and not env.Command()... This is +# unlikely enough that we're going to leave this as is and cross that +# bridge if someone actually comes to it. +Command = _SConscript.DefaultEnvironmentCall('Command', subst=1) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Sig.py b/engine/SCons/Sig.py new file mode 100644 index 0000000..51c1b1a --- /dev/null +++ b/engine/SCons/Sig.py @@ -0,0 +1,63 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Sig.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Place-holder for the old SCons.Sig module hierarchy + +This is no longer used, but code out there (such as the NSIS module on +the SCons wiki) may try to import SCons.Sig. If so, we generate a warning +that points them to the line that caused the import, and don't die. + +If someone actually tried to use the sub-modules or functions within +the package (for example, SCons.Sig.MD5.signature()), then they'll still +get an AttributeError, but at least they'll know where to start looking. +""" + +import SCons.Util +import SCons.Warnings + +msg = 'The SCons.Sig module no longer exists.\n' \ + ' Remove the following "import SCons.Sig" line to eliminate this warning:' + +SCons.Warnings.warn(SCons.Warnings.DeprecatedSigModuleWarning, msg) + +default_calc = None +default_module = None + +class MD5Null(SCons.Util.Null): + def __repr__(self): + return "MD5Null()" + +class TimeStampNull(SCons.Util.Null): + def __repr__(self): + return "TimeStampNull()" + +MD5 = MD5Null() +TimeStamp = TimeStampNull() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Subst.py b/engine/SCons/Subst.py new file mode 100644 index 0000000..d0c8c38 --- /dev/null +++ b/engine/SCons/Subst.py @@ -0,0 +1,904 @@ +"""SCons.Subst + +SCons string substitution. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Subst.py 5357 2011/09/09 21:31:03 bdeegan" + +import collections +import re + +import SCons.Errors + +from SCons.Util import is_String, is_Sequence + +# Indexed by the SUBST_* constants below. +_strconv = [SCons.Util.to_String_for_subst, + SCons.Util.to_String_for_subst, + SCons.Util.to_String_for_signature] + + + +AllowableExceptions = (IndexError, NameError) + +def SetAllowableExceptions(*excepts): + global AllowableExceptions + AllowableExceptions = [_f for _f in excepts if _f] + +def raise_exception(exception, target, s): + name = exception.__class__.__name__ + msg = "%s `%s' trying to evaluate `%s'" % (name, exception, s) + if target: + raise SCons.Errors.BuildError(target[0], msg) + else: + raise SCons.Errors.UserError(msg) + + + +class Literal(object): + """A wrapper for a string. If you use this object wrapped + around a string, then it will be interpreted as literal. + When passed to the command interpreter, all special + characters will be escaped.""" + def __init__(self, lstr): + self.lstr = lstr + + def __str__(self): + return self.lstr + + def escape(self, escape_func): + return escape_func(self.lstr) + + def for_signature(self): + return self.lstr + + def is_literal(self): + return 1 + +class SpecialAttrWrapper(object): + """This is a wrapper for what we call a 'Node special attribute.' + This is any of the attributes of a Node that we can reference from + Environment variable substitution, such as $TARGET.abspath or + $SOURCES[1].filebase. We implement the same methods as Literal + so we can handle special characters, plus a for_signature method, + such that we can return some canonical string during signature + calculation to avoid unnecessary rebuilds.""" + + def __init__(self, lstr, for_signature=None): + """The for_signature parameter, if supplied, will be the + canonical string we return from for_signature(). Else + we will simply return lstr.""" + self.lstr = lstr + if for_signature: + self.forsig = for_signature + else: + self.forsig = lstr + + def __str__(self): + return self.lstr + + def escape(self, escape_func): + return escape_func(self.lstr) + + def for_signature(self): + return self.forsig + + def is_literal(self): + return 1 + +def quote_spaces(arg): + """Generic function for putting double quotes around any string that + has white space in it.""" + if ' ' in arg or '\t' in arg: + return '"%s"' % arg + else: + return str(arg) + +class CmdStringHolder(collections.UserString): + """This is a special class used to hold strings generated by + scons_subst() and scons_subst_list(). It defines a special method + escape(). When passed a function with an escape algorithm for a + particular platform, it will return the contained string with the + proper escape sequences inserted. + """ + def __init__(self, cmd, literal=None): + collections.UserString.__init__(self, cmd) + self.literal = literal + + def is_literal(self): + return self.literal + + def escape(self, escape_func, quote_func=quote_spaces): + """Escape the string with the supplied function. The + function is expected to take an arbitrary string, then + return it with all special characters escaped and ready + for passing to the command interpreter. + + After calling this function, the next call to str() will + return the escaped string. + """ + + if self.is_literal(): + return escape_func(self.data) + elif ' ' in self.data or '\t' in self.data: + return quote_func(self.data) + else: + return self.data + +def escape_list(mylist, escape_func): + """Escape a list of arguments by running the specified escape_func + on every object in the list that has an escape() method.""" + def escape(obj, escape_func=escape_func): + try: + e = obj.escape + except AttributeError: + return obj + else: + return e(escape_func) + return list(map(escape, mylist)) + +class NLWrapper(object): + """A wrapper class that delays turning a list of sources or targets + into a NodeList until it's needed. The specified function supplied + when the object is initialized is responsible for turning raw nodes + into proxies that implement the special attributes like .abspath, + .source, etc. This way, we avoid creating those proxies just + "in case" someone is going to use $TARGET or the like, and only + go through the trouble if we really have to. + + In practice, this might be a wash performance-wise, but it's a little + cleaner conceptually... + """ + + def __init__(self, list, func): + self.list = list + self.func = func + def _return_nodelist(self): + return self.nodelist + def _gen_nodelist(self): + mylist = self.list + if mylist is None: + mylist = [] + elif not is_Sequence(mylist): + mylist = [mylist] + # The map(self.func) call is what actually turns + # a list into appropriate proxies. + self.nodelist = SCons.Util.NodeList(list(map(self.func, mylist))) + self._create_nodelist = self._return_nodelist + return self.nodelist + _create_nodelist = _gen_nodelist + + +class Targets_or_Sources(collections.UserList): + """A class that implements $TARGETS or $SOURCES expansions by in turn + wrapping a NLWrapper. This class handles the different methods used + to access the list, calling the NLWrapper to create proxies on demand. + + 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, nl): + self.nl = nl + def __getattr__(self, attr): + nl = self.nl._create_nodelist() + return getattr(nl, attr) + def __getitem__(self, i): + nl = self.nl._create_nodelist() + return nl[i] + def __getslice__(self, i, j): + nl = self.nl._create_nodelist() + i = max(i, 0); j = max(j, 0) + return nl[i:j] + def __str__(self): + nl = self.nl._create_nodelist() + return str(nl) + def __repr__(self): + nl = self.nl._create_nodelist() + return repr(nl) + +class Target_or_Source(object): + """A class that implements $TARGET or $SOURCE expansions by in turn + wrapping a NLWrapper. This class handles the different methods used + to access an individual proxy Node, calling the NLWrapper to create + a proxy on demand. + """ + def __init__(self, nl): + self.nl = nl + def __getattr__(self, attr): + nl = self.nl._create_nodelist() + try: + nl0 = nl[0] + except IndexError: + # If there is nothing in the list, then we have no attributes to + # pass through, so raise AttributeError for everything. + raise AttributeError("NodeList has no attribute: %s" % attr) + return getattr(nl0, attr) + def __str__(self): + nl = self.nl._create_nodelist() + if nl: + return str(nl[0]) + return '' + def __repr__(self): + nl = self.nl._create_nodelist() + if nl: + return repr(nl[0]) + return '' + +class NullNodeList(SCons.Util.NullSeq): + def __call__(self, *args, **kwargs): return '' + def __str__(self): return '' + +NullNodesList = NullNodeList() + +def subst_dict(target, source): + """Create a dictionary for substitution of special + construction variables. + + This translates the following special arguments: + + target - the target (object or array of objects), + used to generate the TARGET and TARGETS + construction variables + + source - the source (object or array of objects), + used to generate the SOURCES and SOURCE + construction variables + """ + dict = {} + + if target: + def get_tgt_subst_proxy(thing): + try: + subst_proxy = thing.get_subst_proxy() + except AttributeError: + subst_proxy = thing # probably a string, just return it + return subst_proxy + tnl = NLWrapper(target, get_tgt_subst_proxy) + dict['TARGETS'] = Targets_or_Sources(tnl) + dict['TARGET'] = Target_or_Source(tnl) + + # This is a total cheat, but hopefully this dictionary goes + # away soon anyway. We just let these expand to $TARGETS + # because that's "good enough" for the use of ToolSurrogates + # (see test/ToolSurrogate.py) to generate documentation. + dict['CHANGED_TARGETS'] = '$TARGETS' + dict['UNCHANGED_TARGETS'] = '$TARGETS' + else: + dict['TARGETS'] = NullNodesList + dict['TARGET'] = NullNodesList + + if source: + def get_src_subst_proxy(node): + try: + rfile = node.rfile + except AttributeError: + pass + else: + node = rfile() + try: + return node.get_subst_proxy() + except AttributeError: + return node # probably a String, just return it + snl = NLWrapper(source, get_src_subst_proxy) + dict['SOURCES'] = Targets_or_Sources(snl) + dict['SOURCE'] = Target_or_Source(snl) + + # This is a total cheat, but hopefully this dictionary goes + # away soon anyway. We just let these expand to $TARGETS + # because that's "good enough" for the use of ToolSurrogates + # (see test/ToolSurrogate.py) to generate documentation. + dict['CHANGED_SOURCES'] = '$SOURCES' + dict['UNCHANGED_SOURCES'] = '$SOURCES' + else: + dict['SOURCES'] = NullNodesList + dict['SOURCE'] = NullNodesList + + return dict + +# Constants for the "mode" parameter to scons_subst_list() and +# scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD +# gives a command line suitable for passing to a shell. SUBST_SIG +# gives a command line appropriate for calculating the signature +# of a command line...if this changes, we should rebuild. +SUBST_CMD = 0 +SUBST_RAW = 1 +SUBST_SIG = 2 + +_rm = re.compile(r'\$[()]') +_remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)') + +# Indexed by the SUBST_* constants above. +_regex_remove = [ _rm, None, _remove ] + +def _rm_list(list): + #return [ l for l in list if not l in ('$(', '$)') ] + return [l for l in list if not l in ('$(', '$)')] + +def _remove_list(list): + result = [] + do_append = result.append + for l in list: + if l == '$(': + do_append = lambda x: None + elif l == '$)': + do_append = result.append + else: + do_append(l) + return result + +# Indexed by the SUBST_* constants above. +_list_remove = [ _rm_list, None, _remove_list ] + +# Regular expressions for splitting strings and handling substitutions, +# for use by the scons_subst() and scons_subst_list() functions: +# +# The first expression compiled matches all of the $-introduced tokens +# that we need to process in some way, and is used for substitutions. +# The expressions it matches are: +# +# "$$" +# "$(" +# "$)" +# "$variable" [must begin with alphabetic or underscore] +# "${any stuff}" +# +# The second expression compiled is used for splitting strings into tokens +# to be processed, and it matches all of the tokens listed above, plus +# the following that affect how arguments do or don't get joined together: +# +# " " [white space] +# "non-white-space" [without any dollar signs] +# "$" [single dollar sign] +# +_dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}' +_dollar_exps = re.compile(r'(%s)' % _dollar_exps_str) +_separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str) + +# This regular expression is used to replace strings of multiple white +# space characters in the string result from the scons_subst() function. +_space_sep = re.compile(r'[\t ]+(?![^{]*})') + +def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): + """Expand a string or list containing construction variable + substitutions. + + This is the work-horse function for substitutions in file names + and the like. The companion scons_subst_list() function (below) + handles separating command lines into lists of arguments, so see + that function if that's what you're looking for. + """ + if isinstance(strSubst, str) and strSubst.find('$') < 0: + return strSubst + + class StringSubber(object): + """A class to construct the results of a scons_subst() call. + + This binds a specific construction environment, mode, target and + source with two methods (substitute() and expand()) that handle + the expansion. + """ + def __init__(self, env, mode, conv, gvars): + self.env = env + self.mode = mode + self.conv = conv + self.gvars = gvars + + def expand(self, s, lvars): + """Expand a single "token" as necessary, returning an + appropriate string containing the expansion. + + This handles expanding different types of things (strings, + lists, callables) appropriately. It calls the wrapper + substitute() method to re-expand things as necessary, so that + the results of expansions of side-by-side strings still get + re-evaluated separately, not smushed together. + """ + if is_String(s): + try: + s0, s1 = s[:2] + except (IndexError, ValueError): + return s + if s0 != '$': + return s + if s1 == '$': + return '$' + elif s1 in '()': + return s + else: + key = s[1:] + if key[0] == '{' or key.find('.') >= 0: + if key[0] == '{': + key = key[1:-1] + try: + s = eval(key, self.gvars, lvars) + except KeyboardInterrupt: + raise + except Exception, e: + if e.__class__ in AllowableExceptions: + return '' + raise_exception(e, lvars['TARGETS'], s) + else: + if key in lvars: + s = lvars[key] + elif key in self.gvars: + s = self.gvars[key] + elif not NameError in AllowableExceptions: + raise_exception(NameError(key), lvars['TARGETS'], s) + else: + return '' + + # Before re-expanding the result, handle + # recursive expansion by copying the local + # variable dictionary and overwriting a null + # string for the value of the variable name + # we just expanded. + # + # This could potentially be optimized by only + # copying lvars when s contains more expansions, + # but lvars is usually supposed to be pretty + # small, and deeply nested variable expansions + # are probably more the exception than the norm, + # so it should be tolerable for now. + lv = lvars.copy() + var = key.split('.')[0] + lv[var] = '' + return self.substitute(s, lv) + elif is_Sequence(s): + def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): + return conv(substitute(l, lvars)) + return list(map(func, s)) + elif callable(s): + try: + s = s(target=lvars['TARGETS'], + source=lvars['SOURCES'], + env=self.env, + for_signature=(self.mode != SUBST_CMD)) + except TypeError: + # This probably indicates that it's a callable + # object that doesn't match our calling arguments + # (like an Action). + if self.mode == SUBST_RAW: + return s + s = self.conv(s) + return self.substitute(s, lvars) + elif s is None: + return '' + else: + return s + + def substitute(self, args, lvars): + """Substitute expansions in an argument or list of arguments. + + This serves as a wrapper for splitting up a string into + separate tokens. + """ + if is_String(args) and not isinstance(args, CmdStringHolder): + args = str(args) # In case it's a UserString. + try: + def sub_match(match): + return self.conv(self.expand(match.group(1), lvars)) + result = _dollar_exps.sub(sub_match, args) + except TypeError: + # If the internal conversion routine doesn't return + # strings (it could be overridden to return Nodes, for + # example), then the 1.5.2 re module will throw this + # exception. Back off to a slower, general-purpose + # algorithm that works for all data types. + args = _separate_args.findall(args) + result = [] + for a in args: + result.append(self.conv(self.expand(a, lvars))) + if len(result) == 1: + result = result[0] + else: + result = ''.join(map(str, result)) + return result + else: + return self.expand(args, lvars) + + if conv is None: + conv = _strconv[mode] + + # Doing this every time is a bit of a waste, since the Executor + # has typically already populated the OverrideEnvironment with + # $TARGET/$SOURCE variables. We're keeping this (for now), though, + # because it supports existing behavior that allows us to call + # an Action directly with an arbitrary target+source pair, which + # we use in Tool/tex.py to handle calling $BIBTEX when necessary. + # If we dropped that behavior (or found another way to cover it), + # we could get rid of this call completely and just rely on the + # Executor setting the variables. + if 'TARGET' not in lvars: + d = subst_dict(target, source) + if d: + lvars = lvars.copy() + lvars.update(d) + + # We're (most likely) going to eval() things. If Python doesn't + # find a __builtins__ value in the global dictionary used for eval(), + # it copies the current global values for you. Avoid this by + # setting it explicitly and then deleting, so we don't pollute the + # construction environment Dictionary(ies) that are typically used + # for expansion. + gvars['__builtins__'] = __builtins__ + + ss = StringSubber(env, mode, conv, gvars) + result = ss.substitute(strSubst, lvars) + + try: + del gvars['__builtins__'] + except KeyError: + pass + + if is_String(result): + # Remove $(-$) pairs and any stuff in between, + # if that's appropriate. + remove = _regex_remove[mode] + if remove: + result = remove.sub('', result) + if mode != SUBST_RAW: + # Compress strings of white space characters into + # a single space. + result = _space_sep.sub(' ', result).strip() + elif is_Sequence(result): + remove = _list_remove[mode] + if remove: + result = remove(result) + + return result + +#Subst_List_Strings = {} + +def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): + """Substitute construction variables in a string (or list or other + object) and separate the arguments into a command list. + + The companion scons_subst() function (above) handles basic + substitutions within strings, so see that function instead + if that's what you're looking for. + """ +# try: +# Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1 +# except KeyError: +# Subst_List_Strings[strSubst] = 1 +# import SCons.Debug +# SCons.Debug.caller_trace(1) + class ListSubber(collections.UserList): + """A class to construct the results of a scons_subst_list() call. + + Like StringSubber, this class binds a specific construction + environment, mode, target and source with two methods + (substitute() and expand()) that handle the expansion. + + In addition, however, this class is used to track the state of + the result(s) we're gathering so we can do the appropriate thing + whenever we have to append another word to the result--start a new + line, start a new word, append to the current word, etc. We do + this by setting the "append" attribute to the right method so + that our wrapper methods only need ever call ListSubber.append(), + and the rest of the object takes care of doing the right thing + internally. + """ + def __init__(self, env, mode, conv, gvars): + collections.UserList.__init__(self, []) + self.env = env + self.mode = mode + self.conv = conv + self.gvars = gvars + + if self.mode == SUBST_RAW: + self.add_strip = lambda x: self.append(x) + else: + self.add_strip = lambda x: None + self.in_strip = None + self.next_line() + + def expand(self, s, lvars, within_list): + """Expand a single "token" as necessary, appending the + expansion to the current result. + + This handles expanding different types of things (strings, + lists, callables) appropriately. It calls the wrapper + substitute() method to re-expand things as necessary, so that + the results of expansions of side-by-side strings still get + re-evaluated separately, not smushed together. + """ + + if is_String(s): + try: + s0, s1 = s[:2] + except (IndexError, ValueError): + self.append(s) + return + if s0 != '$': + self.append(s) + return + if s1 == '$': + self.append('$') + elif s1 == '(': + self.open_strip('$(') + elif s1 == ')': + self.close_strip('$)') + else: + key = s[1:] + if key[0] == '{' or key.find('.') >= 0: + if key[0] == '{': + key = key[1:-1] + try: + s = eval(key, self.gvars, lvars) + except KeyboardInterrupt: + raise + except Exception, e: + if e.__class__ in AllowableExceptions: + return + raise_exception(e, lvars['TARGETS'], s) + else: + if key in lvars: + s = lvars[key] + elif key in self.gvars: + s = self.gvars[key] + elif not NameError in AllowableExceptions: + raise_exception(NameError(), lvars['TARGETS'], s) + else: + return + + # Before re-expanding the result, handle + # recursive expansion by copying the local + # variable dictionary and overwriting a null + # string for the value of the variable name + # we just expanded. + lv = lvars.copy() + var = key.split('.')[0] + lv[var] = '' + self.substitute(s, lv, 0) + self.this_word() + elif is_Sequence(s): + for a in s: + self.substitute(a, lvars, 1) + self.next_word() + elif callable(s): + try: + s = s(target=lvars['TARGETS'], + source=lvars['SOURCES'], + env=self.env, + for_signature=(self.mode != SUBST_CMD)) + except TypeError: + # This probably indicates that it's a callable + # object that doesn't match our calling arguments + # (like an Action). + if self.mode == SUBST_RAW: + self.append(s) + return + s = self.conv(s) + self.substitute(s, lvars, within_list) + elif s is None: + self.this_word() + else: + self.append(s) + + def substitute(self, args, lvars, within_list): + """Substitute expansions in an argument or list of arguments. + + This serves as a wrapper for splitting up a string into + separate tokens. + """ + + if is_String(args) and not isinstance(args, CmdStringHolder): + args = str(args) # In case it's a UserString. + args = _separate_args.findall(args) + for a in args: + if a[0] in ' \t\n\r\f\v': + if '\n' in a: + self.next_line() + elif within_list: + self.append(a) + else: + self.next_word() + else: + self.expand(a, lvars, within_list) + else: + self.expand(args, lvars, within_list) + + def next_line(self): + """Arrange for the next word to start a new line. This + is like starting a new word, except that we have to append + another line to the result.""" + collections.UserList.append(self, []) + self.next_word() + + def this_word(self): + """Arrange for the next word to append to the end of the + current last word in the result.""" + self.append = self.add_to_current_word + + def next_word(self): + """Arrange for the next word to start a new word.""" + self.append = self.add_new_word + + def add_to_current_word(self, x): + """Append the string x to the end of the current last word + in the result. If that is not possible, then just add + it as a new word. Make sure the entire concatenated string + inherits the object attributes of x (in particular, the + escape function) by wrapping it as CmdStringHolder.""" + + if not self.in_strip or self.mode != SUBST_SIG: + try: + current_word = self[-1][-1] + except IndexError: + self.add_new_word(x) + else: + # All right, this is a hack and it should probably + # be refactored out of existence in the future. + # The issue is that we want to smoosh words together + # and make one file name that gets escaped if + # we're expanding something like foo$EXTENSION, + # but we don't want to smoosh them together if + # it's something like >$TARGET, because then we'll + # treat the '>' like it's part of the file name. + # So for now, just hard-code looking for the special + # command-line redirection characters... + try: + last_char = str(current_word)[-1] + except IndexError: + last_char = '\0' + if last_char in '<>|': + self.add_new_word(x) + else: + y = current_word + x + + # We used to treat a word appended to a literal + # as a literal itself, but this caused problems + # with interpreting quotes around space-separated + # targets on command lines. Removing this makes + # none of the "substantive" end-to-end tests fail, + # so we'll take this out but leave it commented + # for now in case there's a problem not covered + # by the test cases and we need to resurrect this. + #literal1 = self.literal(self[-1][-1]) + #literal2 = self.literal(x) + y = self.conv(y) + if is_String(y): + #y = CmdStringHolder(y, literal1 or literal2) + y = CmdStringHolder(y, None) + self[-1][-1] = y + + def add_new_word(self, x): + if not self.in_strip or self.mode != SUBST_SIG: + literal = self.literal(x) + x = self.conv(x) + if is_String(x): + x = CmdStringHolder(x, literal) + self[-1].append(x) + self.append = self.add_to_current_word + + def literal(self, x): + try: + l = x.is_literal + except AttributeError: + return None + else: + return l() + + def open_strip(self, x): + """Handle the "open strip" $( token.""" + self.add_strip(x) + self.in_strip = 1 + + def close_strip(self, x): + """Handle the "close strip" $) token.""" + self.add_strip(x) + self.in_strip = None + + if conv is None: + conv = _strconv[mode] + + # Doing this every time is a bit of a waste, since the Executor + # has typically already populated the OverrideEnvironment with + # $TARGET/$SOURCE variables. We're keeping this (for now), though, + # because it supports existing behavior that allows us to call + # an Action directly with an arbitrary target+source pair, which + # we use in Tool/tex.py to handle calling $BIBTEX when necessary. + # If we dropped that behavior (or found another way to cover it), + # we could get rid of this call completely and just rely on the + # Executor setting the variables. + if 'TARGET' not in lvars: + d = subst_dict(target, source) + if d: + lvars = lvars.copy() + lvars.update(d) + + # We're (most likely) going to eval() things. If Python doesn't + # find a __builtins__ value in the global dictionary used for eval(), + # it copies the current global values for you. Avoid this by + # setting it explicitly and then deleting, so we don't pollute the + # construction environment Dictionary(ies) that are typically used + # for expansion. + gvars['__builtins__'] = __builtins__ + + ls = ListSubber(env, mode, conv, gvars) + ls.substitute(strSubst, lvars, 0) + + try: + del gvars['__builtins__'] + except KeyError: + pass + + return ls.data + +def scons_subst_once(strSubst, env, key): + """Perform single (non-recursive) substitution of a single + construction variable keyword. + + This is used when setting a variable when copying or overriding values + in an Environment. We want to capture (expand) the old value before + we override it, so people can do things like: + + env2 = env.Clone(CCFLAGS = '$CCFLAGS -g') + + We do this with some straightforward, brute-force code here... + """ + if isinstance(strSubst, str) and strSubst.find('$') < 0: + return strSubst + + matchlist = ['$' + key, '${' + key + '}'] + val = env.get(key, '') + def sub_match(match, val=val, matchlist=matchlist): + a = match.group(1) + if a in matchlist: + a = val + if is_Sequence(a): + return ' '.join(map(str, a)) + else: + return str(a) + + if is_Sequence(strSubst): + result = [] + for arg in strSubst: + if is_String(arg): + if arg in matchlist: + arg = val + if is_Sequence(arg): + result.extend(arg) + else: + result.append(arg) + else: + result.append(_dollar_exps.sub(sub_match, arg)) + else: + result.append(arg) + return result + elif is_String(strSubst): + return _dollar_exps.sub(sub_match, strSubst) + else: + return strSubst + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Taskmaster.py b/engine/SCons/Taskmaster.py new file mode 100644 index 0000000..e98a859 --- /dev/null +++ b/engine/SCons/Taskmaster.py @@ -0,0 +1,1025 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__doc__ = """ +Generic Taskmaster module for the SCons build engine. + +This module contains the primary interface(s) between a wrapping user +interface and the SCons build engine. There are two key classes here: + + Taskmaster + This is the main engine for walking the dependency graph and + calling things to decide what does or doesn't need to be built. + + Task + This is the base class for allowing a wrapping interface to + decide what does or doesn't actually need to be done. The + intention is for a wrapping interface to subclass this as + appropriate for different types of behavior it may need. + + The canonical example is the SCons native Python interface, + which has Task subclasses that handle its specific behavior, + like printing "`foo' is up to date" when a top-level target + doesn't need to be built, and handling the -c option by removing + targets as its "build" action. There is also a separate subclass + for suppressing this output when the -q option is used. + + The Taskmaster instantiates a Task object for each (set of) + target(s) that it decides need to be evaluated and/or built. +""" + +__revision__ = "src/engine/SCons/Taskmaster.py 5357 2011/09/09 21:31:03 bdeegan" + +from itertools import chain +import operator +import sys +import traceback + +import SCons.Errors +import SCons.Node +import SCons.Warnings + +StateString = SCons.Node.StateString +NODE_NO_STATE = SCons.Node.no_state +NODE_PENDING = SCons.Node.pending +NODE_EXECUTING = SCons.Node.executing +NODE_UP_TO_DATE = SCons.Node.up_to_date +NODE_EXECUTED = SCons.Node.executed +NODE_FAILED = SCons.Node.failed + +print_prepare = 0 # set by option --debug=prepare + +# A subsystem for recording stats about how different Nodes are handled by +# the main Taskmaster loop. There's no external control here (no need for +# a --debug= option); enable it by changing the value of CollectStats. + +CollectStats = None + +class Stats(object): + """ + A simple class for holding statistics about the disposition of a + Node by the Taskmaster. If we're collecting statistics, each Node + processed by the Taskmaster gets one of these attached, in which case + the Taskmaster records its decision each time it processes the Node. + (Ideally, that's just once per Node.) + """ + def __init__(self): + """ + Instantiates a Taskmaster.Stats object, initializing all + appropriate counters to zero. + """ + self.considered = 0 + self.already_handled = 0 + self.problem = 0 + self.child_failed = 0 + self.not_built = 0 + self.side_effects = 0 + self.build = 0 + +StatsNodes = [] + +fmt = "%(considered)3d "\ + "%(already_handled)3d " \ + "%(problem)3d " \ + "%(child_failed)3d " \ + "%(not_built)3d " \ + "%(side_effects)3d " \ + "%(build)3d " + +def dump_stats(): + for n in sorted(StatsNodes, key=lambda a: str(a)): + print (fmt % n.stats.__dict__) + str(n) + + + +class Task(object): + """ + Default SCons build engine task. + + This controls the interaction of the actual building of node + and the rest of the engine. + + This is expected to handle all of the normally-customizable + aspects of controlling a build, so any given application + *should* be able to do what it wants by sub-classing this + class and overriding methods as appropriate. If an application + needs to customze something by sub-classing Taskmaster (or + some other build engine class), we should first try to migrate + that functionality into this class. + + Note that it's generally a good idea for sub-classes to call + these methods explicitly to update state, etc., rather than + roll their own interaction with Taskmaster from scratch. + """ + def __init__(self, tm, targets, top, node): + self.tm = tm + self.targets = targets + self.top = top + self.node = node + self.exc_clear() + + def trace_message(self, method, node, description='node'): + fmt = '%-20s %s %s\n' + return fmt % (method + ':', description, self.tm.trace_node(node)) + + def display(self, message): + """ + Hook to allow the calling interface to display a message. + + This hook gets called as part of preparing a task for execution + (that is, a Node to be built). As part of figuring out what Node + should be built next, the actually target list may be altered, + along with a message describing the alteration. The calling + interface can subclass Task and provide a concrete implementation + of this method to see those messages. + """ + pass + + def prepare(self): + """ + Called just before the task is executed. + + This is mainly intended to give the target Nodes a chance to + unlink underlying files and make all necessary directories before + the Action is actually called to build the targets. + """ + global print_prepare + T = self.tm.trace + if T: T.write(self.trace_message(u'Task.prepare()', self.node)) + + # Now that it's the appropriate time, give the TaskMaster a + # chance to raise any exceptions it encountered while preparing + # this task. + self.exception_raise() + + if self.tm.message: + self.display(self.tm.message) + self.tm.message = None + + # Let the targets take care of any necessary preparations. + # This includes verifying that all of the necessary sources + # and dependencies exist, removing the target file(s), etc. + # + # As of April 2008, the get_executor().prepare() method makes + # sure that all of the aggregate sources necessary to build this + # Task's target(s) exist in one up-front check. The individual + # target t.prepare() methods check that each target's explicit + # or implicit dependencies exists, and also initialize the + # .sconsign info. + executor = self.targets[0].get_executor() + executor.prepare() + for t in executor.get_action_targets(): + if print_prepare: + print "Preparing target %s..."%t + for s in t.side_effects: + print "...with side-effect %s..."%s + t.prepare() + for s in t.side_effects: + if print_prepare: + print "...Preparing side-effect %s..."%s + s.prepare() + + def get_target(self): + """Fetch the target being built or updated by this task. + """ + return self.node + + def needs_execute(self): + # TODO(deprecate): "return True" is the old default behavior; + # change it to NotImplementedError (after running through the + # Deprecation Cycle) so the desired behavior is explicitly + # determined by which concrete subclass is used. + #raise NotImplementedError + msg = ('Taskmaster.Task is an abstract base class; instead of\n' + '\tusing it directly, ' + 'derive from it and override the abstract methods.') + SCons.Warnings.warn(SCons.Warnings.TaskmasterNeedsExecuteWarning, msg) + return True + + def execute(self): + """ + Called to execute the task. + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + prepare(), executed() or failed(). + """ + T = self.tm.trace + if T: T.write(self.trace_message(u'Task.execute()', self.node)) + + try: + everything_was_cached = 1 + for t in self.targets: + if t.retrieve_from_cache(): + # Call the .built() method without calling the + # .push_to_cache() method, since we just got the + # target from the cache and don't need to push + # it back there. + t.set_state(NODE_EXECUTED) + t.built() + else: + everything_was_cached = 0 + break + if not everything_was_cached: + self.targets[0].build() + except SystemExit: + exc_value = sys.exc_info()[1] + raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code) + except SCons.Errors.UserError: + raise + except SCons.Errors.BuildError: + raise + except Exception, e: + buildError = SCons.Errors.convert_to_BuildError(e) + buildError.node = self.targets[0] + buildError.exc_info = sys.exc_info() + raise buildError + + def executed_without_callbacks(self): + """ + Called when the task has been successfully executed + and the Taskmaster instance doesn't want to call + the Node's callback methods. + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.executed_without_callbacks()', + self.node)) + + for t in self.targets: + if t.get_state() == NODE_EXECUTING: + for side_effect in t.side_effects: + side_effect.set_state(NODE_NO_STATE) + t.set_state(NODE_EXECUTED) + + def executed_with_callbacks(self): + """ + Called when the task has been successfully executed and + the Taskmaster instance wants to call the Node's callback + methods. + + This may have been a do-nothing operation (to preserve build + order), so we must check the node's state before deciding whether + it was "built", in which case we call the appropriate Node method. + In any event, we always call "visited()", which will handle any + post-visit actions that must take place regardless of whether + or not the target was an actual built target or a source Node. + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.executed_with_callbacks()', + self.node)) + + for t in self.targets: + if t.get_state() == NODE_EXECUTING: + for side_effect in t.side_effects: + side_effect.set_state(NODE_NO_STATE) + t.set_state(NODE_EXECUTED) + t.push_to_cache() + t.built() + t.visited() + + executed = executed_with_callbacks + + def failed(self): + """ + Default action when a task fails: stop the build. + + Note: Although this function is normally invoked on nodes in + the executing state, it might also be invoked on up-to-date + nodes when using Configure(). + """ + self.fail_stop() + + def fail_stop(self): + """ + Explicit stop-the-build failure. + + This sets failure status on the target nodes and all of + their dependent parent nodes. + + Note: Although this function is normally invoked on nodes in + the executing state, it might also be invoked on up-to-date + nodes when using Configure(). + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.failed_stop()', self.node)) + + # Invoke will_not_build() to clean-up the pending children + # list. + self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) + + # Tell the taskmaster to not start any new tasks + self.tm.stop() + + # We're stopping because of a build failure, but give the + # calling Task class a chance to postprocess() the top-level + # target under which the build failure occurred. + self.targets = [self.tm.current_top] + self.top = 1 + + def fail_continue(self): + """ + Explicit continue-the-build failure. + + This sets failure status on the target nodes and all of + their dependent parent nodes. + + Note: Although this function is normally invoked on nodes in + the executing state, it might also be invoked on up-to-date + nodes when using Configure(). + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.failed_continue()', self.node)) + + self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) + + def make_ready_all(self): + """ + Marks all targets in a task ready for execution. + + This is used when the interface needs every target Node to be + visited--the canonical example being the "scons -c" option. + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.make_ready_all()', self.node)) + + self.out_of_date = self.targets[:] + for t in self.targets: + t.disambiguate().set_state(NODE_EXECUTING) + for s in t.side_effects: + # add disambiguate here to mirror the call on targets above + s.disambiguate().set_state(NODE_EXECUTING) + + def make_ready_current(self): + """ + Marks all targets in a task ready for execution if any target + is not current. + + This is the default behavior for building only what's necessary. + """ + T = self.tm.trace + if T: T.write(self.trace_message(u'Task.make_ready_current()', + self.node)) + + self.out_of_date = [] + needs_executing = False + for t in self.targets: + try: + t.disambiguate().make_ready() + is_up_to_date = not t.has_builder() or \ + (not t.always_build and t.is_up_to_date()) + except EnvironmentError, e: + raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) + + if not is_up_to_date: + self.out_of_date.append(t) + needs_executing = True + + if needs_executing: + for t in self.targets: + t.set_state(NODE_EXECUTING) + for s in t.side_effects: + # add disambiguate here to mirror the call on targets in first loop above + s.disambiguate().set_state(NODE_EXECUTING) + else: + for t in self.targets: + # We must invoke visited() to ensure that the node + # information has been computed before allowing the + # parent nodes to execute. (That could occur in a + # parallel build...) + t.visited() + t.set_state(NODE_UP_TO_DATE) + + make_ready = make_ready_current + + def postprocess(self): + """ + Post-processes a task after it's been executed. + + This examines all the targets just built (or not, we don't care + if the build was successful, or even if there was no build + because everything was up-to-date) to see if they have any + waiting parent Nodes, or Nodes waiting on a common side effect, + that can be put back on the candidates list. + """ + T = self.tm.trace + if T: T.write(self.trace_message(u'Task.postprocess()', self.node)) + + # We may have built multiple targets, some of which may have + # common parents waiting for this build. Count up how many + # targets each parent was waiting for so we can subtract the + # values later, and so we *don't* put waiting side-effect Nodes + # back on the candidates list if the Node is also a waiting + # parent. + + targets = set(self.targets) + + pending_children = self.tm.pending_children + parents = {} + for t in targets: + # A node can only be in the pending_children set if it has + # some waiting_parents. + if t.waiting_parents: + if T: T.write(self.trace_message(u'Task.postprocess()', + t, + 'removing')) + pending_children.discard(t) + for p in t.waiting_parents: + parents[p] = parents.get(p, 0) + 1 + + for t in targets: + for s in t.side_effects: + if s.get_state() == NODE_EXECUTING: + s.set_state(NODE_NO_STATE) + for p in s.waiting_parents: + parents[p] = parents.get(p, 0) + 1 + for p in s.waiting_s_e: + if p.ref_count == 0: + self.tm.candidates.append(p) + + for p, subtract in parents.items(): + p.ref_count = p.ref_count - subtract + if T: T.write(self.trace_message(u'Task.postprocess()', + p, + 'adjusted parent ref count')) + if p.ref_count == 0: + self.tm.candidates.append(p) + + for t in targets: + t.postprocess() + + # Exception handling subsystem. + # + # Exceptions that occur while walking the DAG or examining Nodes + # must be raised, but must be raised at an appropriate time and in + # a controlled manner so we can, if necessary, recover gracefully, + # possibly write out signature information for Nodes we've updated, + # etc. This is done by having the Taskmaster tell us about the + # exception, and letting + + def exc_info(self): + """ + Returns info about a recorded exception. + """ + return self.exception + + def exc_clear(self): + """ + Clears any recorded exception. + + This also changes the "exception_raise" attribute to point + to the appropriate do-nothing method. + """ + self.exception = (None, None, None) + self.exception_raise = self._no_exception_to_raise + + def exception_set(self, exception=None): + """ + Records an exception to be raised at the appropriate time. + + This also changes the "exception_raise" attribute to point + to the method that will, in fact + """ + if not exception: + exception = sys.exc_info() + self.exception = exception + self.exception_raise = self._exception_raise + + def _no_exception_to_raise(self): + pass + + def _exception_raise(self): + """ + Raises a pending exception that was recorded while getting a + Task ready for execution. + """ + exc = self.exc_info()[:] + try: + exc_type, exc_value, exc_traceback = exc + except ValueError: + exc_type, exc_value = exc + exc_traceback = None + raise exc_type, exc_value, exc_traceback + +class AlwaysTask(Task): + def needs_execute(self): + """ + Always returns True (indicating this Task should always + be executed). + + Subclasses that need this behavior (as opposed to the default + of only executing Nodes that are out of date w.r.t. their + dependencies) can use this as follows: + + class MyTaskSubclass(SCons.Taskmaster.Task): + needs_execute = SCons.Taskmaster.Task.execute_always + """ + return True + +class OutOfDateTask(Task): + def needs_execute(self): + """ + Returns True (indicating this Task should be executed) if this + Task's target state indicates it needs executing, which has + already been determined by an earlier up-to-date check. + """ + return self.targets[0].get_state() == SCons.Node.executing + + +def find_cycle(stack, visited): + if stack[-1] in visited: + return None + visited.add(stack[-1]) + for n in stack[-1].waiting_parents: + stack.append(n) + if stack[0] == stack[-1]: + return stack + if find_cycle(stack, visited): + return stack + stack.pop() + return None + + +class Taskmaster(object): + """ + The Taskmaster for walking the dependency DAG. + """ + + def __init__(self, targets=[], tasker=None, order=None, trace=None): + self.original_top = targets + self.top_targets_left = targets[:] + self.top_targets_left.reverse() + self.candidates = [] + if tasker is None: + tasker = OutOfDateTask + self.tasker = tasker + if not order: + order = lambda l: l + self.order = order + self.message = None + self.trace = trace + self.next_candidate = self.find_next_candidate + self.pending_children = set() + + def find_next_candidate(self): + """ + Returns the next candidate Node for (potential) evaluation. + + The candidate list (really a stack) initially consists of all of + the top-level (command line) targets provided when the Taskmaster + was initialized. While we walk the DAG, visiting Nodes, all the + children that haven't finished processing get pushed on to the + candidate list. Each child can then be popped and examined in + turn for whether *their* children are all up-to-date, in which + case a Task will be created for their actual evaluation and + potential building. + + Here is where we also allow candidate Nodes to alter the list of + Nodes that should be examined. This is used, for example, when + invoking SCons in a source directory. A source directory Node can + return its corresponding build directory Node, essentially saying, + "Hey, you really need to build this thing over here instead." + """ + try: + return self.candidates.pop() + except IndexError: + pass + try: + node = self.top_targets_left.pop() + except IndexError: + return None + self.current_top = node + alt, message = node.alter_targets() + if alt: + self.message = message + self.candidates.append(node) + self.candidates.extend(self.order(alt)) + node = self.candidates.pop() + return node + + def no_next_candidate(self): + """ + Stops Taskmaster processing by not returning a next candidate. + + Note that we have to clean-up the Taskmaster candidate list + because the cycle detection depends on the fact all nodes have + been processed somehow. + """ + while self.candidates: + candidates = self.candidates + self.candidates = [] + self.will_not_build(candidates) + return None + + def _validate_pending_children(self): + """ + Validate the content of the pending_children set. Assert if an + internal error is found. + + This function is used strictly for debugging the taskmaster by + checking that no invariants are violated. It is not used in + normal operation. + + The pending_children set is used to detect cycles in the + dependency graph. We call a "pending child" a child that is + found in the "pending" state when checking the dependencies of + its parent node. + + A pending child can occur when the Taskmaster completes a loop + through a cycle. For example, lets imagine a graph made of + three node (A, B and C) making a cycle. The evaluation starts + at node A. The taskmaster first consider whether node A's + child B is up-to-date. Then, recursively, node B needs to + check whether node C is up-to-date. This leaves us with a + dependency graph looking like: + + Next candidate \ + \ + Node A (Pending) --> Node B(Pending) --> Node C (NoState) + ^ | + | | + +-------------------------------------+ + + Now, when the Taskmaster examines the Node C's child Node A, + it finds that Node A is in the "pending" state. Therefore, + Node A is a pending child of node C. + + Pending children indicate that the Taskmaster has potentially + loop back through a cycle. We say potentially because it could + also occur when a DAG is evaluated in parallel. For example, + consider the following graph: + + + Node A (Pending) --> Node B(Pending) --> Node C (Pending) --> ... + | ^ + | | + +----------> Node D (NoState) --------+ + / + Next candidate / + + The Taskmaster first evaluates the nodes A, B, and C and + starts building some children of node C. Assuming, that the + maximum parallel level has not been reached, the Taskmaster + will examine Node D. It will find that Node C is a pending + child of Node D. + + In summary, evaluating a graph with a cycle will always + involve a pending child at one point. A pending child might + indicate either a cycle or a diamond-shaped DAG. Only a + fraction of the nodes ends-up being a "pending child" of + another node. This keeps the pending_children set small in + practice. + + We can differentiate between the two cases if we wait until + the end of the build. At this point, all the pending children + nodes due to a diamond-shaped DAG will have been properly + built (or will have failed to build). But, the pending + children involved in a cycle will still be in the pending + state. + + The taskmaster removes nodes from the pending_children set as + soon as a pending_children node moves out of the pending + state. This also helps to keep the pending_children set small. + """ + + for n in self.pending_children: + assert n.state in (NODE_PENDING, NODE_EXECUTING), \ + (str(n), StateString[n.state]) + assert len(n.waiting_parents) != 0, (str(n), len(n.waiting_parents)) + for p in n.waiting_parents: + assert p.ref_count > 0, (str(n), str(p), p.ref_count) + + + def trace_message(self, message): + return 'Taskmaster: %s\n' % message + + def trace_node(self, node): + return '<%-10s %-3s %s>' % (StateString[node.get_state()], + node.ref_count, + repr(str(node))) + + def _find_next_ready_node(self): + """ + Finds the next node that is ready to be built. + + This is *the* main guts of the DAG walk. We loop through the + list of candidates, looking for something that has no un-built + children (i.e., that is a leaf Node or has dependencies that are + all leaf Nodes or up-to-date). Candidate Nodes are re-scanned + (both the target Node itself and its sources, which are always + scanned in the context of a given target) to discover implicit + dependencies. A Node that must wait for some children to be + built will be put back on the candidates list after the children + have finished building. A Node that has been put back on the + candidates list in this way may have itself (or its sources) + re-scanned, in order to handle generated header files (e.g.) and + the implicit dependencies therein. + + Note that this method does not do any signature calculation or + up-to-date check itself. All of that is handled by the Task + class. This is purely concerned with the dependency graph walk. + """ + + self.ready_exc = None + + T = self.trace + if T: T.write(u'\n' + self.trace_message('Looking for a node to evaluate')) + + while True: + node = self.next_candidate() + if node is None: + if T: T.write(self.trace_message('No candidate anymore.') + u'\n') + return None + + node = node.disambiguate() + state = node.get_state() + + # For debugging only: + # + # try: + # self._validate_pending_children() + # except: + # self.ready_exc = sys.exc_info() + # return node + + if CollectStats: + if not hasattr(node, 'stats'): + node.stats = Stats() + StatsNodes.append(node) + S = node.stats + S.considered = S.considered + 1 + else: + S = None + + if T: T.write(self.trace_message(u' Considering node %s and its children:' % self.trace_node(node))) + + if state == NODE_NO_STATE: + # Mark this node as being on the execution stack: + node.set_state(NODE_PENDING) + elif state > NODE_PENDING: + # Skip this node if it has already been evaluated: + if S: S.already_handled = S.already_handled + 1 + if T: T.write(self.trace_message(u' already handled (executed)')) + continue + + executor = node.get_executor() + + try: + children = executor.get_all_children() + except SystemExit: + exc_value = sys.exc_info()[1] + e = SCons.Errors.ExplicitExit(node, exc_value.code) + self.ready_exc = (SCons.Errors.ExplicitExit, e) + if T: T.write(self.trace_message(' SystemExit')) + return node + except Exception, e: + # We had a problem just trying to figure out the + # children (like a child couldn't be linked in to a + # VariantDir, or a Scanner threw something). Arrange to + # raise the exception when the Task is "executed." + self.ready_exc = sys.exc_info() + if S: S.problem = S.problem + 1 + if T: T.write(self.trace_message(' exception %s while scanning children.\n' % e)) + return node + + children_not_visited = [] + children_pending = set() + children_not_ready = [] + children_failed = False + + for child in chain(executor.get_all_prerequisites(), children): + childstate = child.get_state() + + if T: T.write(self.trace_message(u' ' + self.trace_node(child))) + + if childstate == NODE_NO_STATE: + children_not_visited.append(child) + elif childstate == NODE_PENDING: + children_pending.add(child) + elif childstate == NODE_FAILED: + children_failed = True + + if childstate <= NODE_EXECUTING: + children_not_ready.append(child) + + + # These nodes have not even been visited yet. Add + # them to the list so that on some next pass we can + # take a stab at evaluating them (or their children). + children_not_visited.reverse() + self.candidates.extend(self.order(children_not_visited)) + #if T and children_not_visited: + # T.write(self.trace_message(' adding to candidates: %s' % map(str, children_not_visited))) + # T.write(self.trace_message(' candidates now: %s\n' % map(str, self.candidates))) + + # Skip this node if any of its children have failed. + # + # This catches the case where we're descending a top-level + # target and one of our children failed while trying to be + # built by a *previous* descent of an earlier top-level + # target. + # + # It can also occur if a node is reused in multiple + # targets. One first descends though the one of the + # target, the next time occurs through the other target. + # + # Note that we can only have failed_children if the + # --keep-going flag was used, because without it the build + # will stop before diving in the other branch. + # + # Note that even if one of the children fails, we still + # added the other children to the list of candidate nodes + # to keep on building (--keep-going). + if children_failed: + for n in executor.get_action_targets(): + n.set_state(NODE_FAILED) + + if S: S.child_failed = S.child_failed + 1 + if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node))) + continue + + if children_not_ready: + for child in children_not_ready: + # We're waiting on one or more derived targets + # that have not yet finished building. + if S: S.not_built = S.not_built + 1 + + # Add this node to the waiting parents lists of + # anything we're waiting on, with a reference + # count so we can be put back on the list for + # re-evaluation when they've all finished. + node.ref_count = node.ref_count + child.add_to_waiting_parents(node) + if T: T.write(self.trace_message(u' adjusted ref count: %s, child %s' % + (self.trace_node(node), repr(str(child))))) + + if T: + for pc in children_pending: + T.write(self.trace_message(' adding %s to the pending children set\n' % + self.trace_node(pc))) + self.pending_children = self.pending_children | children_pending + + continue + + # Skip this node if it has side-effects that are + # currently being built: + wait_side_effects = False + for se in executor.get_action_side_effects(): + if se.get_state() == NODE_EXECUTING: + se.add_to_waiting_s_e(node) + wait_side_effects = True + + if wait_side_effects: + if S: S.side_effects = S.side_effects + 1 + continue + + # The default when we've gotten through all of the checks above: + # this node is ready to be built. + if S: S.build = S.build + 1 + if T: T.write(self.trace_message(u'Evaluating %s\n' % + self.trace_node(node))) + + # For debugging only: + # + # try: + # self._validate_pending_children() + # except: + # self.ready_exc = sys.exc_info() + # return node + + return node + + return None + + def next_task(self): + """ + Returns the next task to be executed. + + This simply asks for the next Node to be evaluated, and then wraps + it in the specific Task subclass with which we were initialized. + """ + node = self._find_next_ready_node() + + if node is None: + return None + + tlist = node.get_executor().get_all_targets() + + task = self.tasker(self, tlist, node in self.original_top, node) + try: + task.make_ready() + except: + # We had a problem just trying to get this task ready (like + # a child couldn't be linked in to a VariantDir when deciding + # whether this node is current). Arrange to raise the + # exception when the Task is "executed." + self.ready_exc = sys.exc_info() + + if self.ready_exc: + task.exception_set(self.ready_exc) + + self.ready_exc = None + + return task + + def will_not_build(self, nodes, node_func=lambda n: None): + """ + Perform clean-up about nodes that will never be built. Invokes + a user defined function on all of these nodes (including all + of their parents). + """ + + T = self.trace + + pending_children = self.pending_children + + to_visit = set(nodes) + pending_children = pending_children - to_visit + + if T: + for n in nodes: + T.write(self.trace_message(' removing node %s from the pending children set\n' % + self.trace_node(n))) + try: + while len(to_visit): + node = to_visit.pop() + node_func(node) + + # Prune recursion by flushing the waiting children + # list immediately. + parents = node.waiting_parents + node.waiting_parents = set() + + to_visit = to_visit | parents + pending_children = pending_children - parents + + for p in parents: + p.ref_count = p.ref_count - 1 + if T: T.write(self.trace_message(' removing parent %s from the pending children set\n' % + self.trace_node(p))) + except KeyError: + # The container to_visit has been emptied. + pass + + # We have the stick back the pending_children list into the + # taskmaster because the python 1.5.2 compatibility does not + # allow us to use in-place updates + self.pending_children = pending_children + + def stop(self): + """ + Stops the current build completely. + """ + self.next_candidate = self.no_next_candidate + + def cleanup(self): + """ + Check for dependency cycles. + """ + if not self.pending_children: + return + + nclist = [(n, find_cycle([n], set())) for n in self.pending_children] + + genuine_cycles = [ + node for node,cycle in nclist + if cycle or node.get_state() != NODE_EXECUTED + ] + if not genuine_cycles: + # All of the "cycles" found were single nodes in EXECUTED state, + # which is to say, they really weren't cycles. Just return. + return + + desc = 'Found dependency cycle(s):\n' + for node, cycle in nclist: + if cycle: + desc = desc + " " + " -> ".join(map(str, cycle)) + "\n" + else: + desc = desc + \ + " Internal Error: no cycle found for node %s (%s) in state %s\n" % \ + (node, repr(node), StateString[node.get_state()]) + + raise SCons.Errors.UserError(desc) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/386asm.py b/engine/SCons/Tool/386asm.py new file mode 100644 index 0000000..27748cb --- /dev/null +++ b/engine/SCons/Tool/386asm.py @@ -0,0 +1,61 @@ +"""SCons.Tool.386asm + +Tool specification for the 386ASM assembler for the Phar Lap ETS embedded +operating system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/386asm.py 5357 2011/09/09 21:31:03 bdeegan" + +from SCons.Tool.PharLapCommon import addPharLapPaths +import SCons.Util + +as_module = __import__('as', globals(), locals(), []) + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + as_module.generate(env) + + env['AS'] = '386asm' + env['ASFLAGS'] = SCons.Util.CLVar('') + env['ASPPFLAGS'] = '$ASFLAGS' + env['ASCOM'] = '$AS $ASFLAGS $SOURCES -o $TARGET' + env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES -o $TARGET' + + addPharLapPaths(env) + +def exists(env): + return env.Detect('386asm') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/BitKeeper.py b/engine/SCons/Tool/BitKeeper.py new file mode 100644 index 0000000..2b8297b --- /dev/null +++ b/engine/SCons/Tool/BitKeeper.py @@ -0,0 +1,67 @@ +"""SCons.Tool.BitKeeper.py + +Tool-specific initialization for the BitKeeper source code control +system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/BitKeeper.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + BitKeeper to an Environment.""" + + def BitKeeperFactory(env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The BitKeeper() factory is deprecated and there is no replacement.""") + act = SCons.Action.Action("$BITKEEPERCOM", "$BITKEEPERCOMSTR") + return SCons.Builder.Builder(action = act, env = env) + + #setattr(env, 'BitKeeper', BitKeeperFactory) + env.BitKeeper = BitKeeperFactory + + env['BITKEEPER'] = 'bk' + env['BITKEEPERGET'] = '$BITKEEPER get' + env['BITKEEPERGETFLAGS'] = SCons.Util.CLVar('') + env['BITKEEPERCOM'] = '$BITKEEPERGET $BITKEEPERGETFLAGS $TARGET' + +def exists(env): + return env.Detect('bk') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/CVS.py b/engine/SCons/Tool/CVS.py new file mode 100644 index 0000000..f1d3561 --- /dev/null +++ b/engine/SCons/Tool/CVS.py @@ -0,0 +1,73 @@ +"""SCons.Tool.CVS.py + +Tool-specific initialization for CVS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/CVS.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + CVS to an Environment.""" + + def CVSFactory(repos, module='', env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The CVS() factory is deprecated and there is no replacement.""") + # fail if repos is not an absolute path name? + if module != '': + # Don't use os.path.join() because the name we fetch might + # be across a network and must use POSIX slashes as separators. + module = module + '/' + env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS -d ${TARGET.dir} $CVSMODULE${TARGET.posix}' + act = SCons.Action.Action('$CVSCOM', '$CVSCOMSTR') + return SCons.Builder.Builder(action = act, + env = env, + CVSREPOSITORY = repos, + CVSMODULE = module) + + #setattr(env, 'CVS', CVSFactory) + env.CVS = CVSFactory + + env['CVS'] = 'cvs' + env['CVSFLAGS'] = SCons.Util.CLVar('-d $CVSREPOSITORY') + env['CVSCOFLAGS'] = SCons.Util.CLVar('') + env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS ${TARGET.posix}' + +def exists(env): + return env.Detect('cvs') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/FortranCommon.py b/engine/SCons/Tool/FortranCommon.py new file mode 100644 index 0000000..4311cac --- /dev/null +++ b/engine/SCons/Tool/FortranCommon.py @@ -0,0 +1,263 @@ +"""SCons.Tool.FortranCommon + +Stuff for processing Fortran, common to all fortran dialects. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/FortranCommon.py 5357 2011/09/09 21:31:03 bdeegan" + +import re +import os.path + +import SCons.Action +import SCons.Defaults +import SCons.Scanner.Fortran +import SCons.Tool +import SCons.Util + +def isfortran(env, source): + """Return 1 if any of code in source has fortran files in it, 0 + otherwise.""" + try: + fsuffixes = env['FORTRANSUFFIXES'] + except KeyError: + # If no FORTRANSUFFIXES, no fortran tool, so there is no need to look + # for fortran sources. + return 0 + + if not source: + # Source might be None for unusual cases like SConf. + return 0 + for s in source: + if s.sources: + ext = os.path.splitext(str(s.sources[0]))[1] + if ext in fsuffixes: + return 1 + return 0 + +def _fortranEmitter(target, source, env): + node = source[0].rfile() + if not node.exists() and not node.is_derived(): + print "Could not locate " + str(node.name) + return ([], []) + mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" + cre = re.compile(mod_regex,re.M) + # Retrieve all USE'd module names + modules = cre.findall(node.get_text_contents()) + # Remove unique items from the list + modules = SCons.Util.unique(modules) + # Convert module name to a .mod filename + suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source) + moddir = env.subst('$FORTRANMODDIR', target=target, source=source) + modules = [x.lower() + suffix for x in modules] + for m in modules: + target.append(env.fs.File(m, moddir)) + return (target, source) + +def FortranEmitter(target, source, env): + target, source = _fortranEmitter(target, source, env) + return SCons.Defaults.StaticObjectEmitter(target, source, env) + +def ShFortranEmitter(target, source, env): + target, source = _fortranEmitter(target, source, env) + return SCons.Defaults.SharedObjectEmitter(target, source, env) + +def ComputeFortranSuffixes(suffixes, ppsuffixes): + """suffixes are fortran source files, and ppsuffixes the ones to be + pre-processed. Both should be sequences, not strings.""" + assert len(suffixes) > 0 + s = suffixes[0] + sup = s.upper() + upper_suffixes = [_.upper() for _ in suffixes] + if SCons.Util.case_sensitive_suffixes(s, sup): + ppsuffixes.extend(upper_suffixes) + else: + suffixes.extend(upper_suffixes) + +def CreateDialectActions(dialect): + """Create dialect specific actions.""" + CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect) + CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect) + ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect) + ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect) + + return CompAction, CompPPAction, ShCompAction, ShCompPPAction + +def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0): + """Add dialect specific construction variables.""" + ComputeFortranSuffixes(suffixes, ppsuffixes) + + fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect) + + for suffix in suffixes + ppsuffixes: + SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan) + + env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes) + + compaction, compppaction, shcompaction, shcompppaction = \ + CreateDialectActions(dialect) + + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in suffixes: + static_obj.add_action(suffix, compaction) + shared_obj.add_action(suffix, shcompaction) + static_obj.add_emitter(suffix, FortranEmitter) + shared_obj.add_emitter(suffix, ShFortranEmitter) + + for suffix in ppsuffixes: + static_obj.add_action(suffix, compppaction) + shared_obj.add_action(suffix, shcompppaction) + static_obj.add_emitter(suffix, FortranEmitter) + shared_obj.add_emitter(suffix, ShFortranEmitter) + + if '%sFLAGS' % dialect not in env: + env['%sFLAGS' % dialect] = SCons.Util.CLVar('') + + if 'SH%sFLAGS' % dialect not in env: + env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) + + # If a tool does not define fortran prefix/suffix for include path, use C ones + if 'INC%sPREFIX' % dialect not in env: + env['INC%sPREFIX' % dialect] = '$INCPREFIX' + + if 'INC%sSUFFIX' % dialect not in env: + env['INC%sSUFFIX' % dialect] = '$INCSUFFIX' + + env['_%sINCFLAGS' % dialect] = '$( ${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' % (dialect, dialect, dialect) + + if support_module == 1: + env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) + env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) + env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) + env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) + else: + env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) + env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) + env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) + env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) + +def add_fortran_to_env(env): + """Add Builders and construction variables for Fortran to an Environment.""" + try: + FortranSuffixes = env['FORTRANFILESUFFIXES'] + except KeyError: + FortranSuffixes = ['.f', '.for', '.ftn'] + + #print "Adding %s to fortran suffixes" % FortranSuffixes + try: + FortranPPSuffixes = env['FORTRANPPFILESUFFIXES'] + except KeyError: + FortranPPSuffixes = ['.fpp', '.FPP'] + + DialectAddToEnv(env, "FORTRAN", FortranSuffixes, + FortranPPSuffixes, support_module = 1) + + env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX + env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX + + env['FORTRANMODDIR'] = '' # where the compiler should place .mod files + env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX + env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX + env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + +def add_f77_to_env(env): + """Add Builders and construction variables for f77 to an Environment.""" + try: + F77Suffixes = env['F77FILESUFFIXES'] + except KeyError: + F77Suffixes = ['.f77'] + + #print "Adding %s to f77 suffixes" % F77Suffixes + try: + F77PPSuffixes = env['F77PPFILESUFFIXES'] + except KeyError: + F77PPSuffixes = [] + + DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes) + +def add_f90_to_env(env): + """Add Builders and construction variables for f90 to an Environment.""" + try: + F90Suffixes = env['F90FILESUFFIXES'] + except KeyError: + F90Suffixes = ['.f90'] + + #print "Adding %s to f90 suffixes" % F90Suffixes + try: + F90PPSuffixes = env['F90PPFILESUFFIXES'] + except KeyError: + F90PPSuffixes = [] + + DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes, + support_module = 1) + +def add_f95_to_env(env): + """Add Builders and construction variables for f95 to an Environment.""" + try: + F95Suffixes = env['F95FILESUFFIXES'] + except KeyError: + F95Suffixes = ['.f95'] + + #print "Adding %s to f95 suffixes" % F95Suffixes + try: + F95PPSuffixes = env['F95PPFILESUFFIXES'] + except KeyError: + F95PPSuffixes = [] + + DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes, + support_module = 1) + +def add_f03_to_env(env): + """Add Builders and construction variables for f03 to an Environment.""" + try: + F03Suffixes = env['F03FILESUFFIXES'] + except KeyError: + F03Suffixes = ['.f03'] + + #print "Adding %s to f95 suffixes" % F95Suffixes + try: + F03PPSuffixes = env['F03PPFILESUFFIXES'] + except KeyError: + F03PPSuffixes = [] + + DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes, + support_module = 1) + +def add_all_to_env(env): + """Add builders and construction variables for all supported fortran + dialects.""" + add_fortran_to_env(env) + add_f77_to_env(env) + add_f90_to_env(env) + add_f95_to_env(env) + add_f03_to_env(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/JavaCommon.py b/engine/SCons/Tool/JavaCommon.py new file mode 100644 index 0000000..f1eab72 --- /dev/null +++ b/engine/SCons/Tool/JavaCommon.py @@ -0,0 +1,323 @@ +"""SCons.Tool.JavaCommon + +Stuff for processing Java. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/JavaCommon.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path +import re + +java_parsing = 1 + +default_java_version = '1.4' + +if java_parsing: + # Parse Java files for class names. + # + # This is a really cool parser from Charles Crain + # that finds appropriate class names in Java source. + + # A regular expression that will find, in a java file: + # newlines; + # double-backslashes; + # a single-line comment "//"; + # single or double quotes preceeded by a backslash; + # single quotes, double quotes, open or close braces, semi-colons, + # periods, open or close parentheses; + # floating-point numbers; + # any alphanumeric token (keyword, class name, specifier); + # any alphanumeric token surrounded by angle brackets (generics); + # the multi-line comment begin and end tokens /* and */; + # array declarations "[]". + _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + + r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' + + r'/\*|\*/|\[\])') + + class OuterState(object): + """The initial state for parsing a Java file for classes, + interfaces, and anonymous inner classes.""" + def __init__(self, version=default_java_version): + + if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', + '5', '6'): + msg = "Java version %s not supported" % version + raise NotImplementedError(msg) + + self.version = version + self.listClasses = [] + self.listOutputs = [] + self.stackBrackets = [] + self.brackets = 0 + self.nextAnon = 1 + self.localClasses = [] + self.stackAnonClassBrackets = [] + self.anonStacksStack = [[0]] + self.package = None + + def trace(self): + pass + + def __getClassState(self): + try: + return self.classState + except AttributeError: + ret = ClassState(self) + self.classState = ret + return ret + + def __getPackageState(self): + try: + return self.packageState + except AttributeError: + ret = PackageState(self) + self.packageState = ret + return ret + + def __getAnonClassState(self): + try: + return self.anonState + except AttributeError: + self.outer_state = self + ret = SkipState(1, AnonClassState(self)) + self.anonState = ret + return ret + + def __getSkipState(self): + try: + return self.skipState + except AttributeError: + ret = SkipState(1, self) + self.skipState = ret + return ret + + def __getAnonStack(self): + return self.anonStacksStack[-1] + + def openBracket(self): + self.brackets = self.brackets + 1 + + def closeBracket(self): + self.brackets = self.brackets - 1 + if len(self.stackBrackets) and \ + self.brackets == self.stackBrackets[-1]: + self.listOutputs.append('$'.join(self.listClasses)) + self.localClasses.pop() + self.listClasses.pop() + self.anonStacksStack.pop() + self.stackBrackets.pop() + if len(self.stackAnonClassBrackets) and \ + self.brackets == self.stackAnonClassBrackets[-1]: + self.__getAnonStack().pop() + self.stackAnonClassBrackets.pop() + + def parseToken(self, token): + if token[:2] == '//': + return IgnoreState('\n', self) + elif token == '/*': + return IgnoreState('*/', self) + elif token == '{': + self.openBracket() + elif token == '}': + self.closeBracket() + elif token in [ '"', "'" ]: + return IgnoreState(token, self) + elif token == "new": + # anonymous inner class + if len(self.listClasses) > 0: + return self.__getAnonClassState() + return self.__getSkipState() # Skip the class name + elif token in ['class', 'interface', 'enum']: + if len(self.listClasses) == 0: + self.nextAnon = 1 + self.stackBrackets.append(self.brackets) + return self.__getClassState() + elif token == 'package': + return self.__getPackageState() + elif token == '.': + # Skip the attribute, it might be named "class", in which + # case we don't want to treat the following token as + # an inner class name... + return self.__getSkipState() + return self + + def addAnonClass(self): + """Add an anonymous inner class""" + if self.version in ('1.1', '1.2', '1.3', '1.4'): + clazz = self.listClasses[0] + self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) + elif self.version in ('1.5', '1.6', '5', '6'): + self.stackAnonClassBrackets.append(self.brackets) + className = [] + className.extend(self.listClasses) + self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 + for anon in self.__getAnonStack(): + className.append(str(anon)) + self.listOutputs.append('$'.join(className)) + + self.nextAnon = self.nextAnon + 1 + self.__getAnonStack().append(0) + + def setPackage(self, package): + self.package = package + + class AnonClassState(object): + """A state that looks for anonymous inner classes.""" + def __init__(self, old_state): + # outer_state is always an instance of OuterState + self.outer_state = old_state.outer_state + self.old_state = old_state + self.brace_level = 0 + def parseToken(self, token): + # This is an anonymous class if and only if the next + # non-whitespace token is a bracket. Everything between + # braces should be parsed as normal java code. + if token[:2] == '//': + return IgnoreState('\n', self) + elif token == '/*': + return IgnoreState('*/', self) + elif token == '\n': + return self + elif token[0] == '<' and token[-1] == '>': + return self + elif token == '(': + self.brace_level = self.brace_level + 1 + return self + if self.brace_level > 0: + if token == 'new': + # look further for anonymous inner class + return SkipState(1, AnonClassState(self)) + elif token in [ '"', "'" ]: + return IgnoreState(token, self) + elif token == ')': + self.brace_level = self.brace_level - 1 + return self + if token == '{': + self.outer_state.addAnonClass() + return self.old_state.parseToken(token) + + class SkipState(object): + """A state that will skip a specified number of tokens before + reverting to the previous state.""" + def __init__(self, tokens_to_skip, old_state): + self.tokens_to_skip = tokens_to_skip + self.old_state = old_state + def parseToken(self, token): + self.tokens_to_skip = self.tokens_to_skip - 1 + if self.tokens_to_skip < 1: + return self.old_state + return self + + class ClassState(object): + """A state we go into when we hit a class or interface keyword.""" + def __init__(self, outer_state): + # outer_state is always an instance of OuterState + self.outer_state = outer_state + def parseToken(self, token): + # the next non-whitespace token should be the name of the class + if token == '\n': + return self + # If that's an inner class which is declared in a method, it + # requires an index prepended to the class-name, e.g. + # 'Foo$1Inner' (Tigris Issue 2087) + if self.outer_state.localClasses and \ + self.outer_state.stackBrackets[-1] > \ + self.outer_state.stackBrackets[-2]+1: + locals = self.outer_state.localClasses[-1] + try: + idx = locals[token] + locals[token] = locals[token]+1 + except KeyError: + locals[token] = 1 + token = str(locals[token]) + token + self.outer_state.localClasses.append({}) + self.outer_state.listClasses.append(token) + self.outer_state.anonStacksStack.append([0]) + return self.outer_state + + class IgnoreState(object): + """A state that will ignore all tokens until it gets to a + specified token.""" + def __init__(self, ignore_until, old_state): + self.ignore_until = ignore_until + self.old_state = old_state + def parseToken(self, token): + if self.ignore_until == token: + return self.old_state + return self + + class PackageState(object): + """The state we enter when we encounter the package keyword. + We assume the next token will be the package name.""" + def __init__(self, outer_state): + # outer_state is always an instance of OuterState + self.outer_state = outer_state + def parseToken(self, token): + self.outer_state.setPackage(token) + return self.outer_state + + def parse_java_file(fn, version=default_java_version): + return parse_java(open(fn, 'r').read(), version) + + def parse_java(contents, version=default_java_version, trace=None): + """Parse a .java file and return a double of package directory, + plus a list of .class files that compiling that .java file will + produce""" + package = None + initial = OuterState(version) + currstate = initial + for token in _reToken.findall(contents): + # The regex produces a bunch of groups, but only one will + # have anything in it. + currstate = currstate.parseToken(token) + if trace: trace(token, currstate) + if initial.package: + package = initial.package.replace('.', os.sep) + return (package, initial.listOutputs) + +else: + # Don't actually parse Java files for class names. + # + # We might make this a configurable option in the future if + # Java-file parsing takes too long (although it shouldn't relative + # to how long the Java compiler itself seems to take...). + + def parse_java_file(fn): + """ "Parse" a .java file. + + This actually just splits the file name, so the assumption here + is that the file name matches the public class name, and that + the path to the file is the same as the package name. + """ + return os.path.split(file) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/__init__.py b/engine/SCons/Tool/MSCommon/__init__.py new file mode 100644 index 0000000..a5e19e6 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/__init__.py @@ -0,0 +1,56 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """ +Common functions for Microsoft Visual Studio and Visual C/C++. +""" + +import copy +import os +import re +import subprocess + +import SCons.Errors +import SCons.Platform.win32 +import SCons.Util + +from SCons.Tool.MSCommon.sdk import mssdk_exists, \ + mssdk_setup_env + +from SCons.Tool.MSCommon.vc import msvc_exists, \ + msvc_setup_env, \ + msvc_setup_env_once + +from SCons.Tool.MSCommon.vs import get_default_version, \ + get_vs_by_version, \ + merge_default_version, \ + msvs_exists, \ + query_versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/arch.py b/engine/SCons/Tool/MSCommon/arch.py new file mode 100644 index 0000000..0abb9f1 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/arch.py @@ -0,0 +1,61 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Module to define supported Windows chip architectures. +""" + +import os + +class ArchDefinition(object): + """ + A class for defining architecture-specific settings and logic. + """ + def __init__(self, arch, synonyms=[]): + self.arch = arch + self.synonyms = synonyms + +SupportedArchitectureList = [ + ArchitectureDefinition( + 'x86', + ['i386', 'i486', 'i586', 'i686'], + ), + + ArchitectureDefinition( + 'x86_64', + ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], + ), + + ArchitectureDefinition( + 'ia64', + ['IA64'], + ), +] + +SupportedArchitectureMap = {} +for a in SupportedArchitectureList: + SupportedArchitectureMap[a.arch] = a + for s in a.synonyms: + SupportedArchitectureMap[s] = a + diff --git a/engine/SCons/Tool/MSCommon/common.py b/engine/SCons/Tool/MSCommon/common.py new file mode 100644 index 0000000..b99cd77 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/common.py @@ -0,0 +1,240 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/common.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """ +Common helper functions for working with the Microsoft tool chain. +""" + +import copy +import os +import subprocess +import re + +import SCons.Util + + +logfile = os.environ.get('SCONS_MSCOMMON_DEBUG') +if logfile == '-': + def debug(x): + print x +elif logfile: + try: + import logging + except ImportError: + debug = lambda x: open(logfile, 'a').write(x + '\n') + else: + logging.basicConfig(filename=logfile, level=logging.DEBUG) + debug = logging.debug +else: + debug = lambda x: None + + +_is_win64 = None + +def is_win64(): + """Return true if running on windows 64 bits. + + Works whether python itself runs in 64 bits or 32 bits.""" + # Unfortunately, python does not provide a useful way to determine + # if the underlying Windows OS is 32-bit or 64-bit. Worse, whether + # the Python itself is 32-bit or 64-bit affects what it returns, + # so nothing in sys.* or os.* help. + + # Apparently the best solution is to use env vars that Windows + # sets. If PROCESSOR_ARCHITECTURE is not x86, then the python + # process is running in 64 bit mode (on a 64-bit OS, 64-bit + # hardware, obviously). + # If this python is 32-bit but the OS is 64, Windows will set + # ProgramW6432 and PROCESSOR_ARCHITEW6432 to non-null. + # (Checking for HKLM\Software\Wow6432Node in the registry doesn't + # work, because some 32-bit installers create it.) + global _is_win64 + if _is_win64 is None: + # I structured these tests to make it easy to add new ones or + # add exceptions in the future, because this is a bit fragile. + _is_win64 = False + if os.environ.get('PROCESSOR_ARCHITECTURE','x86') != 'x86': + _is_win64 = True + if os.environ.get('PROCESSOR_ARCHITEW6432'): + _is_win64 = True + if os.environ.get('ProgramW6432'): + _is_win64 = True + return _is_win64 + + +def read_reg(value): + return SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, value)[0] + +def has_reg(value): + """Return True if the given key exists in HKEY_LOCAL_MACHINE, False + otherwise.""" + try: + SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value) + ret = True + except WindowsError: + ret = False + return ret + +# Functions for fetching environment variable settings from batch files. + +def normalize_env(env, keys, force=False): + """Given a dictionary representing a shell environment, add the variables + from os.environ needed for the processing of .bat files; the keys are + controlled by the keys argument. + + It also makes sure the environment values are correctly encoded. + + If force=True, then all of the key values that exist are copied + into the returned dictionary. If force=false, values are only + copied if the key does not already exist in the copied dictionary. + + Note: the environment is copied.""" + normenv = {} + if env: + for k in env.keys(): + normenv[k] = copy.deepcopy(env[k]).encode('mbcs') + + for k in keys: + if k in os.environ and (force or not k in normenv): + normenv[k] = os.environ[k].encode('mbcs') + + return normenv + +def get_output(vcbat, args = None, env = None): + """Parse the output of given bat file, with given args.""" + + if env is None: + # Create a blank environment, for use in launching the tools + env = SCons.Environment.Environment(tools=[]) + + # TODO: This is a hard-coded list of the variables that (may) need + # to be imported from os.environ[] for v[sc]*vars*.bat file + # execution to work. This list should really be either directly + # controlled by vc.py, or else derived from the common_tools_var + # settings in vs.py. + vars = [ + 'COMSPEC', + 'VS90COMNTOOLS', + 'VS80COMNTOOLS', + 'VS71COMNTOOLS', + 'VS70COMNTOOLS', + 'VS60COMNTOOLS', + ] + env['ENV'] = normalize_env(env['ENV'], vars, force=False) + + if args: + debug("Calling '%s %s'" % (vcbat, args)) + popen = SCons.Action._subproc(env, + '"%s" %s & set' % (vcbat, args), + stdin = 'devnull', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + else: + debug("Calling '%s'" % vcbat) + popen = SCons.Action._subproc(env, + '"%s" & set' % vcbat, + stdin = 'devnull', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + # Use the .stdout and .stderr attributes directly because the + # .communicate() method uses the threading module on Windows + # and won't work under Pythons not built with threading. + stdout = popen.stdout.read() + stderr = popen.stderr.read() + if stderr: + # TODO: find something better to do with stderr; + # this at least prevents errors from getting swallowed. + import sys + sys.stderr.write(stderr) + if popen.wait() != 0: + raise IOError(stderr.decode("mbcs")) + + output = stdout.decode("mbcs") + return output + +def parse_output(output, keep = ("INCLUDE", "LIB", "LIBPATH", "PATH")): + # dkeep is a dict associating key: path_list, where key is one item from + # keep, and pat_list the associated list of paths + + dkeep = dict([(i, []) for i in keep]) + + # rdk will keep the regex to match the .bat file output line starts + rdk = {} + for i in keep: + rdk[i] = re.compile('%s=(.*)' % i, re.I) + + def add_env(rmatch, key, dkeep=dkeep): + plist = rmatch.group(1).split(os.pathsep) + for p in plist: + # Do not add empty paths (when a var ends with ;) + if p: + p = p.encode('mbcs') + # XXX: For some reason, VC98 .bat file adds "" around the PATH + # values, and it screws up the environment later, so we strip + # it. + p = p.strip('"') + dkeep[key].append(p) + + for line in output.splitlines(): + for k,v in rdk.items(): + m = v.match(line) + if m: + add_env(m, k) + + return dkeep + +# TODO(sgk): unused +def output_to_dict(output): + """Given an output string, parse it to find env variables. + + Return a dict where keys are variables names, and values their content""" + envlinem = re.compile(r'^([a-zA-z0-9]+)=([\S\s]*)$') + parsedenv = {} + for line in output.splitlines(): + m = envlinem.match(line) + if m: + parsedenv[m.group(1)] = m.group(2) + return parsedenv + +# TODO(sgk): unused +def get_new(l1, l2): + """Given two list l1 and l2, return the items in l2 which are not in l1. + Order is maintained.""" + + # We don't try to be smart: lists are small, and this is not the bottleneck + # is any case + new = [] + for i in l2: + if i not in l1: + new.append(i) + + return new + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/netframework.py b/engine/SCons/Tool/MSCommon/netframework.py new file mode 100644 index 0000000..a69a924 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/netframework.py @@ -0,0 +1,82 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/MSCommon/netframework.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """ +""" + +import os +import re + +from common import read_reg, debug + +# Original value recorded by dcournapeau +_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\.NETFramework\InstallRoot' +# On SGK's system +_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\Microsoft SDKs\.NETFramework\v2.0\InstallationFolder' + +def find_framework_root(): + # XXX: find it from environment (FrameworkDir) + try: + froot = read_reg(_FRAMEWORKDIR_HKEY_ROOT) + debug("Found framework install root in registry: %s" % froot) + except WindowsError, e: + debug("Could not read reg key %s" % _FRAMEWORKDIR_HKEY_ROOT) + return None + + if not os.path.exists(froot): + debug("%s not found on fs" % froot) + return None + + return froot + +def query_versions(): + froot = find_framework_root() + if froot: + contents = os.listdir(froot) + + l = re.compile('v[0-9]+.*') + versions = [e for e in contents if l.match(e)] + + def versrt(a,b): + # since version numbers aren't really floats... + aa = a[1:] + bb = b[1:] + aal = aa.split('.') + bbl = bb.split('.') + # sequence comparison in python is lexicographical + # which is exactly what we want. + # Note we sort backwards so the highest version is first. + return cmp(bbl,aal) + + versions.sort(versrt) + else: + versions = [] + + return versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/sdk.py b/engine/SCons/Tool/MSCommon/sdk.py new file mode 100644 index 0000000..1fc5751 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/sdk.py @@ -0,0 +1,391 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Module to detect the Platform/Windows SDK + +PSDK 2003 R1 is the earliest version detected. +""" + +import os + +import SCons.Errors +import SCons.Util + +import common + +debug = common.debug + +# SDK Checks. This is of course a mess as everything else on MS platforms. Here +# is what we do to detect the SDK: +# +# For Windows SDK >= 6.0: just look into the registry entries: +# HKLM\Software\Microsoft\Microsoft SDKs\Windows +# All the keys in there are the available versions. +# +# For Platform SDK before 6.0 (2003 server R1 and R2, etc...), there does not +# seem to be any sane registry key, so the precise location is hardcoded. +# +# For versions below 2003R1, it seems the PSDK is included with Visual Studio? +# +# Also, per the following: +# http://benjamin.smedbergs.us/blog/tag/atl/ +# VC++ Professional comes with the SDK, VC++ Express does not. + +# Location of the SDK (checked for 6.1 only) +_CURINSTALLED_SDK_HKEY_ROOT = \ + r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder" + + +class SDKDefinition(object): + """ + An abstract base class for trying to find installed SDK directories. + """ + def __init__(self, version, **kw): + self.version = version + self.__dict__.update(kw) + + def find_sdk_dir(self): + """Try to find the MS SDK from the registry. + + Return None if failed or the directory does not exist. + """ + if not SCons.Util.can_read_reg: + debug('find_sdk_dir(): can not read registry') + return None + + hkey = self.HKEY_FMT % self.hkey_data + debug('find_sdk_dir(): checking registry:%s'%hkey) + + try: + sdk_dir = common.read_reg(hkey) + except WindowsError, e: + debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey)) + return None + + debug('find_sdk_dir(): Trying SDK Dir: %s'%sdk_dir) + + if not os.path.exists(sdk_dir): + debug('find_sdk_dir(): %s not on file system' % sdk_dir) + return None + + ftc = os.path.join(sdk_dir, self.sanity_check_file) + if not os.path.exists(ftc): + debug("find_sdk_dir(): sanity check %s not found" % ftc) + return None + + return sdk_dir + + def get_sdk_dir(self): + """Return the MSSSDK given the version string.""" + try: + return self._sdk_dir + except AttributeError: + sdk_dir = self.find_sdk_dir() + self._sdk_dir = sdk_dir + return sdk_dir + + def get_sdk_vc_script(self,host_arch, target_arch): + """ Return the script to initialize the VC compiler installed by SDK + """ + + if (host_arch == 'amd64' and target_arch == 'x86'): + # No cross tools needed compiling 32 bits on 64 bit machine + host_arch=target_arch + + arch_string=target_arch + if (host_arch != target_arch): + arch_string='%s_%s'%(host_arch,target_arch) + + debug("sdk.py: get_sdk_vc_script():arch_string:%s host_arch:%s target_arch:%s"%(arch_string, + host_arch, + target_arch)) + file=self.vc_setup_scripts.get(arch_string,None) + debug("sdk.py: get_sdk_vc_script():file:%s"%file) + return file + +class WindowsSDK(SDKDefinition): + """ + A subclass for trying to find installed Windows SDK directories. + """ + HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder' + def __init__(self, *args, **kw): + SDKDefinition.__init__(self, *args, **kw) + self.hkey_data = self.version + +class PlatformSDK(SDKDefinition): + """ + A subclass for trying to find installed Platform SDK directories. + """ + HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir' + def __init__(self, *args, **kw): + SDKDefinition.__init__(self, *args, **kw) + self.hkey_data = self.uuid + +# +# The list of VC initialization scripts installed by the SDK +# These should be tried if the vcvarsall.bat TARGET_ARCH fails +preSDK61VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', + 'amd64' : r'bin\vcvarsamd64.bat', + 'x86_amd64': r'bin\vcvarsx86_amd64.bat', + 'x86_ia64' : r'bin\vcvarsx86_ia64.bat', + 'ia64' : r'bin\vcvarsia64.bat'} + +SDK61VCSetupScripts = {'x86' : r'bin\vcvars32.bat', + 'amd64' : r'bin\amd64\vcvarsamd64.bat', + 'x86_amd64': r'bin\x86_amd64\vcvarsx86_amd64.bat', + 'x86_ia64' : r'bin\x86_ia64\vcvarsx86_ia64.bat', + 'ia64' : r'bin\ia64\vcvarsia64.bat'} + +SDK70VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', + 'amd64' : r'bin\vcvars64.bat', + 'x86_amd64': r'bin\vcvarsx86_amd64.bat', + 'x86_ia64' : r'bin\vcvarsx86_ia64.bat', + 'ia64' : r'bin\vcvarsia64.bat'} + +# The list of support SDKs which we know how to detect. +# +# The first SDK found in the list is the one used by default if there +# are multiple SDKs installed. Barring good reasons to the contrary, +# this means we should list SDKs with from most recent to oldest. +# +# If you update this list, update the documentation in Tool/mssdk.xml. +SupportedSDKList = [ + WindowsSDK('7.0', + sanity_check_file=r'bin\SetEnv.Cmd', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = SDK70VCSetupScripts, + ), + WindowsSDK('6.1', + sanity_check_file=r'bin\SetEnv.Cmd', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = SDK61VCSetupScripts, + ), + + WindowsSDK('6.0A', + sanity_check_file=r'include\windows.h', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = preSDK61VCSetupScripts, + ), + + WindowsSDK('6.0', + sanity_check_file=r'bin\gacutil.exe', + include_subdir='include', + lib_subdir='lib', + vc_setup_scripts = preSDK61VCSetupScripts, + ), + + PlatformSDK('2003R2', + sanity_check_file=r'SetEnv.Cmd', + uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", + vc_setup_scripts = preSDK61VCSetupScripts, + ), + + PlatformSDK('2003R1', + sanity_check_file=r'SetEnv.Cmd', + uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3", + vc_setup_scripts = preSDK61VCSetupScripts, + ), +] + +SupportedSDKMap = {} +for sdk in SupportedSDKList: + SupportedSDKMap[sdk.version] = sdk + + +# Finding installed SDKs isn't cheap, because it goes not only to the +# registry but also to the disk to sanity-check that there is, in fact, +# an SDK installed there and that the registry entry isn't just stale. +# Find this information once, when requested, and cache it. + +InstalledSDKList = None +InstalledSDKMap = None + +def get_installed_sdks(): + global InstalledSDKList + global InstalledSDKMap + debug('sdk.py:get_installed_sdks()') + if InstalledSDKList is None: + InstalledSDKList = [] + InstalledSDKMap = {} + for sdk in SupportedSDKList: + debug('MSCommon/sdk.py: trying to find SDK %s' % sdk.version) + if sdk.get_sdk_dir(): + debug('MSCommon/sdk.py:found SDK %s' % sdk.version) + InstalledSDKList.append(sdk) + InstalledSDKMap[sdk.version] = sdk + return InstalledSDKList + + +# We may be asked to update multiple construction environments with +# SDK information. When doing this, we check on-disk for whether +# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk +# is expensive, cache results by directory. + +SDKEnvironmentUpdates = {} + +def set_sdk_by_directory(env, sdk_dir): + global SDKEnvironmentUpdates + debug('set_sdk_by_directory: Using dir:%s'%sdk_dir) + try: + env_tuple_list = SDKEnvironmentUpdates[sdk_dir] + except KeyError: + env_tuple_list = [] + SDKEnvironmentUpdates[sdk_dir] = env_tuple_list + + include_path = os.path.join(sdk_dir, 'include') + mfc_path = os.path.join(include_path, 'mfc') + atl_path = os.path.join(include_path, 'atl') + + if os.path.exists(mfc_path): + env_tuple_list.append(('INCLUDE', mfc_path)) + if os.path.exists(atl_path): + env_tuple_list.append(('INCLUDE', atl_path)) + env_tuple_list.append(('INCLUDE', include_path)) + + env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) + env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) + env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) + + for variable, directory in env_tuple_list: + env.PrependENVPath(variable, directory) + + +# TODO(sgk): currently unused; remove? +def get_cur_sdk_dir_from_reg(): + """Try to find the platform sdk directory from the registry. + + Return None if failed or the directory does not exist""" + if not SCons.Util.can_read_reg: + debug('SCons cannot read registry') + return None + + try: + val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT) + debug("Found current sdk dir in registry: %s" % val) + except WindowsError, e: + debug("Did not find current sdk in registry") + return None + + if not os.path.exists(val): + debug("Current sdk dir %s not on fs" % val) + return None + + return val + +def get_sdk_by_version(mssdk): + if mssdk not in SupportedSDKMap: + msg = "SDK version %s is not supported" % repr(mssdk) + raise SCons.Errors.UserError(msg) + get_installed_sdks() + return InstalledSDKMap.get(mssdk) + +def get_default_sdk(): + """Set up the default Platform/Windows SDK.""" + get_installed_sdks() + if not InstalledSDKList: + return None + return InstalledSDKList[0] + + + + +def mssdk_setup_env(env): + debug('sdk.py:mssdk_setup_env()') + if 'MSSDK_DIR' in env: + sdk_dir = env['MSSDK_DIR'] + if sdk_dir is None: + return + sdk_dir = env.subst(sdk_dir) + debug('sdk.py:mssdk_setup_env: Using MSSDK_DIR:%s'%sdk_dir) + elif 'MSSDK_VERSION' in env: + sdk_version = env['MSSDK_VERSION'] + if sdk_version is None: + msg = "SDK version %s is not installed" % repr(mssdk) + raise SCons.Errors.UserError(msg) + sdk_version = env.subst(sdk_version) + mssdk = get_sdk_by_version(sdk_version) + sdk_dir = mssdk.get_sdk_dir() + debug('sdk.py:mssdk_setup_env: Using MSSDK_VERSION:%s'%sdk_dir) + elif 'MSVS_VERSION' in env: + msvs_version = env['MSVS_VERSION'] + debug('sdk.py:mssdk_setup_env:Getting MSVS_VERSION from env:%s'%msvs_version) + if msvs_version is None: + debug('sdk.py:mssdk_setup_env thinks msvs_version is None') + return + msvs_version = env.subst(msvs_version) + import vs + msvs = vs.get_vs_by_version(msvs_version) + debug('sdk.py:mssdk_setup_env:msvs is :%s'%msvs) + if not msvs: + debug('sdk.py:mssdk_setup_env: no VS version detected, bailingout:%s'%msvs) + return + sdk_version = msvs.sdk_version + debug('sdk.py:msvs.sdk_version is %s'%sdk_version) + if not sdk_version: + return + mssdk = get_sdk_by_version(sdk_version) + if not mssdk: + mssdk = get_default_sdk() + if not mssdk: + return + sdk_dir = mssdk.get_sdk_dir() + debug('sdk.py:mssdk_setup_env: Using MSVS_VERSION:%s'%sdk_dir) + else: + mssdk = get_default_sdk() + if not mssdk: + return + sdk_dir = mssdk.get_sdk_dir() + debug('sdk.py:mssdk_setup_env: not using any env values. sdk_dir:%s'%sdk_dir) + + set_sdk_by_directory(env, sdk_dir) + + #print "No MSVS_VERSION: this is likely to be a bug" + +def mssdk_exists(version=None): + sdks = get_installed_sdks() + if version is None: + return len(sdks) > 0 + return version in sdks + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/vc.py b/engine/SCons/Tool/MSCommon/vc.py new file mode 100644 index 0000000..6e3965e --- /dev/null +++ b/engine/SCons/Tool/MSCommon/vc.py @@ -0,0 +1,459 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +# TODO: +# * supported arch for versions: for old versions of batch file without +# argument, giving bogus argument cannot be detected, so we have to hardcode +# this here +# * print warning when msvc version specified but not found +# * find out why warning do not print +# * test on 64 bits XP + VS 2005 (and VS 6 if possible) +# * SDK +# * Assembly +__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Module for Visual C/C++ detection and configuration. +""" +import SCons.compat + +import os +import platform +from string import digits as string_digits + +import SCons.Warnings + +import common + +debug = common.debug + +import sdk + +get_installed_sdks = sdk.get_installed_sdks + + +class VisualCException(Exception): + pass + +class UnsupportedVersion(VisualCException): + pass + +class UnsupportedArch(VisualCException): + pass + +class MissingConfiguration(VisualCException): + pass + +class NoVersionFound(VisualCException): + pass + +class BatchFileExecutionError(VisualCException): + pass + +# Dict to 'canonalize' the arch +_ARCH_TO_CANONICAL = { + "amd64" : "amd64", + "emt64" : "amd64", + "i386" : "x86", + "i486" : "x86", + "i586" : "x86", + "i686" : "x86", + "ia64" : "ia64", + "itanium" : "ia64", + "x86" : "x86", + "x86_64" : "amd64", +} + +# Given a (host, target) tuple, return the argument for the bat file. Both host +# and targets should be canonalized. +_HOST_TARGET_ARCH_TO_BAT_ARCH = { + ("x86", "x86"): "x86", + ("x86", "amd64"): "x86_amd64", + ("amd64", "amd64"): "amd64", + ("amd64", "x86"): "x86", + ("x86", "ia64"): "x86_ia64" +} + +def get_host_target(env): + debug('vc.py:get_host_target()') + + host_platform = env.get('HOST_ARCH') + if not host_platform: + host_platform = platform.machine() + # TODO(2.5): the native Python platform.machine() function returns + # '' on all Python versions before 2.6, after which it also uses + # PROCESSOR_ARCHITECTURE. + if not host_platform: + host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') + + # Retain user requested TARGET_ARCH + req_target_platform = env.get('TARGET_ARCH') + debug('vc.py:get_host_target() req_target_platform:%s'%req_target_platform) + + if req_target_platform: + # If user requested a specific platform then only try that one. + target_platform = req_target_platform + else: + target_platform = host_platform + + try: + host = _ARCH_TO_CANONICAL[host_platform.lower()] + except KeyError, e: + msg = "Unrecognized host architecture %s" + raise ValueError(msg % repr(host_platform)) + + try: + target = _ARCH_TO_CANONICAL[target_platform.lower()] + except KeyError, e: + raise ValueError("Unrecognized target architecture %s" % target_platform) + + return (host, target,req_target_platform) + +_VCVER = ["10.0Exp","10.0", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] + +_VCVER_TO_PRODUCT_DIR = { + '10.0Exp' : [ + r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'], + '10.0': [ + r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'], + '9.0': [ + r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir'], + '9.0Exp' : [ + r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'], + '8.0': [ + r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'], + '8.0Exp': [ + r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'], + '7.1': [ + r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'], + '7.0': [ + r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'], + '6.0': [ + r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'] +} + +def msvc_version_to_maj_min(msvc_version): + msvc_version_numeric = ''.join([x for x in msvc_version if x in string_digits + '.']) + + t = msvc_version_numeric.split(".") + if not len(t) == 2: + raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) + try: + maj = int(t[0]) + min = int(t[1]) + return maj, min + except ValueError, e: + raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) + +def is_host_target_supported(host_target, msvc_version): + """Return True if the given (host, target) tuple is supported given the + msvc version. + + Parameters + ---------- + host_target: tuple + tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross + compilation from 32 bits windows to 64 bits. + msvc_version: str + msvc version (major.minor, e.g. 10.0) + + Note + ---- + This only check whether a given version *may* support the given (host, + target), not that the toolchain is actually present on the machine. + """ + # We assume that any Visual Studio version supports x86 as a target + if host_target[1] != "x86": + maj, min = msvc_version_to_maj_min(msvc_version) + if maj < 8: + return False + + return True + +def find_vc_pdir(msvc_version): + """Try to find the product directory for the given + version. + + Note + ---- + If for some reason the requested version could not be found, an + exception which inherits from VisualCException will be raised.""" + root = 'Software\\' + if common.is_win64(): + root = root + 'Wow6432Node\\' + try: + hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] + except KeyError: + debug("Unknown version of MSVC: %s" % msvc_version) + raise UnsupportedVersion("Unknown version %s" % msvc_version) + + for key in hkeys: + key = root + key + try: + comps = common.read_reg(key) + except WindowsError, e: + debug('find_vc_dir(): no VC registry key %s' % repr(key)) + else: + debug('find_vc_dir(): found VC in registry: %s' % comps) + if os.path.exists(comps): + return comps + else: + debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\ + % comps) + raise MissingConfiguration("registry dir %s not found on the filesystem" % comps) + return None + +def find_batch_file(env,msvc_version,host_arch,target_arch): + """ + Find the location of the batch script which should set up the compiler + for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress + """ + pdir = find_vc_pdir(msvc_version) + if pdir is None: + raise NoVersionFound("No version of Visual Studio found") + + debug('vc.py: find_batch_file() pdir:%s'%pdir) + + # filter out e.g. "Exp" from the version name + msvc_ver_numeric = ''.join([x for x in msvc_version if x in string_digits + "."]) + vernum = float(msvc_ver_numeric) + if 7 <= vernum < 8: + pdir = os.path.join(pdir, os.pardir, "Common7", "Tools") + batfilename = os.path.join(pdir, "vsvars32.bat") + elif vernum < 7: + pdir = os.path.join(pdir, "Bin") + batfilename = os.path.join(pdir, "vcvars32.bat") + else: # >= 8 + batfilename = os.path.join(pdir, "vcvarsall.bat") + + if not os.path.exists(batfilename): + debug("Not found: %s" % batfilename) + batfilename = None + + installed_sdks=get_installed_sdks() + for _sdk in installed_sdks: + sdk_bat_file=_sdk.get_sdk_vc_script(host_arch,target_arch) + sdk_bat_file_path=os.path.join(pdir,sdk_bat_file) + debug('vc.py:find_batch_file() sdk_bat_file_path:%s'%sdk_bat_file_path) + if os.path.exists(sdk_bat_file_path): + return (batfilename,sdk_bat_file_path) + else: + debug("vc.py:find_batch_file() not found:%s"%sdk_bat_file_path) + else: + return (batfilename,None) + +__INSTALLED_VCS_RUN = None + +def cached_get_installed_vcs(): + global __INSTALLED_VCS_RUN + + if __INSTALLED_VCS_RUN is None: + ret = get_installed_vcs() + __INSTALLED_VCS_RUN = ret + + return __INSTALLED_VCS_RUN + +def get_installed_vcs(): + installed_versions = [] + for ver in _VCVER: + debug('trying to find VC %s' % ver) + try: + if find_vc_pdir(ver): + debug('found VC %s' % ver) + installed_versions.append(ver) + else: + debug('find_vc_pdir return None for ver %s' % ver) + except VisualCException, e: + debug('did not find VC %s: caught exception %s' % (ver, str(e))) + return installed_versions + +def reset_installed_vcs(): + """Make it try again to find VC. This is just for the tests.""" + __INSTALLED_VCS_RUN = None + +def script_env(script, args=None): + stdout = common.get_output(script, args) + # Stupid batch files do not set return code: we take a look at the + # beginning of the output for an error message instead + olines = stdout.splitlines() + if olines[0].startswith("The specified configuration type is missing"): + raise BatchFileExecutionError("\n".join(olines[:2])) + + return common.parse_output(stdout) + +def get_default_version(env): + debug('get_default_version()') + + msvc_version = env.get('MSVC_VERSION') + msvs_version = env.get('MSVS_VERSION') + + debug('get_default_version(): msvc_version:%s msvs_version:%s'%(msvc_version,msvs_version)) + + if msvs_version and not msvc_version: + SCons.Warnings.warn( + SCons.Warnings.DeprecatedWarning, + "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ") + return msvs_version + elif msvc_version and msvs_version: + if not msvc_version == msvs_version: + SCons.Warnings.warn( + SCons.Warnings.VisualVersionMismatch, + "Requested msvc version (%s) and msvs version (%s) do " \ + "not match: please use MSVC_VERSION only to request a " \ + "visual studio version, MSVS_VERSION is deprecated" \ + % (msvc_version, msvs_version)) + return msvs_version + if not msvc_version: + installed_vcs = cached_get_installed_vcs() + debug('installed_vcs:%s' % installed_vcs) + if not installed_vcs: + #msg = 'No installed VCs' + #debug('msv %s\n' % repr(msg)) + #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) + debug('msvc_setup_env: No installed VCs') + return None + msvc_version = installed_vcs[0] + debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version)) + + return msvc_version + +def msvc_setup_env_once(env): + try: + has_run = env["MSVC_SETUP_RUN"] + except KeyError: + has_run = False + + if not has_run: + msvc_setup_env(env) + env["MSVC_SETUP_RUN"] = True + +def msvc_find_valid_batch_script(env,version): + debug('vc.py:msvc_find_valid_batch_script()') + # Find the host platform, target platform, and if present the requested + # target platform + (host_platform, target_platform,req_target_platform) = get_host_target(env) + + # If the user hasn't specifically requested a TARGET_ARCH, and + # The TARGET_ARCH is amd64 then also try 32 bits if there are no viable + # 64 bit tools installed + try_target_archs = [target_platform] + if not req_target_platform and target_platform=='amd64': + try_target_archs.append('x86') + + d = None + for tp in try_target_archs: + # Set to current arch. + env['TARGET_ARCH']=tp + + debug("vc.py:msvc_find_valid_batch_script() trying target_platform:%s"%tp) + host_target = (host_platform, tp) + if not is_host_target_supported(host_target, version): + warn_msg = "host, target = %s not supported for MSVC version %s" % \ + (host_target, version) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target] + + # Try to locate a batch file for this host/target platform combo + try: + (vc_script,sdk_script) = find_batch_file(env,version,host_platform,tp) + debug('vc.py:msvc_find_valid_batch_script() vc_script:%s sdk_script:%s'%(vc_script,sdk_script)) + except VisualCException, e: + msg = str(e) + debug('Caught exception while looking for batch file (%s)' % msg) + warn_msg = "VC version %s not installed. " + \ + "C/C++ compilers are most likely not set correctly.\n" + \ + " Installed versions are: %s" + warn_msg = warn_msg % (version, cached_get_installed_vcs()) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + continue + + # Try to use the located batch file for this host/target platform combo + debug('vc.py:msvc_find_valid_batch_script() use_script 2 %s, args:%s\n' % (repr(vc_script), arg)) + if vc_script: + try: + d = script_env(vc_script, args=arg) + except BatchFileExecutionError, e: + debug('vc.py:msvc_find_valid_batch_script() use_script 3: failed running VC script %s: %s: Error:%s'%(repr(vc_script),arg,e)) + vc_script=None + if not vc_script and sdk_script: + debug('vc.py:msvc_find_valid_batch_script() use_script 4: trying sdk script: %s'%(sdk_script)) + try: + d = script_env(sdk_script,args=[]) + except BatchFileExecutionError,e: + debug('vc.py:msvc_find_valid_batch_script() use_script 5: failed running SDK script %s: Error:%s'%(repr(sdk_script),e)) + continue + elif not vc_script and not sdk_script: + debug('vc.py:msvc_find_valid_batch_script() use_script 6: Neither VC script nor SDK script found') + continue + + # If we cannot find a viable installed compiler, reset the TARGET_ARCH + # To it's initial value + if not d: + env['TARGET_ARCH']=req_target_platform + + return d + + +def msvc_setup_env(env): + debug('msvc_setup_env()') + + version = get_default_version(env) + if version is None: + warn_msg = "No version of Visual Studio compiler found - C/C++ " \ + "compilers most likely not set correctly" + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + return None + debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version)) + + # XXX: we set-up both MSVS version for backward + # compatibility with the msvs tool + env['MSVC_VERSION'] = version + env['MSVS_VERSION'] = version + env['MSVS'] = {} + + + use_script = env.get('MSVC_USE_SCRIPT', True) + if SCons.Util.is_String(use_script): + debug('vc.py:msvc_setup_env() use_script 1 %s\n' % repr(use_script)) + d = script_env(use_script) + elif use_script: + d = msvc_find_valid_batch_script(env,version) + debug('vc.py:msvc_setup_env() use_script 2 %s\n' % d) + if not d: + return d + else: + debug('MSVC_USE_SCRIPT set to False') + warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \ + "set correctly." + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + return None + + for k, v in d.items(): + debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) + env.PrependENVPath(k, v, delete_existing=True) + +def msvc_exists(version=None): + vcs = cached_get_installed_vcs() + if version is None: + return len(vcs) > 0 + return version in vcs + diff --git a/engine/SCons/Tool/MSCommon/vs.py b/engine/SCons/Tool/MSCommon/vs.py new file mode 100644 index 0000000..ee53e51 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/vs.py @@ -0,0 +1,526 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """Module to detect Visual Studio and/or Visual C/C++ +""" + +import os + +import SCons.Errors +import SCons.Util + +from common import debug, \ + get_output, \ + is_win64, \ + normalize_env, \ + parse_output, \ + read_reg + +import SCons.Tool.MSCommon.vc + +class VisualStudio(object): + """ + An abstract base class for trying to find installed versions of + Visual Studio. + """ + def __init__(self, version, **kw): + self.version = version + kw['vc_version'] = kw.get('vc_version', version) + kw['sdk_version'] = kw.get('sdk_version', version) + self.__dict__.update(kw) + self._cache = {} + + # + + def find_batch_file(self): + vs_dir = self.get_vs_dir() + if not vs_dir: + debug('find_executable(): no vs_dir') + return None + batch_file = os.path.join(vs_dir, self.batch_file_path) + batch_file = os.path.normpath(batch_file) + if not os.path.isfile(batch_file): + debug('find_batch_file(): %s not on file system' % batch_file) + return None + return batch_file + + def find_vs_dir_by_vc(self): + SCons.Tool.MSCommon.vc.get_installed_vcs() + dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version) + if not dir: + debug('find_vs_dir(): no installed VC %s' % self.vc_version) + return None + return dir + + def find_vs_dir_by_reg(self): + root = 'Software\\' + + if is_win64(): + root = root + 'Wow6432Node\\' + for key in self.hkeys: + if key=='use_dir': + return self.find_vs_dir_by_vc() + key = root + key + try: + comps = read_reg(key) + except WindowsError, e: + debug('find_vs_dir_by_reg(): no VS registry key %s' % repr(key)) + else: + debug('find_vs_dir_by_reg(): found VS in registry: %s' % comps) + return comps + return None + + def find_vs_dir(self): + """ Can use registry or location of VC to find vs dir + First try to find by registry, and if that fails find via VC dir + """ + + + if True: + vs_dir=self.find_vs_dir_by_reg() + return vs_dir + else: + return self.find_vs_dir_by_vc() + + def find_executable(self): + vs_dir = self.get_vs_dir() + if not vs_dir: + debug('find_executable(): no vs_dir (%s)'%vs_dir) + return None + executable = os.path.join(vs_dir, self.executable_path) + executable = os.path.normpath(executable) + if not os.path.isfile(executable): + debug('find_executable(): %s not on file system' % executable) + return None + return executable + + # + + def get_batch_file(self): + try: + return self._cache['batch_file'] + except KeyError: + batch_file = self.find_batch_file() + self._cache['batch_file'] = batch_file + return batch_file + + def get_executable(self): + try: + debug('get_executable using cache:%s'%self._cache['executable']) + return self._cache['executable'] + except KeyError: + executable = self.find_executable() + self._cache['executable'] = executable + debug('get_executable not in cache:%s'%executable) + return executable + + def get_vs_dir(self): + try: + return self._cache['vs_dir'] + except KeyError: + vs_dir = self.find_vs_dir() + self._cache['vs_dir'] = vs_dir + return vs_dir + + def get_supported_arch(self): + try: + return self._cache['supported_arch'] + except KeyError: + # RDEVE: for the time being use hardcoded lists + # supported_arch = self.find_supported_arch() + self._cache['supported_arch'] = self.supported_arch + return self.supported_arch + + def reset(self): + self._cache = {} + +# The list of supported Visual Studio versions we know how to detect. +# +# How to look for .bat file ? +# - VS 2008 Express (x86): +# * from registry key productdir, gives the full path to vsvarsall.bat. In +# HKEY_LOCAL_MACHINE): +# Software\Microsoft\VCEpress\9.0\Setup\VC\productdir +# * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC +# relatively to the path given by the variable. +# +# - VS 2008 Express (WoW6432: 32 bits on windows x64): +# Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir +# +# - VS 2005 Express (x86): +# * from registry key productdir, gives the full path to vsvarsall.bat. In +# HKEY_LOCAL_MACHINE): +# Software\Microsoft\VCEpress\8.0\Setup\VC\productdir +# * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC +# relatively to the path given by the variable. +# +# - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a +# productdir ? +# +# - VS 2003 .Net (pro edition ? x86): +# * from registry key productdir. The path is then ..\Common7\Tools\ +# relatively to the key. The key is in HKEY_LOCAL_MACHINE): +# Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir +# * from environmnent variable VS71COMNTOOLS: the path is the full path to +# vsvars32.bat +# +# - VS 98 (VS 6): +# * from registry key productdir. The path is then Bin +# relatively to the key. The key is in HKEY_LOCAL_MACHINE): +# Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir +# +# The first version found in the list is the one used by default if +# there are multiple versions installed. Barring good reasons to +# the contrary, this means we should list versions from most recent +# to oldest. Pro versions get listed before Express versions on the +# assumption that, by default, you'd rather use the version you paid +# good money for in preference to whatever Microsoft makes available +# for free. +# +# If you update this list, update the documentation in Tool/msvs.xml. + +SupportedVSList = [ + # Visual Studio 2010 + # TODO: find the settings, perhaps from someone with a CTP copy? + #VisualStudio('TBD', + # hkey_root=r'TBD', + # common_tools_var='TBD', + # executable_path=r'TBD', + # default_dirname='TBD', + #), + + # Visual Studio 2010 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('10.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VisualStudio\10.0\Setup\VS\ProductDir'], + common_tools_var='VS100COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 10', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 2010 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('10.0Exp', + vc_version='10.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VCExpress\10.0\Setup\VS\ProductDir'], + common_tools_var='VS100COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 10', + supported_arch=['x86'], + ), + + # Visual Studio 2008 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('9.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'], + common_tools_var='VS90COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 9', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 2008 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('9.0Exp', + vc_version='9.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'], + common_tools_var='VS90COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 9', + supported_arch=['x86'], + ), + + # Visual Studio 2005 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('8.0', + sdk_version='6.0A', + hkeys=[r'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'], + common_tools_var='VS80COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 8', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 2005 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('8.0Exp', + vc_version='8.0Exp', + sdk_version='6.0A', + hkeys=[r'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'], + common_tools_var='VS80COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 8', + supported_arch=['x86'], + ), + + # Visual Studio .NET 2003 + # The batch file we look for is in the Common7\Tools directory, + # so the devenv.com executable is next door in ..\IDE. + VisualStudio('7.1', + sdk_version='6.0', + hkeys=[r'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'], + common_tools_var='VS71COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio .NET 2003', + supported_arch=['x86'], + ), + + # Visual Studio .NET + # The batch file we look for is in the Common7\Tools directory, + # so the devenv.com executable is next door in ..\IDE. + VisualStudio('7.0', + sdk_version='2003R2', + hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'], + common_tools_var='VS70COMNTOOLS', + executable_path=r'IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio .NET', + supported_arch=['x86'], + ), + + # Visual Studio 6.0 + VisualStudio('6.0', + sdk_version='2003R1', + hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir', + 'use_dir'], + common_tools_var='VS60COMNTOOLS', + executable_path=r'Common\MSDev98\Bin\MSDEV.COM', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio', + supported_arch=['x86'], + ), +] + +SupportedVSMap = {} +for vs in SupportedVSList: + SupportedVSMap[vs.version] = vs + + +# Finding installed versions of Visual Studio isn't cheap, because it +# goes not only to the registry but also to the disk to sanity-check +# that there is, in fact, a Visual Studio directory there and that the +# registry entry isn't just stale. Find this information once, when +# requested, and cache it. + +InstalledVSList = None +InstalledVSMap = None + +def get_installed_visual_studios(): + global InstalledVSList + global InstalledVSMap + if InstalledVSList is None: + InstalledVSList = [] + InstalledVSMap = {} + for vs in SupportedVSList: + debug('trying to find VS %s' % vs.version) + if vs.get_executable(): + debug('found VS %s' % vs.version) + InstalledVSList.append(vs) + InstalledVSMap[vs.version] = vs + return InstalledVSList + +def reset_installed_visual_studios(): + global InstalledVSList + global InstalledVSMap + InstalledVSList = None + InstalledVSMap = None + for vs in SupportedVSList: + vs.reset() + + # Need to clear installed VC's as well as they are used in finding + # installed VS's + SCons.Tool.MSCommon.vc.reset_installed_vcs() + + +# We may be asked to update multiple construction environments with +# SDK information. When doing this, we check on-disk for whether +# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk +# is expensive, cache results by directory. + +#SDKEnvironmentUpdates = {} +# +#def set_sdk_by_directory(env, sdk_dir): +# global SDKEnvironmentUpdates +# try: +# env_tuple_list = SDKEnvironmentUpdates[sdk_dir] +# except KeyError: +# env_tuple_list = [] +# SDKEnvironmentUpdates[sdk_dir] = env_tuple_list +# +# include_path = os.path.join(sdk_dir, 'include') +# mfc_path = os.path.join(include_path, 'mfc') +# atl_path = os.path.join(include_path, 'atl') +# +# if os.path.exists(mfc_path): +# env_tuple_list.append(('INCLUDE', mfc_path)) +# if os.path.exists(atl_path): +# env_tuple_list.append(('INCLUDE', atl_path)) +# env_tuple_list.append(('INCLUDE', include_path)) +# +# env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) +# env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) +# env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) +# +# for variable, directory in env_tuple_list: +# env.PrependENVPath(variable, directory) + +def msvs_exists(): + return (len(get_installed_visual_studios()) > 0) + +def get_vs_by_version(msvs): + global InstalledVSMap + global SupportedVSMap + + debug('vs.py:get_vs_by_version()') + if msvs not in SupportedVSMap: + msg = "Visual Studio version %s is not supported" % repr(msvs) + raise SCons.Errors.UserError(msg) + get_installed_visual_studios() + vs = InstalledVSMap.get(msvs) + debug('InstalledVSMap:%s'%InstalledVSMap) + debug('vs.py:get_vs_by_version: found vs:%s'%vs) + # Some check like this would let us provide a useful error message + # if they try to set a Visual Studio version that's not installed. + # However, we also want to be able to run tests (like the unit + # tests) on systems that don't, or won't ever, have it installed. + # It might be worth resurrecting this, with some configurable + # setting that the tests can use to bypass the check. + #if not vs: + # msg = "Visual Studio version %s is not installed" % repr(msvs) + # raise SCons.Errors.UserError, msg + return vs + +def get_default_version(env): + """Returns the default version string to use for MSVS. + + If no version was requested by the user through the MSVS environment + variable, query all the available the visual studios through + query_versions, and take the highest one. + + Return + ------ + version: str + the default version. + """ + if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']): + versions = [vs.version for vs in get_installed_visual_studios()] + env['MSVS'] = {'VERSIONS' : versions} + else: + versions = env['MSVS'].get('VERSIONS', []) + + if 'MSVS_VERSION' not in env: + if versions: + env['MSVS_VERSION'] = versions[0] #use highest version by default + else: + env['MSVS_VERSION'] = SupportedVSList[0].version + + env['MSVS']['VERSION'] = env['MSVS_VERSION'] + + return env['MSVS_VERSION'] + +def get_default_arch(env): + """Return the default arch to use for MSVS + + if no version was requested by the user through the MSVS_ARCH environment + variable, select x86 + + Return + ------ + arch: str + """ + arch = env.get('MSVS_ARCH', 'x86') + + msvs = InstalledVSMap.get(env['MSVS_VERSION']) + + if not msvs: + arch = 'x86' + elif not arch in msvs.get_supported_arch(): + fmt = "Visual Studio version %s does not support architecture %s" + raise SCons.Errors.UserError(fmt % (env['MSVS_VERSION'], arch)) + + return arch + +def merge_default_version(env): + version = get_default_version(env) + arch = get_default_arch(env) + +def msvs_setup_env(env): + batfilename = msvs.get_batch_file() + msvs = get_vs_by_version(version) + if msvs is None: + return + + # XXX: I think this is broken. This will silently set a bogus tool instead + # of failing, but there is no other way with the current scons tool + # framework + if batfilename is not None: + + vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE') + + msvs_list = get_installed_visual_studios() + vscommonvarnames = [vs.common_tools_var for vs in msvs_list] + save_ENV = env['ENV'] + nenv = normalize_env(env['ENV'], + ['COMSPEC'] + vscommonvarnames, + force=True) + try: + output = get_output(batfilename, arch, env=nenv) + finally: + env['ENV'] = save_ENV + vars = parse_output(output, vars) + + for k, v in vars.items(): + env.PrependENVPath(k, v, delete_existing=1) + +def query_versions(): + """Query the system to get available versions of VS. A version is + considered when a batfile is found.""" + msvs_list = get_installed_visual_studios() + versions = [msvs.version for msvs in msvs_list] + return versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/Perforce.py b/engine/SCons/Tool/Perforce.py new file mode 100644 index 0000000..40192d0 --- /dev/null +++ b/engine/SCons/Tool/Perforce.py @@ -0,0 +1,103 @@ +"""SCons.Tool.Perforce.py + +Tool-specific initialization for Perforce Source Code Management system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/Perforce.py 5357 2011/09/09 21:31:03 bdeegan" + +import os + +import SCons.Action +import SCons.Builder +import SCons.Node.FS +import SCons.Util + +# This function should maybe be moved to SCons.Util? +from SCons.Tool.PharLapCommon import addPathIfNotExists + + +# Variables that we want to import from the base OS environment. +_import_env = [ 'P4PORT', 'P4CLIENT', 'P4USER', 'USER', 'USERNAME', 'P4PASSWD', + 'P4CHARSET', 'P4LANGUAGE', 'SystemRoot' ] + +PerforceAction = SCons.Action.Action('$P4COM', '$P4COMSTR') + +def generate(env): + """Add a Builder factory function and construction variables for + Perforce to an Environment.""" + + def PerforceFactory(env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The Perforce() factory is deprecated and there is no replacement.""") + return SCons.Builder.Builder(action = PerforceAction, env = env) + + #setattr(env, 'Perforce', PerforceFactory) + env.Perforce = PerforceFactory + + env['P4'] = 'p4' + env['P4FLAGS'] = SCons.Util.CLVar('') + env['P4COM'] = '$P4 $P4FLAGS sync $TARGET' + try: + environ = env['ENV'] + except KeyError: + environ = {} + env['ENV'] = environ + + # Perforce seems to use the PWD environment variable rather than + # calling getcwd() for itself, which is odd. If no PWD variable + # is present, p4 WILL call getcwd, but this seems to cause problems + # with good ol' Windows's tilde-mangling for long file names. + environ['PWD'] = env.Dir('#').get_abspath() + + for var in _import_env: + v = os.environ.get(var) + if v: + environ[var] = v + + if SCons.Util.can_read_reg: + # If we can read the registry, add the path to Perforce to our environment. + try: + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Perforce\\environment') + val, tok = SCons.Util.RegQueryValueEx(k, 'P4INSTROOT') + addPathIfNotExists(environ, 'PATH', val) + except SCons.Util.RegError: + # Can't detect where Perforce is, hope the user has it set in the + # PATH. + pass + +def exists(env): + return env.Detect('p4') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/PharLapCommon.py b/engine/SCons/Tool/PharLapCommon.py new file mode 100644 index 0000000..6f53400 --- /dev/null +++ b/engine/SCons/Tool/PharLapCommon.py @@ -0,0 +1,137 @@ +"""SCons.Tool.PharLapCommon + +This module contains common code used by all Tools for the +Phar Lap ETS tool chain. Right now, this is linkloc and +386asm. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/PharLapCommon.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path +import SCons.Errors +import SCons.Util +import re + +def getPharLapPath(): + """Reads the registry to find the installed path of the Phar Lap ETS + development kit. + + Raises UserError if no installed version of Phar Lap can + be found.""" + + if not SCons.Util.can_read_reg: + raise SCons.Errors.InternalError("No Windows registry module was found") + try: + k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, + 'SOFTWARE\\Pharlap\\ETS') + val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir') + + # The following is a hack...there is (not surprisingly) + # an odd issue in the Phar Lap plug in that inserts + # a bunch of junk data after the phar lap path in the + # registry. We must trim it. + idx=val.find('\0') + if idx >= 0: + val = val[:idx] + + return os.path.normpath(val) + except SCons.Util.RegError: + raise SCons.Errors.UserError("Cannot find Phar Lap ETS path in the registry. Is it installed properly?") + +REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)') + +def getPharLapVersion(): + """Returns the version of the installed ETS Tool Suite as a + decimal number. This version comes from the ETS_VER #define in + the embkern.h header. For example, '#define ETS_VER 1010' (which + is what Phar Lap 10.1 defines) would cause this method to return + 1010. Phar Lap 9.1 does not have such a #define, but this method + will return 910 as a default. + + Raises UserError if no installed version of Phar Lap can + be found.""" + + include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h")) + if not os.path.exists(include_path): + raise SCons.Errors.UserError("Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?") + mo = REGEX_ETS_VER.search(open(include_path, 'r').read()) + if mo: + return int(mo.group(1)) + # Default return for Phar Lap 9.1 + return 910 + +def addPathIfNotExists(env_dict, key, path, sep=os.pathsep): + """This function will take 'key' out of the dictionary + 'env_dict', then add the path 'path' to that key if it is not + already there. This treats the value of env_dict[key] as if it + has a similar format to the PATH variable...a list of paths + separated by tokens. The 'path' will get added to the list if it + is not already there.""" + try: + is_list = 1 + paths = env_dict[key] + if not SCons.Util.is_List(env_dict[key]): + paths = paths.split(sep) + is_list = 0 + if os.path.normcase(path) not in list(map(os.path.normcase, paths)): + paths = [ path ] + paths + if is_list: + env_dict[key] = paths + else: + env_dict[key] = sep.join(paths) + except KeyError: + env_dict[key] = path + +def addPharLapPaths(env): + """This function adds the path to the Phar Lap binaries, includes, + and libraries, if they are not already there.""" + ph_path = getPharLapPath() + + try: + env_dict = env['ENV'] + except KeyError: + env_dict = {} + env['ENV'] = env_dict + addPathIfNotExists(env_dict, 'PATH', + os.path.join(ph_path, 'bin')) + addPathIfNotExists(env_dict, 'INCLUDE', + os.path.join(ph_path, 'include')) + addPathIfNotExists(env_dict, 'LIB', + os.path.join(ph_path, 'lib')) + addPathIfNotExists(env_dict, 'LIB', + os.path.join(ph_path, os.path.normpath('lib/vclib'))) + + env['PHARLAP_PATH'] = getPharLapPath() + env['PHARLAP_VERSION'] = str(getPharLapVersion()) + + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/RCS.py b/engine/SCons/Tool/RCS.py new file mode 100644 index 0000000..b6ff0b5 --- /dev/null +++ b/engine/SCons/Tool/RCS.py @@ -0,0 +1,64 @@ +"""SCons.Tool.RCS.py + +Tool-specific initialization for RCS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/RCS.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + RCS to an Environment.""" + + def RCSFactory(env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The RCS() factory is deprecated and there is no replacement.""") + act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') + return SCons.Builder.Builder(action = act, env = env) + + #setattr(env, 'RCS', RCSFactory) + env.RCS = RCSFactory + + env['RCS'] = 'rcs' + env['RCS_CO'] = 'co' + env['RCS_COFLAGS'] = SCons.Util.CLVar('') + env['RCS_COCOM'] = '$RCS_CO $RCS_COFLAGS $TARGET' + +def exists(env): + return env.Detect('rcs') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/SCCS.py b/engine/SCons/Tool/SCCS.py new file mode 100644 index 0000000..666eb3c --- /dev/null +++ b/engine/SCons/Tool/SCCS.py @@ -0,0 +1,64 @@ +"""SCons.Tool.SCCS.py + +Tool-specific initialization for SCCS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/SCCS.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + SCCS to an Environment.""" + + def SCCSFactory(env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The SCCS() factory is deprecated and there is no replacement.""") + act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') + return SCons.Builder.Builder(action = act, env = env) + + #setattr(env, 'SCCS', SCCSFactory) + env.SCCS = SCCSFactory + + env['SCCS'] = 'sccs' + env['SCCSFLAGS'] = SCons.Util.CLVar('') + env['SCCSGETFLAGS'] = SCons.Util.CLVar('') + env['SCCSCOM'] = '$SCCS $SCCSFLAGS get $SCCSGETFLAGS $TARGET' + +def exists(env): + return env.Detect('sccs') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/Subversion.py b/engine/SCons/Tool/Subversion.py new file mode 100644 index 0000000..37377ed --- /dev/null +++ b/engine/SCons/Tool/Subversion.py @@ -0,0 +1,71 @@ +"""SCons.Tool.Subversion.py + +Tool-specific initialization for Subversion. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/Subversion.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + Subversion to an Environment.""" + + def SubversionFactory(repos, module='', env=env): + """ """ + # fail if repos is not an absolute path name? + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The Subversion() factory is deprecated and there is no replacement.""") + if module != '': + module = os.path.join(module, '') + act = SCons.Action.Action('$SVNCOM', '$SVNCOMSTR') + return SCons.Builder.Builder(action = act, + env = env, + SVNREPOSITORY = repos, + SVNMODULE = module) + + #setattr(env, 'Subversion', SubversionFactory) + env.Subversion = SubversionFactory + + env['SVN'] = 'svn' + env['SVNFLAGS'] = SCons.Util.CLVar('') + env['SVNCOM'] = '$SVN $SVNFLAGS cat $SVNREPOSITORY/$SVNMODULE$TARGET > $TARGET' + +def exists(env): + return env.Detect('svn') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/__init__.py b/engine/SCons/Tool/__init__.py new file mode 100644 index 0000000..ba90b06 --- /dev/null +++ b/engine/SCons/Tool/__init__.py @@ -0,0 +1,681 @@ +"""SCons.Tool + +SCons tool selection. + +This looks for modules that define a callable object that can modify +a construction environment as appropriate for a given tool (or tool +chain). + +Note that because this subsystem just *selects* a callable that can +modify a construction environment, it's possible for people to define +their own "tool specification" in an arbitrary callable function. No +one needs to use or tie in to this subsystem in order to roll their own +tool definition. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +import imp +import sys + +import SCons.Builder +import SCons.Errors +import SCons.Node.FS +import SCons.Scanner +import SCons.Scanner.C +import SCons.Scanner.D +import SCons.Scanner.LaTeX +import SCons.Scanner.Prog + +DefaultToolpath=[] + +CScanner = SCons.Scanner.C.CScanner() +DScanner = SCons.Scanner.D.DScanner() +LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() +PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner() +ProgramScanner = SCons.Scanner.Prog.ProgramScanner() +SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner') + +CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", + ".h", ".H", ".hxx", ".hpp", ".hh", + ".F", ".fpp", ".FPP", + ".m", ".mm", + ".S", ".spp", ".SPP", ".sx"] + +DSuffixes = ['.d'] + +IDLSuffixes = [".idl", ".IDL"] + +LaTeXSuffixes = [".tex", ".ltx", ".latex"] + +for suffix in CSuffixes: + SourceFileScanner.add_scanner(suffix, CScanner) + +for suffix in DSuffixes: + SourceFileScanner.add_scanner(suffix, DScanner) + +# FIXME: what should be done here? Two scanners scan the same extensions, +# but look for different files, e.g., "picture.eps" vs. "picture.pdf". +# The builders for DVI and PDF explicitly reference their scanners +# I think that means this is not needed??? +for suffix in LaTeXSuffixes: + SourceFileScanner.add_scanner(suffix, LaTeXScanner) + SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner) + +class Tool(object): + def __init__(self, name, toolpath=[], **kw): + self.name = name + self.toolpath = toolpath + DefaultToolpath + # remember these so we can merge them into the call + self.init_kw = kw + + module = self._tool_module() + self.generate = module.generate + self.exists = module.exists + if hasattr(module, 'options'): + self.options = module.options + + def _tool_module(self): + # TODO: Interchange zipimport with normal initilization for better error reporting + oldpythonpath = sys.path + sys.path = self.toolpath + sys.path + + try: + try: + file, path, desc = imp.find_module(self.name, self.toolpath) + try: + return imp.load_module(self.name, file, path, desc) + finally: + if file: + file.close() + except ImportError, e: + if str(e)!="No module named %s"%self.name: + raise SCons.Errors.EnvironmentError(e) + try: + import zipimport + except ImportError: + pass + else: + for aPath in self.toolpath: + try: + importer = zipimport.zipimporter(aPath) + return importer.load_module(self.name) + except ImportError, e: + pass + finally: + sys.path = oldpythonpath + + full_name = 'SCons.Tool.' + self.name + try: + return sys.modules[full_name] + except KeyError: + try: + smpath = sys.modules['SCons.Tool'].__path__ + try: + file, path, desc = imp.find_module(self.name, smpath) + module = imp.load_module(full_name, file, path, desc) + setattr(SCons.Tool, self.name, module) + if file: + file.close() + return module + except ImportError, e: + if str(e)!="No module named %s"%self.name: + raise SCons.Errors.EnvironmentError(e) + try: + import zipimport + importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] ) + module = importer.load_module(full_name) + setattr(SCons.Tool, self.name, module) + return module + except ImportError, e: + m = "No tool named '%s': %s" % (self.name, e) + raise SCons.Errors.EnvironmentError(m) + except ImportError, e: + m = "No tool named '%s': %s" % (self.name, e) + raise SCons.Errors.EnvironmentError(m) + + def __call__(self, env, *args, **kw): + if self.init_kw is not None: + # Merge call kws into init kws; + # but don't bash self.init_kw. + if kw is not None: + call_kw = kw + kw = self.init_kw.copy() + kw.update(call_kw) + else: + kw = self.init_kw + env.Append(TOOLS = [ self.name ]) + if hasattr(self, 'options'): + import SCons.Variables + if 'options' not in env: + from SCons.Script import ARGUMENTS + env['options']=SCons.Variables.Variables(args=ARGUMENTS) + opts=env['options'] + + self.options(opts) + opts.Update(env) + + self.generate(env, *args, **kw) + + def __str__(self): + return self.name + +########################################################################## +# Create common executable program / library / object builders + +def createProgBuilder(env): + """This is a utility function that creates the Program + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + program = env['BUILDERS']['Program'] + except KeyError: + import SCons.Defaults + program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction, + emitter = '$PROGEMITTER', + prefix = '$PROGPREFIX', + suffix = '$PROGSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'Object', + target_scanner = ProgramScanner) + env['BUILDERS']['Program'] = program + + return program + +def createStaticLibBuilder(env): + """This is a utility function that creates the StaticLibrary + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + static_lib = env['BUILDERS']['StaticLibrary'] + except KeyError: + action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ] + if env.Detect('ranlib'): + ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR") + action_list.append(ranlib_action) + + static_lib = SCons.Builder.Builder(action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'StaticObject') + env['BUILDERS']['StaticLibrary'] = static_lib + env['BUILDERS']['Library'] = static_lib + + return static_lib + +def createSharedLibBuilder(env): + """This is a utility function that creates the SharedLibrary + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + shared_lib = env['BUILDERS']['SharedLibrary'] + except KeyError: + import SCons.Defaults + action_list = [ SCons.Defaults.SharedCheck, + SCons.Defaults.ShLinkAction ] + shared_lib = SCons.Builder.Builder(action = action_list, + emitter = "$SHLIBEMITTER", + prefix = '$SHLIBPREFIX', + suffix = '$SHLIBSUFFIX', + target_scanner = ProgramScanner, + src_suffix = '$SHOBJSUFFIX', + src_builder = 'SharedObject') + env['BUILDERS']['SharedLibrary'] = shared_lib + + return shared_lib + +def createLoadableModuleBuilder(env): + """This is a utility function that creates the LoadableModule + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + ld_module = env['BUILDERS']['LoadableModule'] + except KeyError: + import SCons.Defaults + action_list = [ SCons.Defaults.SharedCheck, + SCons.Defaults.LdModuleLinkAction ] + ld_module = SCons.Builder.Builder(action = action_list, + emitter = "$LDMODULEEMITTER", + prefix = '$LDMODULEPREFIX', + suffix = '$LDMODULESUFFIX', + target_scanner = ProgramScanner, + src_suffix = '$SHOBJSUFFIX', + src_builder = 'SharedObject') + env['BUILDERS']['LoadableModule'] = ld_module + + return ld_module + +def createObjBuilders(env): + """This is a utility function that creates the StaticObject + and SharedObject Builders in an Environment if they + are not there already. + + If they are there already, we return the existing ones. + + This is a separate function because soooo many Tools + use this functionality. + + The return is a 2-tuple of (StaticObject, SharedObject) + """ + + + try: + static_obj = env['BUILDERS']['StaticObject'] + except KeyError: + static_obj = SCons.Builder.Builder(action = {}, + emitter = {}, + prefix = '$OBJPREFIX', + suffix = '$OBJSUFFIX', + src_builder = ['CFile', 'CXXFile'], + source_scanner = SourceFileScanner, + single_source = 1) + env['BUILDERS']['StaticObject'] = static_obj + env['BUILDERS']['Object'] = static_obj + + try: + shared_obj = env['BUILDERS']['SharedObject'] + except KeyError: + shared_obj = SCons.Builder.Builder(action = {}, + emitter = {}, + prefix = '$SHOBJPREFIX', + suffix = '$SHOBJSUFFIX', + src_builder = ['CFile', 'CXXFile'], + source_scanner = SourceFileScanner, + single_source = 1) + env['BUILDERS']['SharedObject'] = shared_obj + + return (static_obj, shared_obj) + +def createCFileBuilders(env): + """This is a utility function that creates the CFile/CXXFile + Builders in an Environment if they + are not there already. + + If they are there already, we return the existing ones. + + This is a separate function because soooo many Tools + use this functionality. + + The return is a 2-tuple of (CFile, CXXFile) + """ + + try: + c_file = env['BUILDERS']['CFile'] + except KeyError: + c_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$CFILESUFFIX'}) + env['BUILDERS']['CFile'] = c_file + + env.SetDefault(CFILESUFFIX = '.c') + + try: + cxx_file = env['BUILDERS']['CXXFile'] + except KeyError: + cxx_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$CXXFILESUFFIX'}) + env['BUILDERS']['CXXFile'] = cxx_file + env.SetDefault(CXXFILESUFFIX = '.cc') + + return (c_file, cxx_file) + +########################################################################## +# Create common Java builders + +def CreateJarBuilder(env): + try: + java_jar = env['BUILDERS']['Jar'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR') + java_jar = SCons.Builder.Builder(action = jar_com, + suffix = '$JARSUFFIX', + src_suffix = '$JAVACLASSSUFIX', + src_builder = 'JavaClassFile', + source_factory = fs.Entry) + env['BUILDERS']['Jar'] = java_jar + return java_jar + +def CreateJavaHBuilder(env): + try: + java_javah = env['BUILDERS']['JavaH'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR') + java_javah = SCons.Builder.Builder(action = java_javah_com, + src_suffix = '$JAVACLASSSUFFIX', + target_factory = fs.Entry, + source_factory = fs.File, + src_builder = 'JavaClassFile') + env['BUILDERS']['JavaH'] = java_javah + return java_javah + +def CreateJavaClassFileBuilder(env): + try: + java_class_file = env['BUILDERS']['JavaClassFile'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') + java_class_file = SCons.Builder.Builder(action = javac_com, + emitter = {}, + #suffix = '$JAVACLASSSUFFIX', + src_suffix = '$JAVASUFFIX', + src_builder = ['JavaFile'], + target_factory = fs.Entry, + source_factory = fs.File) + env['BUILDERS']['JavaClassFile'] = java_class_file + return java_class_file + +def CreateJavaClassDirBuilder(env): + try: + java_class_dir = env['BUILDERS']['JavaClassDir'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') + java_class_dir = SCons.Builder.Builder(action = javac_com, + emitter = {}, + target_factory = fs.Dir, + source_factory = fs.Dir) + env['BUILDERS']['JavaClassDir'] = java_class_dir + return java_class_dir + +def CreateJavaFileBuilder(env): + try: + java_file = env['BUILDERS']['JavaFile'] + except KeyError: + java_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$JAVASUFFIX'}) + env['BUILDERS']['JavaFile'] = java_file + env['JAVASUFFIX'] = '.java' + return java_file + +class ToolInitializerMethod(object): + """ + This is added to a construction environment in place of a + method(s) normally called for a Builder (env.Object, env.StaticObject, + etc.). When called, it has its associated ToolInitializer + object search the specified list of tools and apply the first + one that exists to the construction environment. It then calls + whatever builder was (presumably) added to the construction + environment in place of this particular instance. + """ + def __init__(self, name, initializer): + """ + Note: we store the tool name as __name__ so it can be used by + the class that attaches this to a construction environment. + """ + self.__name__ = name + self.initializer = initializer + + def get_builder(self, env): + """ + Returns the appropriate real Builder for this method name + after having the associated ToolInitializer object apply + the appropriate Tool module. + """ + builder = getattr(env, self.__name__) + + self.initializer.apply_tools(env) + + builder = getattr(env, self.__name__) + if builder is self: + # There was no Builder added, which means no valid Tool + # for this name was found (or possibly there's a mismatch + # between the name we were called by and the Builder name + # added by the Tool module). + return None + + self.initializer.remove_methods(env) + + return builder + + def __call__(self, env, *args, **kw): + """ + """ + builder = self.get_builder(env) + if builder is None: + return [], [] + return builder(*args, **kw) + +class ToolInitializer(object): + """ + A class for delayed initialization of Tools modules. + + Instances of this class associate a list of Tool modules with + a list of Builder method names that will be added by those Tool + modules. As part of instantiating this object for a particular + construction environment, we also add the appropriate + ToolInitializerMethod objects for the various Builder methods + that we want to use to delay Tool searches until necessary. + """ + def __init__(self, env, tools, names): + if not SCons.Util.is_List(tools): + tools = [tools] + if not SCons.Util.is_List(names): + names = [names] + self.env = env + self.tools = tools + self.names = names + self.methods = {} + for name in names: + method = ToolInitializerMethod(name, self) + self.methods[name] = method + env.AddMethod(method) + + def remove_methods(self, env): + """ + Removes the methods that were added by the tool initialization + so we no longer copy and re-bind them when the construction + environment gets cloned. + """ + for method in self.methods.values(): + env.RemoveMethod(method) + + def apply_tools(self, env): + """ + Searches the list of associated Tool modules for one that + exists, and applies that to the construction environment. + """ + for t in self.tools: + tool = SCons.Tool.Tool(t) + if tool.exists(env): + env.Tool(tool) + return + + # If we fall through here, there was no tool module found. + # This is where we can put an informative error message + # about the inability to find the tool. We'll start doing + # this as we cut over more pre-defined Builder+Tools to use + # the ToolInitializer class. + +def Initializers(env): + ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs']) + def Install(self, *args, **kw): + return self._InternalInstall(*args, **kw) + def InstallAs(self, *args, **kw): + return self._InternalInstallAs(*args, **kw) + env.AddMethod(Install) + env.AddMethod(InstallAs) + +def FindTool(tools, env): + for tool in tools: + t = Tool(tool) + if t.exists(env): + return tool + return None + +def FindAllTools(tools, env): + def ToolExists(tool, env=env): + return Tool(tool).exists(env) + return list(filter (ToolExists, tools)) + +def tool_list(platform, env): + + other_plat_tools=[] + # XXX this logic about what tool to prefer on which platform + # should be moved into either the platform files or + # the tool files themselves. + # The search orders here are described in the man page. If you + # change these search orders, update the man page as well. + if str(platform) == 'win32': + "prefer Microsoft tools on Windows" + linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ] + c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ] + cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ] + assemblers = ['masm', 'nasm', 'gas', '386asm' ] + fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran'] + ars = ['mslib', 'ar', 'tlib'] + other_plat_tools=['msvs','midl'] + elif str(platform) == 'os2': + "prefer IBM tools on OS/2" + linkers = ['ilink', 'gnulink', ]#'mslink'] + c_compilers = ['icc', 'gcc',]# 'msvc', 'cc'] + cxx_compilers = ['icc', 'g++',]# 'msvc', 'c++'] + assemblers = ['nasm',]# 'masm', 'gas'] + fortran_compilers = ['ifl', 'g77'] + ars = ['ar',]# 'mslib'] + elif str(platform) == 'irix': + "prefer MIPSPro on IRIX" + linkers = ['sgilink', 'gnulink'] + c_compilers = ['sgicc', 'gcc', 'cc'] + cxx_compilers = ['sgic++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] + ars = ['sgiar'] + elif str(platform) == 'sunos': + "prefer Forte tools on SunOS" + linkers = ['sunlink', 'gnulink'] + c_compilers = ['suncc', 'gcc', 'cc'] + cxx_compilers = ['sunc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77', + 'gfortran', 'g77', 'fortran'] + ars = ['sunar'] + elif str(platform) == 'hpux': + "prefer aCC tools on HP-UX" + linkers = ['hplink', 'gnulink'] + c_compilers = ['hpcc', 'gcc', 'cc'] + cxx_compilers = ['hpc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] + ars = ['ar'] + elif str(platform) == 'aix': + "prefer AIX Visual Age tools on AIX" + linkers = ['aixlink', 'gnulink'] + c_compilers = ['aixcc', 'gcc', 'cc'] + cxx_compilers = ['aixc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran'] + ars = ['ar'] + elif str(platform) == 'darwin': + "prefer GNU tools on Mac OS X, except for some linkers and IBM tools" + linkers = ['applelink', 'gnulink'] + c_compilers = ['gcc', 'cc'] + cxx_compilers = ['g++', 'c++'] + assemblers = ['as'] + fortran_compilers = ['gfortran', 'f95', 'f90', 'g77'] + ars = ['ar'] + else: + "prefer GNU tools on all other platforms" + linkers = ['gnulink', 'mslink', 'ilink'] + c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc'] + cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++'] + assemblers = ['gas', 'nasm', 'masm'] + fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77'] + ars = ['ar', 'mslib'] + + c_compiler = FindTool(c_compilers, env) or c_compilers[0] + + # XXX this logic about what tool provides what should somehow be + # moved into the tool files themselves. + if c_compiler and c_compiler == 'mingw': + # MinGW contains a linker, C compiler, C++ compiler, + # Fortran compiler, archiver and assembler: + cxx_compiler = None + linker = None + assembler = None + fortran_compiler = None + ar = None + else: + # Don't use g++ if the C compiler has built-in C++ support: + if c_compiler in ('msvc', 'intelc', 'icc'): + cxx_compiler = None + else: + cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0] + linker = FindTool(linkers, env) or linkers[0] + assembler = FindTool(assemblers, env) or assemblers[0] + fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0] + ar = FindTool(ars, env) or ars[0] + + other_tools = FindAllTools(other_plat_tools + [ + 'dmd', + #TODO: merge 'install' into 'filesystem' and + # make 'filesystem' the default + 'filesystem', + 'm4', + 'wix', #'midl', 'msvs', + # Parser generators + 'lex', 'yacc', + # Foreign function interface + 'rpcgen', 'swig', + # Java + 'jar', 'javac', 'javah', 'rmic', + # TeX + 'dvipdf', 'dvips', 'gs', + 'tex', 'latex', 'pdflatex', 'pdftex', + # Archivers + 'tar', 'zip', 'rpm', + # SourceCode factories + 'BitKeeper', 'CVS', 'Perforce', + 'RCS', 'SCCS', # 'Subversion', + ], env) + + tools = ([linker, c_compiler, cxx_compiler, + fortran_compiler, assembler, ar] + + other_tools) + + return [x for x in tools if x] + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/aixc++.py b/engine/SCons/Tool/aixc++.py new file mode 100644 index 0000000..93f8e9e --- /dev/null +++ b/engine/SCons/Tool/aixc++.py @@ -0,0 +1,82 @@ +"""SCons.Tool.aixc++ + +Tool-specific initialization for IBM xlC / Visual Age C++ compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/aixc++.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Platform.aix + +cplusplus = __import__('c++', globals(), locals(), []) + +packages = ['vacpp.cmp.core', 'vacpp.cmp.batch', 'vacpp.cmp.C', 'ibmcxx.cmp'] + +def get_xlc(env): + xlc = env.get('CXX', 'xlC') + xlc_r = env.get('SHCXX', 'xlC_r') + return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) + +def smart_cxxflags(source, target, env, for_signature): + build_dir = env.GetBuildPath() + if build_dir: + return '-qtempinc=' + os.path.join(build_dir, 'tempinc') + return '' + +def generate(env): + """Add Builders and construction variables for xlC / Visual Age + suite to an Environment.""" + path, _cxx, _shcxx, version = get_xlc(env) + if path: + _cxx = os.path.join(path, _cxx) + _shcxx = os.path.join(path, _shcxx) + + cplusplus.generate(env) + + env['CXX'] = _cxx + env['SHCXX'] = _shcxx + env['CXXVERSION'] = version + env['SHOBJSUFFIX'] = '.pic.o' + +def exists(env): + path, _cxx, _shcxx, version = get_xlc(env) + if path and _cxx: + xlc = os.path.join(path, _cxx) + if os.path.exists(xlc): + return xlc + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/aixcc.py b/engine/SCons/Tool/aixcc.py new file mode 100644 index 0000000..230bfb3 --- /dev/null +++ b/engine/SCons/Tool/aixcc.py @@ -0,0 +1,74 @@ +"""SCons.Tool.aixcc + +Tool-specific initialization for IBM xlc / Visual Age C compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/aixcc.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Platform.aix + +import cc + +packages = ['vac.C', 'ibmcxx.cmp'] + +def get_xlc(env): + xlc = env.get('CC', 'xlc') + xlc_r = env.get('SHCC', 'xlc_r') + return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) + +def generate(env): + """Add Builders and construction variables for xlc / Visual Age + suite to an Environment.""" + path, _cc, _shcc, version = get_xlc(env) + if path: + _cc = os.path.join(path, _cc) + _shcc = os.path.join(path, _shcc) + + cc.generate(env) + + env['CC'] = _cc + env['SHCC'] = _shcc + env['CCVERSION'] = version + +def exists(env): + path, _cc, _shcc, version = get_xlc(env) + if path and _cc: + xlc = os.path.join(path, _cc) + if os.path.exists(xlc): + return xlc + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/aixf77.py b/engine/SCons/Tool/aixf77.py new file mode 100644 index 0000000..2348851 --- /dev/null +++ b/engine/SCons/Tool/aixf77.py @@ -0,0 +1,80 @@ +"""engine.SCons.Tool.aixf77 + +Tool-specific initialization for IBM Visual Age f77 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/aixf77.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +#import SCons.Platform.aix + +import f77 + +# It would be good to look for the AIX F77 package the same way we're now +# looking for the C and C++ packages. This should be as easy as supplying +# the correct package names in the following list and uncommenting the +# SCons.Platform.aix_get_xlc() call the in the function below. +packages = [] + +def get_xlf77(env): + xlf77 = env.get('F77', 'xlf77') + xlf77_r = env.get('SHF77', 'xlf77_r') + #return SCons.Platform.aix.get_xlc(env, xlf77, xlf77_r, packages) + return (None, xlf77, xlf77_r, None) + +def generate(env): + """ + Add Builders and construction variables for the Visual Age FORTRAN + compiler to an Environment. + """ + path, _f77, _shf77, version = get_xlf77(env) + if path: + _f77 = os.path.join(path, _f77) + _shf77 = os.path.join(path, _shf77) + + f77.generate(env) + + env['F77'] = _f77 + env['SHF77'] = _shf77 + +def exists(env): + path, _f77, _shf77, version = get_xlf77(env) + if path and _f77: + xlf77 = os.path.join(path, _f77) + if os.path.exists(xlf77): + return xlf77 + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/aixlink.py b/engine/SCons/Tool/aixlink.py new file mode 100644 index 0000000..473c6b8 --- /dev/null +++ b/engine/SCons/Tool/aixlink.py @@ -0,0 +1,76 @@ +"""SCons.Tool.aixlink + +Tool-specific initialization for the IBM Visual Age linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/aixlink.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path + +import SCons.Util + +import aixcc +import link + +cplusplus = __import__('c++', globals(), locals(), []) + +def smart_linkflags(source, target, env, for_signature): + if cplusplus.iscplusplus(source): + build_dir = env.subst('$BUILDDIR', target=target, source=source) + if build_dir: + return '-qtempinc=' + os.path.join(build_dir, 'tempinc') + return '' + +def generate(env): + """ + Add Builders and construction variables for Visual Age linker to + an Environment. + """ + link.generate(env) + + env['SMARTLINKFLAGS'] = smart_linkflags + env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218') + env['SHLIBSUFFIX'] = '.a' + +def exists(env): + path, _cc, _shcc, version = aixcc.get_xlc(env) + if path and _cc: + xlc = os.path.join(path, _cc) + if os.path.exists(xlc): + return xlc + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/applelink.py b/engine/SCons/Tool/applelink.py new file mode 100644 index 0000000..e989b03 --- /dev/null +++ b/engine/SCons/Tool/applelink.py @@ -0,0 +1,71 @@ +"""SCons.Tool.applelink + +Tool-specific initialization for the Apple gnu-like linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/applelink.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +# Even though the Mac is based on the GNU toolchain, it doesn't understand +# the -rpath option, so we use the "link" tool instead of "gnulink". +import link + +def generate(env): + """Add Builders and construction variables for applelink to an + Environment.""" + link.generate(env) + + env['FRAMEWORKPATHPREFIX'] = '-F' + env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' + env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' + env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') + env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + + # override the default for loadable modules, which are different + # on OS X than dynamic shared libs. echoing what XCode does for + # pre/suffixes: + env['LDMODULEPREFIX'] = '' + env['LDMODULESUFFIX'] = '' + env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') + env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + + + +def exists(env): + return env['PLATFORM'] == 'darwin' + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/ar.py b/engine/SCons/Tool/ar.py new file mode 100644 index 0000000..dcb2676 --- /dev/null +++ b/engine/SCons/Tool/ar.py @@ -0,0 +1,63 @@ +"""SCons.Tool.ar + +Tool-specific initialization for ar (library archive). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/ar.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + env['AR'] = 'ar' + env['ARFLAGS'] = SCons.Util.CLVar('rc') + env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + + if env.Detect('ranlib'): + env['RANLIB'] = 'ranlib' + env['RANLIBFLAGS'] = SCons.Util.CLVar('') + env['RANLIBCOM'] = '$RANLIB $RANLIBFLAGS $TARGET' + +def exists(env): + return env.Detect('ar') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/as.py b/engine/SCons/Tool/as.py new file mode 100644 index 0000000..1b1187a --- /dev/null +++ b/engine/SCons/Tool/as.py @@ -0,0 +1,78 @@ +"""SCons.Tool.as + +Tool-specific initialization for as, the generic Posix assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/as.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +assemblers = ['as'] + +ASSuffixes = ['.s', '.asm', '.ASM'] +ASPPSuffixes = ['.spp', '.SPP', '.sx'] +if SCons.Util.case_sensitive_suffixes('.s', '.S'): + ASPPSuffixes.extend(['.S']) +else: + ASSuffixes.extend(['.S']) + +def generate(env): + """Add Builders and construction variables for as to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in ASSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASAction) + shared_obj.add_action(suffix, SCons.Defaults.ASAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + for suffix in ASPPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASPPAction) + shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + env['AS'] = env.Detect(assemblers) or 'as' + env['ASFLAGS'] = SCons.Util.CLVar('') + env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' + env['ASPPFLAGS'] = '$ASFLAGS' + env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + +def exists(env): + return env.Detect(assemblers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/bcc32.py b/engine/SCons/Tool/bcc32.py new file mode 100644 index 0000000..580aa72 --- /dev/null +++ b/engine/SCons/Tool/bcc32.py @@ -0,0 +1,81 @@ +"""SCons.Tool.bcc32 + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/bcc32.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def findIt(program, env): + # First search in the SCons path and then the OS path: + borwin = env.WhereIs(program) or SCons.Util.WhereIs(program) + if borwin: + dir = os.path.dirname(borwin) + env.PrependENVPath('PATH', dir) + return borwin + +def generate(env): + findIt('bcc32', env) + """Add Builders and construction variables for bcc to an + Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + for suffix in ['.c', '.cpp']: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + env['CC'] = 'bcc32' + env['CCFLAGS'] = SCons.Util.CLVar('') + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.dll' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + env['CFILESUFFIX'] = '.cpp' + +def exists(env): + return findIt('bcc32', env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/c++.py b/engine/SCons/Tool/c++.py new file mode 100644 index 0000000..6667d06 --- /dev/null +++ b/engine/SCons/Tool/c++.py @@ -0,0 +1,99 @@ +"""SCons.Tool.c++ + +Tool-specific initialization for generic Posix C++ compilers. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/c++.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Tool +import SCons.Defaults +import SCons.Util + +compilers = ['CC', 'c++'] + +CXXSuffixes = ['.cpp', '.cc', '.cxx', '.c++', '.C++', '.mm'] +if SCons.Util.case_sensitive_suffixes('.c', '.C'): + CXXSuffixes.append('.C') + +def iscplusplus(source): + if not source: + # Source might be None for unusual cases like SConf. + return 0 + for s in source: + if s.sources: + ext = os.path.splitext(str(s.sources[0]))[1] + if ext in CXXSuffixes: + return 1 + return 0 + +def generate(env): + """ + Add Builders and construction variables for Visual Age C++ compilers + to an Environment. + """ + import SCons.Tool + import SCons.Tool.cc + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CXXSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CXXAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + SCons.Tool.cc.add_common_cc_variables(env) + + env['CXX'] = 'c++' + env['CXXFLAGS'] = SCons.Util.CLVar('') + env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' + env['SHCXX'] = '$CXX' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' + + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.os' + env['OBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + + env['CXXFILESUFFIX'] = '.cc' + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/cc.py b/engine/SCons/Tool/cc.py new file mode 100644 index 0000000..4501457 --- /dev/null +++ b/engine/SCons/Tool/cc.py @@ -0,0 +1,102 @@ +"""SCons.Tool.cc + +Tool-specific initialization for generic Posix C compilers. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/cc.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Tool +import SCons.Defaults +import SCons.Util + +CSuffixes = ['.c', '.m'] +if not SCons.Util.case_sensitive_suffixes('.c', '.C'): + CSuffixes.append('.C') + +def add_common_cc_variables(env): + """ + Add underlying common "C compiler" variables that + are used by multiple tools (specifically, c++). + """ + if '_CCCOMCOM' not in env: + env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' + # It's a hack to test for darwin here, but the alternative + # of creating an applecc.py to contain this seems overkill. + # Maybe someday the Apple platform will require more setup and + # this logic will be moved. + env['FRAMEWORKS'] = SCons.Util.CLVar('') + env['FRAMEWORKPATH'] = SCons.Util.CLVar('') + if env['PLATFORM'] == 'darwin': + env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' + + if 'CCFLAGS' not in env: + env['CCFLAGS'] = SCons.Util.CLVar('') + + if 'SHCCFLAGS' not in env: + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + +def generate(env): + """ + Add Builders and construction variables for C compilers to an Environment. + """ + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + add_common_cc_variables(env) + + env['CC'] = 'cc' + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' + env['SHCC'] = '$CC' + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' + + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.os' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + + env['CFILESUFFIX'] = '.c' + +def exists(env): + return env.Detect('cc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/cvf.py b/engine/SCons/Tool/cvf.py new file mode 100644 index 0000000..cb8da4f --- /dev/null +++ b/engine/SCons/Tool/cvf.py @@ -0,0 +1,58 @@ +"""engine.SCons.Tool.cvf + +Tool-specific initialization for the Compaq Visual Fortran compiler. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/cvf.py 5357 2011/09/09 21:31:03 bdeegan" + +import fortran + +compilers = ['f90'] + +def generate(env): + """Add Builders and construction variables for compaq visual fortran to an Environment.""" + + fortran.generate(env) + + env['FORTRAN'] = 'f90' + env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' + env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' + env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' + env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' + env['OBJSUFFIX'] = '.obj' + env['FORTRANMODDIR'] = '${TARGET.dir}' + env['FORTRANMODDIRPREFIX'] = '/module:' + env['FORTRANMODDIRSUFFIX'] = '' + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/default.py b/engine/SCons/Tool/default.py new file mode 100644 index 0000000..552de36 --- /dev/null +++ b/engine/SCons/Tool/default.py @@ -0,0 +1,50 @@ +"""SCons.Tool.default + +Initialization with a default tool list. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/default.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Tool + +def generate(env): + """Add default tools.""" + for t in SCons.Tool.tool_list(env['PLATFORM'], env): + SCons.Tool.Tool(t)(env) + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/dmd.py b/engine/SCons/Tool/dmd.py new file mode 100644 index 0000000..fc83194 --- /dev/null +++ b/engine/SCons/Tool/dmd.py @@ -0,0 +1,240 @@ +"""SCons.Tool.dmd + +Tool-specific initialization for the Digital Mars D compiler. +(http://digitalmars.com/d) + +Coded by Andy Friesen (andy@ikagames.com) +15 November 2003 + +Amended by Russel Winder (russel@russel.org.uk) +2010-02-07 + +There are a number of problems with this script at this point in time. +The one that irritates me the most is the Windows linker setup. The D +linker doesn't have a way to add lib paths on the commandline, as far +as I can see. You have to specify paths relative to the SConscript or +use absolute paths. To hack around it, add '#/blah'. This will link +blah.lib from the directory where SConstruct resides. + +Compiler variables: + DC - The name of the D compiler to use. Defaults to dmd or gdmd, + whichever is found. + DPATH - List of paths to search for import modules. + DVERSIONS - List of version tags to enable when compiling. + DDEBUG - List of debug tags to enable when compiling. + +Linker related variables: + LIBS - List of library files to link in. + DLINK - Name of the linker to use. Defaults to dmd or gdmd. + DLINKFLAGS - List of linker flags. + +Lib tool variables: + DLIB - Name of the lib tool to use. Defaults to lib. + DLIBFLAGS - List of flags to pass to the lib tool. + LIBS - Same as for the linker. (libraries to pull into the .lib) +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/dmd.py 5357 2011/09/09 21:31:03 bdeegan" + +import os + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Scanner.D +import SCons.Tool + +# Adapted from c++.py +def isD(source): + if not source: + return 0 + + for s in source: + if s.sources: + ext = os.path.splitext(str(s.sources[0]))[1] + if ext == '.d': + return 1 + return 0 + +smart_link = {} + +smart_lib = {} + +def generate(env): + global smart_link + global smart_lib + + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + DAction = SCons.Action.Action('$DCOM', '$DCOMSTR') + + static_obj.add_action('.d', DAction) + shared_obj.add_action('.d', DAction) + static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter) + + dc = env.Detect(['dmd', 'gdmd']) + env['DC'] = dc + env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' + env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' + env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' + env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' + + env['DPATH'] = ['#/'] + env['DFLAGS'] = [] + env['DVERSIONS'] = [] + env['DDEBUG'] = [] + + if dc: + # Add the path to the standard library. + # This is merely for the convenience of the dependency scanner. + dmd_path = env.WhereIs(dc) + if dmd_path: + x = dmd_path.rindex(dc) + phobosDir = dmd_path[:x] + '/../src/phobos' + if os.path.isdir(phobosDir): + env.Append(DPATH = [phobosDir]) + + env['DINCPREFIX'] = '-I' + env['DINCSUFFIX'] = '' + env['DVERPREFIX'] = '-version=' + env['DVERSUFFIX'] = '' + env['DDEBUGPREFIX'] = '-debug=' + env['DDEBUGSUFFIX'] = '' + env['DFLAGPREFIX'] = '-' + env['DFLAGSUFFIX'] = '' + env['DFILESUFFIX'] = '.d' + + # Need to use the Digital Mars linker/lib on windows. + # *nix can just use GNU link. + if env['PLATFORM'] == 'win32': + env['DLINK'] = '$DC' + env['DLINKCOM'] = '$DLINK -of$TARGET $SOURCES $DFLAGS $DLINKFLAGS $_DLINKLIBFLAGS' + env['DLIB'] = 'lib' + env['DLIBCOM'] = '$DLIB $_DLIBFLAGS -c $TARGET $SOURCES $_DLINKLIBFLAGS' + + env['_DLINKLIBFLAGS'] = '$( ${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' + env['DLINKFLAGS'] = [] + env['DLIBLINKPREFIX'] = '' + env['DLIBLINKSUFFIX'] = '.lib' + env['DLIBFLAGPREFIX'] = '-' + env['DLIBFLAGSUFFIX'] = '' + env['DLINKFLAGPREFIX'] = '-' + env['DLINKFLAGSUFFIX'] = '' + + SCons.Tool.createStaticLibBuilder(env) + + # Basically, we hijack the link and ar builders with our own. + # these builders check for the presence of D source, and swap out + # the system's defaults for the Digital Mars tools. If there's no D + # source, then we silently return the previous settings. + linkcom = env.get('LINKCOM') + try: + env['SMART_LINKCOM'] = smart_link[linkcom] + except KeyError: + def _smartLink(source, target, env, for_signature, + defaultLinker=linkcom): + if isD(source): + # XXX I'm not sure how to add a $DLINKCOMSTR variable + # so that it works with this _smartLink() logic, + # and I don't have a D compiler/linker to try it out, + # so we'll leave it alone for now. + return '$DLINKCOM' + else: + return defaultLinker + env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink + + arcom = env.get('ARCOM') + try: + env['SMART_ARCOM'] = smart_lib[arcom] + except KeyError: + def _smartLib(source, target, env, for_signature, + defaultLib=arcom): + if isD(source): + # XXX I'm not sure how to add a $DLIBCOMSTR variable + # so that it works with this _smartLib() logic, and + # I don't have a D compiler/archiver to try it out, + # so we'll leave it alone for now. + return '$DLIBCOM' + else: + return defaultLib + env['SMART_ARCOM'] = smart_lib[arcom] = _smartLib + + # It is worth noting that the final space in these strings is + # absolutely pivotal. SCons sees these as actions and not generators + # if it is not there. (very bad) + env['ARCOM'] = '$SMART_ARCOM ' + env['LINKCOM'] = '$SMART_LINKCOM ' + else: # assuming linux + linkcom = env.get('LINKCOM') + try: + env['SMART_LINKCOM'] = smart_link[linkcom] + except KeyError: + def _smartLink(source, target, env, for_signature, + defaultLinker=linkcom, dc=dc): + if isD(source): + try: + libs = env['LIBS'] + except KeyError: + libs = [] + if dc == 'dmd': + # TODO: This assumes that the dmd executable is in the + # bin directory and that the libraries are in a peer + # directory lib. This true of the Digital Mars + # distribution but . . . + import glob + dHome = env.WhereIs(dc).replace('/dmd' , '/..') + if glob.glob(dHome + '/lib/*phobos2*'): + if 'phobos2' not in libs: + env.Append(LIBPATH = [dHome + '/lib']) + env.Append(LIBS = ['phobos2']) + # TODO: Find out when there will be a + # 64-bit version of D. + env.Append(LINKFLAGS = ['-m32']) + else: + if 'phobos' not in libs: + env.Append(LIBS = ['phobos']) + elif dc is 'gdmd': + env.Append(LIBS = ['gphobos']) + if 'pthread' not in libs: + env.Append(LIBS = ['pthread']) + if 'm' not in libs: + env.Append(LIBS = ['m']) + return defaultLinker + env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink + + env['LINKCOM'] = '$SMART_LINKCOM ' + +def exists(env): + return env.Detect(['dmd', 'gdmd']) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/dvi.py b/engine/SCons/Tool/dvi.py new file mode 100644 index 0000000..204821b --- /dev/null +++ b/engine/SCons/Tool/dvi.py @@ -0,0 +1,64 @@ +"""SCons.Tool.dvi + +Common DVI Builder definition for various other Tool modules that use it. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/dvi.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Builder +import SCons.Tool + +DVIBuilder = None + +def generate(env): + try: + env['BUILDERS']['DVI'] + except KeyError: + global DVIBuilder + + if DVIBuilder is None: + # The suffix is hard-coded to '.dvi', not configurable via a + # construction variable like $DVISUFFIX, because the output + # file name is hard-coded within TeX. + DVIBuilder = SCons.Builder.Builder(action = {}, + source_scanner = SCons.Tool.LaTeXScanner, + suffix = '.dvi', + emitter = {}, + source_ext_match = None) + + env['BUILDERS']['DVI'] = DVIBuilder + +def exists(env): + # This only puts a skeleton Builder in place, so if someone + # references this Tool directly, it's always "available." + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/dvipdf.py b/engine/SCons/Tool/dvipdf.py new file mode 100644 index 0000000..0347d2c --- /dev/null +++ b/engine/SCons/Tool/dvipdf.py @@ -0,0 +1,125 @@ +"""SCons.Tool.dvipdf + +Tool-specific initialization for dvipdf. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/dvipdf.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Defaults +import SCons.Tool.pdf +import SCons.Tool.tex +import SCons.Util + +_null = SCons.Scanner.LaTeX._null + +def DviPdfPsFunction(XXXDviAction, target = None, source= None, env=None): + """A builder for DVI files that sets the TEXPICTS environment + variable before running dvi2ps or dvipdf.""" + + try: + abspath = source[0].attributes.path + except AttributeError : + abspath = '' + + saved_env = SCons.Scanner.LaTeX.modify_env_var(env, 'TEXPICTS', abspath) + + result = XXXDviAction(target, source, env) + + if saved_env is _null: + try: + del env['ENV']['TEXPICTS'] + except KeyError: + pass # was never set + else: + env['ENV']['TEXPICTS'] = saved_env + + return result + +def DviPdfFunction(target = None, source= None, env=None): + result = DviPdfPsFunction(PDFAction,target,source,env) + return result + +def DviPdfStrFunction(target = None, source= None, env=None): + """A strfunction for dvipdf that returns the appropriate + command string for the no_exec options.""" + if env.GetOption("no_exec"): + result = env.subst('$DVIPDFCOM',0,target,source) + else: + result = '' + return result + +PDFAction = None +DVIPDFAction = None + +def PDFEmitter(target, source, env): + """Strips any .aux or .log files from the input source list. + These are created by the TeX Builder that in all likelihood was + used to generate the .dvi file we're using as input, and we only + care about the .dvi file. + """ + def strip_suffixes(n): + return not SCons.Util.splitext(str(n))[1] in ['.aux', '.log'] + source = list(filter(strip_suffixes, source)) + return (target, source) + +def generate(env): + """Add Builders and construction variables for dvipdf to an Environment.""" + global PDFAction + if PDFAction is None: + PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR') + + global DVIPDFAction + if DVIPDFAction is None: + DVIPDFAction = SCons.Action.Action(DviPdfFunction, strfunction = DviPdfStrFunction) + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['PDF'] + bld.add_action('.dvi', DVIPDFAction) + bld.add_emitter('.dvi', PDFEmitter) + + env['DVIPDF'] = 'dvipdf' + env['DVIPDFFLAGS'] = SCons.Util.CLVar('') + env['DVIPDFCOM'] = 'cd ${TARGET.dir} && $DVIPDF $DVIPDFFLAGS ${SOURCE.file} ${TARGET.file}' + + # Deprecated synonym. + env['PDFCOM'] = ['$DVIPDFCOM'] + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('dvipdf') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/dvips.py b/engine/SCons/Tool/dvips.py new file mode 100644 index 0000000..02f8e49 --- /dev/null +++ b/engine/SCons/Tool/dvips.py @@ -0,0 +1,95 @@ +"""SCons.Tool.dvips + +Tool-specific initialization for dvips. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/dvips.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Builder +import SCons.Tool.dvipdf +import SCons.Util + +def DviPsFunction(target = None, source= None, env=None): + result = SCons.Tool.dvipdf.DviPdfPsFunction(PSAction,target,source,env) + return result + +def DviPsStrFunction(target = None, source= None, env=None): + """A strfunction for dvipdf that returns the appropriate + command string for the no_exec options.""" + if env.GetOption("no_exec"): + result = env.subst('$PSCOM',0,target,source) + else: + result = '' + return result + +PSAction = None +DVIPSAction = None +PSBuilder = None + +def generate(env): + """Add Builders and construction variables for dvips to an Environment.""" + global PSAction + if PSAction is None: + PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR') + + global DVIPSAction + if DVIPSAction is None: + DVIPSAction = SCons.Action.Action(DviPsFunction, strfunction = DviPsStrFunction) + + global PSBuilder + if PSBuilder is None: + PSBuilder = SCons.Builder.Builder(action = PSAction, + prefix = '$PSPREFIX', + suffix = '$PSSUFFIX', + src_suffix = '.dvi', + src_builder = 'DVI', + single_source=True) + + env['BUILDERS']['PostScript'] = PSBuilder + + env['DVIPS'] = 'dvips' + env['DVIPSFLAGS'] = SCons.Util.CLVar('') + # I'm not quite sure I got the directories and filenames right for variant_dir + # We need to be in the correct directory for the sake of latex \includegraphics eps included files. + env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}' + env['PSPREFIX'] = '' + env['PSSUFFIX'] = '.ps' + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('dvips') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/f03.py b/engine/SCons/Tool/f03.py new file mode 100644 index 0000000..896c03b --- /dev/null +++ b/engine/SCons/Tool/f03.py @@ -0,0 +1,63 @@ +"""engine.SCons.Tool.f03 + +Tool-specific initialization for the generic Posix f03 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/f03.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util +import fortran +from SCons.Tool.FortranCommon import add_all_to_env, add_f03_to_env + +compilers = ['f03'] + +def generate(env): + add_all_to_env(env) + add_f03_to_env(env) + + fcomp = env.Detect(compilers) or 'f03' + env['F03'] = fcomp + env['SHF03'] = fcomp + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = fcomp + + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/f77.py b/engine/SCons/Tool/f77.py new file mode 100644 index 0000000..db538d8 --- /dev/null +++ b/engine/SCons/Tool/f77.py @@ -0,0 +1,62 @@ +"""engine.SCons.Tool.f77 + +Tool-specific initialization for the generic Posix f77 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/f77.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Scanner.Fortran +import SCons.Tool +import SCons.Util +from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env + +compilers = ['f77'] + +def generate(env): + add_all_to_env(env) + add_f77_to_env(env) + + fcomp = env.Detect(compilers) or 'f77' + env['F77'] = fcomp + env['SHF77'] = fcomp + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = fcomp + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/f90.py b/engine/SCons/Tool/f90.py new file mode 100644 index 0000000..bb7e381 --- /dev/null +++ b/engine/SCons/Tool/f90.py @@ -0,0 +1,62 @@ +"""engine.SCons.Tool.f90 + +Tool-specific initialization for the generic Posix f90 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/f90.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Scanner.Fortran +import SCons.Tool +import SCons.Util +from SCons.Tool.FortranCommon import add_all_to_env, add_f90_to_env + +compilers = ['f90'] + +def generate(env): + add_all_to_env(env) + add_f90_to_env(env) + + fc = env.Detect(compilers) or 'f90' + env['F90'] = fc + env['SHF90'] = fc + + env['FORTRAN'] = fc + env['SHFORTRAN'] = fc + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/f95.py b/engine/SCons/Tool/f95.py new file mode 100644 index 0000000..06fcc78 --- /dev/null +++ b/engine/SCons/Tool/f95.py @@ -0,0 +1,63 @@ +"""engine.SCons.Tool.f95 + +Tool-specific initialization for the generic Posix f95 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/f95.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util +import fortran +from SCons.Tool.FortranCommon import add_all_to_env, add_f95_to_env + +compilers = ['f95'] + +def generate(env): + add_all_to_env(env) + add_f95_to_env(env) + + fcomp = env.Detect(compilers) or 'f95' + env['F95'] = fcomp + env['SHF95'] = fcomp + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = fcomp + + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/filesystem.py b/engine/SCons/Tool/filesystem.py new file mode 100644 index 0000000..a91aeb1 --- /dev/null +++ b/engine/SCons/Tool/filesystem.py @@ -0,0 +1,98 @@ +"""SCons.Tool.filesystem + +Tool-specific initialization for the filesystem tools. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/filesystem.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons +from SCons.Tool.install import copyFunc + +copyToBuilder, copyAsBuilder = None, None + +def copyto_emitter(target, source, env): + """ changes the path of the source to be under the target (which + are assumed to be directories. + """ + n_target = [] + + for t in target: + n_target = n_target + [t.File( str( s ) ) for s in source] + + return (n_target, source) + +def copy_action_func(target, source, env): + assert( len(target) == len(source) ), "\ntarget: %s\nsource: %s" %(list(map(str, target)),list(map(str, source))) + + for t, s in zip(target, source): + if copyFunc(t.get_path(), s.get_path(), env): + return 1 + + return 0 + +def copy_action_str(target, source, env): + return env.subst_target_source(env['COPYSTR'], 0, target, source) + +copy_action = SCons.Action.Action( copy_action_func, copy_action_str ) + +def generate(env): + try: + env['BUILDERS']['CopyTo'] + env['BUILDERS']['CopyAs'] + except KeyError, e: + global copyToBuilder + if copyToBuilder is None: + copyToBuilder = SCons.Builder.Builder( + action = copy_action, + target_factory = env.fs.Dir, + source_factory = env.fs.Entry, + multi = 1, + emitter = [ copyto_emitter, ] ) + + global copyAsBuilder + if copyAsBuilder is None: + copyAsBuilder = SCons.Builder.Builder( + action = copy_action, + target_factory = env.fs.Entry, + source_factory = env.fs.Entry ) + + env['BUILDERS']['CopyTo'] = copyToBuilder + env['BUILDERS']['CopyAs'] = copyAsBuilder + + env['COPYSTR'] = 'Copy file(s): "$SOURCES" to "$TARGETS"' + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/fortran.py b/engine/SCons/Tool/fortran.py new file mode 100644 index 0000000..b955e7c --- /dev/null +++ b/engine/SCons/Tool/fortran.py @@ -0,0 +1,62 @@ +"""SCons.Tool.fortran + +Tool-specific initialization for a generic Posix f77/f90 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/fortran.py 5357 2011/09/09 21:31:03 bdeegan" + +import re + +import SCons.Action +import SCons.Defaults +import SCons.Scanner.Fortran +import SCons.Tool +import SCons.Util +from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env + +compilers = ['f95', 'f90', 'f77'] + +def generate(env): + add_all_to_env(env) + add_fortran_to_env(env) + + fc = env.Detect(compilers) or 'f77' + env['SHFORTRAN'] = fc + env['FORTRAN'] = fc + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/g++.py b/engine/SCons/Tool/g++.py new file mode 100644 index 0000000..c2ded0f --- /dev/null +++ b/engine/SCons/Tool/g++.py @@ -0,0 +1,90 @@ +"""SCons.Tool.g++ + +Tool-specific initialization for g++. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/g++.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path +import re +import subprocess + +import SCons.Tool +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +compilers = ['g++'] + +def generate(env): + """Add Builders and construction variables for g++ to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + cplusplus.generate(env) + + env['CXX'] = env.Detect(compilers) + + # platform specific settings + if env['PLATFORM'] == 'aix': + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + elif env['PLATFORM'] == 'hpux': + env['SHOBJSUFFIX'] = '.pic.o' + elif env['PLATFORM'] == 'sunos': + env['SHOBJSUFFIX'] = '.pic.o' + # determine compiler version + if env['CXX']: + #pipe = SCons.Action._subproc(env, [env['CXX'], '-dumpversion'], + pipe = SCons.Action._subproc(env, [env['CXX'], '--version'], + stdin = 'devnull', + stderr = 'devnull', + stdout = subprocess.PIPE) + if pipe.wait() != 0: return + # -dumpversion was added in GCC 3.0. As long as we're supporting + # GCC versions older than that, we should use --version and a + # regular expression. + #line = pipe.stdout.read().strip() + #if line: + # env['CXXVERSION'] = line + line = pipe.stdout.readline() + match = re.search(r'[0-9]+(\.[0-9]+)+', line) + if match: + env['CXXVERSION'] = match.group(0) + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/g77.py b/engine/SCons/Tool/g77.py new file mode 100644 index 0000000..9ff3df6 --- /dev/null +++ b/engine/SCons/Tool/g77.py @@ -0,0 +1,73 @@ +"""engine.SCons.Tool.g77 + +Tool-specific initialization for g77. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/g77.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util +from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env + +compilers = ['g77', 'f77'] + +def generate(env): + """Add Builders and construction variables for g77 to an Environment.""" + add_all_to_env(env) + add_f77_to_env(env) + + fcomp = env.Detect(compilers) or 'g77' + if env['PLATFORM'] in ['cygwin', 'win32']: + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS') + env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS') + else: + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC') + env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC') + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = '$FORTRAN' + + env['F77'] = fcomp + env['SHF77'] = '$F77' + + env['INCFORTRANPREFIX'] = "-I" + env['INCFORTRANSUFFIX'] = "" + + env['INCF77PREFIX'] = "-I" + env['INCF77SUFFIX'] = "" + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/gas.py b/engine/SCons/Tool/gas.py new file mode 100644 index 0000000..0a5ed9f --- /dev/null +++ b/engine/SCons/Tool/gas.py @@ -0,0 +1,53 @@ +"""SCons.Tool.gas + +Tool-specific initialization for as, the Gnu assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/gas.py 5357 2011/09/09 21:31:03 bdeegan" + +as_module = __import__('as', globals(), locals(), []) + +assemblers = ['as', 'gas'] + +def generate(env): + """Add Builders and construction variables for as to an Environment.""" + as_module.generate(env) + + env['AS'] = env.Detect(assemblers) or 'as' + +def exists(env): + return env.Detect(assemblers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/gcc.py b/engine/SCons/Tool/gcc.py new file mode 100644 index 0000000..d8d18bc --- /dev/null +++ b/engine/SCons/Tool/gcc.py @@ -0,0 +1,80 @@ +"""SCons.Tool.gcc + +Tool-specific initialization for gcc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/gcc.py 5357 2011/09/09 21:31:03 bdeegan" + +import cc +import os +import re +import subprocess + +import SCons.Util + +compilers = ['gcc', 'cc'] + +def generate(env): + """Add Builders and construction variables for gcc to an Environment.""" + cc.generate(env) + + env['CC'] = env.Detect(compilers) or 'gcc' + if env['PLATFORM'] in ['cygwin', 'win32']: + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + else: + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') + # determine compiler version + if env['CC']: + #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], + pipe = SCons.Action._subproc(env, [env['CC'], '--version'], + stdin = 'devnull', + stderr = 'devnull', + stdout = subprocess.PIPE) + if pipe.wait() != 0: return + # -dumpversion was added in GCC 3.0. As long as we're supporting + # GCC versions older than that, we should use --version and a + # regular expression. + #line = pipe.stdout.read().strip() + #if line: + # env['CCVERSION'] = line + line = pipe.stdout.readline() + match = re.search(r'[0-9]+(\.[0-9]+)+', line) + if match: + env['CCVERSION'] = match.group(0) + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/gfortran.py b/engine/SCons/Tool/gfortran.py new file mode 100644 index 0000000..143af4c --- /dev/null +++ b/engine/SCons/Tool/gfortran.py @@ -0,0 +1,64 @@ +"""SCons.Tool.gfortran + +Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran +2003 compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/gfortran.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +import fortran + +def generate(env): + """Add Builders and construction variables for gfortran to an + Environment.""" + fortran.generate(env) + + for dialect in ['F77', 'F90', 'FORTRAN', 'F95', 'F03']: + env['%s' % dialect] = 'gfortran' + env['SH%s' % dialect] = '$%s' % dialect + if env['PLATFORM'] in ['cygwin', 'win32']: + env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) + else: + env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) + + env['INC%sPREFIX' % dialect] = "-I" + env['INC%sSUFFIX' % dialect] = "" + +def exists(env): + return env.Detect('gfortran') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/gnulink.py b/engine/SCons/Tool/gnulink.py new file mode 100644 index 0000000..a80611e --- /dev/null +++ b/engine/SCons/Tool/gnulink.py @@ -0,0 +1,62 @@ +"""SCons.Tool.gnulink + +Tool-specific initialization for the gnu linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/gnulink.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +import link + +linkers = ['g++', 'gcc'] + +def generate(env): + """Add Builders and construction variables for gnulink to an Environment.""" + link.generate(env) + + if env['PLATFORM'] == 'hpux': + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared -fPIC') + + # __RPATH is set to $_RPATH in the platform specification if that + # platform supports it. + env['RPATHPREFIX'] = '-Wl,-rpath=' + env['RPATHSUFFIX'] = '' + env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + +def exists(env): + return env.Detect(linkers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/gs.py b/engine/SCons/Tool/gs.py new file mode 100644 index 0000000..fbcb782 --- /dev/null +++ b/engine/SCons/Tool/gs.py @@ -0,0 +1,81 @@ +"""SCons.Tool.gs + +Tool-specific initialization for Ghostscript. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/gs.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Platform +import SCons.Util + +# Ghostscript goes by different names on different platforms... +platform = SCons.Platform.platform_default() + +if platform == 'os2': + gs = 'gsos2' +elif platform == 'win32': + gs = 'gswin32c' +else: + gs = 'gs' + +GhostscriptAction = None + +def generate(env): + """Add Builders and construction variables for Ghostscript to an + Environment.""" + + global GhostscriptAction + if GhostscriptAction is None: + GhostscriptAction = SCons.Action.Action('$GSCOM', '$GSCOMSTR') + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['PDF'] + bld.add_action('.ps', GhostscriptAction) + + env['GS'] = gs + env['GSFLAGS'] = SCons.Util.CLVar('-dNOPAUSE -dBATCH -sDEVICE=pdfwrite') + env['GSCOM'] = '$GS $GSFLAGS -sOutputFile=$TARGET $SOURCES' + + +def exists(env): + if 'PS2PDF' in env: + return env.Detect(env['PS2PDF']) + else: + return env.Detect(gs) or SCons.Util.WhereIs(gs) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/hpc++.py b/engine/SCons/Tool/hpc++.py new file mode 100644 index 0000000..d2bb579 --- /dev/null +++ b/engine/SCons/Tool/hpc++.py @@ -0,0 +1,84 @@ +"""SCons.Tool.hpc++ + +Tool-specific initialization for c++ on HP/UX. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/hpc++.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +acc = None + +# search for the acc compiler and linker front end + +try: + dirs = os.listdir('/opt') +except (IOError, OSError): + # Not being able to read the directory because it doesn't exist + # (IOError) or isn't readable (OSError) is okay. + dirs = [] + +for dir in dirs: + cc = '/opt/' + dir + '/bin/aCC' + if os.path.exists(cc): + acc = cc + break + + +def generate(env): + """Add Builders and construction variables for g++ to an Environment.""" + cplusplus.generate(env) + + if acc: + env['CXX'] = acc or 'aCC' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') + # determine version of aCC + line = os.popen(acc + ' -V 2>&1').readline().rstrip() + if line.find('aCC: HP ANSI C++') == 0: + env['CXXVERSION'] = line.split()[-1] + + if env['PLATFORM'] == 'cygwin': + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + else: + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') + +def exists(env): + return acc + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/hpcc.py b/engine/SCons/Tool/hpcc.py new file mode 100644 index 0000000..38ae2f2 --- /dev/null +++ b/engine/SCons/Tool/hpcc.py @@ -0,0 +1,53 @@ +"""SCons.Tool.hpcc + +Tool-specific initialization for HP aCC and cc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/hpcc.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +import cc + +def generate(env): + """Add Builders and construction variables for aCC & cc to an Environment.""" + cc.generate(env) + + env['CXX'] = 'aCC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS +Z') + +def exists(env): + return env.Detect('aCC') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/hplink.py b/engine/SCons/Tool/hplink.py new file mode 100644 index 0000000..5ebe94d --- /dev/null +++ b/engine/SCons/Tool/hplink.py @@ -0,0 +1,77 @@ +"""SCons.Tool.hplink + +Tool-specific initialization for the HP linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/hplink.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path + +import SCons.Util + +import link + +ccLinker = None + +# search for the acc compiler and linker front end + +try: + dirs = os.listdir('/opt') +except (IOError, OSError): + # Not being able to read the directory because it doesn't exist + # (IOError) or isn't readable (OSError) is okay. + dirs = [] + +for dir in dirs: + linker = '/opt/' + dir + '/bin/aCC' + if os.path.exists(linker): + ccLinker = linker + break + +def generate(env): + """ + Add Builders and construction variables for Visual Age linker to + an Environment. + """ + link.generate(env) + + env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,+s -Wl,+vnocompatwarnings') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -b') + env['SHLIBSUFFIX'] = '.sl' + +def exists(env): + return ccLinker + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/icc.py b/engine/SCons/Tool/icc.py new file mode 100644 index 0000000..10111ec --- /dev/null +++ b/engine/SCons/Tool/icc.py @@ -0,0 +1,59 @@ +"""engine.SCons.Tool.icc + +Tool-specific initialization for the OS/2 icc compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/icc.py 5357 2011/09/09 21:31:03 bdeegan" + +import cc + +def generate(env): + """Add Builders and construction variables for the OS/2 to an Environment.""" + cc.generate(env) + + env['CC'] = 'icc' + env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' + env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' + env['CPPDEFPREFIX'] = '/D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '/I' + env['INCSUFFIX'] = '' + env['CFILESUFFIX'] = '.c' + env['CXXFILESUFFIX'] = '.cc' + +def exists(env): + return env.Detect('icc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/icl.py b/engine/SCons/Tool/icl.py new file mode 100644 index 0000000..1ef7d3a --- /dev/null +++ b/engine/SCons/Tool/icl.py @@ -0,0 +1,52 @@ +"""engine.SCons.Tool.icl + +Tool-specific initialization for the Intel C/C++ compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/icl.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Tool.intelc + +# This has been completely superceded by intelc.py, which can +# handle both Windows and Linux versions. + +def generate(*args, **kw): + """Add Builders and construction variables for icl to an Environment.""" + return SCons.Tool.intelc.generate(*args, **kw) + +def exists(*args, **kw): + return SCons.Tool.intelc.exists(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/ifl.py b/engine/SCons/Tool/ifl.py new file mode 100644 index 0000000..dac5670 --- /dev/null +++ b/engine/SCons/Tool/ifl.py @@ -0,0 +1,72 @@ +"""SCons.Tool.ifl + +Tool-specific initialization for the Intel Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/ifl.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +from SCons.Scanner.Fortran import FortranScan +from FortranCommon import add_all_to_env + +def generate(env): + """Add Builders and construction variables for ifl to an Environment.""" + fscan = FortranScan("FORTRANPATH") + SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) + SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) + + if 'FORTRANFILESUFFIXES' not in env: + env['FORTRANFILESUFFIXES'] = ['.i'] + else: + env['FORTRANFILESUFFIXES'].append('.i') + + if 'F90FILESUFFIXES' not in env: + env['F90FILESUFFIXES'] = ['.i90'] + else: + env['F90FILESUFFIXES'].append('.i90') + + add_all_to_env(env) + + env['FORTRAN'] = 'ifl' + env['SHFORTRAN'] = '$FORTRAN' + env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' + env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' + env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' + env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' + +def exists(env): + return env.Detect('ifl') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/ifort.py b/engine/SCons/Tool/ifort.py new file mode 100644 index 0000000..c6768e9 --- /dev/null +++ b/engine/SCons/Tool/ifort.py @@ -0,0 +1,88 @@ +"""SCons.Tool.ifort + +Tool-specific initialization for newer versions of the Intel Fortran Compiler +for Linux/Windows (and possibly Mac OS X). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/ifort.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +from SCons.Scanner.Fortran import FortranScan +from FortranCommon import add_all_to_env + +def generate(env): + """Add Builders and construction variables for ifort to an Environment.""" + # ifort supports Fortran 90 and Fortran 95 + # Additionally, ifort recognizes more file extensions. + fscan = FortranScan("FORTRANPATH") + SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) + SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) + + if 'FORTRANFILESUFFIXES' not in env: + env['FORTRANFILESUFFIXES'] = ['.i'] + else: + env['FORTRANFILESUFFIXES'].append('.i') + + if 'F90FILESUFFIXES' not in env: + env['F90FILESUFFIXES'] = ['.i90'] + else: + env['F90FILESUFFIXES'].append('.i90') + + add_all_to_env(env) + + fc = 'ifort' + + for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: + env['%s' % dialect] = fc + env['SH%s' % dialect] = '$%s' % dialect + if env['PLATFORM'] == 'posix': + env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) + + if env['PLATFORM'] == 'win32': + # On Windows, the ifort compiler specifies the object on the + # command line with -object:, not -o. Massage the necessary + # command-line construction variables. + for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: + for var in ['%sCOM' % dialect, '%sPPCOM' % dialect, + 'SH%sCOM' % dialect, 'SH%sPPCOM' % dialect]: + env[var] = env[var].replace('-o $TARGET', '-object:$TARGET') + env['FORTRANMODDIRPREFIX'] = "/module:" + else: + env['FORTRANMODDIRPREFIX'] = "-module " + +def exists(env): + return env.Detect('ifort') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/ilink.py b/engine/SCons/Tool/ilink.py new file mode 100644 index 0000000..73d7622 --- /dev/null +++ b/engine/SCons/Tool/ilink.py @@ -0,0 +1,59 @@ +"""SCons.Tool.ilink + +Tool-specific initialization for the OS/2 ilink linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/ilink.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ilink to an Environment.""" + SCons.Tool.createProgBuilder(env) + + env['LINK'] = 'ilink' + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '$LINK $LINKFLAGS /O:$TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBDIRPREFIX']='/LIBPATH:' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + +def exists(env): + return env.Detect('ilink') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/ilink32.py b/engine/SCons/Tool/ilink32.py new file mode 100644 index 0000000..ebdf435 --- /dev/null +++ b/engine/SCons/Tool/ilink32.py @@ -0,0 +1,60 @@ +"""SCons.Tool.ilink32 + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/ilink32.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Tool +import SCons.Tool.bcc32 +import SCons.Util + +def generate(env): + """Add Builders and construction variables for Borland ilink to an + Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['LINK'] = '$CC' + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '$LINK -q $LINKFLAGS -e$TARGET $SOURCES $LIBS' + env['LIBDIRPREFIX']='' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + + +def exists(env): + # Uses bcc32 to do linking as it generally knows where the standard + # LIBS are and set up the linking correctly + return SCons.Tool.bcc32.findIt('bcc32', env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/install.py b/engine/SCons/Tool/install.py new file mode 100644 index 0000000..58e2dc7 --- /dev/null +++ b/engine/SCons/Tool/install.py @@ -0,0 +1,283 @@ +"""SCons.Tool.install + +Tool-specific initialization for the install tool. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/install.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import shutil +import stat + +import SCons.Action +from SCons.Util import make_path_relative + +# +# We keep track of *all* installed files. +_INSTALLED_FILES = [] +_UNIQUE_INSTALLED_FILES = None + +class CopytreeError(EnvironmentError): + pass + +# This is a patched version of shutil.copytree from python 2.5. It +# doesn't fail if the dir exists, which regular copytree does +# (annoyingly). Note the XXX comment in the docstring. +def scons_copytree(src, dst, symlinks=False): + """Recursively copy a directory tree using copy2(). + + The destination directory must not already exist. + If exception(s) occur, an CopytreeError is raised with a list of reasons. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. + + XXX Consider this example code rather than the ultimate tool. + + """ + names = os.listdir(src) + # garyo@genarts.com fix: check for dir before making dirs. + if not os.path.exists(dst): + os.makedirs(dst) + errors = [] + for name in names: + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if symlinks and os.path.islink(srcname): + linkto = os.readlink(srcname) + os.symlink(linkto, dstname) + elif os.path.isdir(srcname): + scons_copytree(srcname, dstname, symlinks) + else: + shutil.copy2(srcname, dstname) + # XXX What about devices, sockets etc.? + except (IOError, os.error), why: + errors.append((srcname, dstname, str(why))) + # catch the CopytreeError from the recursive copytree so that we can + # continue with other files + except CopytreeError, err: + errors.extend(err.args[0]) + try: + shutil.copystat(src, dst) + except WindowsError: + # can't copy file access times on Windows + pass + except OSError, why: + errors.extend((src, dst, str(why))) + if errors: + raise CopytreeError, errors + + +# +# Functions doing the actual work of the Install Builder. +# +def copyFunc(dest, source, env): + """Install a source file or directory into a destination by copying, + (including copying permission/mode bits).""" + + if os.path.isdir(source): + if os.path.exists(dest): + if not os.path.isdir(dest): + raise SCons.Errors.UserError("cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))) + else: + parent = os.path.split(dest)[0] + if not os.path.exists(parent): + os.makedirs(parent) + scons_copytree(source, dest) + else: + shutil.copy2(source, dest) + st = os.stat(source) + os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + + return 0 + +def installFunc(target, source, env): + """Install a source file into a target using the function specified + as the INSTALL construction variable.""" + try: + install = env['INSTALL'] + except KeyError: + raise SCons.Errors.UserError('Missing INSTALL construction variable.') + + assert len(target)==len(source), \ + "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) + for t,s in zip(target,source): + if install(t.get_path(),s.get_path(),env): + return 1 + + return 0 + +def stringFunc(target, source, env): + installstr = env.get('INSTALLSTR') + if installstr: + return env.subst_target_source(installstr, 0, target, source) + target = str(target[0]) + source = str(source[0]) + if os.path.isdir(source): + type = 'directory' + else: + type = 'file' + return 'Install %s: "%s" as "%s"' % (type, source, target) + +# +# Emitter functions +# +def add_targets_to_INSTALLED_FILES(target, source, env): + """ an emitter that adds all target files to the list stored in the + _INSTALLED_FILES global variable. This way all installed files of one + scons call will be collected. + """ + global _INSTALLED_FILES, _UNIQUE_INSTALLED_FILES + _INSTALLED_FILES.extend(target) + _UNIQUE_INSTALLED_FILES = None + return (target, source) + +class DESTDIR_factory(object): + """ a node factory, where all files will be relative to the dir supplied + in the constructor. + """ + def __init__(self, env, dir): + self.env = env + self.dir = env.arg2nodes( dir, env.fs.Dir )[0] + + def Entry(self, name): + name = make_path_relative(name) + return self.dir.Entry(name) + + def Dir(self, name): + name = make_path_relative(name) + return self.dir.Dir(name) + +# +# The Builder Definition +# +install_action = SCons.Action.Action(installFunc, stringFunc) +installas_action = SCons.Action.Action(installFunc, stringFunc) + +BaseInstallBuilder = None + +def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): + if target and dir: + import SCons.Errors + raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") + if not dir: + dir=target + + import SCons.Script + install_sandbox = SCons.Script.GetOption('install_sandbox') + if install_sandbox: + target_factory = DESTDIR_factory(env, install_sandbox) + else: + target_factory = env.fs + + try: + dnodes = env.arg2nodes(dir, target_factory.Dir) + except TypeError: + raise SCons.Errors.UserError("Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)) + sources = env.arg2nodes(source, env.fs.Entry) + tgt = [] + for dnode in dnodes: + for src in sources: + # Prepend './' so the lookup doesn't interpret an initial + # '#' on the file name portion as meaning the Node should + # be relative to the top-level SConstruct directory. + target = env.fs.Entry('.'+os.sep+src.name, dnode) + #tgt.extend(BaseInstallBuilder(env, target, src, **kw)) + tgt.extend(BaseInstallBuilder(env, target, src, **kw)) + return tgt + +def InstallAsBuilderWrapper(env, target=None, source=None, **kw): + result = [] + for src, tgt in map(lambda x, y: (x, y), source, target): + #result.extend(BaseInstallBuilder(env, tgt, src, **kw)) + result.extend(BaseInstallBuilder(env, tgt, src, **kw)) + return result + +added = None + +def generate(env): + + from SCons.Script import AddOption, GetOption + global added + if not added: + added = 1 + AddOption('--install-sandbox', + dest='install_sandbox', + type="string", + action="store", + help='A directory under which all installed files will be placed.') + + global BaseInstallBuilder + if BaseInstallBuilder is None: + install_sandbox = GetOption('install_sandbox') + if install_sandbox: + target_factory = DESTDIR_factory(env, install_sandbox) + else: + target_factory = env.fs + + BaseInstallBuilder = SCons.Builder.Builder( + action = install_action, + target_factory = target_factory.Entry, + source_factory = env.fs.Entry, + multi = 1, + emitter = [ add_targets_to_INSTALLED_FILES, ], + name = 'InstallBuilder') + + env['BUILDERS']['_InternalInstall'] = InstallBuilderWrapper + env['BUILDERS']['_InternalInstallAs'] = InstallAsBuilderWrapper + + # We'd like to initialize this doing something like the following, + # but there isn't yet support for a ${SOURCE.type} expansion that + # will print "file" or "directory" depending on what's being + # installed. For now we punt by not initializing it, and letting + # the stringFunc() that we put in the action fall back to the + # hand-crafted default string if it's not set. + # + #try: + # env['INSTALLSTR'] + #except KeyError: + # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' + + try: + env['INSTALL'] + except KeyError: + env['INSTALL'] = copyFunc + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/intelc.py b/engine/SCons/Tool/intelc.py new file mode 100644 index 0000000..552fada --- /dev/null +++ b/engine/SCons/Tool/intelc.py @@ -0,0 +1,522 @@ +"""SCons.Tool.icl + +Tool-specific initialization for the Intel C/C++ compiler. +Supports Linux and Windows compilers, v7 and up. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import division + +__revision__ = "src/engine/SCons/Tool/intelc.py 5357 2011/09/09 21:31:03 bdeegan" + +import math, sys, os.path, glob, string, re + +is_windows = sys.platform == 'win32' +is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or + ('PROCESSOR_ARCHITEW6432' in os.environ and + os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64')) +is_linux = sys.platform.startswith('linux') +is_mac = sys.platform == 'darwin' + +if is_windows: + import SCons.Tool.msvc +elif is_linux: + import SCons.Tool.gcc +elif is_mac: + import SCons.Tool.gcc +import SCons.Util +import SCons.Warnings + +# Exceptions for this tool +class IntelCError(SCons.Errors.InternalError): + pass +class MissingRegistryError(IntelCError): # missing registry entry + pass +class MissingDirError(IntelCError): # dir not found + pass +class NoRegistryModuleError(IntelCError): # can't read registry at all + pass + +def uniquify(s): + """Return a sequence containing only one copy of each unique element from input sequence s. + Does not preserve order. + Input sequence must be hashable (i.e. must be usable as a dictionary key).""" + u = {} + for x in s: + u[x] = 1 + return list(u.keys()) + +def linux_ver_normalize(vstr): + """Normalize a Linux compiler version number. + Intel changed from "80" to "9.0" in 2005, so we assume if the number + is greater than 60 it's an old-style number and otherwise new-style. + Always returns an old-style float like 80 or 90 for compatibility with Windows. + Shades of Y2K!""" + # Check for version number like 9.1.026: return 91.026 + m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', vstr) + if m: + vmaj,vmin,build = m.groups() + return float(vmaj) * 10. + float(vmin) + float(build) / 1000.; + else: + f = float(vstr) + if is_windows: + return f + else: + if f < 60: return f * 10.0 + else: return f + +def check_abi(abi): + """Check for valid ABI (application binary interface) name, + and map into canonical one""" + if not abi: + return None + abi = abi.lower() + # valid_abis maps input name to canonical name + if is_windows: + valid_abis = {'ia32' : 'ia32', + 'x86' : 'ia32', + 'ia64' : 'ia64', + 'em64t' : 'em64t', + 'amd64' : 'em64t'} + if is_linux: + valid_abis = {'ia32' : 'ia32', + 'x86' : 'ia32', + 'x86_64' : 'x86_64', + 'em64t' : 'x86_64', + 'amd64' : 'x86_64'} + if is_mac: + valid_abis = {'ia32' : 'ia32', + 'x86' : 'ia32', + 'x86_64' : 'x86_64', + 'em64t' : 'x86_64'} + try: + abi = valid_abis[abi] + except KeyError: + raise SCons.Errors.UserError("Intel compiler: Invalid ABI %s, valid values are %s"% \ + (abi, list(valid_abis.keys()))) + return abi + +def vercmp(a, b): + """Compare strings as floats, + but Intel changed Linux naming convention at 9.0""" + return cmp(linux_ver_normalize(b), linux_ver_normalize(a)) + +def get_version_from_list(v, vlist): + """See if we can match v (string) in vlist (list of strings) + Linux has to match in a fuzzy way.""" + if is_windows: + # Simple case, just find it in the list + if v in vlist: return v + else: return None + else: + # Fuzzy match: normalize version number first, but still return + # original non-normalized form. + fuzz = 0.001 + for vi in vlist: + if math.fabs(linux_ver_normalize(vi) - linux_ver_normalize(v)) < fuzz: + return vi + # Not found + return None + +def get_intel_registry_value(valuename, version=None, abi=None): + """ + Return a value from the Intel compiler registry tree. (Windows only) + """ + # Open the key: + if is_win64: + K = 'Software\\Wow6432Node\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() + else: + K = 'Software\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() + try: + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) + except SCons.Util.RegError: + raise MissingRegistryError("%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi)) + + # Get the value: + try: + v = SCons.Util.RegQueryValueEx(k, valuename)[0] + return v # or v.encode('iso-8859-1', 'replace') to remove unicode? + except SCons.Util.RegError: + raise MissingRegistryError("%s\\%s was not found in the registry."%(K, valuename)) + + +def get_all_compiler_versions(): + """Returns a sorted list of strings, like "70" or "80" or "9.0" + with most recent compiler version first. + """ + versions=[] + if is_windows: + if is_win64: + keyname = 'Software\\WoW6432Node\\Intel\\Compilers\\C++' + else: + keyname = 'Software\\Intel\\Compilers\\C++' + try: + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, + keyname) + except WindowsError: + return [] + i = 0 + versions = [] + try: + while i < 100: + subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError + # Check that this refers to an existing dir. + # This is not 100% perfect but should catch common + # installation issues like when the compiler was installed + # and then the install directory deleted or moved (rather + # than uninstalling properly), so the registry values + # are still there. + ok = False + for try_abi in ('IA32', 'IA32e', 'IA64', 'EM64T'): + try: + d = get_intel_registry_value('ProductDir', subkey, try_abi) + except MissingRegistryError: + continue # not found in reg, keep going + if os.path.exists(d): ok = True + if ok: + versions.append(subkey) + else: + try: + # Registry points to nonexistent dir. Ignore this + # version. + value = get_intel_registry_value('ProductDir', subkey, 'IA32') + except MissingRegistryError, e: + + # Registry key is left dangling (potentially + # after uninstalling). + + print \ + "scons: *** Ignoring the registry key for the Intel compiler version %s.\n" \ + "scons: *** It seems that the compiler was uninstalled and that the registry\n" \ + "scons: *** was not cleaned up properly.\n" % subkey + else: + print "scons: *** Ignoring "+str(value) + + i = i + 1 + except EnvironmentError: + # no more subkeys + pass + elif is_linux: + for d in glob.glob('/opt/intel_cc_*'): + # Typical dir here is /opt/intel_cc_80. + m = re.search(r'cc_(.*)$', d) + if m: + versions.append(m.group(1)) + for d in glob.glob('/opt/intel/cc*/*'): + # Typical dir here is /opt/intel/cc/9.0 for IA32, + # /opt/intel/cce/9.0 for EMT64 (AMD64) + m = re.search(r'([0-9][0-9.]*)$', d) + if m: + versions.append(m.group(1)) + for d in glob.glob('/opt/intel/Compiler/*'): + # Typical dir here is /opt/intel/Compiler/11.1 + m = re.search(r'([0-9][0-9.]*)$', d) + if m: + versions.append(m.group(1)) + elif is_mac: + for d in glob.glob('/opt/intel/cc*/*'): + # Typical dir here is /opt/intel/cc/9.0 for IA32, + # /opt/intel/cce/9.0 for EMT64 (AMD64) + m = re.search(r'([0-9][0-9.]*)$', d) + if m: + versions.append(m.group(1)) + def keyfunc(str): + """Given a dot-separated version string, return a tuple of ints representing it.""" + return [int(x) for x in str.split('.')] + # split into ints, sort, then remove dups + return sorted(uniquify(versions), key=keyfunc, reverse=True) + +def get_intel_compiler_top(version, abi): + """ + Return the main path to the top-level dir of the Intel compiler, + using the given version. + The compiler will be in /bin/icl.exe (icc on linux), + the include dir is /include, etc. + """ + + if is_windows: + if not SCons.Util.can_read_reg: + raise NoRegistryModuleError("No Windows registry module was found") + top = get_intel_registry_value('ProductDir', version, abi) + # pre-11, icl was in Bin. 11 and later, it's in Bin/ apparently. + if not os.path.exists(os.path.join(top, "Bin", "icl.exe")) \ + and not os.path.exists(os.path.join(top, "Bin", abi, "icl.exe")): + raise MissingDirError("Can't find Intel compiler in %s"%(top)) + elif is_mac or is_linux: + def find_in_2008style_dir(version): + # first dir is new (>=9.0) style, second is old (8.0) style. + dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s') + if abi == 'x86_64': + dirs=('/opt/intel/cce/%s',) # 'e' stands for 'em64t', aka x86_64 aka amd64 + top=None + for d in dirs: + if os.path.exists(os.path.join(d%version, "bin", "icc")): + top = d%version + break + return top + def find_in_2010style_dir(version): + dirs=('/opt/intel/Compiler/%s/*'%version) + # typically /opt/intel/Compiler/11.1/064 (then bin/intel64/icc) + dirs=glob.glob(dirs) + # find highest sub-version number by reverse sorting and picking first existing one. + dirs.sort() + dirs.reverse() + top=None + for d in dirs: + if (os.path.exists(os.path.join(d, "bin", "ia32", "icc")) or + os.path.exists(os.path.join(d, "bin", "intel64", "icc"))): + top = d + break + return top + top = find_in_2010style_dir(version) or find_in_2008style_dir(version) + print "INTELC: top=",top + if not top: + raise MissingDirError("Can't find version %s Intel compiler in %s (abi='%s')"%(version,top, abi)) + return top + + +def generate(env, version=None, abi=None, topdir=None, verbose=0): + """Add Builders and construction variables for Intel C/C++ compiler + to an Environment. + args: + version: (string) compiler version to use, like "80" + abi: (string) 'win32' or whatever Itanium version wants + topdir: (string) compiler top dir, like + "c:\Program Files\Intel\Compiler70" + If topdir is used, version and abi are ignored. + verbose: (int) if >0, prints compiler version used. + """ + if not (is_mac or is_linux or is_windows): + # can't handle this platform + return + + if is_windows: + SCons.Tool.msvc.generate(env) + elif is_linux: + SCons.Tool.gcc.generate(env) + elif is_mac: + SCons.Tool.gcc.generate(env) + + # if version is unspecified, use latest + vlist = get_all_compiler_versions() + if not version: + if vlist: + version = vlist[0] + else: + # User may have specified '90' but we need to get actual dirname '9.0'. + # get_version_from_list does that mapping. + v = get_version_from_list(version, vlist) + if not v: + raise SCons.Errors.UserError("Invalid Intel compiler version %s: "%version + \ + "installed versions are %s"%(', '.join(vlist))) + version = v + + # if abi is unspecified, use ia32 + # alternatives are ia64 for Itanium, or amd64 or em64t or x86_64 (all synonyms here) + abi = check_abi(abi) + if abi is None: + if is_mac or is_linux: + # Check if we are on 64-bit linux, default to 64 then. + uname_m = os.uname()[4] + if uname_m == 'x86_64': + abi = 'x86_64' + else: + abi = 'ia32' + else: + if is_win64: + abi = 'em64t' + else: + abi = 'ia32' + + if version and not topdir: + try: + topdir = get_intel_compiler_top(version, abi) + except (SCons.Util.RegError, IntelCError): + topdir = None + + if not topdir: + # Normally this is an error, but it might not be if the compiler is + # on $PATH and the user is importing their env. + class ICLTopDirWarning(SCons.Warnings.Warning): + pass + if (is_mac or is_linux) and not env.Detect('icc') or \ + is_windows and not env.Detect('icl'): + + SCons.Warnings.enableWarningClass(ICLTopDirWarning) + SCons.Warnings.warn(ICLTopDirWarning, + "Failed to find Intel compiler for version='%s', abi='%s'"% + (str(version), str(abi))) + else: + # should be cleaned up to say what this other version is + # since in this case we have some other Intel compiler installed + SCons.Warnings.enableWarningClass(ICLTopDirWarning) + SCons.Warnings.warn(ICLTopDirWarning, + "Can't find Intel compiler top dir for version='%s', abi='%s'"% + (str(version), str(abi))) + + if topdir: + archdir={'x86_64': 'intel64', + 'amd64' : 'intel64', + 'em64t' : 'intel64', + 'x86' : 'ia32', + 'i386' : 'ia32', + 'ia32' : 'ia32' + }[abi] # for v11 and greater + if os.path.exists(os.path.join(topdir, 'bin', archdir)): + bindir="bin/%s"%archdir + libdir="lib/%s"%archdir + else: + bindir="bin" + libdir="lib" + if verbose: + print "Intel C compiler: using version %s (%g), abi %s, in '%s/%s'"%\ + (repr(version), linux_ver_normalize(version),abi,topdir,bindir) + if is_linux: + # Show the actual compiler version by running the compiler. + os.system('%s/%s/icc --version'%(topdir,bindir)) + if is_mac: + # Show the actual compiler version by running the compiler. + os.system('%s/%s/icc --version'%(topdir,bindir)) + + env['INTEL_C_COMPILER_TOP'] = topdir + if is_linux: + paths={'INCLUDE' : 'include', + 'LIB' : libdir, + 'PATH' : bindir, + 'LD_LIBRARY_PATH' : libdir} + for p in paths.keys(): + env.PrependENVPath(p, os.path.join(topdir, paths[p])) + if is_mac: + paths={'INCLUDE' : 'include', + 'LIB' : libdir, + 'PATH' : bindir, + 'LD_LIBRARY_PATH' : libdir} + for p in paths.keys(): + env.PrependENVPath(p, os.path.join(topdir, paths[p])) + if is_windows: + # env key reg valname default subdir of top + paths=(('INCLUDE', 'IncludeDir', 'Include'), + ('LIB' , 'LibDir', 'Lib'), + ('PATH' , 'BinDir', 'Bin')) + # We are supposed to ignore version if topdir is set, so set + # it to the emptry string if it's not already set. + if version is None: + version = '' + # Each path has a registry entry, use that or default to subdir + for p in paths: + try: + path=get_intel_registry_value(p[1], version, abi) + # These paths may have $(ICInstallDir) + # which needs to be substituted with the topdir. + path=path.replace('$(ICInstallDir)', topdir + os.sep) + except IntelCError: + # Couldn't get it from registry: use default subdir of topdir + env.PrependENVPath(p[0], os.path.join(topdir, p[2])) + else: + env.PrependENVPath(p[0], path.split(os.pathsep)) + # print "ICL %s: %s, final=%s"%(p[0], path, str(env['ENV'][p[0]])) + + if is_windows: + env['CC'] = 'icl' + env['CXX'] = 'icl' + env['LINK'] = 'xilink' + else: + env['CC'] = 'icc' + env['CXX'] = 'icpc' + # Don't reset LINK here; + # use smart_link which should already be here from link.py. + #env['LINK'] = '$CC' + env['AR'] = 'xiar' + env['LD'] = 'xild' # not used by default + + # This is not the exact (detailed) compiler version, + # just the major version as determined above or specified + # by the user. It is a float like 80 or 90, in normalized form for Linux + # (i.e. even for Linux 9.0 compiler, still returns 90 rather than 9.0) + if version: + env['INTEL_C_COMPILER_VERSION']=linux_ver_normalize(version) + + if is_windows: + # Look for license file dir + # in system environment, registry, and default location. + envlicdir = os.environ.get("INTEL_LICENSE_FILE", '') + K = ('SOFTWARE\Intel\Licenses') + try: + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) + reglicdir = SCons.Util.RegQueryValueEx(k, "w_cpp")[0] + except (AttributeError, SCons.Util.RegError): + reglicdir = "" + defaultlicdir = r'C:\Program Files\Common Files\Intel\Licenses' + + licdir = None + for ld in [envlicdir, reglicdir]: + # If the string contains an '@', then assume it's a network + # license (port@system) and good by definition. + if ld and (ld.find('@') != -1 or os.path.exists(ld)): + licdir = ld + break + if not licdir: + licdir = defaultlicdir + if not os.path.exists(licdir): + class ICLLicenseDirWarning(SCons.Warnings.Warning): + pass + SCons.Warnings.enableWarningClass(ICLLicenseDirWarning) + SCons.Warnings.warn(ICLLicenseDirWarning, + "Intel license dir was not found." + " Tried using the INTEL_LICENSE_FILE environment variable (%s), the registry (%s) and the default path (%s)." + " Using the default path as a last resort." + % (envlicdir, reglicdir, defaultlicdir)) + env['ENV']['INTEL_LICENSE_FILE'] = licdir + +def exists(env): + if not (is_mac or is_linux or is_windows): + # can't handle this platform + return 0 + + try: + versions = get_all_compiler_versions() + except (SCons.Util.RegError, IntelCError): + versions = None + detected = versions is not None and len(versions) > 0 + if not detected: + # try env.Detect, maybe that will work + if is_windows: + return env.Detect('icl') + elif is_linux: + return env.Detect('icc') + elif is_mac: + return env.Detect('icc') + return detected + +# end of file + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/ipkg.py b/engine/SCons/Tool/ipkg.py new file mode 100644 index 0000000..2ad08ab --- /dev/null +++ b/engine/SCons/Tool/ipkg.py @@ -0,0 +1,67 @@ +"""SCons.Tool.ipkg + +Tool-specific initialization for ipkg. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +The ipkg tool calls the ipkg-build. Its only argument should be the +packages fake_root. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/ipkg.py 5357 2011/09/09 21:31:03 bdeegan" + +import os + +import SCons.Builder + +def generate(env): + """Add Builders and construction variables for ipkg to an Environment.""" + try: + bld = env['BUILDERS']['Ipkg'] + except KeyError: + bld = SCons.Builder.Builder( action = '$IPKGCOM', + suffix = '$IPKGSUFFIX', + source_scanner = None, + target_scanner = None) + env['BUILDERS']['Ipkg'] = bld + + env['IPKG'] = 'ipkg-build' + env['IPKGCOM'] = '$IPKG $IPKGFLAGS ${SOURCE}' + env['IPKGUSER'] = os.popen('id -un').read().strip() + env['IPKGGROUP'] = os.popen('id -gn').read().strip() + env['IPKGFLAGS'] = SCons.Util.CLVar('-o $IPKGUSER -g $IPKGGROUP') + env['IPKGSUFFIX'] = '.ipk' + +def exists(env): + return env.Detect('ipkg-build') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/jar.py b/engine/SCons/Tool/jar.py new file mode 100644 index 0000000..6ebd762 --- /dev/null +++ b/engine/SCons/Tool/jar.py @@ -0,0 +1,116 @@ +"""SCons.Tool.jar + +Tool-specific initialization for jar. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/jar.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Subst +import SCons.Util + +def jarSources(target, source, env, for_signature): + """Only include sources that are not a manifest file.""" + try: + env['JARCHDIR'] + except KeyError: + jarchdir_set = False + else: + jarchdir_set = True + jarchdir = env.subst('$JARCHDIR', target=target, source=source) + if jarchdir: + jarchdir = env.fs.Dir(jarchdir) + result = [] + for src in source: + contents = src.get_text_contents() + if contents[:16] != "Manifest-Version": + if jarchdir_set: + _chdir = jarchdir + else: + try: + _chdir = src.attributes.java_classdir + except AttributeError: + _chdir = None + if _chdir: + # If we are changing the dir with -C, then sources should + # be relative to that directory. + src = SCons.Subst.Literal(src.get_path(_chdir)) + result.append('-C') + result.append(_chdir) + result.append(src) + return result + +def jarManifest(target, source, env, for_signature): + """Look in sources for a manifest file, if any.""" + for src in source: + contents = src.get_text_contents() + if contents[:16] == "Manifest-Version": + return src + return '' + +def jarFlags(target, source, env, for_signature): + """If we have a manifest, make sure that the 'm' + flag is specified.""" + jarflags = env.subst('$JARFLAGS', target=target, source=source) + for src in source: + contents = src.get_text_contents() + if contents[:16] == "Manifest-Version": + if not 'm' in jarflags: + return jarflags + 'm' + break + return jarflags + +def generate(env): + """Add Builders and construction variables for jar to an Environment.""" + SCons.Tool.CreateJarBuilder(env) + + env['JAR'] = 'jar' + env['JARFLAGS'] = SCons.Util.CLVar('cf') + env['_JARFLAGS'] = jarFlags + env['_JARMANIFEST'] = jarManifest + env['_JARSOURCES'] = jarSources + env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES' + env['JARCOM'] = "${TEMPFILE('$_JARCOM')}" + env['JARSUFFIX'] = '.jar' + +def exists(env): + # As reported by Jan Nijtmans in issue #2730, the simple + # return env.Detect('jar') + # doesn't always work during initialization. For now, we + # stop trying to detect an executable (analogous to the + # javac Builder). + # TODO: Come up with a proper detect() routine...and enable it. + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/javac.py b/engine/SCons/Tool/javac.py new file mode 100644 index 0000000..5a43b25 --- /dev/null +++ b/engine/SCons/Tool/javac.py @@ -0,0 +1,230 @@ +"""SCons.Tool.javac + +Tool-specific initialization for javac. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/javac.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path + +import SCons.Action +import SCons.Builder +from SCons.Node.FS import _my_normcase +from SCons.Tool.JavaCommon import parse_java_file +import SCons.Util + +def classname(path): + """Turn a string (path name) into a Java class name.""" + return os.path.normpath(path).replace(os.sep, '.') + +def emit_java_classes(target, source, env): + """Create and return lists of source java files + and their corresponding target class files. + """ + java_suffix = env.get('JAVASUFFIX', '.java') + class_suffix = env.get('JAVACLASSSUFFIX', '.class') + + target[0].must_be_same(SCons.Node.FS.Dir) + classdir = target[0] + + s = source[0].rentry().disambiguate() + if isinstance(s, SCons.Node.FS.File): + sourcedir = s.dir.rdir() + elif isinstance(s, SCons.Node.FS.Dir): + sourcedir = s.rdir() + else: + raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__) + + slist = [] + js = _my_normcase(java_suffix) + for entry in source: + entry = entry.rentry().disambiguate() + if isinstance(entry, SCons.Node.FS.File): + slist.append(entry) + elif isinstance(entry, SCons.Node.FS.Dir): + result = SCons.Util.OrderedDict() + dirnode = entry.rdir() + def find_java_files(arg, dirpath, filenames): + java_files = sorted([n for n in filenames + if _my_normcase(n).endswith(js)]) + mydir = dirnode.Dir(dirpath) + java_paths = [mydir.File(f) for f in java_files] + for jp in java_paths: + arg[jp] = True + for dirpath, dirnames, filenames in os.walk(dirnode.get_abspath()): + find_java_files(result, dirpath, filenames) + entry.walk(find_java_files, result) + + slist.extend(list(result.keys())) + else: + raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__) + + version = env.get('JAVAVERSION', '1.4') + full_tlist = [] + for f in slist: + tlist = [] + source_file_based = True + pkg_dir = None + if not f.is_derived(): + pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version) + if classes: + source_file_based = False + if pkg_dir: + d = target[0].Dir(pkg_dir) + p = pkg_dir + os.sep + else: + d = target[0] + p = '' + for c in classes: + t = d.File(c + class_suffix) + t.attributes.java_classdir = classdir + t.attributes.java_sourcedir = sourcedir + t.attributes.java_classname = classname(p + c) + tlist.append(t) + + if source_file_based: + base = f.name[:-len(java_suffix)] + if pkg_dir: + t = target[0].Dir(pkg_dir).File(base + class_suffix) + else: + t = target[0].File(base + class_suffix) + t.attributes.java_classdir = classdir + t.attributes.java_sourcedir = f.dir + t.attributes.java_classname = classname(base) + tlist.append(t) + + for t in tlist: + t.set_specific_source([f]) + + full_tlist.extend(tlist) + + return full_tlist, slist + +JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') + +JavaBuilder = SCons.Builder.Builder(action = JavaAction, + emitter = emit_java_classes, + target_factory = SCons.Node.FS.Entry, + source_factory = SCons.Node.FS.Entry) + +class pathopt(object): + """ + Callable object for generating javac-style path options from + a construction variable (e.g. -classpath, -sourcepath). + """ + def __init__(self, opt, var, default=None): + self.opt = opt + self.var = var + self.default = default + + def __call__(self, target, source, env, for_signature): + path = env[self.var] + if path and not SCons.Util.is_List(path): + path = [path] + if self.default: + path = path + [ env[self.default] ] + if path: + return [self.opt, os.pathsep.join(path)] + #return self.opt + " " + os.pathsep.join(path) + else: + return [] + #return "" + +def Java(env, target, source, *args, **kw): + """ + A pseudo-Builder wrapper around the separate JavaClass{File,Dir} + Builders. + """ + if not SCons.Util.is_List(target): + target = [target] + if not SCons.Util.is_List(source): + source = [source] + + # Pad the target list with repetitions of the last element in the + # list so we have a target for every source element. + target = target + ([target[-1]] * (len(source) - len(target))) + + java_suffix = env.subst('$JAVASUFFIX') + result = [] + + for t, s in zip(target, source): + if isinstance(s, SCons.Node.FS.Base): + if isinstance(s, SCons.Node.FS.File): + b = env.JavaClassFile + else: + b = env.JavaClassDir + else: + if os.path.isfile(s): + b = env.JavaClassFile + elif os.path.isdir(s): + b = env.JavaClassDir + elif s[-len(java_suffix):] == java_suffix: + b = env.JavaClassFile + else: + b = env.JavaClassDir + result.extend(b(t, s, *args, **kw)) + + return result + +def generate(env): + """Add Builders and construction variables for javac to an Environment.""" + java_file = SCons.Tool.CreateJavaFileBuilder(env) + java_class = SCons.Tool.CreateJavaClassFileBuilder(env) + java_class_dir = SCons.Tool.CreateJavaClassDirBuilder(env) + java_class.add_emitter(None, emit_java_classes) + java_class.add_emitter(env.subst('$JAVASUFFIX'), emit_java_classes) + java_class_dir.emitter = emit_java_classes + + env.AddMethod(Java) + + env['JAVAC'] = 'javac' + env['JAVACFLAGS'] = SCons.Util.CLVar('') + env['JAVABOOTCLASSPATH'] = [] + env['JAVACLASSPATH'] = [] + env['JAVASOURCEPATH'] = [] + env['_javapathopt'] = pathopt + env['_JAVABOOTCLASSPATH'] = '${_javapathopt("-bootclasspath", "JAVABOOTCLASSPATH")} ' + env['_JAVACLASSPATH'] = '${_javapathopt("-classpath", "JAVACLASSPATH")} ' + env['_JAVASOURCEPATH'] = '${_javapathopt("-sourcepath", "JAVASOURCEPATH", "_JAVASOURCEPATHDEFAULT")} ' + env['_JAVASOURCEPATHDEFAULT'] = '${TARGET.attributes.java_sourcedir}' + env['_JAVACCOM'] = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES' + env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM')}" + env['JAVACLASSSUFFIX'] = '.class' + env['JAVASUFFIX'] = '.java' + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/javah.py b/engine/SCons/Tool/javah.py new file mode 100644 index 0000000..ccdfb3c --- /dev/null +++ b/engine/SCons/Tool/javah.py @@ -0,0 +1,137 @@ +"""SCons.Tool.javah + +Tool-specific initialization for javah. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/javah.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Action +import SCons.Builder +import SCons.Node.FS +import SCons.Tool.javac +import SCons.Util + +def emit_java_headers(target, source, env): + """Create and return lists of Java stub header files that will + be created from a set of class files. + """ + class_suffix = env.get('JAVACLASSSUFFIX', '.class') + classdir = env.get('JAVACLASSDIR') + + if not classdir: + try: + s = source[0] + except IndexError: + classdir = '.' + else: + try: + classdir = s.attributes.java_classdir + except AttributeError: + classdir = '.' + classdir = env.Dir(classdir).rdir() + + if str(classdir) == '.': + c_ = None + else: + c_ = str(classdir) + os.sep + + slist = [] + for src in source: + try: + classname = src.attributes.java_classname + except AttributeError: + classname = str(src) + if c_ and classname[:len(c_)] == c_: + classname = classname[len(c_):] + if class_suffix and classname[-len(class_suffix):] == class_suffix: + classname = classname[:-len(class_suffix)] + classname = SCons.Tool.javac.classname(classname) + s = src.rfile() + s.attributes.java_classname = classname + slist.append(s) + + s = source[0].rfile() + if not hasattr(s.attributes, 'java_classdir'): + s.attributes.java_classdir = classdir + + if target[0].__class__ is SCons.Node.FS.File: + tlist = target + else: + if not isinstance(target[0], SCons.Node.FS.Dir): + target[0].__class__ = SCons.Node.FS.Dir + target[0]._morph() + tlist = [] + for s in source: + fname = s.attributes.java_classname.replace('.', '_') + '.h' + t = target[0].File(fname) + t.attributes.java_lookupdir = target[0] + tlist.append(t) + + return tlist, source + +def JavaHOutFlagGenerator(target, source, env, for_signature): + try: + t = target[0] + except (AttributeError, IndexError, TypeError): + t = target + try: + return '-d ' + str(t.attributes.java_lookupdir) + except AttributeError: + return '-o ' + str(t) + +def getJavaHClassPath(env,target, source, for_signature): + path = "${SOURCE.attributes.java_classdir}" + if 'JAVACLASSPATH' in env and env['JAVACLASSPATH']: + path = SCons.Util.AppendPath(path, env['JAVACLASSPATH']) + return "-classpath %s" % (path) + +def generate(env): + """Add Builders and construction variables for javah to an Environment.""" + java_javah = SCons.Tool.CreateJavaHBuilder(env) + java_javah.emitter = emit_java_headers + + env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator + env['JAVAH'] = 'javah' + env['JAVAHFLAGS'] = SCons.Util.CLVar('') + env['_JAVAHCLASSPATH'] = getJavaHClassPath + env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG $_JAVAHCLASSPATH ${SOURCES.attributes.java_classname}' + env['JAVACLASSSUFFIX'] = '.class' + +def exists(env): + return env.Detect('javah') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/latex.py b/engine/SCons/Tool/latex.py new file mode 100644 index 0000000..b746ad1 --- /dev/null +++ b/engine/SCons/Tool/latex.py @@ -0,0 +1,80 @@ +"""SCons.Tool.latex + +Tool-specific initialization for LaTeX. +Generates .dvi files from .latex or .ltx files + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/latex.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Defaults +import SCons.Scanner.LaTeX +import SCons.Util +import SCons.Tool +import SCons.Tool.tex + +def LaTeXAuxFunction(target = None, source= None, env=None): + result = SCons.Tool.tex.InternalLaTeXAuxAction( SCons.Tool.tex.LaTeXAction, target, source, env ) + if result != 0: + SCons.Tool.tex.check_file_error_message(env['LATEX']) + return result + +LaTeXAuxAction = SCons.Action.Action(LaTeXAuxFunction, + strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) + +def generate(env): + """Add Builders and construction variables for LaTeX to an Environment.""" + + env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) + + import dvi + dvi.generate(env) + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['DVI'] + bld.add_action('.ltx', LaTeXAuxAction) + bld.add_action('.latex', LaTeXAuxAction) + bld.add_emitter('.ltx', SCons.Tool.tex.tex_eps_emitter) + bld.add_emitter('.latex', SCons.Tool.tex.tex_eps_emitter) + + SCons.Tool.tex.generate_common(env) + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('latex') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/lex.py b/engine/SCons/Tool/lex.py new file mode 100644 index 0000000..25f7c3b --- /dev/null +++ b/engine/SCons/Tool/lex.py @@ -0,0 +1,97 @@ +"""SCons.Tool.lex + +Tool-specific initialization for lex. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/lex.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Action +import SCons.Tool +import SCons.Util + +LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") + +def lexEmitter(target, source, env): + sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) + + if sourceExt == ".lm": # If using Objective-C + target = [sourceBase + ".m"] # the extension is ".m". + + # This emitter essentially tries to add to the target all extra + # files generated by flex. + + # Different options that are used to trigger the creation of extra files. + fileGenOptions = ["--header-file=", "--tables-file="] + + lexflags = env.subst("$LEXFLAGS", target=target, source=source) + for option in SCons.Util.CLVar(lexflags): + for fileGenOption in fileGenOptions: + l = len(fileGenOption) + if option[:l] == fileGenOption: + # A file generating option is present, so add the + # file name to the target list. + fileName = option[l:].strip() + target.append(fileName) + return (target, source) + +def generate(env): + """Add Builders and construction variables for lex to an Environment.""" + c_file, cxx_file = SCons.Tool.createCFileBuilders(env) + + # C + c_file.add_action(".l", LexAction) + c_file.add_emitter(".l", lexEmitter) + + c_file.add_action(".lex", LexAction) + c_file.add_emitter(".lex", lexEmitter) + + # Objective-C + cxx_file.add_action(".lm", LexAction) + cxx_file.add_emitter(".lm", lexEmitter) + + # C++ + cxx_file.add_action(".ll", LexAction) + cxx_file.add_emitter(".ll", lexEmitter) + + env["LEX"] = env.Detect("flex") or "lex" + env["LEXFLAGS"] = SCons.Util.CLVar("") + env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET" + +def exists(env): + return env.Detect(["flex", "lex"]) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/link.py b/engine/SCons/Tool/link.py new file mode 100644 index 0000000..067099a --- /dev/null +++ b/engine/SCons/Tool/link.py @@ -0,0 +1,122 @@ +"""SCons.Tool.link + +Tool-specific initialization for the generic Posix linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/link.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util +import SCons.Warnings + +from SCons.Tool.FortranCommon import isfortran + +cplusplus = __import__('c++', globals(), locals(), []) + +issued_mixed_link_warning = False + +def smart_link(source, target, env, for_signature): + has_cplusplus = cplusplus.iscplusplus(source) + has_fortran = isfortran(env, source) + if has_cplusplus and has_fortran: + global issued_mixed_link_warning + if not issued_mixed_link_warning: + msg = "Using $CXX to link Fortran and C++ code together.\n\t" + \ + "This may generate a buggy executable if the '%s'\n\t" + \ + "compiler does not know how to deal with Fortran runtimes." + SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning, + msg % env.subst('$CXX')) + issued_mixed_link_warning = True + return '$CXX' + elif has_fortran: + return '$FORTRAN' + elif has_cplusplus: + return '$CXX' + return '$CC' + +def shlib_emitter(target, source, env): + for tgt in target: + tgt.attributes.shared = 1 + return (target, source) + +def generate(env): + """Add Builders and construction variables for gnulink to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + # don't set up the emitter, cause AppendUnique will generate a list + # starting with None :-( + env.Append(SHLIBEMITTER = [shlib_emitter]) + env['SMARTLINK'] = smart_link + env['LINK'] = "$SMARTLINK" + env['LINKFLAGS'] = SCons.Util.CLVar('') + # __RPATH is only set to something ($_RPATH typically) on platforms that support it. + env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBDIRPREFIX']='-L' + env['LIBDIRSUFFIX']='' + env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' + env['LIBLINKPREFIX']='-l' + env['LIBLINKSUFFIX']='' + + if env['PLATFORM'] == 'hpux': + env['SHLIBSUFFIX'] = '.sl' + elif env['PLATFORM'] == 'aix': + env['SHLIBSUFFIX'] = '.a' + + # For most platforms, a loadable module is the same as a shared + # library. Platforms which are different can override these, but + # setting them the same means that LoadableModule works everywhere. + SCons.Tool.createLoadableModuleBuilder(env) + env['LDMODULE'] = '$SHLINK' + # don't set up the emitter, cause AppendUnique will generate a list + # starting with None :-( + env.Append(LDMODULEEMITTER='$SHLIBEMITTER') + env['LDMODULEPREFIX'] = '$SHLIBPREFIX' + env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' + env['LDMODULEFLAGS'] = '$SHLINKFLAGS' + env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + + + +def exists(env): + # This module isn't really a Tool on its own, it's common logic for + # other linkers. + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/linkloc.py b/engine/SCons/Tool/linkloc.py new file mode 100644 index 0000000..d520100 --- /dev/null +++ b/engine/SCons/Tool/linkloc.py @@ -0,0 +1,112 @@ +"""SCons.Tool.linkloc + +Tool specification for the LinkLoc linker for the Phar Lap ETS embedded +operating system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/linkloc.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path +import re + +import SCons.Action +import SCons.Defaults +import SCons.Errors +import SCons.Tool +import SCons.Util + +from SCons.Tool.MSCommon import msvs_exists, merge_default_version +from SCons.Tool.PharLapCommon import addPharLapPaths + +_re_linker_command = re.compile(r'(\s)@\s*([^\s]+)') + +def repl_linker_command(m): + # Replaces any linker command file directives (e.g. "@foo.lnk") with + # the actual contents of the file. + try: + f=open(m.group(2), "r") + return m.group(1) + f.read() + except IOError: + # the linker should return an error if it can't + # find the linker command file so we will remain quiet. + # However, we will replace the @ with a # so we will not continue + # to find it with recursive substitution + return m.group(1) + '#' + m.group(2) + +class LinklocGenerator(object): + def __init__(self, cmdline): + self.cmdline = cmdline + + def __call__(self, env, target, source, for_signature): + if for_signature: + # Expand the contents of any linker command files recursively + subs = 1 + strsub = env.subst(self.cmdline, target=target, source=source) + while subs: + strsub, subs = _re_linker_command.subn(repl_linker_command, strsub) + return strsub + else: + return "${TEMPFILE('" + self.cmdline + "')}" + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SUBST_CMD_FILE'] = LinklocGenerator + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS') + env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -dll $TARGET $SOURCES")}' + env['SHLIBEMITTER']= None + env['LINK'] = "linkloc" + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -exe $TARGET $SOURCES")}' + env['LIBDIRPREFIX']='-libpath ' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='-lib ' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + + # Set-up ms tools paths for default version + merge_default_version(env) + + addPharLapPaths(env) + +def exists(env): + if msvs_exists(): + return env.Detect('linkloc') + else: + return 0 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/m4.py b/engine/SCons/Tool/m4.py new file mode 100644 index 0000000..8d997a4 --- /dev/null +++ b/engine/SCons/Tool/m4.py @@ -0,0 +1,63 @@ +"""SCons.Tool.m4 + +Tool-specific initialization for m4. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/m4.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add Builders and construction variables for m4 to an Environment.""" + M4Action = SCons.Action.Action('$M4COM', '$M4COMSTR') + bld = SCons.Builder.Builder(action = M4Action, src_suffix = '.m4') + + env['BUILDERS']['M4'] = bld + + # .m4 files might include other files, and it would be pretty hard + # to write a scanner for it, so let's just cd to the dir of the m4 + # file and run from there. + # The src_suffix setup is like so: file.c.m4 -> file.c, + # file.cpp.m4 -> file.cpp etc. + env['M4'] = 'm4' + env['M4FLAGS'] = SCons.Util.CLVar('-E') + env['M4COM'] = 'cd ${SOURCE.rsrcdir} && $M4 $M4FLAGS < ${SOURCE.file} > ${TARGET.abspath}' + +def exists(env): + return env.Detect('m4') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/masm.py b/engine/SCons/Tool/masm.py new file mode 100644 index 0000000..bbed9bb --- /dev/null +++ b/engine/SCons/Tool/masm.py @@ -0,0 +1,77 @@ +"""SCons.Tool.masm + +Tool-specific initialization for the Microsoft Assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/masm.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +ASSuffixes = ['.s', '.asm', '.ASM'] +ASPPSuffixes = ['.spp', '.SPP', '.sx'] +if SCons.Util.case_sensitive_suffixes('.s', '.S'): + ASPPSuffixes.extend(['.S']) +else: + ASSuffixes.extend(['.S']) + +def generate(env): + """Add Builders and construction variables for masm to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in ASSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASAction) + shared_obj.add_action(suffix, SCons.Defaults.ASAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + for suffix in ASPPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASPPAction) + shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + env['AS'] = 'ml' + env['ASFLAGS'] = SCons.Util.CLVar('/nologo') + env['ASPPFLAGS'] = '$ASFLAGS' + env['ASCOM'] = '$AS $ASFLAGS /c /Fo$TARGET $SOURCES' + env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c /Fo$TARGET $SOURCES' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + +def exists(env): + return env.Detect('ml') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/midl.py b/engine/SCons/Tool/midl.py new file mode 100644 index 0000000..5381df3 --- /dev/null +++ b/engine/SCons/Tool/midl.py @@ -0,0 +1,88 @@ +"""SCons.Tool.midl + +Tool-specific initialization for midl (Microsoft IDL compiler). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/midl.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Scanner.IDL +import SCons.Util + +from MSCommon import msvc_exists + +def midl_emitter(target, source, env): + """Produces a list of outputs from the MIDL compiler""" + base, ext = SCons.Util.splitext(str(target[0])) + tlb = target[0] + incl = base + '.h' + interface = base + '_i.c' + t = [tlb, incl, interface] + + midlcom = env['MIDLCOM'] + + if midlcom.find('/proxy') != -1: + proxy = base + '_p.c' + t.append(proxy) + if midlcom.find('/dlldata') != -1: + dlldata = base + '_data.c' + t.append(dlldata) + + return (t,source) + +idl_scanner = SCons.Scanner.IDL.IDLScan() + +midl_action = SCons.Action.Action('$MIDLCOM', '$MIDLCOMSTR') + +midl_builder = SCons.Builder.Builder(action = midl_action, + src_suffix = '.idl', + suffix='.tlb', + emitter = midl_emitter, + source_scanner = idl_scanner) + +def generate(env): + """Add Builders and construction variables for midl to an Environment.""" + + env['MIDL'] = 'MIDL.EXE' + env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo') + env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL' + env['BUILDERS']['TypeLibrary'] = midl_builder + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/mingw.py b/engine/SCons/Tool/mingw.py new file mode 100644 index 0000000..1df68bc --- /dev/null +++ b/engine/SCons/Tool/mingw.py @@ -0,0 +1,179 @@ +"""SCons.Tool.gcc + +Tool-specific initialization for MinGW (http://www.mingw.org/) + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/mingw.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Tool +import SCons.Util + +# This is what we search for to find mingw: +key_program = 'mingw32-gcc' + +def find(env): + # First search in the SCons path + path=env.WhereIs(key_program) + if (path): + return path + # then the OS path: + path=SCons.Util.WhereIs(key_program) + if (path): + return path + + # If that doesn't work try default location for mingw + save_path=env['ENV']['PATH'] + env.AppendENVPath('PATH',r'c:\MinGW\bin') + path =env.WhereIs(key_program) + if not path: + env['ENV']['PATH']=save_path + return path + +def shlib_generator(target, source, env, for_signature): + cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) + + def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') + insert_def = env.subst("$WINDOWS_INSERT_DEF") + if not insert_def in ['', '0', 0] and def_target: \ + cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) + + return [cmd] + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Create list of target libraries as strings + targetStrings=env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX') + + # Now add file nodes to target list + target.append(env.fs.File(targetStrings)) + + # Append a def file target if there isn't already a def file target + # or a def file source. There is no option to disable def file + # target emitting, because I can't figure out why someone would ever + # want to turn it off. + def_source = env.FindIxes(source, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') + def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') + if not def_source and not def_target: + # Create list of target libraries and def files as strings + targetStrings=env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') + + # Now add file nodes to target list + target.append(env.fs.File(targetStrings)) + + return (target, source) + + +shlib_action = SCons.Action.Action(shlib_generator, generator=1) + +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') + +res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', + source_scanner=SCons.Tool.SourceFileScanner) +SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) + +def generate(env): + mingw = find(env) + if mingw: + dir = os.path.dirname(mingw) + env.PrependENVPath('PATH', dir ) + + + # Most of mingw is the same as gcc and friends... + gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas', 'm4'] + for tool in gnu_tools: + SCons.Tool.Tool(tool)(env) + + #... but a few things differ: + env['CC'] = 'gcc' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['CXX'] = 'g++' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = shlib_action + env['LDMODULECOM'] = shlib_action + env.Append(SHLIBEMITTER = [shlib_emitter]) + env['AS'] = 'as' + + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' + env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' + + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = 'windres' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['RCINCPREFIX'] = '--include-dir ' + env['RCINCSUFFIX'] = '' + env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET' + env['BUILDERS']['RES'] = res_builder + + # Some setting from the platform also have to be overridden: + env['OBJSUFFIX'] = '.o' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + +def exists(env): + return find(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/mslib.py b/engine/SCons/Tool/mslib.py new file mode 100644 index 0000000..cbd1cd7 --- /dev/null +++ b/engine/SCons/Tool/mslib.py @@ -0,0 +1,64 @@ +"""SCons.Tool.mslib + +Tool-specific initialization for lib (MicroSoft library archiver). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/mslib.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Tool.msvs +import SCons.Tool.msvc +import SCons.Util + +from MSCommon import msvc_exists, msvc_setup_env_once + +def generate(env): + """Add Builders and construction variables for lib to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + # Set-up ms tools paths + msvc_setup_env_once(env) + + env['AR'] = 'lib' + env['ARFLAGS'] = SCons.Util.CLVar('/nologo') + env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}" + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/mslink.py b/engine/SCons/Tool/mslink.py new file mode 100644 index 0000000..ac533c8 --- /dev/null +++ b/engine/SCons/Tool/mslink.py @@ -0,0 +1,318 @@ +"""SCons.Tool.mslink + +Tool-specific initialization for the Microsoft linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/mslink.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Action +import SCons.Defaults +import SCons.Errors +import SCons.Platform.win32 +import SCons.Tool +import SCons.Tool.msvc +import SCons.Tool.msvs +import SCons.Util + +from MSCommon import msvc_setup_env_once, msvc_exists + +def pdbGenerator(env, target, source, for_signature): + try: + return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG'] + except (AttributeError, IndexError): + return None + +def _dllTargets(target, source, env, for_signature, paramtp): + listCmd = [] + dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp) + if dll: listCmd.append("/out:%s"%dll.get_string(for_signature)) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature)) + + return listCmd + +def _dllSources(target, source, env, for_signature, paramtp): + listCmd = [] + + deffile = env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX") + for src in source: + # Check explicitly for a non-None deffile so that the __cmp__ + # method of the base SCons.Util.Proxy class used for some Node + # proxies doesn't try to use a non-existent __dict__ attribute. + if deffile and src == deffile: + # Treat this source as a .def file. + listCmd.append("/def:%s" % src.get_string(for_signature)) + else: + # Just treat it as a generic source file. + listCmd.append(src) + return listCmd + +def windowsShlinkTargets(target, source, env, for_signature): + return _dllTargets(target, source, env, for_signature, 'SHLIB') + +def windowsShlinkSources(target, source, env, for_signature): + return _dllSources(target, source, env, for_signature, 'SHLIB') + +def _windowsLdmodTargets(target, source, env, for_signature): + """Get targets for loadable modules.""" + return _dllTargets(target, source, env, for_signature, 'LDMODULE') + +def _windowsLdmodSources(target, source, env, for_signature): + """Get sources for loadable modules.""" + return _dllSources(target, source, env, for_signature, 'LDMODULE') + +def _dllEmitter(target, source, env, paramtp): + """Common implementation of dll emitter.""" + SCons.Tool.msvc.validate_vars(env) + + extratargets = [] + extrasources = [] + + dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp) + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError('A shared library should have exactly one target with the suffix: %s' % env.subst('$%sSUFFIX' % paramtp)) + + insert_def = env.subst("$WINDOWS_INSERT_DEF") + if not insert_def in ['', '0', 0] and \ + not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"): + + # append a def file to the list of sources + extrasources.append( + env.ReplaceIxes(dll, + '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, + "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")) + + version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) + if version_num >= 8.0 and \ + (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): + # MSVC 8 and above automatically generate .manifest files that must be installed + extratargets.append( + env.ReplaceIxes(dll, + '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, + "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX")) + + if 'PDB' in env and env['PDB']: + pdb = env.arg2nodes('$PDB', target=target, source=source)[0] + extratargets.append(pdb) + target[0].attributes.pdb = pdb + + if not no_import_lib and \ + not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): + # Append an import library to the list of targets. + extratargets.append( + env.ReplaceIxes(dll, + '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, + "LIBPREFIX", "LIBSUFFIX")) + # and .exp file is created if there are exports from a DLL + extratargets.append( + env.ReplaceIxes(dll, + '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, + "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX")) + + return (target+extratargets, source+extrasources) + +def windowsLibEmitter(target, source, env): + return _dllEmitter(target, source, env, 'SHLIB') + +def ldmodEmitter(target, source, env): + """Emitter for loadable modules. + + Loadable modules are identical to shared libraries on Windows, but building + them is subject to different parameters (LDMODULE*). + """ + return _dllEmitter(target, source, env, 'LDMODULE') + +def prog_emitter(target, source, env): + SCons.Tool.msvc.validate_vars(env) + + extratargets = [] + + exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX") + if not exe: + raise SCons.Errors.UserError("An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX")) + + version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) + if version_num >= 8.0 and \ + (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): + # MSVC 8 and above automatically generate .manifest files that have to be installed + extratargets.append( + env.ReplaceIxes(exe, + "PROGPREFIX", "PROGSUFFIX", + "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX")) + + if 'PDB' in env and env['PDB']: + pdb = env.arg2nodes('$PDB', target=target, source=source)[0] + extratargets.append(pdb) + target[0].attributes.pdb = pdb + + return (target+extratargets,source) + +def RegServerFunc(target, source, env): + if 'register' in env and env['register']: + ret = regServerAction([target[0]], [source[0]], env) + if ret: + raise SCons.Errors.UserError("Unable to register %s" % target[0]) + else: + print "Registered %s sucessfully" % target[0] + return ret + return 0 + +# These are the actual actions run to embed the manifest. +# They are only called from the Check versions below. +embedManifestExeAction = SCons.Action.Action('$MTEXECOM') +embedManifestDllAction = SCons.Action.Action('$MTSHLIBCOM') + +def embedManifestDllCheck(target, source, env): + """Function run by embedManifestDllCheckAction to check for existence of manifest + and other conditions, and embed the manifest by calling embedManifestDllAction if so.""" + if env.get('WINDOWS_EMBED_MANIFEST', 0): + manifestSrc = target[0].abspath + '.manifest' + if os.path.exists(manifestSrc): + ret = (embedManifestDllAction) ([target[0]],None,env) + if ret: + raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) + return ret + else: + print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) + return 0 + +def embedManifestExeCheck(target, source, env): + """Function run by embedManifestExeCheckAction to check for existence of manifest + and other conditions, and embed the manifest by calling embedManifestExeAction if so.""" + if env.get('WINDOWS_EMBED_MANIFEST', 0): + manifestSrc = target[0].abspath + '.manifest' + if os.path.exists(manifestSrc): + ret = (embedManifestExeAction) ([target[0]],None,env) + if ret: + raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) + return ret + else: + print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) + return 0 + +embedManifestDllCheckAction = SCons.Action.Action(embedManifestDllCheck, None) +embedManifestExeCheckAction = SCons.Action.Action(embedManifestExeCheck, None) + +regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR") +regServerCheck = SCons.Action.Action(RegServerFunc, None) +shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}') +compositeShLinkAction = shlibLinkAction + regServerCheck + embedManifestDllCheckAction +ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES")}') +compositeLdmodAction = ldmodLinkAction + regServerCheck + embedManifestDllCheckAction +exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows")}') +compositeLinkAction = exeLinkAction + embedManifestExeCheckAction + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll') + env['_SHLINK_TARGETS'] = windowsShlinkTargets + env['_SHLINK_SOURCES'] = windowsShlinkSources + env['SHLINKCOM'] = compositeShLinkAction + env.Append(SHLIBEMITTER = [windowsLibEmitter]) + env['LINK'] = 'link' + env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') + env['_PDB'] = pdbGenerator + env['LINKCOM'] = compositeLinkAction + env.Append(PROGEMITTER = [prog_emitter]) + env['LIBDIRPREFIX']='/LIBPATH:' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['WIN32_INSERT_DEF'] = 0 + env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' + env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' + env['WINDOWS_INSERT_DEF'] = '${WIN32_INSERT_DEF}' + + env['WIN32EXPPREFIX'] = '' + env['WIN32EXPSUFFIX'] = '.exp' + env['WINDOWSEXPPREFIX'] = '${WIN32EXPPREFIX}' + env['WINDOWSEXPSUFFIX'] = '${WIN32EXPSUFFIX}' + + env['WINDOWSSHLIBMANIFESTPREFIX'] = '' + env['WINDOWSSHLIBMANIFESTSUFFIX'] = '${SHLIBSUFFIX}.manifest' + env['WINDOWSPROGMANIFESTPREFIX'] = '' + env['WINDOWSPROGMANIFESTSUFFIX'] = '${PROGSUFFIX}.manifest' + + env['REGSVRACTION'] = regServerCheck + env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32') + env['REGSVRFLAGS'] = '/s ' + env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}' + + env['WINDOWS_EMBED_MANIFEST'] = 0 + env['MT'] = 'mt' + #env['MTFLAGS'] = ['-hashupdate'] + env['MTFLAGS'] = SCons.Util.CLVar('/nologo') + # Note: use - here to prevent build failure if no manifest produced. + # This seems much simpler than a fancy system using a function action to see + # if the manifest actually exists before trying to run mt with it. + env['MTEXECOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;1' + env['MTSHLIBCOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;2' + # Future work garyo 27-Feb-11 + env['_MANIFEST_SOURCES'] = None # _windowsManifestSources + + # Set-up ms tools paths + msvc_setup_env_once(env) + + + # Loadable modules are on Windows the same as shared libraries, but they + # are subject to different build parameters (LDMODULE* variables). + # Therefore LDMODULE* variables correspond as much as possible to + # SHLINK*/SHLIB* ones. + SCons.Tool.createLoadableModuleBuilder(env) + env['LDMODULE'] = '$SHLINK' + env['LDMODULEPREFIX'] = '$SHLIBPREFIX' + env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' + env['LDMODULEFLAGS'] = '$SHLINKFLAGS' + env['_LDMODULE_TARGETS'] = _windowsLdmodTargets + env['_LDMODULE_SOURCES'] = _windowsLdmodSources + env['LDMODULEEMITTER'] = [ldmodEmitter] + env['LDMODULECOM'] = compositeLdmodAction + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/mssdk.py b/engine/SCons/Tool/mssdk.py new file mode 100644 index 0000000..500b792 --- /dev/null +++ b/engine/SCons/Tool/mssdk.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/mssdk.py 5357 2011/09/09 21:31:03 bdeegan" + +"""engine.SCons.Tool.mssdk + +Tool-specific initialization for Microsoft SDKs, both Platform +SDKs and Windows SDKs. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +from MSCommon import mssdk_exists, \ + mssdk_setup_env + +def generate(env): + """Add construction variables for an MS SDK to an Environment.""" + mssdk_setup_env(env) + +def exists(env): + return mssdk_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/msvc.py b/engine/SCons/Tool/msvc.py new file mode 100644 index 0000000..e8e20a6 --- /dev/null +++ b/engine/SCons/Tool/msvc.py @@ -0,0 +1,278 @@ +"""engine.SCons.Tool.msvc + +Tool-specific initialization for Microsoft Visual C/C++. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/msvc.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path +import re +import sys + +import SCons.Action +import SCons.Builder +import SCons.Errors +import SCons.Platform.win32 +import SCons.Tool +import SCons.Tool.msvs +import SCons.Util +import SCons.Warnings +import SCons.Scanner.RC + +from MSCommon import msvc_exists, msvc_setup_env_once + +CSuffixes = ['.c', '.C'] +CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] + +def validate_vars(env): + """Validate the PCH and PCHSTOP construction variables.""" + if 'PCH' in env and env['PCH']: + if 'PCHSTOP' not in env: + raise SCons.Errors.UserError("The PCHSTOP construction must be defined if PCH is defined.") + if not SCons.Util.is_String(env['PCHSTOP']): + raise SCons.Errors.UserError("The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']) + +def pch_emitter(target, source, env): + """Adds the object file target.""" + + validate_vars(env) + + pch = None + obj = None + + for t in target: + if SCons.Util.splitext(str(t))[1] == '.pch': + pch = t + if SCons.Util.splitext(str(t))[1] == '.obj': + obj = t + + if not obj: + obj = SCons.Util.splitext(str(pch))[0]+'.obj' + + target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work + + return (target, source) + +def object_emitter(target, source, env, parent_emitter): + """Sets up the PCH dependencies for an object file.""" + + validate_vars(env) + + parent_emitter(target, source, env) + + # Add a dependency, but only if the target (e.g. 'Source1.obj') + # doesn't correspond to the pre-compiled header ('Source1.pch'). + # If the basenames match, then this was most likely caused by + # someone adding the source file to both the env.PCH() and the + # env.Program() calls, and adding the explicit dependency would + # cause a cycle on the .pch file itself. + # + # See issue #2505 for a discussion of what to do if it turns + # out this assumption causes trouble in the wild: + # http://scons.tigris.org/issues/show_bug.cgi?id=2505 + if 'PCH' in env: + pch = env['PCH'] + if str(target[0]) != SCons.Util.splitext(str(pch))[0] + '.obj': + env.Depends(target, pch) + + return (target, source) + +def static_object_emitter(target, source, env): + return object_emitter(target, source, env, + SCons.Defaults.StaticObjectEmitter) + +def shared_object_emitter(target, source, env): + return object_emitter(target, source, env, + SCons.Defaults.SharedObjectEmitter) + +pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR') +pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch', + emitter=pch_emitter, + source_scanner=SCons.Tool.SourceFileScanner) + + +# Logic to build .rc files into .res files (resource files) +res_scanner = SCons.Scanner.RC.RCScan() +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') +res_builder = SCons.Builder.Builder(action=res_action, + src_suffix='.rc', + suffix='.res', + src_builder=[], + source_scanner=res_scanner) + +def msvc_batch_key(action, env, target, source): + """ + Returns a key to identify unique batches of sources for compilation. + + If batching is enabled (via the $MSVC_BATCH setting), then all + target+source pairs that use the same action, defined by the same + environment, and have the same target and source directories, will + be batched. + + Returning None specifies that the specified target+source should not + be batched with other compilations. + """ + + # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH + # was set to False. This new version should work better. + # Note we need to do the env.subst so $MSVC_BATCH can be a reference to + # another construction variable, which is why we test for False and 0 + # as strings. + if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): + # We're not using batching; return no key. + return None + t = target[0] + s = source[0] + if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]: + # The base names are different, so this *must* be compiled + # separately; return no key. + return None + return (id(action), id(env), t.dir, s.dir) + +def msvc_output_flag(target, source, env, for_signature): + """ + Returns the correct /Fo flag for batching. + + If batching is disabled or there's only one source file, then we + return an /Fo string that specifies the target explicitly. Otherwise, + we return an /Fo string that just specifies the first target's + directory (where the Visual C/C++ compiler will put the .obj files). + """ + + # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH + # was set to False. This new version should work better. Removed + # len(source)==1 as batch mode can compile only one file + # (and it also fixed problem with compiling only one changed file + # with batch mode enabled) + if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): + return '/Fo$TARGET' + else: + # The Visual C/C++ compiler requires a \ at the end of the /Fo + # option to indicate an output directory. We use os.sep here so + # that the test(s) for this can be run on non-Windows systems + # without having a hard-coded backslash mess up command-line + # argument parsing. + return '/Fo${TARGET.dir}' + os.sep + +CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') + +def generate(env): + """Add Builders and construction variables for MSVC++ to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + # TODO(batch): shouldn't reach in to cmdgen this way; necessary + # for now to bypass the checks in Builder.DictCmdGenerator.__call__() + # and allow .cc and .cpp to be compiled in the same command line. + static_obj.cmdgen.source_ext_match = False + shared_obj.cmdgen.source_ext_match = False + + for suffix in CSuffixes: + static_obj.add_action(suffix, CAction) + shared_obj.add_action(suffix, ShCAction) + static_obj.add_emitter(suffix, static_object_emitter) + shared_obj.add_emitter(suffix, shared_object_emitter) + + for suffix in CXXSuffixes: + static_obj.add_action(suffix, CXXAction) + shared_obj.add_action(suffix, ShCXXAction) + static_obj.add_emitter(suffix, static_object_emitter) + shared_obj.add_emitter(suffix, shared_object_emitter) + + env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}']) + env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s \\\"/Fp%s\\\""%(PCHSTOP or "",File(PCH))) or ""}']) + env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag + env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS' + env['CC'] = 'cl' + env['CCFLAGS'] = SCons.Util.CLVar('/nologo') + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '${TEMPFILE("$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM")}' + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '${TEMPFILE("$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM")}' + env['CXX'] = '$CC' + env['CXXFLAGS'] = SCons.Util.CLVar('$( /TP $)') + env['CXXCOM'] = '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM")}' + env['SHCXX'] = '$CXX' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHCXXCOM'] = '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM")}' + env['CPPDEFPREFIX'] = '/D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '/I' + env['INCSUFFIX'] = '' +# env.Append(OBJEMITTER = [static_object_emitter]) +# env.Append(SHOBJEMITTER = [shared_object_emitter]) + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = 'rc' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCSUFFIXES']=['.rc','.rc2'] + env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES' + env['BUILDERS']['RES'] = res_builder + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.obj' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + + # Set-up ms tools paths + msvc_setup_env_once(env) + + env['CFILESUFFIX'] = '.c' + env['CXXFILESUFFIX'] = '.cc' + + env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}']) + env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS' + env['BUILDERS']['PCH'] = pch_builder + + if 'ENV' not in env: + env['ENV'] = {} + if 'SystemRoot' not in env['ENV']: # required for dlls in the winsxs folders + env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root() + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/msvs.py b/engine/SCons/Tool/msvs.py new file mode 100644 index 0000000..c9b7970 --- /dev/null +++ b/engine/SCons/Tool/msvs.py @@ -0,0 +1,1806 @@ +"""SCons.Tool.msvs + +Tool-specific initialization for Microsoft Visual Studio project files. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/msvs.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.compat + +import base64 +import hashlib +import ntpath +import os +# compat layer imports "cPickle" for us if it's available. +import pickle +import re +import sys + +import SCons.Builder +import SCons.Node.FS +import SCons.Platform.win32 +import SCons.Script.SConscript +import SCons.PathList +import SCons.Util +import SCons.Warnings + +from MSCommon import msvc_exists, msvc_setup_env_once +from SCons.Defaults import processDefines + +############################################################################## +# Below here are the classes and functions for generation of +# DSP/DSW/SLN/VCPROJ files. +############################################################################## + +def xmlify(s): + s = s.replace("&", "&") # do this first + s = s.replace("'", "'") + s = s.replace('"', """) + return s + +# Process a CPPPATH list in includes, given the env, target and source. +# Returns a tuple of nodes. +def processIncludes(includes, env, target, source): + return SCons.PathList.PathList(includes).subst_path(env, target, source) + + +external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' + +def _generateGUID(slnfile, name): + """This generates a dummy GUID for the sln file to use. It is + based on the MD5 signatures of the sln filename plus the name of + the project. It basically just needs to be unique, and not + change with each invocation.""" + m = hashlib.md5() + # Normalize the slnfile path to a Windows path (\ separators) so + # the generated file has a consistent GUID even if we generate + # it on a non-Windows platform. + m.update(ntpath.normpath(str(slnfile)) + str(name)) + solution = m.hexdigest().upper() + # convert most of the signature to GUID form (discard the rest) + solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}" + return solution + +version_re = re.compile(r'(\d+\.\d+)(.*)') + +def msvs_parse_version(s): + """ + Split a Visual Studio version, which may in fact be something like + '7.0Exp', into is version number (returned as a float) and trailing + "suite" portion. + """ + num, suite = version_re.match(s).groups() + return float(num), suite + +# os.path.relpath has been introduced in Python 2.6 +# We define it locally for earlier versions of Python +def relpath(path, start=os.path.curdir): + """Return a relative version of a path""" + import sys + if not path: + raise ValueError("no path specified") + start_list = os.path.abspath(start).split(os.sep) + path_list = os.path.abspath(path).split(os.sep) + if 'posix' in sys.builtin_module_names: + # Work out how much of the filepath is shared by start and path. + i = len(os.path.commonprefix([start_list, path_list])) + else: + if start_list[0].lower() != path_list[0].lower(): + unc_path, rest = os.path.splitunc(path) + unc_start, rest = os.path.splitunc(start) + if bool(unc_path) ^ bool(unc_start): + raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" + % (path, start)) + else: + raise ValueError("path is on drive %s, start on drive %s" + % (path_list[0], start_list[0])) + # Work out how much of the filepath is shared by start and path. + for i in range(min(len(start_list), len(path_list))): + if start_list[i].lower() != path_list[i].lower(): + break + else: + i += 1 + rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return os.path.curdir + return os.path.join(*rel_list) + +if not "relpath" in os.path.__all__: + os.path.relpath = relpath + +# This is how we re-invoke SCons from inside MSVS Project files. +# The problem is that we might have been invoked as either scons.bat +# or scons.py. If we were invoked directly as scons.py, then we could +# use sys.argv[0] to find the SCons "executable," but that doesn't work +# if we were invoked as scons.bat, which uses "python -c" to execute +# things and ends up with "-c" as sys.argv[0]. Consequently, we have +# the MSVS Project file invoke SCons the same way that scons.bat does, +# which works regardless of how we were invoked. +def getExecScriptMain(env, xml=None): + scons_home = env.get('SCONS_HOME') + if not scons_home and 'SCONS_LIB_DIR' in os.environ: + scons_home = os.environ['SCONS_LIB_DIR'] + if scons_home: + exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home + else: + version = SCons.__version__ + exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals() + if xml: + exec_script_main = xmlify(exec_script_main) + return exec_script_main + +# The string for the Python executable we tell the Project file to use +# is either sys.executable or, if an external PYTHON_ROOT environment +# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to +# pluck the actual executable name from sys.executable). +try: + python_root = os.environ['PYTHON_ROOT'] +except KeyError: + python_executable = sys.executable +else: + python_executable = os.path.join('$$(PYTHON_ROOT)', + os.path.split(sys.executable)[1]) + +class Config(object): + pass + +def splitFully(path): + dir, base = os.path.split(path) + if dir and dir != '' and dir != path: + return splitFully(dir)+[base] + if base == '': + return [] + return [base] + +def makeHierarchy(sources): + '''Break a list of files into a hierarchy; for each value, if it is a string, + then it is a file. If it is a dictionary, it is a folder. The string is + the original path of the file.''' + + hierarchy = {} + for file in sources: + path = splitFully(file) + if len(path): + dict = hierarchy + for part in path[:-1]: + if part not in dict: + dict[part] = {} + dict = dict[part] + dict[path[-1]] = file + #else: + # print 'Warning: failed to decompose path for '+str(file) + return hierarchy + +class _DSPGenerator(object): + """ Base class for DSP generators """ + + srcargs = [ + 'srcs', + 'incs', + 'localincs', + 'resources', + 'misc'] + + def __init__(self, dspfile, source, env): + self.dspfile = str(dspfile) + try: + get_abspath = dspfile.get_abspath + except AttributeError: + self.dspabs = os.path.abspath(dspfile) + else: + self.dspabs = get_abspath() + + if 'variant' not in env: + raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ + "'Release') to create an MSVSProject.") + elif SCons.Util.is_String(env['variant']): + variants = [env['variant']] + elif SCons.Util.is_List(env['variant']): + variants = env['variant'] + + if 'buildtarget' not in env or env['buildtarget'] == None: + buildtarget = [''] + elif SCons.Util.is_String(env['buildtarget']): + buildtarget = [env['buildtarget']] + elif SCons.Util.is_List(env['buildtarget']): + if len(env['buildtarget']) != len(variants): + raise SCons.Errors.InternalError("Sizes of 'buildtarget' and 'variant' lists must be the same.") + buildtarget = [] + for bt in env['buildtarget']: + if SCons.Util.is_String(bt): + buildtarget.append(bt) + else: + buildtarget.append(bt.get_abspath()) + else: + buildtarget = [env['buildtarget'].get_abspath()] + if len(buildtarget) == 1: + bt = buildtarget[0] + buildtarget = [] + for _ in variants: + buildtarget.append(bt) + + if 'outdir' not in env or env['outdir'] == None: + outdir = [''] + elif SCons.Util.is_String(env['outdir']): + outdir = [env['outdir']] + elif SCons.Util.is_List(env['outdir']): + if len(env['outdir']) != len(variants): + raise SCons.Errors.InternalError("Sizes of 'outdir' and 'variant' lists must be the same.") + outdir = [] + for s in env['outdir']: + if SCons.Util.is_String(s): + outdir.append(s) + else: + outdir.append(s.get_abspath()) + else: + outdir = [env['outdir'].get_abspath()] + if len(outdir) == 1: + s = outdir[0] + outdir = [] + for v in variants: + outdir.append(s) + + if 'runfile' not in env or env['runfile'] == None: + runfile = buildtarget[-1:] + elif SCons.Util.is_String(env['runfile']): + runfile = [env['runfile']] + elif SCons.Util.is_List(env['runfile']): + if len(env['runfile']) != len(variants): + raise SCons.Errors.InternalError("Sizes of 'runfile' and 'variant' lists must be the same.") + runfile = [] + for s in env['runfile']: + if SCons.Util.is_String(s): + runfile.append(s) + else: + runfile.append(s.get_abspath()) + else: + runfile = [env['runfile'].get_abspath()] + if len(runfile) == 1: + s = runfile[0] + runfile = [] + for v in variants: + runfile.append(s) + + self.sconscript = env['MSVSSCONSCRIPT'] + + cmdargs = env.get('cmdargs', '') + + self.env = env + + if 'name' in self.env: + self.name = self.env['name'] + else: + self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0]) + self.name = self.env.subst(self.name) + + sourcenames = [ + 'Source Files', + 'Header Files', + 'Local Headers', + 'Resource Files', + 'Other Files'] + + self.sources = {} + for n in sourcenames: + self.sources[n] = [] + + self.configs = {} + + self.nokeep = 0 + if 'nokeep' in env and env['variant'] != 0: + self.nokeep = 1 + + if self.nokeep == 0 and os.path.exists(self.dspabs): + self.Parse() + + for t in zip(sourcenames,self.srcargs): + if t[1] in self.env: + if SCons.Util.is_List(self.env[t[1]]): + for i in self.env[t[1]]: + if not i in self.sources[t[0]]: + self.sources[t[0]].append(i) + else: + if not self.env[t[1]] in self.sources[t[0]]: + self.sources[t[0]].append(self.env[t[1]]) + + for n in sourcenames: + #TODO 2.4: compat layer supports sorted(key=) but not sort(key=) + #TODO 2.4: self.sources[n].sort(key=lambda a: a.lower()) + self.sources[n] = sorted(self.sources[n], key=lambda a: a.lower()) + + def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile): + config = Config() + config.buildtarget = buildtarget + config.outdir = outdir + config.cmdargs = cmdargs + config.runfile = runfile + + match = re.match('(.*)\|(.*)', variant) + if match: + config.variant = match.group(1) + config.platform = match.group(2) + else: + config.variant = variant + config.platform = 'Win32' + + self.configs[variant] = config + print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'" + + for i in range(len(variants)): + AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs) + + self.platforms = [] + for key in self.configs.keys(): + platform = self.configs[key].platform + if not platform in self.platforms: + self.platforms.append(platform) + + def Build(self): + pass + +V6DSPHeader = """\ +# Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=%(name)s - Win32 %(confkey)s +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "%(name)s.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +""" + +class _GenerateV6DSP(_DSPGenerator): + """Generates a Project file for MSVS 6.0""" + + def PrintHeader(self): + # pick a default config + confkeys = sorted(self.configs.keys()) + + name = self.name + confkey = confkeys[0] + + self.file.write(V6DSPHeader % locals()) + + for kind in confkeys: + self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) + + self.file.write('!MESSAGE \n\n') + + def PrintProject(self): + name = self.name + self.file.write('# Begin Project\n' + '# PROP AllowPerConfigDependencies 0\n' + '# PROP Scc_ProjName ""\n' + '# PROP Scc_LocalPath ""\n\n') + + first = 1 + confkeys = sorted(self.configs.keys()) + for kind in confkeys: + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + if first == 1: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + first = 0 + else: + self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + + env_has_buildtarget = 'MSVSBUILDTARGET' in self.env + if not env_has_buildtarget: + self.env['MSVSBUILDTARGET'] = buildtarget + + # have to write this twice, once with the BASE settings, and once without + for base in ("BASE ",""): + self.file.write('# PROP %sUse_MFC 0\n' + '# PROP %sUse_Debug_Libraries ' % (base, base)) + if kind.lower().find('debug') < 0: + self.file.write('0\n') + else: + self.file.write('1\n') + self.file.write('# PROP %sOutput_Dir "%s"\n' + '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir)) + cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1) + self.file.write('# PROP %sCmd_Line "%s"\n' + '# PROP %sRebuild_Opt "-c && %s"\n' + '# PROP %sTarget_File "%s"\n' + '# PROP %sBsc_Name ""\n' + '# PROP %sTarget_Dir ""\n'\ + %(base,cmd,base,cmd,base,buildtarget,base,base)) + + if not env_has_buildtarget: + del self.env['MSVSBUILDTARGET'] + + self.file.write('\n!ENDIF\n\n' + '# Begin Target\n\n') + for kind in confkeys: + self.file.write('# Name "%s - Win32 %s"\n' % (name,kind)) + self.file.write('\n') + first = 0 + for kind in confkeys: + if first == 0: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + first = 1 + else: + self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + self.file.write('!ENDIF \n\n') + self.PrintSourceFiles() + self.file.write('# End Target\n' + '# End Project\n') + + if self.nokeep == 0: + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + pdata = pickle.dumps(self.sources,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + + def PrintSourceFiles(self): + categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat', + 'Header Files': 'h|hpp|hxx|hm|inl', + 'Local Headers': 'h|hpp|hxx|hm|inl', + 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', + 'Other Files': ''} + + for kind in sorted(categories.keys(), key=lambda a: a.lower()): + if not self.sources[kind]: + continue # skip empty groups + + self.file.write('# Begin Group "' + kind + '"\n\n') + typelist = categories[kind].replace('|', ';') + self.file.write('# PROP Default_Filter "' + typelist + '"\n') + + for file in self.sources[kind]: + file = os.path.normpath(file) + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + file + '"\n' + '# End Source File\n') + self.file.write('# End Group\n') + + # add the SConscript file outside of the groups + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + str(self.sconscript) + '"\n' + '# End Source File\n') + + def Parse(self): + try: + dspfile = open(self.dspabs,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + if line.find("# End Project") > -1: + break + line = dspfile.readline() + + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.configs.update(data) + + data = None + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + # it has a "# " in front of it, so we strip that. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.sources.update(data) + + def Build(self): + try: + self.file = open(self.dspabs,'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail)) + else: + self.PrintHeader() + self.PrintProject() + self.file.close() + +V7DSPHeader = """\ + + +""" + +V7DSPConfiguration = """\ +\t\t +\t\t\t +\t\t +""" + +V8DSPHeader = """\ + + +""" + +V8DSPConfiguration = """\ +\t\t +\t\t\t +\t\t +""" +class _GenerateV7DSP(_DSPGenerator): + """Generates a Project file for MSVS .NET""" + + def __init__(self, dspfile, source, env): + _DSPGenerator.__init__(self, dspfile, source, env) + self.version = env['MSVS_VERSION'] + self.version_num, self.suite = msvs_parse_version(self.version) + if self.version_num >= 9.0: + self.versionstr = '9.00' + self.dspheader = V8DSPHeader + self.dspconfiguration = V8DSPConfiguration + elif self.version_num >= 8.0: + self.versionstr = '8.00' + self.dspheader = V8DSPHeader + self.dspconfiguration = V8DSPConfiguration + else: + if self.version_num >= 7.1: + self.versionstr = '7.10' + else: + self.versionstr = '7.00' + self.dspheader = V7DSPHeader + self.dspconfiguration = V7DSPConfiguration + self.file = None + + def PrintHeader(self): + env = self.env + versionstr = self.versionstr + name = self.name + encoding = self.env.subst('$MSVSENCODING') + scc_provider = env.get('MSVS_SCC_PROVIDER', '') + scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') + scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '') + # MSVS_SCC_LOCAL_PATH is kept for backwards compatibility purpose and should + # be deprecated as soon as possible. + scc_local_path_legacy = env.get('MSVS_SCC_LOCAL_PATH', '') + scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) + scc_local_path = os.path.relpath(scc_connection_root, os.path.dirname(self.dspabs)) + project_guid = env.get('MSVS_PROJECT_GUID', '') + if not project_guid: + project_guid = _generateGUID(self.dspfile, '') + if scc_provider != '': + scc_attrs = '\tSccProjectName="%s"\n' % scc_project_name + if scc_aux_path != '': + scc_attrs += '\tSccAuxPath="%s"\n' % scc_aux_path + scc_attrs += ('\tSccLocalPath="%s"\n' + '\tSccProvider="%s"' % (scc_local_path, scc_provider)) + elif scc_local_path_legacy != '': + # This case is kept for backwards compatibility purpose and should + # be deprecated as soon as possible. + scc_attrs = ('\tSccProjectName="%s"\n' + '\tSccLocalPath="%s"' % (scc_project_name, scc_local_path_legacy)) + else: + self.dspheader = self.dspheader.replace('%(scc_attrs)s\n', '') + + self.file.write(self.dspheader % locals()) + + self.file.write('\t\n') + for platform in self.platforms: + self.file.write( + '\t\t\n' % platform) + self.file.write('\t\n') + + if self.version_num >= 8.0: + self.file.write('\t\n' + '\t\n') + + def PrintProject(self): + self.file.write('\t\n') + + confkeys = sorted(self.configs.keys()) + for kind in confkeys: + variant = self.configs[kind].variant + platform = self.configs[kind].platform + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + runfile = self.configs[kind].runfile + cmdargs = self.configs[kind].cmdargs + + env_has_buildtarget = 'MSVSBUILDTARGET' in self.env + if not env_has_buildtarget: + self.env['MSVSBUILDTARGET'] = buildtarget + + starting = 'echo Starting SCons && ' + if cmdargs: + cmdargs = ' ' + cmdargs + else: + cmdargs = '' + buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs) + rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs) + cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs) + + # This isn't perfect; CPPDEFINES and CPPPATH can contain $TARGET and $SOURCE, + # so they could vary depending on the command being generated. This code + # assumes they don't. + preprocdefs = xmlify(';'.join(processDefines(self.env.get('CPPDEFINES', [])))) + includepath_Dirs = processIncludes(self.env.get('CPPPATH', []), self.env, None, None) + includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) + + if not env_has_buildtarget: + del self.env['MSVSBUILDTARGET'] + + self.file.write(self.dspconfiguration % locals()) + + self.file.write('\t\n') + + if self.version_num >= 7.1: + self.file.write('\t\n' + '\t\n') + + self.PrintSourceFiles() + + self.file.write('\n') + + if self.nokeep == 0: + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write('\n') + + def printSources(self, hierarchy, commonprefix): + sorteditems = sorted(hierarchy.items(), key=lambda a: a[0].lower()) + + # First folders, then files + for key, value in sorteditems: + if SCons.Util.is_Dict(value): + self.file.write('\t\t\t\n' % (key)) + self.printSources(value, commonprefix) + self.file.write('\t\t\t\n') + + for key, value in sorteditems: + if SCons.Util.is_String(value): + file = value + if commonprefix: + file = os.path.join(commonprefix, value) + file = os.path.normpath(file) + self.file.write('\t\t\t\n' + '\t\t\t\n' % (file)) + + def PrintSourceFiles(self): + categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', + 'Header Files': 'h;hpp;hxx;hm;inl', + 'Local Headers': 'h;hpp;hxx;hm;inl', + 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', + 'Other Files': ''} + + self.file.write('\t\n') + + cats = sorted([k for k in categories.keys() if self.sources[k]], + key=lambda a: a.lower()) + for kind in cats: + if len(cats) > 1: + self.file.write('\t\t\n' % (kind, categories[kind])) + + sources = self.sources[kind] + + # First remove any common prefix + commonprefix = None + if len(sources) > 1: + s = list(map(os.path.normpath, sources)) + # take the dirname because the prefix may include parts + # of the filenames (e.g. if you have 'dir\abcd' and + # 'dir\acde' then the cp will be 'dir\a' ) + cp = os.path.dirname( os.path.commonprefix(s) ) + if cp and s[0][len(cp)] == os.sep: + # +1 because the filename starts after the separator + sources = [s[len(cp)+1:] for s in sources] + commonprefix = cp + elif len(sources) == 1: + commonprefix = os.path.dirname( sources[0] ) + sources[0] = os.path.basename( sources[0] ) + + hierarchy = makeHierarchy(sources) + self.printSources(hierarchy, commonprefix=commonprefix) + + if len(cats)>1: + self.file.write('\t\t\n') + + # add the SConscript file outside of the groups + self.file.write('\t\t\n' + '\t\t\n' % str(self.sconscript)) + + self.file.write('\t\n' + '\t\n' + '\t\n') + + def Parse(self): + try: + dspfile = open(self.dspabs,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + if line.find('\n') + + def printFilters(self, hierarchy, name): + sorteditems = sorted(hierarchy.items(), key = lambda a: a[0].lower()) + + for key, value in sorteditems: + if SCons.Util.is_Dict(value): + filter_name = name + '\\' + key + self.filters_file.write('\t\t\n' + '\t\t\t%s\n' + '\t\t\n' % (filter_name, _generateGUID(self.dspabs, filter_name))) + self.printFilters(value, filter_name) + + def printSources(self, hierarchy, kind, commonprefix, filter_name): + keywords = {'Source Files': 'ClCompile', + 'Header Files': 'ClInclude', + 'Local Headers': 'ClInclude', + 'Resource Files': 'None', + 'Other Files': 'None'} + + sorteditems = sorted(hierarchy.items(), key = lambda a: a[0].lower()) + + # First folders, then files + for key, value in sorteditems: + if SCons.Util.is_Dict(value): + self.printSources(value, kind, commonprefix, filter_name + '\\' + key) + + for key, value in sorteditems: + if SCons.Util.is_String(value): + file = value + if commonprefix: + file = os.path.join(commonprefix, value) + file = os.path.normpath(file) + + self.file.write('\t\t<%s Include="%s" />\n' % (keywords[kind], file)) + self.filters_file.write('\t\t<%s Include="%s">\n' + '\t\t\t%s\n' + '\t\t\n' % (keywords[kind], file, filter_name, keywords[kind])) + + def PrintSourceFiles(self): + categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', + 'Header Files': 'h;hpp;hxx;hm;inl', + 'Local Headers': 'h;hpp;hxx;hm;inl', + 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', + 'Other Files': ''} + + cats = sorted([k for k in categories.keys() if self.sources[k]], + key = lambda a: a.lower()) + + # print vcxproj.filters file first + self.filters_file.write('\t\n') + for kind in cats: + self.filters_file.write('\t\t\n' + '\t\t\t{7b42d31d-d53c-4868-8b92-ca2bc9fc052f}\n' + '\t\t\t%s\n' + '\t\t\n' % (kind, categories[kind])) + + # First remove any common prefix + sources = self.sources[kind] + commonprefix = None + if len(sources) > 1: + s = list(map(os.path.normpath, sources)) + # take the dirname because the prefix may include parts + # of the filenames (e.g. if you have 'dir\abcd' and + # 'dir\acde' then the cp will be 'dir\a' ) + cp = os.path.dirname( os.path.commonprefix(s) ) + if cp and s[0][len(cp)] == os.sep: + # +1 because the filename starts after the separator + sources = [s[len(cp)+1:] for s in sources] + commonprefix = cp + elif len(sources) == 1: + commonprefix = os.path.dirname( sources[0] ) + sources[0] = os.path.basename( sources[0] ) + + hierarchy = makeHierarchy(sources) + self.printFilters(hierarchy, kind) + + self.filters_file.write('\t\n') + + # then print files and filters + for kind in cats: + self.file.write('\t\n') + self.filters_file.write('\t\n') + + # First remove any common prefix + sources = self.sources[kind] + commonprefix = None + if len(sources) > 1: + s = list(map(os.path.normpath, sources)) + # take the dirname because the prefix may include parts + # of the filenames (e.g. if you have 'dir\abcd' and + # 'dir\acde' then the cp will be 'dir\a' ) + cp = os.path.dirname( os.path.commonprefix(s) ) + if cp and s[0][len(cp)] == os.sep: + # +1 because the filename starts after the separator + sources = [s[len(cp)+1:] for s in sources] + commonprefix = cp + elif len(sources) == 1: + commonprefix = os.path.dirname( sources[0] ) + sources[0] = os.path.basename( sources[0] ) + + hierarchy = makeHierarchy(sources) + self.printSources(hierarchy, kind, commonprefix, kind) + + self.file.write('\t\n') + self.filters_file.write('\t\n') + + # add the SConscript file outside of the groups + self.file.write('\t\n' + '\t\t\n' + #'\t\t\n' + '\t\n' % str(self.sconscript)) + + def Parse(self): + print "_GenerateV10DSP.Parse()" + + def Build(self): + try: + self.file = open(self.dspabs, 'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail)) + else: + self.PrintHeader() + self.PrintProject() + self.file.close() + +class _DSWGenerator(object): + """ Base class for DSW generators """ + def __init__(self, dswfile, source, env): + self.dswfile = os.path.normpath(str(dswfile)) + self.dsw_folder_path = os.path.dirname(os.path.abspath(self.dswfile)) + self.env = env + + if 'projects' not in env: + raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.") + projects = env['projects'] + if not SCons.Util.is_List(projects): + raise SCons.Errors.InternalError("The 'projects' argument must be a list of nodes.") + projects = SCons.Util.flatten(projects) + if len(projects) < 1: + raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.") + self.dspfiles = list(map(str, projects)) + + if 'name' in self.env: + self.name = self.env['name'] + else: + self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0]) + self.name = self.env.subst(self.name) + + def Build(self): + pass + +class _GenerateV7DSW(_DSWGenerator): + """Generates a Solution file for MSVS .NET""" + def __init__(self, dswfile, source, env): + _DSWGenerator.__init__(self, dswfile, source, env) + + self.file = None + self.version = self.env['MSVS_VERSION'] + self.version_num, self.suite = msvs_parse_version(self.version) + self.versionstr = '7.00' + if self.version_num >= 10.0: + self.versionstr = '11.00' + elif self.version_num >= 9.0: + self.versionstr = '10.00' + elif self.version_num >= 8.0: + self.versionstr = '9.00' + elif self.version_num >= 7.1: + self.versionstr = '8.00' + + if 'slnguid' in env and env['slnguid']: + self.slnguid = env['slnguid'] + else: + self.slnguid = _generateGUID(dswfile, self.name) + + self.configs = {} + + self.nokeep = 0 + if 'nokeep' in env and env['variant'] != 0: + self.nokeep = 1 + + if self.nokeep == 0 and os.path.exists(self.dswfile): + self.Parse() + + def AddConfig(self, variant, dswfile=dswfile): + config = Config() + + match = re.match('(.*)\|(.*)', variant) + if match: + config.variant = match.group(1) + config.platform = match.group(2) + else: + config.variant = variant + config.platform = 'Win32' + + self.configs[variant] = config + print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'" + + if 'variant' not in env: + raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ + "'Release') to create an MSVS Solution File.") + elif SCons.Util.is_String(env['variant']): + AddConfig(self, env['variant']) + elif SCons.Util.is_List(env['variant']): + for variant in env['variant']: + AddConfig(self, variant) + + self.platforms = [] + for key in self.configs.keys(): + platform = self.configs[key].platform + if not platform in self.platforms: + self.platforms.append(platform) + + def GenerateProjectFilesInfo(self): + for dspfile in self.dspfiles: + dsp_folder_path, name = os.path.split(dspfile) + dsp_folder_path = os.path.abspath(dsp_folder_path) + dsp_relative_folder_path = os.path.relpath(dsp_folder_path, self.dsw_folder_path) + if dsp_relative_folder_path == os.curdir: + dsp_relative_file_path = name + else: + dsp_relative_file_path = os.path.join(dsp_relative_folder_path, name) + dspfile_info = {'NAME': name, + 'GUID': _generateGUID(dspfile, ''), + 'FOLDER_PATH': dsp_folder_path, + 'FILE_PATH': dspfile, + 'SLN_RELATIVE_FOLDER_PATH': dsp_relative_folder_path, + 'SLN_RELATIVE_FILE_PATH': dsp_relative_file_path} + self.dspfiles_info.append(dspfile_info) + + self.dspfiles_info = [] + GenerateProjectFilesInfo(self) + + def Parse(self): + try: + dswfile = open(self.dswfile,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dswfile.readline() + while line: + if line[:9] == "EndGlobal": + break + line = dswfile.readline() + + line = dswfile.readline() + datas = line + while line: + line = dswfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.configs.update(data) + + def PrintSolution(self): + """Writes a solution file""" + self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr ) + if self.version_num >= 10.0: + self.file.write('# Visual Studio 2010\n') + elif self.version_num >= 9.0: + self.file.write('# Visual Studio 2008\n') + elif self.version_num >= 8.0: + self.file.write('# Visual Studio 2005\n') + for dspinfo in self.dspfiles_info: + name = dspinfo['NAME'] + base, suffix = SCons.Util.splitext(name) + if suffix == '.vcproj': + name = base + self.file.write('Project("%s") = "%s", "%s", "%s"\n' + % (external_makefile_guid, name, dspinfo['SLN_RELATIVE_FILE_PATH'], dspinfo['GUID'])) + if self.version_num >= 7.1 and self.version_num < 8.0: + self.file.write('\tProjectSection(ProjectDependencies) = postProject\n' + '\tEndProjectSection\n') + self.file.write('EndProject\n') + + self.file.write('Global\n') + + env = self.env + if 'MSVS_SCC_PROVIDER' in env: + scc_number_of_projects = len(self.dspfiles) + 1 + slnguid = self.slnguid + scc_provider = env.get('MSVS_SCC_PROVIDER', '').replace(' ', r'\u0020') + scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '').replace(' ', r'\u0020') + scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) + scc_local_path = os.path.relpath(scc_connection_root, self.dsw_folder_path).replace('\\', '\\\\') + self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n' + '\t\tSccNumberOfProjects = %(scc_number_of_projects)d\n' + '\t\tSccProjectName0 = %(scc_project_name)s\n' + '\t\tSccLocalPath0 = %(scc_local_path)s\n' + '\t\tSccProvider0 = %(scc_provider)s\n' + '\t\tCanCheckoutShared = true\n' % locals()) + sln_relative_path_from_scc = os.path.relpath(self.dsw_folder_path, scc_connection_root) + if sln_relative_path_from_scc != os.curdir: + self.file.write('\t\tSccProjectFilePathRelativizedFromConnection0 = %s\\\\\n' + % sln_relative_path_from_scc.replace('\\', '\\\\')) + if self.version_num < 8.0: + # When present, SolutionUniqueID is automatically removed by VS 2005 + # TODO: check for Visual Studio versions newer than 2005 + self.file.write('\t\tSolutionUniqueID = %s\n' % slnguid) + for dspinfo in self.dspfiles_info: + i = self.dspfiles_info.index(dspinfo) + 1 + dsp_relative_file_path = dspinfo['SLN_RELATIVE_FILE_PATH'].replace('\\', '\\\\') + dsp_scc_relative_folder_path = os.path.relpath(dspinfo['FOLDER_PATH'], scc_connection_root).replace('\\', '\\\\') + self.file.write('\t\tSccProjectUniqueName%(i)s = %(dsp_relative_file_path)s\n' + '\t\tSccLocalPath%(i)d = %(scc_local_path)s\n' + '\t\tCanCheckoutShared = true\n' + '\t\tSccProjectFilePathRelativizedFromConnection%(i)s = %(dsp_scc_relative_folder_path)s\\\\\n' + % locals()) + self.file.write('\tEndGlobalSection\n') + if self.version_num >= 8.0: + self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') + else: + self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n') + + confkeys = sorted(self.configs.keys()) + cnt = 0 + for name in confkeys: + variant = self.configs[name].variant + platform = self.configs[name].platform + if self.version_num >= 8.0: + self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform)) + else: + self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant)) + cnt = cnt + 1 + self.file.write('\tEndGlobalSection\n') + if self.version_num <= 7.1: + self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n' + '\tEndGlobalSection\n') + if self.version_num >= 8.0: + self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') + else: + self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n') + + for name in confkeys: + variant = self.configs[name].variant + platform = self.configs[name].platform + if self.version_num >= 8.0: + for dspinfo in self.dspfiles_info: + guid = dspinfo['GUID'] + self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n' + '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform)) + else: + for dspinfo in self.dspfiles_info: + guid = dspinfo['GUID'] + self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n' + '\t\t%s.%s.Build.0 = %s|%s\n' %(guid,variant,variant,platform,guid,variant,variant,platform)) + + self.file.write('\tEndGlobalSection\n') + + if self.version_num >= 8.0: + self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n' + '\t\tHideSolutionNode = FALSE\n' + '\tEndGlobalSection\n') + else: + self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n' + '\tEndGlobalSection\n' + '\tGlobalSection(ExtensibilityAddIns) = postSolution\n' + '\tEndGlobalSection\n') + self.file.write('EndGlobal\n') + if self.nokeep == 0: + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + + def Build(self): + try: + self.file = open(self.dswfile,'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail)) + else: + self.PrintSolution() + self.file.close() + +V6DSWHeader = """\ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "%(name)s"="%(dspfile)s" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +""" + +class _GenerateV6DSW(_DSWGenerator): + """Generates a Workspace file for MSVS 6.0""" + + def PrintWorkspace(self): + """ writes a DSW file """ + name = self.name + dspfile = os.path.relpath(self.dspfiles[0], self.dsw_folder_path) + self.file.write(V6DSWHeader % locals()) + + def Build(self): + try: + self.file = open(self.dswfile,'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail)) + else: + self.PrintWorkspace() + self.file.close() + + +def GenerateDSP(dspfile, source, env): + """Generates a Project file based on the version of MSVS that is being used""" + + version_num = 6.0 + if 'MSVS_VERSION' in env: + version_num, suite = msvs_parse_version(env['MSVS_VERSION']) + if version_num >= 10.0: + g = _GenerateV10DSP(dspfile, source, env) + g.Build() + elif version_num >= 7.0: + g = _GenerateV7DSP(dspfile, source, env) + g.Build() + else: + g = _GenerateV6DSP(dspfile, source, env) + g.Build() + +def GenerateDSW(dswfile, source, env): + """Generates a Solution/Workspace file based on the version of MSVS that is being used""" + + version_num = 6.0 + if 'MSVS_VERSION' in env: + version_num, suite = msvs_parse_version(env['MSVS_VERSION']) + if version_num >= 7.0: + g = _GenerateV7DSW(dswfile, source, env) + g.Build() + else: + g = _GenerateV6DSW(dswfile, source, env) + g.Build() + + +############################################################################## +# Above here are the classes and functions for generation of +# DSP/DSW/SLN/VCPROJ files. +############################################################################## + +def GetMSVSProjectSuffix(target, source, env, for_signature): + return env['MSVS']['PROJECTSUFFIX'] + +def GetMSVSSolutionSuffix(target, source, env, for_signature): + return env['MSVS']['SOLUTIONSUFFIX'] + +def GenerateProject(target, source, env): + # generate the dsp file, according to the version of MSVS. + builddspfile = target[0] + dspfile = builddspfile.srcnode() + + # this detects whether or not we're using a VariantDir + if not dspfile is builddspfile: + try: + bdsp = open(str(builddspfile), "w+") + except IOError, detail: + print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' + raise + + bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath()) + + GenerateDSP(dspfile, source, env) + + if env.get('auto_build_solution', 1): + builddswfile = target[1] + dswfile = builddswfile.srcnode() + + if not dswfile is builddswfile: + + try: + bdsw = open(str(builddswfile), "w+") + except IOError, detail: + print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' + raise + + bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath()) + + GenerateDSW(dswfile, source, env) + +def GenerateSolution(target, source, env): + GenerateDSW(target[0], source, env) + +def projectEmitter(target, source, env): + """Sets up the DSP dependencies.""" + + # todo: Not sure what sets source to what user has passed as target, + # but this is what happens. When that is fixed, we also won't have + # to make the user always append env['MSVSPROJECTSUFFIX'] to target. + if source[0] == target[0]: + source = [] + + # make sure the suffix is correct for the version of MSVS we're running. + (base, suff) = SCons.Util.splitext(str(target[0])) + suff = env.subst('$MSVSPROJECTSUFFIX') + target[0] = base + suff + + if not source: + source = 'prj_inputs:' + source = source + env.subst('$MSVSSCONSCOM', 1) + source = source + env.subst('$MSVSENCODING', 1) + + # Project file depends on CPPDEFINES and CPPPATH + preprocdefs = xmlify(';'.join(processDefines(env.get('CPPDEFINES', [])))) + includepath_Dirs = processIncludes(env.get('CPPPATH', []), env, None, None) + includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) + source = source + "; ppdefs:%s incpath:%s"%(preprocdefs, includepath) + + if 'buildtarget' in env and env['buildtarget'] != None: + if SCons.Util.is_String(env['buildtarget']): + source = source + ' "%s"' % env['buildtarget'] + elif SCons.Util.is_List(env['buildtarget']): + for bt in env['buildtarget']: + if SCons.Util.is_String(bt): + source = source + ' "%s"' % bt + else: + try: source = source + ' "%s"' % bt.get_abspath() + except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") + else: + try: source = source + ' "%s"' % env['buildtarget'].get_abspath() + except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") + + if 'outdir' in env and env['outdir'] != None: + if SCons.Util.is_String(env['outdir']): + source = source + ' "%s"' % env['outdir'] + elif SCons.Util.is_List(env['outdir']): + for s in env['outdir']: + if SCons.Util.is_String(s): + source = source + ' "%s"' % s + else: + try: source = source + ' "%s"' % s.get_abspath() + except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None") + else: + try: source = source + ' "%s"' % env['outdir'].get_abspath() + except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None") + + if 'name' in env: + if SCons.Util.is_String(env['name']): + source = source + ' "%s"' % env['name'] + else: + raise SCons.Errors.InternalError("name must be a string") + + if 'variant' in env: + if SCons.Util.is_String(env['variant']): + source = source + ' "%s"' % env['variant'] + elif SCons.Util.is_List(env['variant']): + for variant in env['variant']: + if SCons.Util.is_String(variant): + source = source + ' "%s"' % variant + else: + raise SCons.Errors.InternalError("name must be a string or a list of strings") + else: + raise SCons.Errors.InternalError("variant must be a string or a list of strings") + else: + raise SCons.Errors.InternalError("variant must be specified") + + for s in _DSPGenerator.srcargs: + if s in env: + if SCons.Util.is_String(env[s]): + source = source + ' "%s' % env[s] + elif SCons.Util.is_List(env[s]): + for t in env[s]: + if SCons.Util.is_String(t): + source = source + ' "%s"' % t + else: + raise SCons.Errors.InternalError(s + " must be a string or a list of strings") + else: + raise SCons.Errors.InternalError(s + " must be a string or a list of strings") + + source = source + ' "%s"' % str(target[0]) + source = [SCons.Node.Python.Value(source)] + + targetlist = [target[0]] + sourcelist = source + + if env.get('auto_build_solution', 1): + env['projects'] = [env.File(t).srcnode() for t in targetlist] + t, s = solutionEmitter(target, target, env) + targetlist = targetlist + t + + return (targetlist, sourcelist) + +def solutionEmitter(target, source, env): + """Sets up the DSW dependencies.""" + + # todo: Not sure what sets source to what user has passed as target, + # but this is what happens. When that is fixed, we also won't have + # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target. + if source[0] == target[0]: + source = [] + + # make sure the suffix is correct for the version of MSVS we're running. + (base, suff) = SCons.Util.splitext(str(target[0])) + suff = env.subst('$MSVSSOLUTIONSUFFIX') + target[0] = base + suff + + if not source: + source = 'sln_inputs:' + + if 'name' in env: + if SCons.Util.is_String(env['name']): + source = source + ' "%s"' % env['name'] + else: + raise SCons.Errors.InternalError("name must be a string") + + if 'variant' in env: + if SCons.Util.is_String(env['variant']): + source = source + ' "%s"' % env['variant'] + elif SCons.Util.is_List(env['variant']): + for variant in env['variant']: + if SCons.Util.is_String(variant): + source = source + ' "%s"' % variant + else: + raise SCons.Errors.InternalError("name must be a string or a list of strings") + else: + raise SCons.Errors.InternalError("variant must be a string or a list of strings") + else: + raise SCons.Errors.InternalError("variant must be specified") + + if 'slnguid' in env: + if SCons.Util.is_String(env['slnguid']): + source = source + ' "%s"' % env['slnguid'] + else: + raise SCons.Errors.InternalError("slnguid must be a string") + + if 'projects' in env: + if SCons.Util.is_String(env['projects']): + source = source + ' "%s"' % env['projects'] + elif SCons.Util.is_List(env['projects']): + for t in env['projects']: + if SCons.Util.is_String(t): + source = source + ' "%s"' % t + + source = source + ' "%s"' % str(target[0]) + source = [SCons.Node.Python.Value(source)] + + return ([target[0]], source) + +projectAction = SCons.Action.Action(GenerateProject, None) + +solutionAction = SCons.Action.Action(GenerateSolution, None) + +projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM', + suffix = '$MSVSPROJECTSUFFIX', + emitter = projectEmitter) + +solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM', + suffix = '$MSVSSOLUTIONSUFFIX', + emitter = solutionEmitter) + +default_MSVS_SConscript = None + +def generate(env): + """Add Builders and construction variables for Microsoft Visual + Studio project files to an Environment.""" + try: + env['BUILDERS']['MSVSProject'] + except KeyError: + env['BUILDERS']['MSVSProject'] = projectBuilder + + try: + env['BUILDERS']['MSVSSolution'] + except KeyError: + env['BUILDERS']['MSVSSolution'] = solutionBuilder + + env['MSVSPROJECTCOM'] = projectAction + env['MSVSSOLUTIONCOM'] = solutionAction + + if SCons.Script.call_stack: + # XXX Need to find a way to abstract this; the build engine + # shouldn't depend on anything in SCons.Script. + env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript + else: + global default_MSVS_SConscript + if default_MSVS_SConscript is None: + default_MSVS_SConscript = env.File('SConstruct') + env['MSVSSCONSCRIPT'] = default_MSVS_SConscript + + env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env)) + env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}' + env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS' + env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' + env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' + env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"' + + # Set-up ms tools paths for default version + msvc_setup_env_once(env) + + if 'MSVS_VERSION' in env: + version_num, suite = msvs_parse_version(env['MSVS_VERSION']) + else: + (version_num, suite) = (7.0, None) # guess at a default + if 'MSVS' not in env: + env['MSVS'] = {} + if (version_num < 7.0): + env['MSVS']['PROJECTSUFFIX'] = '.dsp' + env['MSVS']['SOLUTIONSUFFIX'] = '.dsw' + elif (version_num < 10.0): + env['MSVS']['PROJECTSUFFIX'] = '.vcproj' + env['MSVS']['SOLUTIONSUFFIX'] = '.sln' + else: + env['MSVS']['PROJECTSUFFIX'] = '.vcxproj' + env['MSVS']['SOLUTIONSUFFIX'] = '.sln' + + if (version_num >= 10.0): + env['MSVSENCODING'] = 'utf-8' + else: + env['MSVSENCODING'] = 'Windows-1252' + + env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix + env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix + env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}' + env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}' + env['SCONS_HOME'] = os.environ.get('SCONS_HOME') + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/mwcc.py b/engine/SCons/Tool/mwcc.py new file mode 100644 index 0000000..849aa74 --- /dev/null +++ b/engine/SCons/Tool/mwcc.py @@ -0,0 +1,207 @@ +"""SCons.Tool.mwcc + +Tool-specific initialization for the Metrowerks CodeWarrior compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/mwcc.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path + +import SCons.Util + +def set_vars(env): + """Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars + + MWCW_VERSIONS is set to a list of objects representing installed versions + + MWCW_VERSION is set to the version object that will be used for building. + MWCW_VERSION can be set to a string during Environment + construction to influence which version is chosen, otherwise + the latest one from MWCW_VERSIONS is used. + + Returns true if at least one version is found, false otherwise + """ + desired = env.get('MWCW_VERSION', '') + + # return right away if the variables are already set + if isinstance(desired, MWVersion): + return 1 + elif desired is None: + return 0 + + versions = find_versions() + version = None + + if desired: + for v in versions: + if str(v) == desired: + version = v + elif versions: + version = versions[-1] + + env['MWCW_VERSIONS'] = versions + env['MWCW_VERSION'] = version + + if version is None: + return 0 + + env.PrependENVPath('PATH', version.clpath) + env.PrependENVPath('PATH', version.dllpath) + ENV = env['ENV'] + ENV['CWFolder'] = version.path + ENV['LM_LICENSE_FILE'] = version.license + plus = lambda x: '+%s' % x + ENV['MWCIncludes'] = os.pathsep.join(map(plus, version.includes)) + ENV['MWLibraries'] = os.pathsep.join(map(plus, version.libs)) + return 1 + + +def find_versions(): + """Return a list of MWVersion objects representing installed versions""" + versions = [] + + ### This function finds CodeWarrior by reading from the registry on + ### Windows. Some other method needs to be implemented for other + ### platforms, maybe something that calls env.WhereIs('mwcc') + + if SCons.Util.can_read_reg: + try: + HLM = SCons.Util.HKEY_LOCAL_MACHINE + product = 'SOFTWARE\\Metrowerks\\CodeWarrior\\Product Versions' + product_key = SCons.Util.RegOpenKeyEx(HLM, product) + + i = 0 + while True: + name = product + '\\' + SCons.Util.RegEnumKey(product_key, i) + name_key = SCons.Util.RegOpenKeyEx(HLM, name) + + try: + version = SCons.Util.RegQueryValueEx(name_key, 'VERSION') + path = SCons.Util.RegQueryValueEx(name_key, 'PATH') + mwv = MWVersion(version[0], path[0], 'Win32-X86') + versions.append(mwv) + except SCons.Util.RegError: + pass + + i = i + 1 + + except SCons.Util.RegError: + pass + + return versions + + +class MWVersion(object): + def __init__(self, version, path, platform): + self.version = version + self.path = path + self.platform = platform + self.clpath = os.path.join(path, 'Other Metrowerks Tools', + 'Command Line Tools') + self.dllpath = os.path.join(path, 'Bin') + + # The Metrowerks tools don't store any configuration data so they + # are totally dumb when it comes to locating standard headers, + # libraries, and other files, expecting all the information + # to be handed to them in environment variables. The members set + # below control what information scons injects into the environment + + ### The paths below give a normal build environment in CodeWarrior for + ### Windows, other versions of CodeWarrior might need different paths. + + msl = os.path.join(path, 'MSL') + support = os.path.join(path, '%s Support' % platform) + + self.license = os.path.join(path, 'license.dat') + self.includes = [msl, support] + self.libs = [msl, support] + + def __str__(self): + return self.version + + +CSuffixes = ['.c', '.C'] +CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] + + +def generate(env): + """Add Builders and construction variables for the mwcc to an Environment.""" + import SCons.Defaults + import SCons.Tool + + set_vars(env) + + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + + for suffix in CXXSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CXXAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) + + env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -nolink -o $TARGET $SOURCES' + + env['CC'] = 'mwcc' + env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS' + + env['CXX'] = 'mwcc' + env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' + + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = '$CCFLAGS' + env['SHCFLAGS'] = '$CFLAGS' + env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS' + + env['SHCXX'] = '$CXX' + env['SHCXXFLAGS'] = '$CXXFLAGS' + env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS' + + env['CFILESUFFIX'] = '.c' + env['CXXFILESUFFIX'] = '.cpp' + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + + #env['PCH'] = ? + #env['PCHSTOP'] = ? + + +def exists(env): + return set_vars(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/mwld.py b/engine/SCons/Tool/mwld.py new file mode 100644 index 0000000..a8cf2a6 --- /dev/null +++ b/engine/SCons/Tool/mwld.py @@ -0,0 +1,107 @@ +"""SCons.Tool.mwld + +Tool-specific initialization for the Metrowerks CodeWarrior linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/mwld.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Tool + + +def generate(env): + """Add Builders and construction variables for lib to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['AR'] = 'mwld' + env['ARCOM'] = '$AR $ARFLAGS -library -o $TARGET $SOURCES' + + env['LIBDIRPREFIX'] = '-L' + env['LIBDIRSUFFIX'] = '' + env['LIBLINKPREFIX'] = '-l' + env['LIBLINKSUFFIX'] = '.lib' + + env['LINK'] = 'mwld' + env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = '$LINKFLAGS' + env['SHLINKCOM'] = shlib_action + env['SHLIBEMITTER']= shlib_emitter + + +def exists(env): + import SCons.Tool.mwcc + return SCons.Tool.mwcc.set_vars(env) + + +def shlib_generator(target, source, env, for_signature): + cmd = ['$SHLINK', '$SHLINKFLAGS', '-shared'] + + no_import_lib = env.get('no_import_lib', 0) + if no_import_lib: cmd.extend('-noimplib') + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.extend(['-implib', implib.get_string(for_signature)]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + return [cmd] + + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Append an import library to the list of targets. + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX')) + + return target, source + + +shlib_action = SCons.Action.Action(shlib_generator, generator=1) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/nasm.py b/engine/SCons/Tool/nasm.py new file mode 100644 index 0000000..5641518 --- /dev/null +++ b/engine/SCons/Tool/nasm.py @@ -0,0 +1,72 @@ +"""SCons.Tool.nasm + +Tool-specific initialization for nasm, the famous Netwide Assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/nasm.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +ASSuffixes = ['.s', '.asm', '.ASM'] +ASPPSuffixes = ['.spp', '.SPP', '.sx'] +if SCons.Util.case_sensitive_suffixes('.s', '.S'): + ASPPSuffixes.extend(['.S']) +else: + ASSuffixes.extend(['.S']) + +def generate(env): + """Add Builders and construction variables for nasm to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in ASSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + + for suffix in ASPPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASPPAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + + env['AS'] = 'nasm' + env['ASFLAGS'] = SCons.Util.CLVar('') + env['ASPPFLAGS'] = '$ASFLAGS' + env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' + env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + +def exists(env): + return env.Detect('nasm') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/__init__.py b/engine/SCons/Tool/packaging/__init__.py new file mode 100644 index 0000000..9d1978b --- /dev/null +++ b/engine/SCons/Tool/packaging/__init__.py @@ -0,0 +1,312 @@ +"""SCons.Tool.Packaging + +SCons Packaging Tool. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/packaging/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Environment +from SCons.Variables import * +from SCons.Errors import * +from SCons.Util import is_List, make_path_relative +from SCons.Warnings import warn, Warning + +import os, imp +import SCons.Defaults + +__all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] + +# +# Utility and Builder function +# +def Tag(env, target, source, *more_tags, **kw_tags): + """ Tag a file with the given arguments, just sets the accordingly named + attribute on the file object. + + TODO: FIXME + """ + if not target: + target=source + first_tag=None + else: + first_tag=source + + if first_tag: + kw_tags[first_tag[0]] = '' + + if len(kw_tags) == 0 and len(more_tags) == 0: + raise UserError("No tags given.") + + # XXX: sanity checks + for x in more_tags: + kw_tags[x] = '' + + if not SCons.Util.is_List(target): + target=[target] + else: + # hmm, sometimes the target list, is a list of a list + # make sure it is flattened prior to processing. + # TODO: perhaps some bug ?!? + target=env.Flatten(target) + + for t in target: + for (k,v) in kw_tags.items(): + # all file tags have to start with PACKAGING_, so we can later + # differentiate between "normal" object attributes and the + # packaging attributes. As the user should not be bothered with + # that, the prefix will be added here if missing. + #if not k.startswith('PACKAGING_'): + if k[:10] != 'PACKAGING_': + k='PACKAGING_'+k + setattr(t, k, v) + +def Package(env, target=None, source=None, **kw): + """ Entry point for the package tool. + """ + # check if we need to find the source files ourself + if not source: + source = env.FindInstalledFiles() + + if len(source)==0: + raise UserError("No source for Package() given") + + # decide which types of packages shall be built. Can be defined through + # four mechanisms: command line argument, keyword argument, + # environment argument and default selection( zip or tar.gz ) in that + # order. + try: kw['PACKAGETYPE']=env['PACKAGETYPE'] + except KeyError: pass + + if not kw.get('PACKAGETYPE'): + from SCons.Script import GetOption + kw['PACKAGETYPE'] = GetOption('package_type') + + if kw['PACKAGETYPE'] == None: + if 'Tar' in env['BUILDERS']: + kw['PACKAGETYPE']='targz' + elif 'Zip' in env['BUILDERS']: + kw['PACKAGETYPE']='zip' + else: + raise UserError("No type for Package() given") + + PACKAGETYPE=kw['PACKAGETYPE'] + if not is_List(PACKAGETYPE): + PACKAGETYPE=PACKAGETYPE.split(',') + + # load the needed packagers. + def load_packager(type): + try: + file,path,desc=imp.find_module(type, __path__) + return imp.load_module(type, file, path, desc) + except ImportError, e: + raise EnvironmentError("packager %s not available: %s"%(type,str(e))) + + packagers=list(map(load_packager, PACKAGETYPE)) + + # set up targets and the PACKAGEROOT + try: + # fill up the target list with a default target name until the PACKAGETYPE + # list is of the same size as the target list. + if not target: target = [] + + size_diff = len(PACKAGETYPE)-len(target) + default_name = "%(NAME)s-%(VERSION)s" + + if size_diff>0: + default_target = default_name%kw + target.extend( [default_target]*size_diff ) + + if 'PACKAGEROOT' not in kw: + kw['PACKAGEROOT'] = default_name%kw + + except KeyError, e: + raise SCons.Errors.UserError( "Missing Packagetag '%s'"%e.args[0] ) + + # setup the source files + source=env.arg2nodes(source, env.fs.Entry) + + # call the packager to setup the dependencies. + targets=[] + try: + for packager in packagers: + t=[target.pop(0)] + t=packager.package(env,t,source, **kw) + targets.extend(t) + + assert( len(target) == 0 ) + + except KeyError, e: + raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ + % (e.args[0],packager.__name__) ) + except TypeError, e: + # this exception means that a needed argument for the packager is + # missing. As our packagers get their "tags" as named function + # arguments we need to find out which one is missing. + from inspect import getargspec + args,varargs,varkw,defaults=getargspec(packager.package) + if defaults!=None: + args=args[:-len(defaults)] # throw away arguments with default values + args.remove('env') + args.remove('target') + args.remove('source') + # now remove any args for which we have a value in kw. + args=[x for x in args if x not in kw] + + if len(args)==0: + raise # must be a different error, so reraise + elif len(args)==1: + raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ + % (args[0],packager.__name__) ) + else: + raise SCons.Errors.UserError( "Missing Packagetags '%s' for %s packager"\ + % (", ".join(args),packager.__name__) ) + + target=env.arg2nodes(target, env.fs.Entry) + targets.extend(env.Alias( 'package', targets )) + return targets + +# +# SCons tool initialization functions +# + +added = None + +def generate(env): + from SCons.Script import AddOption + global added + if not added: + added = 1 + AddOption('--package-type', + dest='package_type', + default=None, + type="string", + action="store", + help='The type of package to create.') + + try: + env['BUILDERS']['Package'] + env['BUILDERS']['Tag'] + except KeyError: + env['BUILDERS']['Package'] = Package + env['BUILDERS']['Tag'] = Tag + +def exists(env): + return 1 + +# XXX +def options(opts): + opts.AddVariables( + EnumVariable( 'PACKAGETYPE', + 'the type of package to create.', + None, allowed_values=list(map( str, __all__ )), + ignorecase=2 + ) + ) + +# +# Internal utility functions +# + +def copy_attr(f1, f2): + """ copies the special packaging file attributes from f1 to f2. + """ + #pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\ + # x.startswith('PACKAGING_')] + copyit = lambda x: not hasattr(f2, x) and x[:10] == 'PACKAGING_' + pattrs = list(filter(copyit, dir(f1))) + for attr in pattrs: + setattr(f2, attr, getattr(f1, attr)) +def putintopackageroot(target, source, env, pkgroot, honor_install_location=1): + """ Uses the CopyAs builder to copy all source files to the directory given + in pkgroot. + + If honor_install_location is set and the copied source file has an + PACKAGING_INSTALL_LOCATION attribute, the PACKAGING_INSTALL_LOCATION is + used as the new name of the source file under pkgroot. + + The source file will not be copied if it is already under the the pkgroot + directory. + + All attributes of the source file will be copied to the new file. + """ + # make sure the packageroot is a Dir object. + if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) + if not SCons.Util.is_List(source): source=[source] + + new_source = [] + for file in source: + if SCons.Util.is_String(file): file = env.File(file) + + if file.is_under(pkgroot): + new_source.append(file) + else: + if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\ + honor_install_location: + new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION) + else: + new_name=make_path_relative(file.get_path()) + + new_file=pkgroot.File(new_name) + new_file=env.CopyAs(new_file, file)[0] + copy_attr(file, new_file) + new_source.append(new_file) + + return (target, new_source) + +def stripinstallbuilder(target, source, env): + """ strips the install builder action from the source list and stores + the final installation location as the "PACKAGING_INSTALL_LOCATION" of + the source of the source file. This effectively removes the final installed + files from the source list while remembering the installation location. + + It also warns about files which have no install builder attached. + """ + def has_no_install_location(file): + return not (file.has_builder() and\ + hasattr(file.builder, 'name') and\ + (file.builder.name=="InstallBuilder" or\ + file.builder.name=="InstallAsBuilder")) + + if len(list(filter(has_no_install_location, source))): + warn(Warning, "there are files to package which have no\ + InstallBuilder attached, this might lead to irreproducible packages") + + n_source=[] + for s in source: + if has_no_install_location(s): + n_source.append(s) + else: + for ss in s.sources: + n_source.append(ss) + copy_attr(s, ss) + setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path()) + + return (target, n_source) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/ipk.py b/engine/SCons/Tool/packaging/ipk.py new file mode 100644 index 0000000..2aaa722 --- /dev/null +++ b/engine/SCons/Tool/packaging/ipk.py @@ -0,0 +1,185 @@ +"""SCons.Tool.Packaging.ipk +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/packaging/ipk.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Builder +import SCons.Node.FS +import os + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, + SUMMARY, X_IPK_PRIORITY, X_IPK_SECTION, SOURCE_URL, + X_IPK_MAINTAINER, X_IPK_DEPENDS, **kw): + """ this function prepares the packageroot directory for packaging with the + ipkg builder. + """ + SCons.Tool.Tool('ipkg').generate(env) + + # setup the Ipkg builder + bld = env['BUILDERS']['Ipkg'] + target, source = stripinstallbuilder(target, source, env) + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + + # This should be overridable from the construction environment, + # which it is by using ARCHITECTURE=. + # Guessing based on what os.uname() returns at least allows it + # to work for both i386 and x86_64 Linux systems. + archmap = { + 'i686' : 'i386', + 'i586' : 'i386', + 'i486' : 'i386', + } + + buildarchitecture = os.uname()[4] + buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) + + if 'ARCHITECTURE' in kw: + buildarchitecture = kw['ARCHITECTURE'] + + # setup the kw to contain the mandatory arguments to this fucntion. + # do this before calling any builder or setup function + loc=locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # generate the specfile + specfile = gen_ipk_dir(PACKAGEROOT, source, env, kw) + + # override the default target. + if str(target[0])=="%s-%s"%(NAME, VERSION): + target=[ "%s_%s_%s.ipk"%(NAME, VERSION, buildarchitecture) ] + + # now apply the Ipkg builder + return bld(env, target, specfile, **kw) + +def gen_ipk_dir(proot, source, env, kw): + # make sure the packageroot is a Dir object. + if SCons.Util.is_String(proot): proot=env.Dir(proot) + + # create the specfile builder + s_bld=SCons.Builder.Builder( + action = build_specfiles, + ) + + # create the specfile targets + spec_target=[] + control=proot.Dir('CONTROL') + spec_target.append(control.File('control')) + spec_target.append(control.File('conffiles')) + spec_target.append(control.File('postrm')) + spec_target.append(control.File('prerm')) + spec_target.append(control.File('postinst')) + spec_target.append(control.File('preinst')) + + # apply the builder to the specfile targets + s_bld(env, spec_target, source, **kw) + + # the packageroot directory does now contain the specfiles. + return proot + +def build_specfiles(source, target, env): + """ filter the targets for the needed files and use the variables in env + to create the specfile. + """ + # + # At first we care for the CONTROL/control file, which is the main file for ipk. + # + # For this we need to open multiple files in random order, so we store into + # a dict so they can be easily accessed. + # + # + opened_files={} + def open_file(needle, haystack): + try: + return opened_files[needle] + except KeyError: + file=filter(lambda x: x.get_path().rfind(needle)!=-1, haystack)[0] + opened_files[needle]=open(file.abspath, 'w') + return opened_files[needle] + + control_file=open_file('control', target) + + if 'X_IPK_DESCRIPTION' not in env: + env['X_IPK_DESCRIPTION']="%s\n %s"%(env['SUMMARY'], + env['DESCRIPTION'].replace('\n', '\n ')) + + + content = """ +Package: $NAME +Version: $VERSION +Priority: $X_IPK_PRIORITY +Section: $X_IPK_SECTION +Source: $SOURCE_URL +Architecture: $ARCHITECTURE +Maintainer: $X_IPK_MAINTAINER +Depends: $X_IPK_DEPENDS +Description: $X_IPK_DESCRIPTION +""" + + control_file.write(env.subst(content)) + + # + # now handle the various other files, which purpose it is to set post-, + # pre-scripts and mark files as config files. + # + # We do so by filtering the source files for files which are marked with + # the "config" tag and afterwards we do the same for x_ipk_postrm, + # x_ipk_prerm, x_ipk_postinst and x_ipk_preinst tags. + # + # The first one will write the name of the file into the file + # CONTROL/configfiles, the latter add the content of the x_ipk_* variable + # into the same named file. + # + for f in [x for x in source if 'PACKAGING_CONFIG' in dir(x)]: + config=open_file('conffiles') + config.write(f.PACKAGING_INSTALL_LOCATION) + config.write('\n') + + for str in 'POSTRM PRERM POSTINST PREINST'.split(): + name="PACKAGING_X_IPK_%s"%str + for f in [x for x in source if name in dir(x)]: + file=open_file(name) + file.write(env[str]) + + # + # close all opened files + for f in opened_files.values(): + f.close() + + # call a user specified function + if 'CHANGE_SPECFILE' in env: + content += env['CHANGE_SPECFILE'](target) + + return 0 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/msi.py b/engine/SCons/Tool/packaging/msi.py new file mode 100644 index 0000000..ccb3596 --- /dev/null +++ b/engine/SCons/Tool/packaging/msi.py @@ -0,0 +1,527 @@ +"""SCons.Tool.packaging.msi + +The msi packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/packaging/msi.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import SCons +from SCons.Action import Action +from SCons.Builder import Builder + +from xml.dom.minidom import * +from xml.sax.saxutils import escape + +from SCons.Tool.packaging import stripinstallbuilder + +# +# Utility functions +# +def convert_to_id(s, id_set): + """ Some parts of .wxs need an Id attribute (for example: The File and + Directory directives. The charset is limited to A-Z, a-z, digits, + underscores, periods. Each Id must begin with a letter or with a + underscore. Google for "CNDL0015" for information about this. + + Requirements: + * the string created must only contain chars from the target charset. + * the string created must have a minimal editing distance from the + original string. + * the string created must be unique for the whole .wxs file. + + Observation: + * There are 62 chars in the charset. + + Idea: + * filter out forbidden characters. Check for a collision with the help + of the id_set. Add the number of the number of the collision at the + end of the created string. Furthermore care for a correct start of + the string. + """ + charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789_.' + if s[0] in '0123456789.': + s += '_'+s + id = [c for c in s if c in charset] + + # did we already generate an id for this file? + try: + return id_set[id][s] + except KeyError: + # no we did not so initialize with the id + if id not in id_set: id_set[id] = { s : id } + # there is a collision, generate an id which is unique by appending + # the collision number + else: id_set[id][s] = id + str(len(id_set[id])) + + return id_set[id][s] + +def is_dos_short_file_name(file): + """ examine if the given file is in the 8.3 form. + """ + fname, ext = os.path.splitext(file) + proper_ext = len(ext) == 0 or (2 <= len(ext) <= 4) # the ext contains the dot + proper_fname = file.isupper() and len(fname) <= 8 + + return proper_ext and proper_fname + +def gen_dos_short_file_name(file, filename_set): + """ see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982 + + These are no complete 8.3 dos short names. The ~ char is missing and + replaced with one character from the filename. WiX warns about such + filenames, since a collision might occur. Google for "CNDL1014" for + more information. + """ + # guard this to not confuse the generation + if is_dos_short_file_name(file): + return file + + fname, ext = os.path.splitext(file) # ext contains the dot + + # first try if it suffices to convert to upper + file = file.upper() + if is_dos_short_file_name(file): + return file + + # strip forbidden characters. + forbidden = '."/[]:;=, ' + fname = [c for c in fname if c not in forbidden] + + # check if we already generated a filename with the same number: + # thisis1.txt, thisis2.txt etc. + duplicate, num = not None, 1 + while duplicate: + shortname = "%s%s" % (fname[:8-len(str(num))].upper(),\ + str(num)) + if len(ext) >= 2: + shortname = "%s%s" % (shortname, ext[:4].upper()) + + duplicate, num = shortname in filename_set, num+1 + + assert( is_dos_short_file_name(shortname) ), 'shortname is %s, longname is %s' % (shortname, file) + filename_set.append(shortname) + return shortname + +def create_feature_dict(files): + """ X_MSI_FEATURE and doc FileTag's can be used to collect files in a + hierarchy. This function collects the files into this hierarchy. + """ + dict = {} + + def add_to_dict( feature, file ): + if not SCons.Util.is_List( feature ): + feature = [ feature ] + + for f in feature: + if f not in dict: + dict[ f ] = [ file ] + else: + dict[ f ].append( file ) + + for file in files: + if hasattr( file, 'PACKAGING_X_MSI_FEATURE' ): + add_to_dict(file.PACKAGING_X_MSI_FEATURE, file) + elif hasattr( file, 'PACKAGING_DOC' ): + add_to_dict( 'PACKAGING_DOC', file ) + else: + add_to_dict( 'default', file ) + + return dict + +def generate_guids(root): + """ generates globally unique identifiers for parts of the xml which need + them. + + Component tags have a special requirement. Their UUID is only allowed to + change if the list of their contained resources has changed. This allows + for clean removal and proper updates. + + To handle this requirement, the uuid is generated with an md5 hashing the + whole subtree of a xml node. + """ + from hashlib import md5 + + # specify which tags need a guid and in which attribute this should be stored. + needs_id = { 'Product' : 'Id', + 'Package' : 'Id', + 'Component' : 'Guid', + } + + # find all XMl nodes matching the key, retrieve their attribute, hash their + # subtree, convert hash to string and add as a attribute to the xml node. + for (key,value) in needs_id.items(): + node_list = root.getElementsByTagName(key) + attribute = value + for node in node_list: + hash = md5(node.toxml()).hexdigest() + hash_str = '%s-%s-%s-%s-%s' % ( hash[:8], hash[8:12], hash[12:16], hash[16:20], hash[20:] ) + node.attributes[attribute] = hash_str + + + +def string_wxsfile(target, source, env): + return "building WiX file %s"%( target[0].path ) + +def build_wxsfile(target, source, env): + """ compiles a .wxs file from the keywords given in env['msi_spec'] and + by analyzing the tree of source nodes and their tags. + """ + file = open(target[0].abspath, 'w') + + try: + # Create a document with the Wix root tag + doc = Document() + root = doc.createElement( 'Wix' ) + root.attributes['xmlns']='http://schemas.microsoft.com/wix/2003/01/wi' + doc.appendChild( root ) + + filename_set = [] # this is to circumvent duplicates in the shortnames + id_set = {} # this is to circumvent duplicates in the ids + + # Create the content + build_wxsfile_header_section(root, env) + build_wxsfile_file_section(root, source, env['NAME'], env['VERSION'], env['VENDOR'], filename_set, id_set) + generate_guids(root) + build_wxsfile_features_section(root, source, env['NAME'], env['VERSION'], env['SUMMARY'], id_set) + build_wxsfile_default_gui(root) + build_license_file(target[0].get_dir(), env) + + # write the xml to a file + file.write( doc.toprettyxml() ) + + # call a user specified function + if 'CHANGE_SPECFILE' in env: + env['CHANGE_SPECFILE'](target, source) + + except KeyError, e: + raise SCons.Errors.UserError( '"%s" package field for MSI is missing.' % e.args[0] ) + +# +# setup function +# +def create_default_directory_layout(root, NAME, VERSION, VENDOR, filename_set): + """ Create the wix default target directory layout and return the innermost + directory. + + We assume that the XML tree delivered in the root argument already contains + the Product tag. + + Everything is put under the PFiles directory property defined by WiX. + After that a directory with the 'VENDOR' tag is placed and then a + directory with the name of the project and its VERSION. This leads to the + following TARGET Directory Layout: + C:\\\\ + Example: C:\Programme\Company\Product-1.2\ + """ + doc = Document() + d1 = doc.createElement( 'Directory' ) + d1.attributes['Id'] = 'TARGETDIR' + d1.attributes['Name'] = 'SourceDir' + + d2 = doc.createElement( 'Directory' ) + d2.attributes['Id'] = 'ProgramFilesFolder' + d2.attributes['Name'] = 'PFiles' + + d3 = doc.createElement( 'Directory' ) + d3.attributes['Id'] = 'VENDOR_folder' + d3.attributes['Name'] = escape( gen_dos_short_file_name( VENDOR, filename_set ) ) + d3.attributes['LongName'] = escape( VENDOR ) + + d4 = doc.createElement( 'Directory' ) + project_folder = "%s-%s" % ( NAME, VERSION ) + d4.attributes['Id'] = 'MY_DEFAULT_FOLDER' + d4.attributes['Name'] = escape( gen_dos_short_file_name( project_folder, filename_set ) ) + d4.attributes['LongName'] = escape( project_folder ) + + d1.childNodes.append( d2 ) + d2.childNodes.append( d3 ) + d3.childNodes.append( d4 ) + + root.getElementsByTagName('Product')[0].childNodes.append( d1 ) + + return d4 + +# +# mandatory and optional file tags +# +def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, id_set): + """ builds the Component sections of the wxs file with their included files. + + Files need to be specified in 8.3 format and in the long name format, long + filenames will be converted automatically. + + Features are specficied with the 'X_MSI_FEATURE' or 'DOC' FileTag. + """ + root = create_default_directory_layout( root, NAME, VERSION, VENDOR, filename_set ) + components = create_feature_dict( files ) + factory = Document() + + def get_directory( node, dir ): + """ returns the node under the given node representing the directory. + + Returns the component node if dir is None or empty. + """ + if dir == '' or not dir: + return node + + Directory = node + dir_parts = dir.split(os.path.sep) + + # to make sure that our directory ids are unique, the parent folders are + # consecutively added to upper_dir + upper_dir = '' + + # walk down the xml tree finding parts of the directory + dir_parts = [d for d in dir_parts if d != ''] + for d in dir_parts[:]: + already_created = [c for c in Directory.childNodes + if c.nodeName == 'Directory' + and c.attributes['LongName'].value == escape(d)] + + if already_created != []: + Directory = already_created[0] + dir_parts.remove(d) + upper_dir += d + else: + break + + for d in dir_parts: + nDirectory = factory.createElement( 'Directory' ) + nDirectory.attributes['LongName'] = escape( d ) + nDirectory.attributes['Name'] = escape( gen_dos_short_file_name( d, filename_set ) ) + upper_dir += d + nDirectory.attributes['Id'] = convert_to_id( upper_dir, id_set ) + + Directory.childNodes.append( nDirectory ) + Directory = nDirectory + + return Directory + + for file in files: + drive, path = os.path.splitdrive( file.PACKAGING_INSTALL_LOCATION ) + filename = os.path.basename( path ) + dirname = os.path.dirname( path ) + + h = { + # tagname : default value + 'PACKAGING_X_MSI_VITAL' : 'yes', + 'PACKAGING_X_MSI_FILEID' : convert_to_id(filename, id_set), + 'PACKAGING_X_MSI_LONGNAME' : filename, + 'PACKAGING_X_MSI_SHORTNAME' : gen_dos_short_file_name(filename, filename_set), + 'PACKAGING_X_MSI_SOURCE' : file.get_path(), + } + + # fill in the default tags given above. + for k,v in [ (k, v) for (k,v) in h.items() if not hasattr(file, k) ]: + setattr( file, k, v ) + + File = factory.createElement( 'File' ) + File.attributes['LongName'] = escape( file.PACKAGING_X_MSI_LONGNAME ) + File.attributes['Name'] = escape( file.PACKAGING_X_MSI_SHORTNAME ) + File.attributes['Source'] = escape( file.PACKAGING_X_MSI_SOURCE ) + File.attributes['Id'] = escape( file.PACKAGING_X_MSI_FILEID ) + File.attributes['Vital'] = escape( file.PACKAGING_X_MSI_VITAL ) + + # create the Tag under which this file should appear + Component = factory.createElement('Component') + Component.attributes['DiskId'] = '1' + Component.attributes['Id'] = convert_to_id( filename, id_set ) + + # hang the component node under the root node and the file node + # under the component node. + Directory = get_directory( root, dirname ) + Directory.childNodes.append( Component ) + Component.childNodes.append( File ) + +# +# additional functions +# +def build_wxsfile_features_section(root, files, NAME, VERSION, SUMMARY, id_set): + """ This function creates the tag based on the supplied xml tree. + + This is achieved by finding all s and adding them to a default target. + + It should be called after the tree has been built completly. We assume + that a MY_DEFAULT_FOLDER Property is defined in the wxs file tree. + + Furthermore a top-level with the name and VERSION of the software will be created. + + An PACKAGING_X_MSI_FEATURE can either be a string, where the feature + DESCRIPTION will be the same as its title or a Tuple, where the first + part will be its title and the second its DESCRIPTION. + """ + factory = Document() + Feature = factory.createElement('Feature') + Feature.attributes['Id'] = 'complete' + Feature.attributes['ConfigurableDirectory'] = 'MY_DEFAULT_FOLDER' + Feature.attributes['Level'] = '1' + Feature.attributes['Title'] = escape( '%s %s' % (NAME, VERSION) ) + Feature.attributes['Description'] = escape( SUMMARY ) + Feature.attributes['Display'] = 'expand' + + for (feature, files) in create_feature_dict(files).items(): + SubFeature = factory.createElement('Feature') + SubFeature.attributes['Level'] = '1' + + if SCons.Util.is_Tuple(feature): + SubFeature.attributes['Id'] = convert_to_id( feature[0], id_set ) + SubFeature.attributes['Title'] = escape(feature[0]) + SubFeature.attributes['Description'] = escape(feature[1]) + else: + SubFeature.attributes['Id'] = convert_to_id( feature, id_set ) + if feature=='default': + SubFeature.attributes['Description'] = 'Main Part' + SubFeature.attributes['Title'] = 'Main Part' + elif feature=='PACKAGING_DOC': + SubFeature.attributes['Description'] = 'Documentation' + SubFeature.attributes['Title'] = 'Documentation' + else: + SubFeature.attributes['Description'] = escape(feature) + SubFeature.attributes['Title'] = escape(feature) + + # build the componentrefs. As one of the design decision is that every + # file is also a component we walk the list of files and create a + # reference. + for f in files: + ComponentRef = factory.createElement('ComponentRef') + ComponentRef.attributes['Id'] = convert_to_id( os.path.basename(f.get_path()), id_set ) + SubFeature.childNodes.append(ComponentRef) + + Feature.childNodes.append(SubFeature) + + root.getElementsByTagName('Product')[0].childNodes.append(Feature) + +def build_wxsfile_default_gui(root): + """ this function adds a default GUI to the wxs file + """ + factory = Document() + Product = root.getElementsByTagName('Product')[0] + + UIRef = factory.createElement('UIRef') + UIRef.attributes['Id'] = 'WixUI_Mondo' + Product.childNodes.append(UIRef) + + UIRef = factory.createElement('UIRef') + UIRef.attributes['Id'] = 'WixUI_ErrorProgressText' + Product.childNodes.append(UIRef) + +def build_license_file(directory, spec): + """ creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT" + in the given directory + """ + name, text = '', '' + + try: + name = spec['LICENSE'] + text = spec['X_MSI_LICENSE_TEXT'] + except KeyError: + pass # ignore this as X_MSI_LICENSE_TEXT is optional + + if name!='' or text!='': + file = open( os.path.join(directory.get_path(), 'License.rtf'), 'w' ) + file.write('{\\rtf') + if text!='': + file.write(text.replace('\n', '\\par ')) + else: + file.write(name+'\\par\\par') + file.write('}') + file.close() + +# +# mandatory and optional package tags +# +def build_wxsfile_header_section(root, spec): + """ Adds the xml file node which define the package meta-data. + """ + # Create the needed DOM nodes and add them at the correct position in the tree. + factory = Document() + Product = factory.createElement( 'Product' ) + Package = factory.createElement( 'Package' ) + + root.childNodes.append( Product ) + Product.childNodes.append( Package ) + + # set "mandatory" default values + if 'X_MSI_LANGUAGE' not in spec: + spec['X_MSI_LANGUAGE'] = '1033' # select english + + # mandatory sections, will throw a KeyError if the tag is not available + Product.attributes['Name'] = escape( spec['NAME'] ) + Product.attributes['Version'] = escape( spec['VERSION'] ) + Product.attributes['Manufacturer'] = escape( spec['VENDOR'] ) + Product.attributes['Language'] = escape( spec['X_MSI_LANGUAGE'] ) + Package.attributes['Description'] = escape( spec['SUMMARY'] ) + + # now the optional tags, for which we avoid the KeyErrror exception + if 'DESCRIPTION' in spec: + Package.attributes['Comments'] = escape( spec['DESCRIPTION'] ) + + if 'X_MSI_UPGRADE_CODE' in spec: + Package.attributes['X_MSI_UPGRADE_CODE'] = escape( spec['X_MSI_UPGRADE_CODE'] ) + + # We hardcode the media tag as our current model cannot handle it. + Media = factory.createElement('Media') + Media.attributes['Id'] = '1' + Media.attributes['Cabinet'] = 'default.cab' + Media.attributes['EmbedCab'] = 'yes' + root.getElementsByTagName('Product')[0].childNodes.append(Media) + +# this builder is the entry-point for .wxs file compiler. +wxs_builder = Builder( + action = Action( build_wxsfile, string_wxsfile ), + ensure_suffix = '.wxs' ) + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, + DESCRIPTION, SUMMARY, VENDOR, X_MSI_LANGUAGE, **kw): + # make sure that the Wix Builder is in the environment + SCons.Tool.Tool('wix').generate(env) + + # get put the keywords for the specfile compiler. These are the arguments + # given to the package function and all optional ones stored in kw, minus + # the the source, target and env one. + loc = locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # strip the install builder from the source files + target, source = stripinstallbuilder(target, source, env) + + # put the arguments into the env and call the specfile builder. + env['msi_spec'] = kw + specfile = wxs_builder(* [env, target, source], **kw) + + # now call the WiX Tool with the built specfile added as a source. + msifile = env.WiX(target, specfile) + + # return the target and source tuple. + return (msifile, source+[specfile]) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/rpm.py b/engine/SCons/Tool/packaging/rpm.py new file mode 100644 index 0000000..4baa016 --- /dev/null +++ b/engine/SCons/Tool/packaging/rpm.py @@ -0,0 +1,365 @@ +"""SCons.Tool.Packaging.rpm + +The rpm packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Tool/packaging/rpm.py 5357 2011/09/09 21:31:03 bdeegan" + +import os + +import SCons.Builder + +from SCons.Environment import OverrideEnvironment +from SCons.Tool.packaging import stripinstallbuilder, src_targz +from SCons.Errors import UserError + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, + PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE, + **kw): + # initialize the rpm tool + SCons.Tool.Tool('rpm').generate(env) + + bld = env['BUILDERS']['Rpm'] + + # Generate a UserError whenever the target name has been set explicitly, + # since rpm does not allow for controlling it. This is detected by + # checking if the target has been set to the default by the Package() + # Environment function. + if str(target[0])!="%s-%s"%(NAME, VERSION): + raise UserError( "Setting target is not supported for rpm." ) + else: + # This should be overridable from the construction environment, + # which it is by using ARCHITECTURE=. + # Guessing based on what os.uname() returns at least allows it + # to work for both i386 and x86_64 Linux systems. + archmap = { + 'i686' : 'i386', + 'i586' : 'i386', + 'i486' : 'i386', + } + + buildarchitecture = os.uname()[4] + buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) + + if 'ARCHITECTURE' in kw: + buildarchitecture = kw['ARCHITECTURE'] + + fmt = '%s-%s-%s.%s.rpm' + srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src') + binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture) + + target = [ srcrpm, binrpm ] + + # get the correct arguments into the kw hash + loc=locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # if no "SOURCE_URL" tag is given add a default one. + if 'SOURCE_URL' not in kw: + #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') + kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') + + # mangle the source and target list for the rpmbuild + env = OverrideEnvironment(env, kw) + target, source = stripinstallbuilder(target, source, env) + target, source = addspecfile(target, source, env) + target, source = collectintargz(target, source, env) + + # now call the rpm builder to actually build the packet. + return bld(env, target, source, **kw) + +def collectintargz(target, source, env): + """ Puts all source files into a tar.gz file. """ + # the rpm tool depends on a source package, until this is chagned + # this hack needs to be here that tries to pack all sources in. + sources = env.FindSourceFiles() + + # filter out the target we are building the source list for. + #sources = [s for s in sources if not (s in target)] + sources = [s for s in sources if s not in target] + + # find the .spec file for rpm and add it since it is not necessarily found + # by the FindSourceFiles function. + #sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] ) + spec_file = lambda s: str(s).rfind('.spec') != -1 + sources.extend( list(filter(spec_file, source)) ) + + # as the source contains the url of the source package this rpm package + # is built from, we extract the target name + #tarball = (str(target[0])+".tar.gz").replace('.rpm', '') + tarball = (str(target[0])+".tar.gz").replace('.rpm', '') + try: + #tarball = env['SOURCE_URL'].split('/')[-1] + tarball = env['SOURCE_URL'].split('/')[-1] + except KeyError, e: + raise SCons.Errors.UserError( "Missing PackageTag '%s' for RPM packager" % e.args[0] ) + + tarball = src_targz.package(env, source=sources, target=tarball, + PACKAGEROOT=env['PACKAGEROOT'], ) + + return (target, tarball) + +def addspecfile(target, source, env): + specfile = "%s-%s" % (env['NAME'], env['VERSION']) + + bld = SCons.Builder.Builder(action = build_specfile, + suffix = '.spec', + target_factory = SCons.Node.FS.File) + + source.extend(bld(env, specfile, source)) + + return (target,source) + +def build_specfile(target, source, env): + """ Builds a RPM specfile from a dictionary with string metadata and + by analyzing a tree of nodes. + """ + file = open(target[0].abspath, 'w') + str = "" + + try: + file.write( build_specfile_header(env) ) + file.write( build_specfile_sections(env) ) + file.write( build_specfile_filesection(env, source) ) + file.close() + + # call a user specified function + if 'CHANGE_SPECFILE' in env: + env['CHANGE_SPECFILE'](target, source) + + except KeyError, e: + raise SCons.Errors.UserError( '"%s" package field for RPM is missing.' % e.args[0] ) + + +# +# mandatory and optional package tag section +# +def build_specfile_sections(spec): + """ Builds the sections of a rpm specfile. + """ + str = "" + + mandatory_sections = { + 'DESCRIPTION' : '\n%%description\n%s\n\n', } + + str = str + SimpleTagCompiler(mandatory_sections).compile( spec ) + + optional_sections = { + 'DESCRIPTION_' : '%%description -l %s\n%s\n\n', + 'CHANGELOG' : '%%changelog\n%s\n\n', + 'X_RPM_PREINSTALL' : '%%pre\n%s\n\n', + 'X_RPM_POSTINSTALL' : '%%post\n%s\n\n', + 'X_RPM_PREUNINSTALL' : '%%preun\n%s\n\n', + 'X_RPM_POSTUNINSTALL' : '%%postun\n%s\n\n', + 'X_RPM_VERIFY' : '%%verify\n%s\n\n', + + # These are for internal use but could possibly be overriden + 'X_RPM_PREP' : '%%prep\n%s\n\n', + 'X_RPM_BUILD' : '%%build\n%s\n\n', + 'X_RPM_INSTALL' : '%%install\n%s\n\n', + 'X_RPM_CLEAN' : '%%clean\n%s\n\n', + } + + # Default prep, build, install and clean rules + # TODO: optimize those build steps, to not compile the project a second time + if 'X_RPM_PREP' not in spec: + spec['X_RPM_PREP'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' + '\n%setup -q' + + if 'X_RPM_BUILD' not in spec: + spec['X_RPM_BUILD'] = 'mkdir "$RPM_BUILD_ROOT"' + + if 'X_RPM_INSTALL' not in spec: + spec['X_RPM_INSTALL'] = 'scons --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"' + + if 'X_RPM_CLEAN' not in spec: + spec['X_RPM_CLEAN'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' + + str = str + SimpleTagCompiler(optional_sections, mandatory=0).compile( spec ) + + return str + +def build_specfile_header(spec): + """ Builds all section but the %file of a rpm specfile + """ + str = "" + + # first the mandatory sections + mandatory_header_fields = { + 'NAME' : '%%define name %s\nName: %%{name}\n', + 'VERSION' : '%%define version %s\nVersion: %%{version}\n', + 'PACKAGEVERSION' : '%%define release %s\nRelease: %%{release}\n', + 'X_RPM_GROUP' : 'Group: %s\n', + 'SUMMARY' : 'Summary: %s\n', + 'LICENSE' : 'License: %s\n', } + + str = str + SimpleTagCompiler(mandatory_header_fields).compile( spec ) + + # now the optional tags + optional_header_fields = { + 'VENDOR' : 'Vendor: %s\n', + 'X_RPM_URL' : 'Url: %s\n', + 'SOURCE_URL' : 'Source: %s\n', + 'SUMMARY_' : 'Summary(%s): %s\n', + 'X_RPM_DISTRIBUTION' : 'Distribution: %s\n', + 'X_RPM_ICON' : 'Icon: %s\n', + 'X_RPM_PACKAGER' : 'Packager: %s\n', + 'X_RPM_GROUP_' : 'Group(%s): %s\n', + + 'X_RPM_REQUIRES' : 'Requires: %s\n', + 'X_RPM_PROVIDES' : 'Provides: %s\n', + 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', + 'X_RPM_BUILDREQUIRES' : 'BuildRequires: %s\n', + + 'X_RPM_SERIAL' : 'Serial: %s\n', + 'X_RPM_EPOCH' : 'Epoch: %s\n', + 'X_RPM_AUTOREQPROV' : 'AutoReqProv: %s\n', + 'X_RPM_EXCLUDEARCH' : 'ExcludeArch: %s\n', + 'X_RPM_EXCLUSIVEARCH' : 'ExclusiveArch: %s\n', + 'X_RPM_PREFIX' : 'Prefix: %s\n', + 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', + + # internal use + 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', } + + # fill in default values: + # Adding a BuildRequires renders the .rpm unbuildable under System, which + # are not managed by rpm, since the database to resolve this dependency is + # missing (take Gentoo as an example) +# if not s.has_key('x_rpm_BuildRequires'): +# s['x_rpm_BuildRequires'] = 'scons' + + if 'X_RPM_BUILDROOT' not in spec: + spec['X_RPM_BUILDROOT'] = '%{_tmppath}/%{name}-%{version}-%{release}' + + str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec ) + return str + +# +# mandatory and optional file tags +# +def build_specfile_filesection(spec, files): + """ builds the %file section of the specfile + """ + str = '%files\n' + + if 'X_RPM_DEFATTR' not in spec: + spec['X_RPM_DEFATTR'] = '(-,root,root)' + + str = str + '%%defattr %s\n' % spec['X_RPM_DEFATTR'] + + supported_tags = { + 'PACKAGING_CONFIG' : '%%config %s', + 'PACKAGING_CONFIG_NOREPLACE' : '%%config(noreplace) %s', + 'PACKAGING_DOC' : '%%doc %s', + 'PACKAGING_UNIX_ATTR' : '%%attr %s', + 'PACKAGING_LANG_' : '%%lang(%s) %s', + 'PACKAGING_X_RPM_VERIFY' : '%%verify %s', + 'PACKAGING_X_RPM_DIR' : '%%dir %s', + 'PACKAGING_X_RPM_DOCDIR' : '%%docdir %s', + 'PACKAGING_X_RPM_GHOST' : '%%ghost %s', } + + for file in files: + # build the tagset + tags = {} + for k in supported_tags.keys(): + try: + tags[k]=getattr(file, k) + except AttributeError: + pass + + # compile the tagset + str = str + SimpleTagCompiler(supported_tags, mandatory=0).compile( tags ) + + str = str + ' ' + str = str + file.PACKAGING_INSTALL_LOCATION + str = str + '\n\n' + + return str + +class SimpleTagCompiler(object): + """ This class is a simple string substition utility: + the replacement specfication is stored in the tagset dictionary, something + like: + { "abc" : "cdef %s ", + "abc_" : "cdef %s %s" } + + the compile function gets a value dictionary, which may look like: + { "abc" : "ghij", + "abc_gh" : "ij" } + + The resulting string will be: + "cdef ghij cdef gh ij" + """ + def __init__(self, tagset, mandatory=1): + self.tagset = tagset + self.mandatory = mandatory + + def compile(self, values): + """ compiles the tagset and returns a str containing the result + """ + def is_international(tag): + #return tag.endswith('_') + return tag[-1:] == '_' + + def get_country_code(tag): + return tag[-2:] + + def strip_country_code(tag): + return tag[:-2] + + replacements = list(self.tagset.items()) + + str = "" + #domestic = [ (k,v) for k,v in replacements if not is_international(k) ] + domestic = [t for t in replacements if not is_international(t[0])] + for key, replacement in domestic: + try: + str = str + replacement % values[key] + except KeyError, e: + if self.mandatory: + raise e + + #international = [ (k,v) for k,v in replacements if is_international(k) ] + international = [t for t in replacements if is_international(t[0])] + for key, replacement in international: + try: + #int_values_for_key = [ (get_country_code(k),v) for k,v in values.items() if strip_country_code(k) == key ] + x = [t for t in values.items() if strip_country_code(t[0]) == key] + int_values_for_key = [(get_country_code(t[0]),t[1]) for t in x] + for v in int_values_for_key: + str = str + replacement % v + except KeyError, e: + if self.mandatory: + raise e + + return str + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/src_tarbz2.py b/engine/SCons/Tool/packaging/src_tarbz2.py new file mode 100644 index 0000000..af7e9ce --- /dev/null +++ b/engine/SCons/Tool/packaging/src_tarbz2.py @@ -0,0 +1,43 @@ +"""SCons.Tool.Packaging.tarbz2 + +The tarbz2 SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/packaging/src_tarbz2.py 5357 2011/09/09 21:31:03 bdeegan" + +from SCons.Tool.packaging import putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.bz2') + target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) + return bld(env, target, source, TARFLAGS='-jc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/src_targz.py b/engine/SCons/Tool/packaging/src_targz.py new file mode 100644 index 0000000..202afa2 --- /dev/null +++ b/engine/SCons/Tool/packaging/src_targz.py @@ -0,0 +1,43 @@ +"""SCons.Tool.Packaging.targz + +The targz SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/packaging/src_targz.py 5357 2011/09/09 21:31:03 bdeegan" + +from SCons.Tool.packaging import putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) + return bld(env, target, source, TARFLAGS='-zc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/src_zip.py b/engine/SCons/Tool/packaging/src_zip.py new file mode 100644 index 0000000..4efef3d --- /dev/null +++ b/engine/SCons/Tool/packaging/src_zip.py @@ -0,0 +1,43 @@ +"""SCons.Tool.Packaging.zip + +The zip SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/packaging/src_zip.py 5357 2011/09/09 21:31:03 bdeegan" + +from SCons.Tool.packaging import putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Zip'] + bld.set_suffix('.zip') + target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) + return bld(env, target, source) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/tarbz2.py b/engine/SCons/Tool/packaging/tarbz2.py new file mode 100644 index 0000000..3a3cbcb --- /dev/null +++ b/engine/SCons/Tool/packaging/tarbz2.py @@ -0,0 +1,44 @@ +"""SCons.Tool.Packaging.tarbz2 + +The tarbz2 SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/packaging/tarbz2.py 5357 2011/09/09 21:31:03 bdeegan" + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + target, source = stripinstallbuilder(target, source, env) + return bld(env, target, source, TARFLAGS='-jc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/targz.py b/engine/SCons/Tool/packaging/targz.py new file mode 100644 index 0000000..9a742df --- /dev/null +++ b/engine/SCons/Tool/packaging/targz.py @@ -0,0 +1,44 @@ +"""SCons.Tool.Packaging.targz + +The targz SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/packaging/targz.py 5357 2011/09/09 21:31:03 bdeegan" + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + target, source = stripinstallbuilder(target, source, env) + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + return bld(env, target, source, TARFLAGS='-zc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/packaging/zip.py b/engine/SCons/Tool/packaging/zip.py new file mode 100644 index 0000000..9749a6c --- /dev/null +++ b/engine/SCons/Tool/packaging/zip.py @@ -0,0 +1,44 @@ +"""SCons.Tool.Packaging.zip + +The zip SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/packaging/zip.py 5357 2011/09/09 21:31:03 bdeegan" + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Zip'] + bld.set_suffix('.zip') + target, source = stripinstallbuilder(target, source, env) + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + return bld(env, target, source) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/pdf.py b/engine/SCons/Tool/pdf.py new file mode 100644 index 0000000..fb30cb8 --- /dev/null +++ b/engine/SCons/Tool/pdf.py @@ -0,0 +1,78 @@ +"""SCons.Tool.pdf + +Common PDF Builder definition for various other Tool modules that use it. +Add an explicit action to run epstopdf to convert .eps files to .pdf + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/pdf.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Builder +import SCons.Tool + +PDFBuilder = None + +EpsPdfAction = SCons.Action.Action('$EPSTOPDFCOM', '$EPSTOPDFCOMSTR') + +def generate(env): + try: + env['BUILDERS']['PDF'] + except KeyError: + global PDFBuilder + if PDFBuilder is None: + PDFBuilder = SCons.Builder.Builder(action = {}, + source_scanner = SCons.Tool.PDFLaTeXScanner, + prefix = '$PDFPREFIX', + suffix = '$PDFSUFFIX', + emitter = {}, + source_ext_match = None, + single_source=True) + env['BUILDERS']['PDF'] = PDFBuilder + + env['PDFPREFIX'] = '' + env['PDFSUFFIX'] = '.pdf' + +# put the epstopdf builder in this routine so we can add it after +# the pdftex builder so that one is the default for no source suffix +def generate2(env): + bld = env['BUILDERS']['PDF'] + #bld.add_action('.ps', EpsPdfAction) # this is covered by direct Ghostcript action in gs.py + bld.add_action('.eps', EpsPdfAction) + + env['EPSTOPDF'] = 'epstopdf' + env['EPSTOPDFFLAGS'] = SCons.Util.CLVar('') + env['EPSTOPDFCOM'] = '$EPSTOPDF $EPSTOPDFFLAGS ${SOURCE} --outfile=${TARGET}' + +def exists(env): + # This only puts a skeleton Builder in place, so if someone + # references this Tool directly, it's always "available." + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/pdflatex.py b/engine/SCons/Tool/pdflatex.py new file mode 100644 index 0000000..40a120b --- /dev/null +++ b/engine/SCons/Tool/pdflatex.py @@ -0,0 +1,84 @@ +"""SCons.Tool.pdflatex + +Tool-specific initialization for pdflatex. +Generates .pdf files from .latex or .ltx files + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/pdflatex.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Util +import SCons.Tool.pdf +import SCons.Tool.tex + +PDFLaTeXAction = None + +def PDFLaTeXAuxFunction(target = None, source= None, env=None): + result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) + if result != 0: + SCons.Tool.tex.check_file_error_message(env['PDFLATEX']) + return result + +PDFLaTeXAuxAction = None + +def generate(env): + """Add Builders and construction variables for pdflatex to an Environment.""" + global PDFLaTeXAction + if PDFLaTeXAction is None: + PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM', '$PDFLATEXCOMSTR') + + global PDFLaTeXAuxAction + if PDFLaTeXAuxAction is None: + PDFLaTeXAuxAction = SCons.Action.Action(PDFLaTeXAuxFunction, + strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) + + env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['PDF'] + bld.add_action('.ltx', PDFLaTeXAuxAction) + bld.add_action('.latex', PDFLaTeXAuxAction) + bld.add_emitter('.ltx', SCons.Tool.tex.tex_pdf_emitter) + bld.add_emitter('.latex', SCons.Tool.tex.tex_pdf_emitter) + + SCons.Tool.tex.generate_common(env) + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('pdflatex') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/pdftex.py b/engine/SCons/Tool/pdftex.py new file mode 100644 index 0000000..8b72502 --- /dev/null +++ b/engine/SCons/Tool/pdftex.py @@ -0,0 +1,109 @@ +"""SCons.Tool.pdftex + +Tool-specific initialization for pdftex. +Generates .pdf files from .tex files + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/pdftex.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import SCons.Action +import SCons.Util +import SCons.Tool.tex + +PDFTeXAction = None + +# This action might be needed more than once if we are dealing with +# labels and bibtex. +PDFLaTeXAction = None + +def PDFLaTeXAuxAction(target = None, source= None, env=None): + result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) + return result + +def PDFTeXLaTeXFunction(target = None, source= None, env=None): + """A builder for TeX and LaTeX that scans the source file to + decide the "flavor" of the source and then executes the appropriate + program.""" + basedir = os.path.split(str(source[0]))[0] + abspath = os.path.abspath(basedir) + + if SCons.Tool.tex.is_LaTeX(source,env,abspath): + result = PDFLaTeXAuxAction(target,source,env) + if result != 0: + SCons.Tool.tex.check_file_error_message(env['PDFLATEX']) + else: + result = PDFTeXAction(target,source,env) + if result != 0: + SCons.Tool.tex.check_file_error_message(env['PDFTEX']) + return result + +PDFTeXLaTeXAction = None + +def generate(env): + """Add Builders and construction variables for pdftex to an Environment.""" + global PDFTeXAction + if PDFTeXAction is None: + PDFTeXAction = SCons.Action.Action('$PDFTEXCOM', '$PDFTEXCOMSTR') + + global PDFLaTeXAction + if PDFLaTeXAction is None: + PDFLaTeXAction = SCons.Action.Action("$PDFLATEXCOM", "$PDFLATEXCOMSTR") + + global PDFTeXLaTeXAction + if PDFTeXLaTeXAction is None: + PDFTeXLaTeXAction = SCons.Action.Action(PDFTeXLaTeXFunction, + strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) + + env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['PDF'] + bld.add_action('.tex', PDFTeXLaTeXAction) + bld.add_emitter('.tex', SCons.Tool.tex.tex_pdf_emitter) + + # Add the epstopdf builder after the pdftex builder + # so pdftex is the default for no source suffix + pdf.generate2(env) + + SCons.Tool.tex.generate_common(env) + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('pdftex') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/qt.py b/engine/SCons/Tool/qt.py new file mode 100644 index 0000000..d0ff108 --- /dev/null +++ b/engine/SCons/Tool/qt.py @@ -0,0 +1,336 @@ + +"""SCons.Tool.qt + +Tool-specific initialization for Qt. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/qt.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path +import re + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Scanner +import SCons.Tool +import SCons.Util + +class ToolQtWarning(SCons.Warnings.Warning): + pass + +class GeneratedMocFileNotIncluded(ToolQtWarning): + pass + +class QtdirNotFound(ToolQtWarning): + pass + +SCons.Warnings.enableWarningClass(ToolQtWarning) + +header_extensions = [".h", ".hxx", ".hpp", ".hh"] +if SCons.Util.case_sensitive_suffixes('.h', '.H'): + header_extensions.append('.H') +cplusplus = __import__('c++', globals(), locals(), []) +cxx_suffixes = cplusplus.CXXSuffixes + +def checkMocIncluded(target, source, env): + moc = target[0] + cpp = source[0] + # looks like cpp.includes is cleared before the build stage :-( + # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ + path = SCons.Defaults.CScan.path(env, moc.cwd) + includes = SCons.Defaults.CScan(cpp, env, path) + if not moc in includes: + SCons.Warnings.warn( + GeneratedMocFileNotIncluded, + "Generated moc file '%s' is not included by '%s'" % + (str(moc), str(cpp))) + +def find_file(filename, paths, node_factory): + for dir in paths: + node = node_factory(filename, dir) + if node.rexists(): + return node + return None + +class _Automoc(object): + """ + Callable class, which works as an emitter for Programs, SharedLibraries and + StaticLibraries. + """ + + def __init__(self, objBuilderName): + self.objBuilderName = objBuilderName + + def __call__(self, target, source, env): + """ + Smart autoscan function. Gets the list of objects for the Program + or Lib. Adds objects and builders for the special qt files. + """ + try: + if int(env.subst('$QT_AUTOSCAN')) == 0: + return target, source + except ValueError: + pass + try: + debug = int(env.subst('$QT_DEBUG')) + except ValueError: + debug = 0 + + # some shortcuts used in the scanner + splitext = SCons.Util.splitext + objBuilder = getattr(env, self.objBuilderName) + + # some regular expressions: + # Q_OBJECT detection + q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') + # cxx and c comment 'eater' + #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') + # CW: something must be wrong with the regexp. See also bug #998222 + # CURRENTLY THERE IS NO TEST CASE FOR THAT + + # The following is kind of hacky to get builders working properly (FIXME) + objBuilderEnv = objBuilder.env + objBuilder.env = env + mocBuilderEnv = env.Moc.env + env.Moc.env = env + + # make a deep copy for the result; MocH objects will be appended + out_sources = source[:] + + for obj in source: + if not obj.has_builder(): + # binary obj file provided + if debug: + print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) + continue + cpp = obj.sources[0] + if not splitext(str(cpp))[1] in cxx_suffixes: + if debug: + print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) + # c or fortran source + continue + #cpp_contents = comment.sub('', cpp.get_text_contents()) + cpp_contents = cpp.get_text_contents() + h=None + for h_ext in header_extensions: + # try to find the header file in the corresponding source + # directory + hname = splitext(cpp.name)[0] + h_ext + h = find_file(hname, (cpp.get_dir(),), env.File) + if h: + if debug: + print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) + #h_contents = comment.sub('', h.get_text_contents()) + h_contents = h.get_text_contents() + break + if not h and debug: + print "scons: qt: no header for '%s'." % (str(cpp)) + if h and q_object_search.search(h_contents): + # h file with the Q_OBJECT macro found -> add moc_cpp + moc_cpp = env.Moc(h) + moc_o = objBuilder(moc_cpp) + out_sources.append(moc_o) + #moc_cpp.target_scanner = SCons.Defaults.CScan + if debug: + print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) + if cpp and q_object_search.search(cpp_contents): + # cpp file with Q_OBJECT macro found -> add moc + # (to be included in cpp) + moc = env.Moc(cpp) + env.Ignore(moc, moc) + if debug: + print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) + #moc.source_scanner = SCons.Defaults.CScan + # restore the original env attributes (FIXME) + objBuilder.env = objBuilderEnv + env.Moc.env = mocBuilderEnv + + return (target, out_sources) + +AutomocShared = _Automoc('SharedObject') +AutomocStatic = _Automoc('StaticObject') + +def _detect(env): + """Not really safe, but fast method to detect the QT library""" + QTDIR = None + if not QTDIR: + QTDIR = env.get('QTDIR',None) + if not QTDIR: + QTDIR = os.environ.get('QTDIR',None) + if not QTDIR: + moc = env.WhereIs('moc') + if moc: + QTDIR = os.path.dirname(os.path.dirname(moc)) + SCons.Warnings.warn( + QtdirNotFound, + "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR) + else: + QTDIR = None + SCons.Warnings.warn( + QtdirNotFound, + "Could not detect qt, using empty QTDIR") + return QTDIR + +def uicEmitter(target, source, env): + adjustixes = SCons.Util.adjustixes + bs = SCons.Util.splitext(str(source[0].name))[0] + bs = os.path.join(str(target[0].get_dir()),bs) + # first target (header) is automatically added by builder + if len(target) < 2: + # second target is implementation + target.append(adjustixes(bs, + env.subst('$QT_UICIMPLPREFIX'), + env.subst('$QT_UICIMPLSUFFIX'))) + if len(target) < 3: + # third target is moc file + target.append(adjustixes(bs, + env.subst('$QT_MOCHPREFIX'), + env.subst('$QT_MOCHSUFFIX'))) + return target, source + +def uicScannerFunc(node, env, path): + lookout = [] + lookout.extend(env['CPPPATH']) + lookout.append(str(node.rfile().dir)) + includes = re.findall("(.*?)", node.get_text_contents()) + result = [] + for incFile in includes: + dep = env.FindFile(incFile,lookout) + if dep: + result.append(dep) + return result + +uicScanner = SCons.Scanner.Base(uicScannerFunc, + name = "UicScanner", + node_class = SCons.Node.FS.File, + node_factory = SCons.Node.FS.File, + recursive = 0) + +def generate(env): + """Add Builders and construction variables for qt to an Environment.""" + CLVar = SCons.Util.CLVar + Action = SCons.Action.Action + Builder = SCons.Builder.Builder + + env.SetDefault(QTDIR = _detect(env), + QT_BINPATH = os.path.join('$QTDIR', 'bin'), + QT_CPPPATH = os.path.join('$QTDIR', 'include'), + QT_LIBPATH = os.path.join('$QTDIR', 'lib'), + QT_MOC = os.path.join('$QT_BINPATH','moc'), + QT_UIC = os.path.join('$QT_BINPATH','uic'), + QT_LIB = 'qt', # may be set to qt-mt + + QT_AUTOSCAN = 1, # scan for moc'able sources + + # Some QT specific flags. I don't expect someone wants to + # manipulate those ... + QT_UICIMPLFLAGS = CLVar(''), + QT_UICDECLFLAGS = CLVar(''), + QT_MOCFROMHFLAGS = CLVar(''), + QT_MOCFROMCXXFLAGS = CLVar('-i'), + + # suffixes/prefixes for the headers / sources to generate + QT_UICDECLPREFIX = '', + QT_UICDECLSUFFIX = '.h', + QT_UICIMPLPREFIX = 'uic_', + QT_UICIMPLSUFFIX = '$CXXFILESUFFIX', + QT_MOCHPREFIX = 'moc_', + QT_MOCHSUFFIX = '$CXXFILESUFFIX', + QT_MOCCXXPREFIX = '', + QT_MOCCXXSUFFIX = '.moc', + QT_UISUFFIX = '.ui', + + # Commands for the qt support ... + # command to generate header, implementation and moc-file + # from a .ui file + QT_UICCOM = [ + CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'), + CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} ' + '-o ${TARGETS[1]} $SOURCE'), + CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')], + # command to generate meta object information for a class + # declarated in a header + QT_MOCFROMHCOM = ( + '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'), + # command to generate meta object information for a class + # declarated in a cpp file + QT_MOCFROMCXXCOM = [ + CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'), + Action(checkMocIncluded,None)]) + + # ... and the corresponding builders + uicBld = Builder(action=SCons.Action.Action('$QT_UICCOM', '$QT_UICCOMSTR'), + emitter=uicEmitter, + src_suffix='$QT_UISUFFIX', + suffix='$QT_UICDECLSUFFIX', + prefix='$QT_UICDECLPREFIX', + source_scanner=uicScanner) + mocBld = Builder(action={}, prefix={}, suffix={}) + for h in header_extensions: + act = SCons.Action.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR') + mocBld.add_action(h, act) + mocBld.prefix[h] = '$QT_MOCHPREFIX' + mocBld.suffix[h] = '$QT_MOCHSUFFIX' + for cxx in cxx_suffixes: + act = SCons.Action.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR') + mocBld.add_action(cxx, act) + mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX' + mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' + + # register the builders + env['BUILDERS']['Uic'] = uicBld + env['BUILDERS']['Moc'] = mocBld + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + static_obj.add_src_builder('Uic') + shared_obj.add_src_builder('Uic') + + # We use the emitters of Program / StaticLibrary / SharedLibrary + # to scan for moc'able files + # We can't refer to the builders directly, we have to fetch them + # as Environment attributes because that sets them up to be called + # correctly later by our emitter. + env.AppendUnique(PROGEMITTER =[AutomocStatic], + SHLIBEMITTER=[AutomocShared], + LIBEMITTER =[AutomocStatic], + # Of course, we need to link against the qt libraries + CPPPATH=["$QT_CPPPATH"], + LIBPATH=["$QT_LIBPATH"], + LIBS=['$QT_LIB']) + +def exists(env): + return _detect(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/rmic.py b/engine/SCons/Tool/rmic.py new file mode 100644 index 0000000..84266a7 --- /dev/null +++ b/engine/SCons/Tool/rmic.py @@ -0,0 +1,126 @@ +"""SCons.Tool.rmic + +Tool-specific initialization for rmic. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/rmic.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Action +import SCons.Builder +import SCons.Node.FS +import SCons.Util + +def emit_rmic_classes(target, source, env): + """Create and return lists of Java RMI stub and skeleton + class files to be created from a set of class files. + """ + class_suffix = env.get('JAVACLASSSUFFIX', '.class') + classdir = env.get('JAVACLASSDIR') + + if not classdir: + try: + s = source[0] + except IndexError: + classdir = '.' + else: + try: + classdir = s.attributes.java_classdir + except AttributeError: + classdir = '.' + classdir = env.Dir(classdir).rdir() + if str(classdir) == '.': + c_ = None + else: + c_ = str(classdir) + os.sep + + slist = [] + for src in source: + try: + classname = src.attributes.java_classname + except AttributeError: + classname = str(src) + if c_ and classname[:len(c_)] == c_: + classname = classname[len(c_):] + if class_suffix and classname[:-len(class_suffix)] == class_suffix: + classname = classname[-len(class_suffix):] + s = src.rfile() + s.attributes.java_classdir = classdir + s.attributes.java_classname = classname + slist.append(s) + + stub_suffixes = ['_Stub'] + if env.get('JAVAVERSION') == '1.4': + stub_suffixes.append('_Skel') + + tlist = [] + for s in source: + for suff in stub_suffixes: + fname = s.attributes.java_classname.replace('.', os.sep) + \ + suff + class_suffix + t = target[0].File(fname) + t.attributes.java_lookupdir = target[0] + tlist.append(t) + + return tlist, source + +RMICAction = SCons.Action.Action('$RMICCOM', '$RMICCOMSTR') + +RMICBuilder = SCons.Builder.Builder(action = RMICAction, + emitter = emit_rmic_classes, + src_suffix = '$JAVACLASSSUFFIX', + target_factory = SCons.Node.FS.Dir, + source_factory = SCons.Node.FS.File) + +def generate(env): + """Add Builders and construction variables for rmic to an Environment.""" + env['BUILDERS']['RMIC'] = RMICBuilder + + env['RMIC'] = 'rmic' + env['RMICFLAGS'] = SCons.Util.CLVar('') + env['RMICCOM'] = '$RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}' + env['JAVACLASSSUFFIX'] = '.class' + +def exists(env): + # As reported by Jan Nijtmans in issue #2730, the simple + # return env.Detect('rmic') + # doesn't always work during initialization. For now, we + # stop trying to detect an executable (analogous to the + # javac Builder). + # TODO: Come up with a proper detect() routine...and enable it. + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/rpcgen.py b/engine/SCons/Tool/rpcgen.py new file mode 100644 index 0000000..76f98f2 --- /dev/null +++ b/engine/SCons/Tool/rpcgen.py @@ -0,0 +1,70 @@ +"""SCons.Tool.rpcgen + +Tool-specific initialization for RPCGEN tools. + +Three normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/rpcgen.py 5357 2011/09/09 21:31:03 bdeegan" + +from SCons.Builder import Builder +import SCons.Util + +cmd = "cd ${SOURCE.dir} && $RPCGEN -%s $RPCGENFLAGS %s -o ${TARGET.abspath} ${SOURCE.file}" + +rpcgen_client = cmd % ('l', '$RPCGENCLIENTFLAGS') +rpcgen_header = cmd % ('h', '$RPCGENHEADERFLAGS') +rpcgen_service = cmd % ('m', '$RPCGENSERVICEFLAGS') +rpcgen_xdr = cmd % ('c', '$RPCGENXDRFLAGS') + +def generate(env): + "Add RPCGEN Builders and construction variables for an Environment." + + client = Builder(action=rpcgen_client, suffix='_clnt.c', src_suffix='.x') + header = Builder(action=rpcgen_header, suffix='.h', src_suffix='.x') + service = Builder(action=rpcgen_service, suffix='_svc.c', src_suffix='.x') + xdr = Builder(action=rpcgen_xdr, suffix='_xdr.c', src_suffix='.x') + env.Append(BUILDERS={'RPCGenClient' : client, + 'RPCGenHeader' : header, + 'RPCGenService' : service, + 'RPCGenXDR' : xdr}) + env['RPCGEN'] = 'rpcgen' + env['RPCGENFLAGS'] = SCons.Util.CLVar('') + env['RPCGENCLIENTFLAGS'] = SCons.Util.CLVar('') + env['RPCGENHEADERFLAGS'] = SCons.Util.CLVar('') + env['RPCGENSERVICEFLAGS'] = SCons.Util.CLVar('') + env['RPCGENXDRFLAGS'] = SCons.Util.CLVar('') + +def exists(env): + return env.Detect('rpcgen') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/rpm.py b/engine/SCons/Tool/rpm.py new file mode 100644 index 0000000..f6ae365 --- /dev/null +++ b/engine/SCons/Tool/rpm.py @@ -0,0 +1,132 @@ +"""SCons.Tool.rpm + +Tool-specific initialization for rpm. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +The rpm tool calls the rpmbuild command. The first and only argument should a +tar.gz consisting of the source file and a specfile. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/rpm.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import re +import shutil +import subprocess + +import SCons.Builder +import SCons.Node.FS +import SCons.Util +import SCons.Action +import SCons.Defaults + +def get_cmd(source, env): + tar_file_with_included_specfile = source + if SCons.Util.is_List(source): + tar_file_with_included_specfile = source[0] + return "%s %s %s"%(env['RPM'], env['RPMFLAGS'], + tar_file_with_included_specfile.abspath ) + +def build_rpm(target, source, env): + # create a temporary rpm build root. + tmpdir = os.path.join( os.path.dirname( target[0].abspath ), 'rpmtemp' ) + if os.path.exists(tmpdir): + shutil.rmtree(tmpdir) + + # now create the mandatory rpm directory structure. + for d in ['RPMS', 'SRPMS', 'SPECS', 'BUILD']: + os.makedirs( os.path.join( tmpdir, d ) ) + + # set the topdir as an rpmflag. + env.Prepend( RPMFLAGS = '--define \'_topdir %s\'' % tmpdir ) + + # now call rpmbuild to create the rpm package. + handle = subprocess.Popen(get_cmd(source, env), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True) + output = handle.stdout.read() + status = handle.wait() + + if status: + raise SCons.Errors.BuildError( node=target[0], + errstr=output, + filename=str(target[0]) ) + else: + # XXX: assume that LC_ALL=c is set while running rpmbuild + output_files = re.compile( 'Wrote: (.*)' ).findall( output ) + + for output, input in zip( output_files, target ): + rpm_output = os.path.basename(output) + expected = os.path.basename(input.get_path()) + + assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected) + shutil.copy( output, input.abspath ) + + + # cleanup before leaving. + shutil.rmtree(tmpdir) + + return status + +def string_rpm(target, source, env): + try: + return env['RPMCOMSTR'] + except KeyError: + return get_cmd(source, env) + +rpmAction = SCons.Action.Action(build_rpm, string_rpm) + +RpmBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$RPMCOM', '$RPMCOMSTR'), + source_scanner = SCons.Defaults.DirScanner, + suffix = '$RPMSUFFIX') + + + +def generate(env): + """Add Builders and construction variables for rpm to an Environment.""" + try: + bld = env['BUILDERS']['Rpm'] + except KeyError: + bld = RpmBuilder + env['BUILDERS']['Rpm'] = bld + + env.SetDefault(RPM = 'LC_ALL=c rpmbuild') + env.SetDefault(RPMFLAGS = SCons.Util.CLVar('-ta')) + env.SetDefault(RPMCOM = rpmAction) + env.SetDefault(RPMSUFFIX = '.rpm') + +def exists(env): + return env.Detect('rpmbuild') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sgiar.py b/engine/SCons/Tool/sgiar.py new file mode 100644 index 0000000..3dcbdaf --- /dev/null +++ b/engine/SCons/Tool/sgiar.py @@ -0,0 +1,68 @@ +"""SCons.Tool.sgiar + +Tool-specific initialization for SGI ar (library archive). If CC +exists, static libraries should be built with it, so the prelinker has +a chance to resolve C++ template instantiations. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sgiar.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + if env.Detect('CC'): + env['AR'] = 'CC' + env['ARFLAGS'] = SCons.Util.CLVar('-ar') + env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' + else: + env['AR'] = 'ar' + env['ARFLAGS'] = SCons.Util.CLVar('r') + env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + +def exists(env): + return env.Detect('CC') or env.Detect('ar') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sgic++.py b/engine/SCons/Tool/sgic++.py new file mode 100644 index 0000000..e67974d --- /dev/null +++ b/engine/SCons/Tool/sgic++.py @@ -0,0 +1,58 @@ +"""SCons.Tool.sgic++ + +Tool-specific initialization for MIPSpro C++ on SGI. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sgic++.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +def generate(env): + """Add Builders and construction variables for SGI MIPS C++ to an Environment.""" + + cplusplus.generate(env) + + env['CXX'] = 'CC' + env['CXXFLAGS'] = SCons.Util.CLVar('-LANG:std') + env['SHCXX'] = '$CXX' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + +def exists(env): + return env.Detect('CC') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sgicc.py b/engine/SCons/Tool/sgicc.py new file mode 100644 index 0000000..3f8af89 --- /dev/null +++ b/engine/SCons/Tool/sgicc.py @@ -0,0 +1,53 @@ +"""SCons.Tool.sgicc + +Tool-specific initialization for MIPSPro cc on SGI. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sgicc.py 5357 2011/09/09 21:31:03 bdeegan" + +import cc + +def generate(env): + """Add Builders and construction variables for gcc to an Environment.""" + cc.generate(env) + + env['CXX'] = 'CC' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + +def exists(env): + return env.Detect('cc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sgilink.py b/engine/SCons/Tool/sgilink.py new file mode 100644 index 0000000..cb6658f --- /dev/null +++ b/engine/SCons/Tool/sgilink.py @@ -0,0 +1,62 @@ +"""SCons.Tool.sgilink + +Tool-specific initialization for the SGI MIPSPro linker on SGI. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sgilink.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +import link + +linkers = ['CC', 'cc'] + +def generate(env): + """Add Builders and construction variables for MIPSPro to an Environment.""" + link.generate(env) + + env['LINK'] = env.Detect(linkers) or 'cc' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + + # __RPATH is set to $_RPATH in the platform specification if that + # platform supports it. + env['RPATHPREFIX'] = '-rpath ' + env['RPATHSUFFIX'] = '' + env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + +def exists(env): + return env.Detect(linkers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sunar.py b/engine/SCons/Tool/sunar.py new file mode 100644 index 0000000..32641e0 --- /dev/null +++ b/engine/SCons/Tool/sunar.py @@ -0,0 +1,67 @@ +"""engine.SCons.Tool.sunar + +Tool-specific initialization for Solaris (Forte) ar (library archive). If CC +exists, static libraries should be built with it, so that template +instantians can be resolved. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sunar.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + if env.Detect('CC'): + env['AR'] = 'CC' + env['ARFLAGS'] = SCons.Util.CLVar('-xar') + env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' + else: + env['AR'] = 'ar' + env['ARFLAGS'] = SCons.Util.CLVar('r') + env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') + env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + +def exists(env): + return env.Detect('CC') or env.Detect('ar') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sunc++.py b/engine/SCons/Tool/sunc++.py new file mode 100644 index 0000000..de404da --- /dev/null +++ b/engine/SCons/Tool/sunc++.py @@ -0,0 +1,142 @@ +"""SCons.Tool.sunc++ + +Tool-specific initialization for C++ on SunOS / Solaris. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sunc++.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons + +import os +import re +import subprocess + +cplusplus = __import__('c++', globals(), locals(), []) + +package_info = {} + +def get_package_info(package_name, pkginfo, pkgchk): + try: + return package_info[package_name] + except KeyError: + version = None + pathname = None + try: + sadm_contents = open('/var/sadm/install/contents', 'r').read() + except EnvironmentError: + pass + else: + sadm_re = re.compile('^(\S*/bin/CC)(=\S*)? %s$' % package_name, re.M) + sadm_match = sadm_re.search(sadm_contents) + if sadm_match: + pathname = os.path.dirname(sadm_match.group(1)) + + try: + p = subprocess.Popen([pkginfo, '-l', package_name], + stdout=subprocess.PIPE, + stderr=open('/dev/null', 'w')) + except EnvironmentError: + pass + else: + pkginfo_contents = p.communicate()[0] + version_re = re.compile('^ *VERSION:\s*(.*)$', re.M) + version_match = version_re.search(pkginfo_contents) + if version_match: + version = version_match.group(1) + + if pathname is None: + try: + p = subprocess.Popen([pkgchk, '-l', package_name], + stdout=subprocess.PIPE, + stderr=open('/dev/null', 'w')) + except EnvironmentError: + pass + else: + pkgchk_contents = p.communicate()[0] + pathname_re = re.compile(r'^Pathname:\s*(.*/bin/CC)$', re.M) + pathname_match = pathname_re.search(pkgchk_contents) + if pathname_match: + pathname = os.path.dirname(pathname_match.group(1)) + + package_info[package_name] = (pathname, version) + return package_info[package_name] + +# use the package installer tool lslpp to figure out where cppc and what +# version of it is installed +def get_cppc(env): + cxx = env.subst('$CXX') + if cxx: + cppcPath = os.path.dirname(cxx) + else: + cppcPath = None + + cppcVersion = None + + pkginfo = env.subst('$PKGINFO') + pkgchk = env.subst('$PKGCHK') + + for package in ['SPROcpl']: + path, version = get_package_info(package, pkginfo, pkgchk) + if path and version: + cppcPath, cppcVersion = path, version + break + + return (cppcPath, 'CC', 'CC', cppcVersion) + +def generate(env): + """Add Builders and construction variables for SunPRO C++.""" + path, cxx, shcxx, version = get_cppc(env) + if path: + cxx = os.path.join(path, cxx) + shcxx = os.path.join(path, shcxx) + + cplusplus.generate(env) + + env['CXX'] = cxx + env['SHCXX'] = shcxx + env['CXXVERSION'] = version + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -KPIC') + env['SHOBJPREFIX'] = 'so_' + env['SHOBJSUFFIX'] = '.o' + +def exists(env): + path, cxx, shcxx, version = get_cppc(env) + if path and cxx: + cppc = os.path.join(path, cxx) + if os.path.exists(cppc): + return cppc + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/suncc.py b/engine/SCons/Tool/suncc.py new file mode 100644 index 0000000..b937dc4 --- /dev/null +++ b/engine/SCons/Tool/suncc.py @@ -0,0 +1,58 @@ +"""SCons.Tool.suncc + +Tool-specific initialization for Sun Solaris (Forte) CC and cc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/suncc.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +import cc + +def generate(env): + """ + Add Builders and construction variables for Forte C and C++ compilers + to an Environment. + """ + cc.generate(env) + + env['CXX'] = 'CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -KPIC') + env['SHOBJPREFIX'] = 'so_' + env['SHOBJSUFFIX'] = '.o' + +def exists(env): + return env.Detect('CC') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sunf77.py b/engine/SCons/Tool/sunf77.py new file mode 100644 index 0000000..4f22d56 --- /dev/null +++ b/engine/SCons/Tool/sunf77.py @@ -0,0 +1,63 @@ +"""SCons.Tool.sunf77 + +Tool-specific initialization for sunf77, the Sun Studio F77 compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sunf77.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +from FortranCommon import add_all_to_env + +compilers = ['sunf77', 'f77'] + +def generate(env): + """Add Builders and construction variables for sunf77 to an Environment.""" + add_all_to_env(env) + + fcomp = env.Detect(compilers) or 'f77' + env['FORTRAN'] = fcomp + env['F77'] = fcomp + + env['SHFORTRAN'] = '$FORTRAN' + env['SHF77'] = '$F77' + + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') + env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -KPIC') + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sunf90.py b/engine/SCons/Tool/sunf90.py new file mode 100644 index 0000000..f080085 --- /dev/null +++ b/engine/SCons/Tool/sunf90.py @@ -0,0 +1,64 @@ +"""SCons.Tool.sunf90 + +Tool-specific initialization for sunf90, the Sun Studio F90 compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sunf90.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +from FortranCommon import add_all_to_env + +compilers = ['sunf90', 'f90'] + +def generate(env): + """Add Builders and construction variables for sun f90 compiler to an + Environment.""" + add_all_to_env(env) + + fcomp = env.Detect(compilers) or 'f90' + env['FORTRAN'] = fcomp + env['F90'] = fcomp + + env['SHFORTRAN'] = '$FORTRAN' + env['SHF90'] = '$F90' + + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') + env['SHF90FLAGS'] = SCons.Util.CLVar('$F90FLAGS -KPIC') + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sunf95.py b/engine/SCons/Tool/sunf95.py new file mode 100644 index 0000000..3e4351e --- /dev/null +++ b/engine/SCons/Tool/sunf95.py @@ -0,0 +1,64 @@ +"""SCons.Tool.sunf95 + +Tool-specific initialization for sunf95, the Sun Studio F95 compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sunf95.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Util + +from FortranCommon import add_all_to_env + +compilers = ['sunf95', 'f95'] + +def generate(env): + """Add Builders and construction variables for sunf95 to an + Environment.""" + add_all_to_env(env) + + fcomp = env.Detect(compilers) or 'f95' + env['FORTRAN'] = fcomp + env['F95'] = fcomp + + env['SHFORTRAN'] = '$FORTRAN' + env['SHF95'] = '$F95' + + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') + env['SHF95FLAGS'] = SCons.Util.CLVar('$F95FLAGS -KPIC') + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/sunlink.py b/engine/SCons/Tool/sunlink.py new file mode 100644 index 0000000..3952baa --- /dev/null +++ b/engine/SCons/Tool/sunlink.py @@ -0,0 +1,76 @@ +"""SCons.Tool.sunlink + +Tool-specific initialization for the Sun Solaris (Forte) linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/sunlink.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import os.path + +import SCons.Util + +import link + +ccLinker = None + +# search for the acc compiler and linker front end + +try: + dirs = os.listdir('/opt') +except (IOError, OSError): + # Not being able to read the directory because it doesn't exist + # (IOError) or isn't readable (OSError) is okay. + dirs = [] + +for d in dirs: + linker = '/opt/' + d + '/bin/CC' + if os.path.exists(linker): + ccLinker = linker + break + +def generate(env): + """Add Builders and construction variables for Forte to an Environment.""" + link.generate(env) + + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') + + env['RPATHPREFIX'] = '-R' + env['RPATHSUFFIX'] = '' + env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + +def exists(env): + return ccLinker + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/swig.py b/engine/SCons/Tool/swig.py new file mode 100644 index 0000000..c773cd5 --- /dev/null +++ b/engine/SCons/Tool/swig.py @@ -0,0 +1,183 @@ +"""SCons.Tool.swig + +Tool-specific initialization for swig. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/swig.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path +import re +import subprocess + +import SCons.Action +import SCons.Defaults +import SCons.Scanner +import SCons.Tool +import SCons.Util + +SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR') + +def swigSuffixEmitter(env, source): + if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS", source=source)): + return '$SWIGCXXFILESUFFIX' + else: + return '$SWIGCFILESUFFIX' + +# Match '%module test', as well as '%module(directors="1") test' +# Also allow for test to be quoted (SWIG permits double quotes, but not single) +# Also allow for the line to have spaces after test if not quoted +_reModule = re.compile(r'%module(\s*\(.*\))?\s+("?)(\S+)\2') + +def _find_modules(src): + """Find all modules referenced by %module lines in `src`, a SWIG .i file. + Returns a list of all modules, and a flag set if SWIG directors have + been requested (SWIG will generate an additional header file in this + case.)""" + directors = 0 + mnames = [] + try: + matches = _reModule.findall(open(src).read()) + except IOError: + # If the file's not yet generated, guess the module name from the file stem + matches = [] + mnames.append(os.path.splitext(os.path.basename(src))[0]) + + for m in matches: + mnames.append(m[2]) + directors = directors or m[0].find('directors') >= 0 + return mnames, directors + +def _add_director_header_targets(target, env): + # Directors only work with C++ code, not C + suffix = env.subst(env['SWIGCXXFILESUFFIX']) + # For each file ending in SWIGCXXFILESUFFIX, add a new target director + # header by replacing the ending with SWIGDIRECTORSUFFIX. + for x in target[:]: + n = x.name + d = x.dir + if n[-len(suffix):] == suffix: + target.append(d.File(n[:-len(suffix)] + env['SWIGDIRECTORSUFFIX'])) + +def _swigEmitter(target, source, env): + swigflags = env.subst("$SWIGFLAGS", target=target, source=source) + flags = SCons.Util.CLVar(swigflags) + for src in source: + src = str(src.rfile()) + mnames = None + if "-python" in flags and "-noproxy" not in flags: + if mnames is None: + mnames, directors = _find_modules(src) + if directors: + _add_director_header_targets(target, env) + python_files = [m + ".py" for m in mnames] + outdir = env.subst('$SWIGOUTDIR', target=target, source=source) + # .py files should be generated in SWIGOUTDIR if specified, + # otherwise in the same directory as the target + if outdir: + python_files = [env.fs.File(os.path.join(outdir, j)) for j in python_files] + else: + python_files = [target[0].dir.File(m) for m in python_files] + target.extend(python_files) + if "-java" in flags: + if mnames is None: + mnames, directors = _find_modules(src) + if directors: + _add_director_header_targets(target, env) + java_files = [[m + ".java", m + "JNI.java"] for m in mnames] + java_files = SCons.Util.flatten(java_files) + outdir = env.subst('$SWIGOUTDIR', target=target, source=source) + if outdir: + java_files = [os.path.join(outdir, j) for j in java_files] + java_files = list(map(env.fs.File, java_files)) + for jf in java_files: + t_from_s = lambda t, p, s, x: t.dir + SCons.Util.AddMethod(jf, t_from_s, 'target_from_source') + target.extend(java_files) + return (target, source) + +def _get_swig_version(env): + """Run the SWIG command line tool to get and return the version number""" + pipe = SCons.Action._subproc(env, [env['SWIG'], '-version'], + stdin = 'devnull', + stderr = 'devnull', + stdout = subprocess.PIPE) + if pipe.wait() != 0: return + + out = pipe.stdout.read() + match = re.search(r'SWIG Version\s+(\S+)$', out, re.MULTILINE) + if match: + return match.group(1) + +def generate(env): + """Add Builders and construction variables for swig to an Environment.""" + c_file, cxx_file = SCons.Tool.createCFileBuilders(env) + + c_file.suffix['.i'] = swigSuffixEmitter + cxx_file.suffix['.i'] = swigSuffixEmitter + + c_file.add_action('.i', SwigAction) + c_file.add_emitter('.i', _swigEmitter) + cxx_file.add_action('.i', SwigAction) + cxx_file.add_emitter('.i', _swigEmitter) + + java_file = SCons.Tool.CreateJavaFileBuilder(env) + + java_file.suffix['.i'] = swigSuffixEmitter + + java_file.add_action('.i', SwigAction) + java_file.add_emitter('.i', _swigEmitter) + + env['SWIG'] = 'swig' + env['SWIGVERSION'] = _get_swig_version(env) + env['SWIGFLAGS'] = SCons.Util.CLVar('') + env['SWIGDIRECTORSUFFIX'] = '_wrap.h' + env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' + env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX' + env['_SWIGOUTDIR'] = r'${"-outdir \"%s\"" % SWIGOUTDIR}' + env['SWIGPATH'] = [] + env['SWIGINCPREFIX'] = '-I' + env['SWIGINCSUFFIX'] = '' + env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES' + + expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' + scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr) + + env.Append(SCANNERS = scanner) + +def exists(env): + return env.Detect(['swig']) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/tar.py b/engine/SCons/Tool/tar.py new file mode 100644 index 0000000..774312c --- /dev/null +++ b/engine/SCons/Tool/tar.py @@ -0,0 +1,73 @@ +"""SCons.Tool.tar + +Tool-specific initialization for tar. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/tar.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Node.FS +import SCons.Util + +tars = ['tar', 'gtar'] + +TarAction = SCons.Action.Action('$TARCOM', '$TARCOMSTR') + +TarBuilder = SCons.Builder.Builder(action = TarAction, + source_factory = SCons.Node.FS.Entry, + source_scanner = SCons.Defaults.DirScanner, + suffix = '$TARSUFFIX', + multi = 1) + + +def generate(env): + """Add Builders and construction variables for tar to an Environment.""" + try: + bld = env['BUILDERS']['Tar'] + except KeyError: + bld = TarBuilder + env['BUILDERS']['Tar'] = bld + + env['TAR'] = env.Detect(tars) or 'gtar' + env['TARFLAGS'] = SCons.Util.CLVar('-c') + env['TARCOM'] = '$TAR $TARFLAGS -f $TARGET $SOURCES' + env['TARSUFFIX'] = '.tar' + +def exists(env): + return env.Detect(tars) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/tex.py b/engine/SCons/Tool/tex.py new file mode 100644 index 0000000..79da6be --- /dev/null +++ b/engine/SCons/Tool/tex.py @@ -0,0 +1,866 @@ +"""SCons.Tool.tex + +Tool-specific initialization for TeX. +Generates .dvi files from .tex files + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/tex.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path +import re +import shutil +import sys +import platform +import glob + +import SCons.Action +import SCons.Node +import SCons.Node.FS +import SCons.Util +import SCons.Scanner.LaTeX + +Verbose = False + +must_rerun_latex = True + +# these are files that just need to be checked for changes and then rerun latex +check_suffixes = ['.toc', '.lof', '.lot', '.out', '.nav', '.snm'] + +# these are files that require bibtex or makeindex to be run when they change +all_suffixes = check_suffixes + ['.bbl', '.idx', '.nlo', '.glo', '.acn'] + +# +# regular expressions used to search for Latex features +# or outputs that require rerunning latex +# +# search for all .aux files opened by latex (recorded in the .fls file) +openout_aux_re = re.compile(r"INPUT *(.*\.aux)") + +#printindex_re = re.compile(r"^[^%]*\\printindex", re.MULTILINE) +#printnomenclature_re = re.compile(r"^[^%]*\\printnomenclature", re.MULTILINE) +#printglossary_re = re.compile(r"^[^%]*\\printglossary", re.MULTILINE) + +# search to find rerun warnings +warning_rerun_str = '(^LaTeX Warning:.*Rerun)|(^Package \w+ Warning:.*Rerun)' +warning_rerun_re = re.compile(warning_rerun_str, re.MULTILINE) + +# search to find citation rerun warnings +rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct" +rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE) + +# search to find undefined references or citations warnings +undefined_references_str = '(^LaTeX Warning:.*undefined references)|(^Package \w+ Warning:.*undefined citations)' +undefined_references_re = re.compile(undefined_references_str, re.MULTILINE) + +# used by the emitter +auxfile_re = re.compile(r".", re.MULTILINE) +tableofcontents_re = re.compile(r"^[^%\n]*\\tableofcontents", re.MULTILINE) +makeindex_re = re.compile(r"^[^%\n]*\\makeindex", re.MULTILINE) +bibliography_re = re.compile(r"^[^%\n]*\\bibliography", re.MULTILINE) +bibunit_re = re.compile(r"^[^%\n]*\\begin\{bibunit\}", re.MULTILINE) +listoffigures_re = re.compile(r"^[^%\n]*\\listoffigures", re.MULTILINE) +listoftables_re = re.compile(r"^[^%\n]*\\listoftables", re.MULTILINE) +hyperref_re = re.compile(r"^[^%\n]*\\usepackage.*\{hyperref\}", re.MULTILINE) +makenomenclature_re = re.compile(r"^[^%\n]*\\makenomenclature", re.MULTILINE) +makeglossary_re = re.compile(r"^[^%\n]*\\makeglossary", re.MULTILINE) +makeglossaries_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE) +makeacronyms_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE) +beamer_re = re.compile(r"^[^%\n]*\\documentclass\{beamer\}", re.MULTILINE) + +# search to find all files included by Latex +include_re = re.compile(r'^[^%\n]*\\(?:include|input){([^}]*)}', re.MULTILINE) +includeOnly_re = re.compile(r'^[^%\n]*\\(?:include){([^}]*)}', re.MULTILINE) + +# search to find all graphics files included by Latex +includegraphics_re = re.compile(r'^[^%\n]*\\(?:includegraphics(?:\[[^\]]+\])?){([^}]*)}', re.MULTILINE) + +# search to find all files opened by Latex (recorded in .log file) +openout_re = re.compile(r"OUTPUT *(.*)") + +# list of graphics file extensions for TeX and LaTeX +TexGraphics = SCons.Scanner.LaTeX.TexGraphics +LatexGraphics = SCons.Scanner.LaTeX.LatexGraphics + +# An Action sufficient to build any generic tex file. +TeXAction = None + +# An action to build a latex file. This action might be needed more +# than once if we are dealing with labels and bibtex. +LaTeXAction = None + +# An action to run BibTeX on a file. +BibTeXAction = None + +# An action to run MakeIndex on a file. +MakeIndexAction = None + +# An action to run MakeIndex (for nomencl) on a file. +MakeNclAction = None + +# An action to run MakeIndex (for glossary) on a file. +MakeGlossaryAction = None + +# An action to run MakeIndex (for acronyms) on a file. +MakeAcronymsAction = None + +# Used as a return value of modify_env_var if the variable is not set. +_null = SCons.Scanner.LaTeX._null + +modify_env_var = SCons.Scanner.LaTeX.modify_env_var + +def check_file_error_message(utility, filename='log'): + msg = '%s returned an error, check the %s file\n' % (utility, filename) + sys.stdout.write(msg) + +def FindFile(name,suffixes,paths,env,requireExt=False): + if requireExt: + name,ext = SCons.Util.splitext(name) + # if the user gave an extension use it. + if ext: + name = name + ext + if Verbose: + print " searching for '%s' with extensions: " % name,suffixes + + for path in paths: + testName = os.path.join(path,name) + if Verbose: + print " look for '%s'" % testName + if os.path.isfile(testName): + if Verbose: + print " found '%s'" % testName + return env.fs.File(testName) + else: + name_ext = SCons.Util.splitext(testName)[1] + if name_ext: + continue + + # if no suffix try adding those passed in + for suffix in suffixes: + testNameExt = testName + suffix + if Verbose: + print " look for '%s'" % testNameExt + + if os.path.isfile(testNameExt): + if Verbose: + print " found '%s'" % testNameExt + return env.fs.File(testNameExt) + if Verbose: + print " did not find '%s'" % name + return None + +def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None): + """A builder for LaTeX files that checks the output in the aux file + and decides how many times to use LaTeXAction, and BibTeXAction.""" + + global must_rerun_latex + + # This routine is called with two actions. In this file for DVI builds + # with LaTeXAction and from the pdflatex.py with PDFLaTeXAction + # set this up now for the case where the user requests a different extension + # for the target filename + if (XXXLaTeXAction == LaTeXAction): + callerSuffix = ".dvi" + else: + callerSuffix = env['PDFSUFFIX'] + + basename = SCons.Util.splitext(str(source[0]))[0] + basedir = os.path.split(str(source[0]))[0] + basefile = os.path.split(str(basename))[1] + abspath = os.path.abspath(basedir) + + targetext = os.path.splitext(str(target[0]))[1] + targetdir = os.path.split(str(target[0]))[0] + + saved_env = {} + for var in SCons.Scanner.LaTeX.LaTeX.env_variables: + saved_env[var] = modify_env_var(env, var, abspath) + + # Create base file names with the target directory since the auxiliary files + # will be made there. That's because the *COM variables have the cd + # command in the prolog. We check + # for the existence of files before opening them--even ones like the + # aux file that TeX always creates--to make it possible to write tests + # with stubs that don't necessarily generate all of the same files. + + targetbase = os.path.join(targetdir, basefile) + + # if there is a \makeindex there will be a .idx and thus + # we have to run makeindex at least once to keep the build + # happy even if there is no index. + # Same for glossaries, nomenclature, and acronyms + src_content = source[0].get_text_contents() + run_makeindex = makeindex_re.search(src_content) and not os.path.isfile(targetbase + '.idx') + run_nomenclature = makenomenclature_re.search(src_content) and not os.path.isfile(targetbase + '.nlo') + run_glossary = makeglossary_re.search(src_content) and not os.path.isfile(targetbase + '.glo') + run_glossaries = makeglossaries_re.search(src_content) and not os.path.isfile(targetbase + '.glo') + run_acronyms = makeacronyms_re.search(src_content) and not os.path.isfile(targetbase + '.acn') + + saved_hashes = {} + suffix_nodes = {} + + for suffix in all_suffixes: + theNode = env.fs.File(targetbase + suffix) + suffix_nodes[suffix] = theNode + saved_hashes[suffix] = theNode.get_csig() + + if Verbose: + print "hashes: ",saved_hashes + + must_rerun_latex = True + + # + # routine to update MD5 hash and compare + # + def check_MD5(filenode, suffix): + global must_rerun_latex + # two calls to clear old csig + filenode.clear_memoized_values() + filenode.ninfo = filenode.new_ninfo() + new_md5 = filenode.get_csig() + + if saved_hashes[suffix] == new_md5: + if Verbose: + print "file %s not changed" % (targetbase+suffix) + return False # unchanged + saved_hashes[suffix] = new_md5 + must_rerun_latex = True + if Verbose: + print "file %s changed, rerunning Latex, new hash = " % (targetbase+suffix), new_md5 + return True # changed + + # generate the file name that latex will generate + resultfilename = targetbase + callerSuffix + + count = 0 + + while (must_rerun_latex and count < int(env.subst('$LATEXRETRIES'))) : + result = XXXLaTeXAction(target, source, env) + if result != 0: + return result + + count = count + 1 + + must_rerun_latex = False + # Decide if various things need to be run, or run again. + + # Read the log file to find warnings/errors + logfilename = targetbase + '.log' + logContent = '' + if os.path.isfile(logfilename): + logContent = open(logfilename, "rb").read() + + + # Read the fls file to find all .aux files + flsfilename = targetbase + '.fls' + flsContent = '' + auxfiles = [] + if os.path.isfile(flsfilename): + flsContent = open(flsfilename, "rb").read() + auxfiles = openout_aux_re.findall(flsContent) + # remove duplicates + dups = {} + for x in auxfiles: + dups[x] = 1 + auxfiles = list(dups.keys()) + + if Verbose: + print "auxfiles ",auxfiles + + # Now decide if bibtex will need to be run. + # The information that bibtex reads from the .aux file is + # pass-independent. If we find (below) that the .bbl file is unchanged, + # then the last latex saw a correct bibliography. + # Therefore only do this on the first pass + if count == 1: + for auxfilename in auxfiles: + target_aux = os.path.join(targetdir, auxfilename) + if os.path.isfile(target_aux): + content = open(target_aux, "rb").read() + if content.find("bibdata") != -1: + if Verbose: + print "Need to run bibtex" + bibfile = env.fs.File(SCons.Util.splitext(target_aux)[0]) + result = BibTeXAction(bibfile, bibfile, env) + if result != 0: + check_file_error_message(env['BIBTEX'], 'blg') + must_rerun_latex = must_rerun_latex or check_MD5(suffix_nodes['.bbl'],'.bbl') + + # Now decide if latex will need to be run again due to index. + if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex): + # We must run makeindex + if Verbose: + print "Need to run makeindex" + idxfile = suffix_nodes['.idx'] + result = MakeIndexAction(idxfile, idxfile, env) + if result != 0: + check_file_error_message(env['MAKEINDEX'], 'ilg') + return result + + # TO-DO: need to add a way for the user to extend this list for whatever + # auxiliary files they create in other (or their own) packages + # Harder is case is where an action needs to be called -- that should be rare (I hope?) + + for index in check_suffixes: + check_MD5(suffix_nodes[index],index) + + # Now decide if latex will need to be run again due to nomenclature. + if check_MD5(suffix_nodes['.nlo'],'.nlo') or (count == 1 and run_nomenclature): + # We must run makeindex + if Verbose: + print "Need to run makeindex for nomenclature" + nclfile = suffix_nodes['.nlo'] + result = MakeNclAction(nclfile, nclfile, env) + if result != 0: + check_file_error_message('%s (nomenclature)' % env['MAKENCL'], + 'nlg') + #return result + + # Now decide if latex will need to be run again due to glossary. + if check_MD5(suffix_nodes['.glo'],'.glo') or (count == 1 and run_glossaries) or (count == 1 and run_glossary): + # We must run makeindex + if Verbose: + print "Need to run makeindex for glossary" + glofile = suffix_nodes['.glo'] + result = MakeGlossaryAction(glofile, glofile, env) + if result != 0: + check_file_error_message('%s (glossary)' % env['MAKEGLOSSARY'], + 'glg') + #return result + + # Now decide if latex will need to be run again due to acronyms. + if check_MD5(suffix_nodes['.acn'],'.acn') or (count == 1 and run_acronyms): + # We must run makeindex + if Verbose: + print "Need to run makeindex for acronyms" + acrfile = suffix_nodes['.acn'] + result = MakeAcronymsAction(acrfile, acrfile, env) + if result != 0: + check_file_error_message('%s (acronyms)' % env['MAKEACRONYMS'], + 'alg') + return result + + # Now decide if latex needs to be run yet again to resolve warnings. + if warning_rerun_re.search(logContent): + must_rerun_latex = True + if Verbose: + print "rerun Latex due to latex or package rerun warning" + + if rerun_citations_re.search(logContent): + must_rerun_latex = True + if Verbose: + print "rerun Latex due to 'Rerun to get citations correct' warning" + + if undefined_references_re.search(logContent): + must_rerun_latex = True + if Verbose: + print "rerun Latex due to undefined references or citations" + + if (count >= int(env.subst('$LATEXRETRIES')) and must_rerun_latex): + print "reached max number of retries on Latex ,",int(env.subst('$LATEXRETRIES')) +# end of while loop + + # rename Latex's output to what the target name is + if not (str(target[0]) == resultfilename and os.path.isfile(resultfilename)): + if os.path.isfile(resultfilename): + print "move %s to %s" % (resultfilename, str(target[0]), ) + shutil.move(resultfilename,str(target[0])) + + # Original comment (when TEXPICTS was not restored): + # The TEXPICTS enviroment variable is needed by a dvi -> pdf step + # later on Mac OSX so leave it + # + # It is also used when searching for pictures (implicit dependencies). + # Why not set the variable again in the respective builder instead + # of leaving local modifications in the environment? What if multiple + # latex builds in different directories need different TEXPICTS? + for var in SCons.Scanner.LaTeX.LaTeX.env_variables: + if var == 'TEXPICTS': + continue + if saved_env[var] is _null: + try: + del env['ENV'][var] + except KeyError: + pass # was never set + else: + env['ENV'][var] = saved_env[var] + + return result + +def LaTeXAuxAction(target = None, source= None, env=None): + result = InternalLaTeXAuxAction( LaTeXAction, target, source, env ) + return result + +LaTeX_re = re.compile("\\\\document(style|class)") + +def is_LaTeX(flist,env,abspath): + """Scan a file list to decide if it's TeX- or LaTeX-flavored.""" + + # We need to scan files that are included in case the + # \documentclass command is in them. + + # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] + savedpath = modify_env_var(env, 'TEXINPUTS', abspath) + paths = env['ENV']['TEXINPUTS'] + if SCons.Util.is_List(paths): + pass + else: + # Split at os.pathsep to convert into absolute path + paths = paths.split(os.pathsep) + + # now that we have the path list restore the env + if savedpath is _null: + try: + del env['ENV']['TEXINPUTS'] + except KeyError: + pass # was never set + else: + env['ENV']['TEXINPUTS'] = savedpath + if Verbose: + print "is_LaTeX search path ",paths + print "files to search :",flist + + # Now that we have the search path and file list, check each one + for f in flist: + if Verbose: + print " checking for Latex source ",str(f) + + content = f.get_text_contents() + if LaTeX_re.search(content): + if Verbose: + print "file %s is a LaTeX file" % str(f) + return 1 + if Verbose: + print "file %s is not a LaTeX file" % str(f) + + # now find included files + inc_files = [ ] + inc_files.extend( include_re.findall(content) ) + if Verbose: + print "files included by '%s': "%str(f),inc_files + # inc_files is list of file names as given. need to find them + # using TEXINPUTS paths. + + # search the included files + for src in inc_files: + srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) + # make this a list since is_LaTeX takes a list. + fileList = [srcNode,] + if Verbose: + print "FindFile found ",srcNode + if srcNode is not None: + file_test = is_LaTeX(fileList, env, abspath) + + # return on first file that finds latex is needed. + if file_test: + return file_test + + if Verbose: + print " done scanning ",str(f) + + return 0 + +def TeXLaTeXFunction(target = None, source= None, env=None): + """A builder for TeX and LaTeX that scans the source file to + decide the "flavor" of the source and then executes the appropriate + program.""" + + # find these paths for use in is_LaTeX to search for included files + basedir = os.path.split(str(source[0]))[0] + abspath = os.path.abspath(basedir) + + if is_LaTeX(source,env,abspath): + result = LaTeXAuxAction(target,source,env) + if result != 0: + check_file_error_message(env['LATEX']) + else: + result = TeXAction(target,source,env) + if result != 0: + check_file_error_message(env['TEX']) + return result + +def TeXLaTeXStrFunction(target = None, source= None, env=None): + """A strfunction for TeX and LaTeX that scans the source file to + decide the "flavor" of the source and then returns the appropriate + command string.""" + if env.GetOption("no_exec"): + + # find these paths for use in is_LaTeX to search for included files + basedir = os.path.split(str(source[0]))[0] + abspath = os.path.abspath(basedir) + + if is_LaTeX(source,env,abspath): + result = env.subst('$LATEXCOM',0,target,source)+" ..." + else: + result = env.subst("$TEXCOM",0,target,source)+" ..." + else: + result = '' + return result + +def tex_eps_emitter(target, source, env): + """An emitter for TeX and LaTeX sources when + executing tex or latex. It will accept .ps and .eps + graphics files + """ + (target, source) = tex_emitter_core(target, source, env, TexGraphics) + + return (target, source) + +def tex_pdf_emitter(target, source, env): + """An emitter for TeX and LaTeX sources when + executing pdftex or pdflatex. It will accept graphics + files of types .pdf, .jpg, .png, .gif, and .tif + """ + (target, source) = tex_emitter_core(target, source, env, LatexGraphics) + + return (target, source) + +def ScanFiles(theFile, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files): + """ For theFile (a Node) update any file_tests and search for graphics files + then find all included files and call ScanFiles recursively for each of them""" + + content = theFile.get_text_contents() + if Verbose: + print " scanning ",str(theFile) + + for i in range(len(file_tests_search)): + if file_tests[i][0] is None: + file_tests[i][0] = file_tests_search[i].search(content) + + incResult = includeOnly_re.search(content) + if incResult: + aux_files.append(os.path.join(targetdir, incResult.group(1))) + if Verbose: + print "\include file names : ", aux_files + # recursively call this on each of the included files + inc_files = [ ] + inc_files.extend( include_re.findall(content) ) + if Verbose: + print "files included by '%s': "%str(theFile),inc_files + # inc_files is list of file names as given. need to find them + # using TEXINPUTS paths. + + for src in inc_files: + srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) + if srcNode is not None: + file_tests = ScanFiles(srcNode, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files) + if Verbose: + print " done scanning ",str(theFile) + return file_tests + +def tex_emitter_core(target, source, env, graphics_extensions): + """An emitter for TeX and LaTeX sources. + For LaTeX sources we try and find the common created files that + are needed on subsequent runs of latex to finish tables of contents, + bibliographies, indices, lists of figures, and hyperlink references. + """ + basename = SCons.Util.splitext(str(source[0]))[0] + basefile = os.path.split(str(basename))[1] + targetdir = os.path.split(str(target[0]))[0] + targetbase = os.path.join(targetdir, basefile) + + basedir = os.path.split(str(source[0]))[0] + abspath = os.path.abspath(basedir) + target[0].attributes.path = abspath + + # + # file names we will make use of in searching the sources and log file + # + emit_suffixes = ['.aux', '.log', '.ilg', '.blg', '.nls', '.nlg', '.gls', '.glg', '.alg'] + all_suffixes + auxfilename = targetbase + '.aux' + logfilename = targetbase + '.log' + flsfilename = targetbase + '.fls' + + env.SideEffect(auxfilename,target[0]) + env.SideEffect(logfilename,target[0]) + env.SideEffect(flsfilename,target[0]) + if Verbose: + print "side effect :",auxfilename,logfilename,flsfilename + env.Clean(target[0],auxfilename) + env.Clean(target[0],logfilename) + env.Clean(target[0],flsfilename) + + content = source[0].get_text_contents() + + # These variables are no longer used. + #idx_exists = os.path.isfile(targetbase + '.idx') + #nlo_exists = os.path.isfile(targetbase + '.nlo') + #glo_exists = os.path.isfile(targetbase + '.glo') + #acr_exists = os.path.isfile(targetbase + '.acn') + + # set up list with the regular expressions + # we use to find features used + file_tests_search = [auxfile_re, + makeindex_re, + bibliography_re, + bibunit_re, + tableofcontents_re, + listoffigures_re, + listoftables_re, + hyperref_re, + makenomenclature_re, + makeglossary_re, + makeglossaries_re, + makeacronyms_re, + beamer_re ] + # set up list with the file suffixes that need emitting + # when a feature is found + file_tests_suff = [['.aux','aux_file'], + ['.idx', '.ind', '.ilg','makeindex'], + ['.bbl', '.blg','bibliography'], + ['.bbl', '.blg','bibunit'], + ['.toc','contents'], + ['.lof','figures'], + ['.lot','tables'], + ['.out','hyperref'], + ['.nlo', '.nls', '.nlg','nomenclature'], + ['.glo', '.gls', '.glg','glossary'], + ['.glo', '.gls', '.glg','glossaries'], + ['.acn', '.acr', '.alg','acronyms'], + ['.nav', '.snm', '.out', '.toc','beamer'] ] + # build the list of lists + file_tests = [] + for i in range(len(file_tests_search)): + file_tests.append( [None, file_tests_suff[i]] ) + + # TO-DO: need to add a way for the user to extend this list for whatever + # auxiliary files they create in other (or their own) packages + + # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] + savedpath = modify_env_var(env, 'TEXINPUTS', abspath) + paths = env['ENV']['TEXINPUTS'] + if SCons.Util.is_List(paths): + pass + else: + # Split at os.pathsep to convert into absolute path + paths = paths.split(os.pathsep) + + # now that we have the path list restore the env + if savedpath is _null: + try: + del env['ENV']['TEXINPUTS'] + except KeyError: + pass # was never set + else: + env['ENV']['TEXINPUTS'] = savedpath + if Verbose: + print "search path ",paths + + aux_files = [] + file_tests = ScanFiles(source[0], target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files) + + for (theSearch,suffix_list) in file_tests: + # add side effects if feature is present.If file is to be generated,add all side effects + if (theSearch != None) or (not source[0].exists() ): + file_list = [targetbase,] + # for bibunit we need a list of files + if suffix_list[-1] == 'bibunit': + file_basename = os.path.join(targetdir, 'bu*.aux') + file_list = glob.glob(file_basename) + # remove the suffix '.aux' + for i in range(len(file_list)): + file_list[i] = SCons.Util.splitext(file_list[i])[0] + # now define the side effects + for file_name in file_list: + for suffix in suffix_list[:-1]: + env.SideEffect(file_name + suffix,target[0]) + if Verbose: + print "side effect :",file_name + suffix + env.Clean(target[0],file_name + suffix) + + for aFile in aux_files: + aFile_base = SCons.Util.splitext(aFile)[0] + env.SideEffect(aFile_base + '.aux',target[0]) + if Verbose: + print "side effect :",aFile_base + '.aux' + env.Clean(target[0],aFile_base + '.aux') + # read fls file to get all other files that latex creates and will read on the next pass + # remove files from list that we explicitly dealt with above + if os.path.isfile(flsfilename): + content = open(flsfilename, "rb").read() + out_files = openout_re.findall(content) + myfiles = [auxfilename, logfilename, flsfilename, targetbase+'.dvi',targetbase+'.pdf'] + for filename in out_files[:]: + if filename in myfiles: + out_files.remove(filename) + env.SideEffect(out_files,target[0]) + if Verbose: + print "side effect :",out_files + env.Clean(target[0],out_files) + + return (target, source) + + +TeXLaTeXAction = None + +def generate(env): + """Add Builders and construction variables for TeX to an Environment.""" + + global TeXLaTeXAction + if TeXLaTeXAction is None: + TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction, + strfunction=TeXLaTeXStrFunction) + + env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) + + generate_common(env) + + import dvi + dvi.generate(env) + + bld = env['BUILDERS']['DVI'] + bld.add_action('.tex', TeXLaTeXAction) + bld.add_emitter('.tex', tex_eps_emitter) + +def generate_darwin(env): + try: + environ = env['ENV'] + except KeyError: + environ = {} + env['ENV'] = environ + + if (platform.system() == 'Darwin'): + try: + ospath = env['ENV']['PATHOSX'] + except: + ospath = None + if ospath: + env.AppendENVPath('PATH', ospath) + +def generate_common(env): + """Add internal Builders and construction variables for LaTeX to an Environment.""" + + # Add OSX system paths so TeX tools can be found + # when a list of tools is given the exists() method is not called + generate_darwin(env) + + # A generic tex file Action, sufficient for all tex files. + global TeXAction + if TeXAction is None: + TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR") + + # An Action to build a latex file. This might be needed more + # than once if we are dealing with labels and bibtex. + global LaTeXAction + if LaTeXAction is None: + LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR") + + # Define an action to run BibTeX on a file. + global BibTeXAction + if BibTeXAction is None: + BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR") + + # Define an action to run MakeIndex on a file. + global MakeIndexAction + if MakeIndexAction is None: + MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXCOMSTR") + + # Define an action to run MakeIndex on a file for nomenclatures. + global MakeNclAction + if MakeNclAction is None: + MakeNclAction = SCons.Action.Action("$MAKENCLCOM", "$MAKENCLCOMSTR") + + # Define an action to run MakeIndex on a file for glossaries. + global MakeGlossaryAction + if MakeGlossaryAction is None: + MakeGlossaryAction = SCons.Action.Action("$MAKEGLOSSARYCOM", "$MAKEGLOSSARYCOMSTR") + + # Define an action to run MakeIndex on a file for acronyms. + global MakeAcronymsAction + if MakeAcronymsAction is None: + MakeAcronymsAction = SCons.Action.Action("$MAKEACRONYMSCOM", "$MAKEACRONYMSCOMSTR") + + try: + environ = env['ENV'] + except KeyError: + environ = {} + env['ENV'] = environ + + # Some Linux platforms have pdflatex set up in a way + # that requires that the HOME environment variable be set. + # Add it here if defined. + v = os.environ.get('HOME') + if v: + environ['HOME'] = v + + CDCOM = 'cd ' + if platform.system() == 'Windows': + # allow cd command to change drives on Windows + CDCOM = 'cd /D ' + + env['TEX'] = 'tex' + env['TEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') + env['TEXCOM'] = CDCOM + '${TARGET.dir} && $TEX $TEXFLAGS ${SOURCE.file}' + + env['PDFTEX'] = 'pdftex' + env['PDFTEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') + env['PDFTEXCOM'] = CDCOM + '${TARGET.dir} && $PDFTEX $PDFTEXFLAGS ${SOURCE.file}' + + env['LATEX'] = 'latex' + env['LATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') + env['LATEXCOM'] = CDCOM + '${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}' + env['LATEXRETRIES'] = 3 + + env['PDFLATEX'] = 'pdflatex' + env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') + env['PDFLATEXCOM'] = CDCOM + '${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}' + + env['BIBTEX'] = 'bibtex' + env['BIBTEXFLAGS'] = SCons.Util.CLVar('') + env['BIBTEXCOM'] = CDCOM + '${TARGET.dir} && $BIBTEX $BIBTEXFLAGS ${SOURCE.filebase}' + + env['MAKEINDEX'] = 'makeindex' + env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('') + env['MAKEINDEXCOM'] = CDCOM + '${TARGET.dir} && $MAKEINDEX $MAKEINDEXFLAGS ${SOURCE.file}' + + env['MAKEGLOSSARY'] = 'makeindex' + env['MAKEGLOSSARYSTYLE'] = '${SOURCE.filebase}.ist' + env['MAKEGLOSSARYFLAGS'] = SCons.Util.CLVar('-s ${MAKEGLOSSARYSTYLE} -t ${SOURCE.filebase}.glg') + env['MAKEGLOSSARYCOM'] = CDCOM + '${TARGET.dir} && $MAKEGLOSSARY ${SOURCE.filebase}.glo $MAKEGLOSSARYFLAGS -o ${SOURCE.filebase}.gls' + + env['MAKEACRONYMS'] = 'makeindex' + env['MAKEACRONYMSSTYLE'] = '${SOURCE.filebase}.ist' + env['MAKEACRONYMSFLAGS'] = SCons.Util.CLVar('-s ${MAKEACRONYMSSTYLE} -t ${SOURCE.filebase}.alg') + env['MAKEACRONYMSCOM'] = CDCOM + '${TARGET.dir} && $MAKEACRONYMS ${SOURCE.filebase}.acn $MAKEACRONYMSFLAGS -o ${SOURCE.filebase}.acr' + + env['MAKENCL'] = 'makeindex' + env['MAKENCLSTYLE'] = 'nomencl.ist' + env['MAKENCLFLAGS'] = '-s ${MAKENCLSTYLE} -t ${SOURCE.filebase}.nlg' + env['MAKENCLCOM'] = CDCOM + '${TARGET.dir} && $MAKENCL ${SOURCE.filebase}.nlo $MAKENCLFLAGS -o ${SOURCE.filebase}.nls' + +def exists(env): + generate_darwin(env) + return env.Detect('tex') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/textfile.py b/engine/SCons/Tool/textfile.py new file mode 100644 index 0000000..b849e71 --- /dev/null +++ b/engine/SCons/Tool/textfile.py @@ -0,0 +1,175 @@ +# -*- python -*- +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__doc__ = """ +Textfile/Substfile builder for SCons. + + Create file 'target' which typically is a textfile. The 'source' + may be any combination of strings, Nodes, or lists of same. A + 'linesep' will be put between any part written and defaults to + os.linesep. + + The only difference between the Textfile builder and the Substfile + builder is that strings are converted to Value() nodes for the + former and File() nodes for the latter. To insert files in the + former or strings in the latter, wrap them in a File() or Value(), + respectively. + + The values of SUBST_DICT first have any construction variables + expanded (its keys are not expanded). If a value of SUBST_DICT is + a python callable function, it is called and the result is expanded + as the value. Values are substituted in a "random" order; if any + substitution could be further expanded by another subsitition, it + is unpredictible whether the expansion will occur. +""" + +__revision__ = "src/engine/SCons/Tool/textfile.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons + +import os +import re + +from SCons.Node import Node +from SCons.Node.Python import Value +from SCons.Util import is_String, is_Sequence, is_Dict + +def _do_subst(node, subs): + """ + Fetch the node contents and replace all instances of the keys with + their values. For example, if subs is + {'%VERSION%': '1.2345', '%BASE%': 'MyProg', '%prefix%': '/bin'}, + then all instances of %VERSION% in the file will be replaced with + 1.2345 and so forth. + """ + contents = node.get_text_contents() + if not subs: return contents + for (k,v) in subs: + contents = re.sub(k, v, contents) + return contents + +def _action(target, source, env): + # prepare the line separator + linesep = env['LINESEPARATOR'] + if linesep is None: + linesep = os.linesep + elif is_String(linesep): + pass + elif isinstance(linesep, Value): + linesep = linesep.get_text_contents() + else: + raise SCons.Errors.UserError( + 'unexpected type/class for LINESEPARATOR: %s' + % repr(linesep), None) + + # create a dictionary to use for the substitutions + if 'SUBST_DICT' not in env: + subs = None # no substitutions + else: + d = env['SUBST_DICT'] + if is_Dict(d): + d = list(d.items()) + elif is_Sequence(d): + pass + else: + raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') + subs = [] + for (k,v) in d: + if callable(v): + v = v() + if is_String(v): + v = env.subst(v) + else: + v = str(v) + subs.append((k,v)) + + # write the file + try: + fd = open(target[0].get_path(), "wb") + except (OSError,IOError), e: + raise SCons.Errors.UserError("Can't write target file %s" % target[0]) + # separate lines by 'linesep' only if linesep is not empty + lsep = None + for s in source: + if lsep: fd.write(lsep) + fd.write(_do_subst(s, subs)) + lsep = linesep + fd.close() + +def _strfunc(target, source, env): + return "Creating '%s'" % target[0] + +def _convert_list_R(newlist, sources): + for elem in sources: + if is_Sequence(elem): + _convert_list_R(newlist, elem) + elif isinstance(elem, Node): + newlist.append(elem) + else: + newlist.append(Value(elem)) +def _convert_list(target, source, env): + if len(target) != 1: + raise SCons.Errors.UserError("Only one target file allowed") + newlist = [] + _convert_list_R(newlist, source) + return target, newlist + +_common_varlist = ['SUBST_DICT', 'LINESEPARATOR'] + +_text_varlist = _common_varlist + ['TEXTFILEPREFIX', 'TEXTFILESUFFIX'] +_text_builder = SCons.Builder.Builder( + action = SCons.Action.Action(_action, _strfunc, varlist = _text_varlist), + source_factory = Value, + emitter = _convert_list, + prefix = '$TEXTFILEPREFIX', + suffix = '$TEXTFILESUFFIX', + ) + +_subst_varlist = _common_varlist + ['SUBSTFILEPREFIX', 'TEXTFILESUFFIX'] +_subst_builder = SCons.Builder.Builder( + action = SCons.Action.Action(_action, _strfunc, varlist = _subst_varlist), + source_factory = SCons.Node.FS.File, + emitter = _convert_list, + prefix = '$SUBSTFILEPREFIX', + suffix = '$SUBSTFILESUFFIX', + src_suffix = ['.in'], + ) + +def generate(env): + env['LINESEPARATOR'] = os.linesep + env['BUILDERS']['Textfile'] = _text_builder + env['TEXTFILEPREFIX'] = '' + env['TEXTFILESUFFIX'] = '.txt' + env['BUILDERS']['Substfile'] = _subst_builder + env['SUBSTFILEPREFIX'] = '' + env['SUBSTFILESUFFIX'] = '' + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/tlib.py b/engine/SCons/Tool/tlib.py new file mode 100644 index 0000000..918d3cd --- /dev/null +++ b/engine/SCons/Tool/tlib.py @@ -0,0 +1,53 @@ +"""SCons.Tool.tlib + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/tlib.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Tool +import SCons.Tool.bcc32 +import SCons.Util + +def generate(env): + SCons.Tool.bcc32.findIt('tlib', env) + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + env['AR'] = 'tlib' + env['ARFLAGS'] = SCons.Util.CLVar('') + env['ARCOM'] = '$AR $TARGET $ARFLAGS /a $SOURCES' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + +def exists(env): + return SCons.Tool.bcc32.findIt('tlib', env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/wix.py b/engine/SCons/Tool/wix.py new file mode 100644 index 0000000..208e96d --- /dev/null +++ b/engine/SCons/Tool/wix.py @@ -0,0 +1,99 @@ +"""SCons.Tool.wix + +Tool-specific initialization for wix, the Windows Installer XML Tool. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/wix.py 5357 2011/09/09 21:31:03 bdeegan" + +import SCons.Builder +import SCons.Action +import os + +def generate(env): + """Add Builders and construction variables for WiX to an Environment.""" + if not exists(env): + return + + env['WIXCANDLEFLAGS'] = ['-nologo'] + env['WIXCANDLEINCLUDE'] = [] + env['WIXCANDLECOM'] = '$WIXCANDLE $WIXCANDLEFLAGS -I $WIXCANDLEINCLUDE -o ${TARGET} ${SOURCE}' + + env['WIXLIGHTFLAGS'].append( '-nologo' ) + env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}" + + object_builder = SCons.Builder.Builder( + action = '$WIXCANDLECOM', + suffix = '.wxiobj', + src_suffix = '.wxs') + + linker_builder = SCons.Builder.Builder( + action = '$WIXLIGHTCOM', + src_suffix = '.wxiobj', + src_builder = object_builder) + + env['BUILDERS']['WiX'] = linker_builder + +def exists(env): + env['WIXCANDLE'] = 'candle.exe' + env['WIXLIGHT'] = 'light.exe' + + # try to find the candle.exe and light.exe tools and + # add the install directory to light libpath. + #for path in os.environ['PATH'].split(os.pathsep): + for path in os.environ['PATH'].split(os.pathsep): + if not path: + continue + + # workaround for some weird python win32 bug. + if path[0] == '"' and path[-1:]=='"': + path = path[1:-1] + + # normalize the path + path = os.path.normpath(path) + + # search for the tools in the PATH environment variable + try: + if env['WIXCANDLE'] in os.listdir(path) and\ + env['WIXLIGHT'] in os.listdir(path): + env.PrependENVPath('PATH', path) + env['WIXLIGHTFLAGS'] = [ os.path.join( path, 'wixui.wixlib' ), + '-loc', + os.path.join( path, 'WixUI_en-us.wxl' ) ] + return 1 + except OSError: + pass # ignore this, could be a stale PATH entry. + + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/yacc.py b/engine/SCons/Tool/yacc.py new file mode 100644 index 0000000..60e1a83 --- /dev/null +++ b/engine/SCons/Tool/yacc.py @@ -0,0 +1,140 @@ +"""SCons.Tool.yacc + +Tool-specific initialization for yacc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/yacc.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") + +def _yaccEmitter(target, source, env, ysuf, hsuf): + yaccflags = env.subst("$YACCFLAGS", target=target, source=source) + flags = SCons.Util.CLVar(yaccflags) + targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0])) + + if '.ym' in ysuf: # If using Objective-C + target = [targetBase + ".m"] # the extension is ".m". + + + # If -d is specified on the command line, yacc will emit a .h + # or .hpp file with the same name as the .c or .cpp output file. + if '-d' in flags: + target.append(targetBase + env.subst(hsuf, target=target, source=source)) + + # If -g is specified on the command line, yacc will emit a .vcg + # file with the same base name as the .y, .yacc, .ym or .yy file. + if "-g" in flags: + base, ext = os.path.splitext(SCons.Util.to_String(source[0])) + target.append(base + env.subst("$YACCVCGFILESUFFIX")) + + # If -v is specirfied yacc will create the output debug file + # which is not really source for any process, but should + # be noted and also be cleaned + # Bug #2558 + if "-v" in flags: + env.SideEffect(targetBase+'.output',target[0]) + env.Clean(target[0],targetBase+'.output') + + + + # With --defines and --graph, the name of the file is totally defined + # in the options. + fileGenOptions = ["--defines=", "--graph="] + for option in flags: + for fileGenOption in fileGenOptions: + l = len(fileGenOption) + if option[:l] == fileGenOption: + # A file generating option is present, so add the file + # name to the list of targets. + fileName = option[l:].strip() + target.append(fileName) + + return (target, source) + +def yEmitter(target, source, env): + return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX') + +def ymEmitter(target, source, env): + return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX') + +def yyEmitter(target, source, env): + return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX') + +def generate(env): + """Add Builders and construction variables for yacc to an Environment.""" + c_file, cxx_file = SCons.Tool.createCFileBuilders(env) + + # C + c_file.add_action('.y', YaccAction) + c_file.add_emitter('.y', yEmitter) + + c_file.add_action('.yacc', YaccAction) + c_file.add_emitter('.yacc', yEmitter) + + # Objective-C + c_file.add_action('.ym', YaccAction) + c_file.add_emitter('.ym', ymEmitter) + + # C++ + cxx_file.add_action('.yy', YaccAction) + cxx_file.add_emitter('.yy', yyEmitter) + + env['YACC'] = env.Detect('bison') or 'yacc' + env['YACCFLAGS'] = SCons.Util.CLVar('') + env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' + env['YACCHFILESUFFIX'] = '.h' + + # Apparently, OS X now creates file.hpp like everybody else + # I have no idea when it changed; it was fixed in 10.4 + #if env['PLATFORM'] == 'darwin': + # # Bison on Mac OS X just appends ".h" to the generated target .cc + # # or .cpp file name. Hooray for delayed expansion of variables. + # env['YACCHXXFILESUFFIX'] = '${TARGET.suffix}.h' + #else: + # env['YACCHXXFILESUFFIX'] = '.hpp' + env['YACCHXXFILESUFFIX'] = '.hpp' + + env['YACCVCGFILESUFFIX'] = '.vcg' + +def exists(env): + return env.Detect(['bison', 'yacc']) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/zip.py b/engine/SCons/Tool/zip.py new file mode 100644 index 0000000..fe2f4be --- /dev/null +++ b/engine/SCons/Tool/zip.py @@ -0,0 +1,99 @@ +"""SCons.Tool.zip + +Tool-specific initialization for zip. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/zip.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path + +import SCons.Builder +import SCons.Defaults +import SCons.Node.FS +import SCons.Util + +try: + import zipfile + internal_zip = 1 +except ImportError: + internal_zip = 0 + +if internal_zip: + zipcompression = zipfile.ZIP_DEFLATED + def zip(target, source, env): + compression = env.get('ZIPCOMPRESSION', 0) + zf = zipfile.ZipFile(str(target[0]), 'w', compression) + for s in source: + if s.isdir(): + for dirpath, dirnames, filenames in os.walk(str(s)): + for fname in filenames: + path = os.path.join(dirpath, fname) + if os.path.isfile(path): + zf.write(path) + else: + zf.write(str(s)) + zf.close() +else: + zipcompression = 0 + zip = "$ZIP $ZIPFLAGS ${TARGET.abspath} $SOURCES" + + +zipAction = SCons.Action.Action(zip, varlist=['ZIPCOMPRESSION']) + +ZipBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$ZIPCOM', '$ZIPCOMSTR'), + source_factory = SCons.Node.FS.Entry, + source_scanner = SCons.Defaults.DirScanner, + suffix = '$ZIPSUFFIX', + multi = 1) + + +def generate(env): + """Add Builders and construction variables for zip to an Environment.""" + try: + bld = env['BUILDERS']['Zip'] + except KeyError: + bld = ZipBuilder + env['BUILDERS']['Zip'] = bld + + env['ZIP'] = 'zip' + env['ZIPFLAGS'] = SCons.Util.CLVar('') + env['ZIPCOM'] = zipAction + env['ZIPCOMPRESSION'] = zipcompression + env['ZIPSUFFIX'] = '.zip' + +def exists(env): + return internal_zip or env.Detect('zip') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Util.py b/engine/SCons/Util.py new file mode 100644 index 0000000..cedd78c --- /dev/null +++ b/engine/SCons/Util.py @@ -0,0 +1,1492 @@ +"""SCons.Util + +Various utility functions go here. +""" +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Util.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import sys +import copy +import re +import types + +from collections import UserDict, UserList, UserString + +# Don't "from types import ..." these because we need to get at the +# types module later to look for UnicodeType. +InstanceType = types.InstanceType +MethodType = types.MethodType +FunctionType = types.FunctionType +try: unicode +except NameError: UnicodeType = None +else: UnicodeType = unicode + +def dictify(keys, values, result={}): + for k, v in zip(keys, values): + result[k] = v + return result + +_altsep = os.altsep +if _altsep is None and sys.platform == 'win32': + # My ActivePython 2.0.1 doesn't set os.altsep! What gives? + _altsep = '/' +if _altsep: + def rightmost_separator(path, sep): + return max(path.rfind(sep), path.rfind(_altsep)) +else: + def rightmost_separator(path, sep): + return path.rfind(sep) + +# First two from the Python Cookbook, just for completeness. +# (Yeah, yeah, YAGNI...) +def containsAny(str, set): + """Check whether sequence str contains ANY of the items in set.""" + for c in set: + if c in str: return 1 + return 0 + +def containsAll(str, set): + """Check whether sequence str contains ALL of the items in set.""" + for c in set: + if c not in str: return 0 + return 1 + +def containsOnly(str, set): + """Check whether sequence str contains ONLY items in set.""" + for c in str: + if c not in set: return 0 + return 1 + +def splitext(path): + "Same as os.path.splitext() but faster." + sep = rightmost_separator(path, os.sep) + dot = path.rfind('.') + # An ext is only real if it has at least one non-digit char + if dot > sep and not containsOnly(path[dot:], "0123456789."): + return path[:dot],path[dot:] + else: + return path,"" + +def updrive(path): + """ + Make the drive letter (if any) upper case. + This is useful because Windows is inconsitent on the case + of the drive letter, which can cause inconsistencies when + calculating command signatures. + """ + drive, rest = os.path.splitdrive(path) + if drive: + path = drive.upper() + rest + return path + +class NodeList(UserList): + """This class is almost exactly like a regular list of Nodes + (actually it can hold any object), with one important difference. + If you try to get an attribute from this list, it will return that + attribute from every item in the list. For example: + + >>> someList = NodeList([ ' foo ', ' bar ' ]) + >>> someList.strip() + [ 'foo', 'bar' ] + """ + def __nonzero__(self): + return len(self.data) != 0 + + def __str__(self): + return ' '.join(map(str, self.data)) + + def __iter__(self): + return iter(self.data) + + def __call__(self, *args, **kwargs): + result = [x(*args, **kwargs) for x in self.data] + return self.__class__(result) + + def __getattr__(self, name): + result = [getattr(x, name) for x in self.data] + return self.__class__(result) + + +_get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') + +def get_environment_var(varstr): + """Given a string, first determine if it looks like a reference + to a single environment variable, like "$FOO" or "${FOO}". + If so, return that variable with no decorations ("FOO"). + If not, return None.""" + mo=_get_env_var.match(to_String(varstr)) + if mo: + var = mo.group(1) + if var[0] == '{': + return var[1:-1] + else: + return var + else: + return None + +class DisplayEngine(object): + print_it = True + def __call__(self, text, append_newline=1): + if not self.print_it: + return + if append_newline: text = text + '\n' + try: + sys.stdout.write(unicode(text)) + except IOError: + # Stdout might be connected to a pipe that has been closed + # by now. The most likely reason for the pipe being closed + # is that the user has press ctrl-c. It this is the case, + # then SCons is currently shutdown. We therefore ignore + # IOError's here so that SCons can continue and shutdown + # properly so that the .sconsign is correctly written + # before SCons exits. + pass + + def set_mode(self, mode): + self.print_it = mode + +def render_tree(root, child_func, prune=0, margin=[0], visited={}): + """ + Render a tree of nodes into an ASCII tree view. + root - the root node of the tree + child_func - the function called to get the children of a node + prune - don't visit the same node twice + margin - the format of the left margin to use for children of root. + 1 results in a pipe, and 0 results in no pipe. + visited - a dictionary of visited nodes in the current branch if not prune, + or in the whole tree if prune. + """ + + rname = str(root) + + children = child_func(root) + retval = "" + for pipe in margin[:-1]: + if pipe: + retval = retval + "| " + else: + retval = retval + " " + + if rname in visited: + return retval + "+-[" + rname + "]\n" + + retval = retval + "+-" + rname + "\n" + if not prune: + visited = copy.copy(visited) + visited[rname] = 1 + + for i in range(len(children)): + margin.append(i 0 + last = t[0] + lasti = i = 1 + while i < n: + if t[i] != last: + t[lasti] = last = t[i] + lasti = lasti + 1 + i = i + 1 + return t[:lasti] + del t + + # Brute force is all that's left. + u = [] + for x in s: + if x not in u: + u.append(x) + return u + + + +# From Alex Martelli, +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 +# ASPN: Python Cookbook: Remove duplicates from a sequence +# First comment, dated 2001/10/13. +# (Also in the printed Python Cookbook.) + +def uniquer(seq, idfun=None): + if idfun is None: + def idfun(x): return x + seen = {} + result = [] + for item in seq: + marker = idfun(item) + # in old Python versions: + # if seen.has_key(marker) + # but in new ones: + if marker in seen: continue + seen[marker] = 1 + result.append(item) + return result + +# A more efficient implementation of Alex's uniquer(), this avoids the +# idfun() argument and function-call overhead by assuming that all +# items in the sequence are hashable. + +def uniquer_hashables(seq): + seen = {} + result = [] + for item in seq: + #if not item in seen: + if item not in seen: + seen[item] = 1 + result.append(item) + return result + + + +# Much of the logic here was originally based on recipe 4.9 from the +# Python CookBook, but we had to dumb it way down for Python 1.5.2. +class LogicalLines(object): + + def __init__(self, fileobj): + self.fileobj = fileobj + + def readline(self): + result = [] + while True: + line = self.fileobj.readline() + if not line: + break + if line[-2:] == '\\\n': + result.append(line[:-2]) + else: + result.append(line) + break + return ''.join(result) + + def readlines(self): + result = [] + while True: + line = self.readline() + if not line: + break + result.append(line) + return result + + + +class UniqueList(UserList): + def __init__(self, seq = []): + UserList.__init__(self, seq) + self.unique = True + def __make_unique(self): + if not self.unique: + self.data = uniquer_hashables(self.data) + self.unique = True + def __lt__(self, other): + self.__make_unique() + return UserList.__lt__(self, other) + def __le__(self, other): + self.__make_unique() + return UserList.__le__(self, other) + def __eq__(self, other): + self.__make_unique() + return UserList.__eq__(self, other) + def __ne__(self, other): + self.__make_unique() + return UserList.__ne__(self, other) + def __gt__(self, other): + self.__make_unique() + return UserList.__gt__(self, other) + def __ge__(self, other): + self.__make_unique() + return UserList.__ge__(self, other) + def __cmp__(self, other): + self.__make_unique() + return UserList.__cmp__(self, other) + def __len__(self): + self.__make_unique() + return UserList.__len__(self) + def __getitem__(self, i): + self.__make_unique() + return UserList.__getitem__(self, i) + def __setitem__(self, i, item): + UserList.__setitem__(self, i, item) + self.unique = False + def __getslice__(self, i, j): + self.__make_unique() + return UserList.__getslice__(self, i, j) + def __setslice__(self, i, j, other): + UserList.__setslice__(self, i, j, other) + self.unique = False + def __add__(self, other): + result = UserList.__add__(self, other) + result.unique = False + return result + def __radd__(self, other): + result = UserList.__radd__(self, other) + result.unique = False + return result + def __iadd__(self, other): + result = UserList.__iadd__(self, other) + result.unique = False + return result + def __mul__(self, other): + result = UserList.__mul__(self, other) + result.unique = False + return result + def __rmul__(self, other): + result = UserList.__rmul__(self, other) + result.unique = False + return result + def __imul__(self, other): + result = UserList.__imul__(self, other) + result.unique = False + return result + def append(self, item): + UserList.append(self, item) + self.unique = False + def insert(self, i): + UserList.insert(self, i) + self.unique = False + def count(self, item): + self.__make_unique() + return UserList.count(self, item) + def index(self, item): + self.__make_unique() + return UserList.index(self, item) + def reverse(self): + self.__make_unique() + UserList.reverse(self) + def sort(self, *args, **kwds): + self.__make_unique() + return UserList.sort(self, *args, **kwds) + def extend(self, other): + UserList.extend(self, other) + self.unique = False + + +class Unbuffered(object): + """ + A proxy class that wraps a file object, flushing after every write, + and delegating everything else to the wrapped object. + """ + def __init__(self, file): + self.file = file + self.softspace = 0 ## backward compatibility; not supported in Py3k + def write(self, arg): + try: + self.file.write(arg) + self.file.flush() + except IOError: + # Stdout might be connected to a pipe that has been closed + # by now. The most likely reason for the pipe being closed + # is that the user has press ctrl-c. It this is the case, + # then SCons is currently shutdown. We therefore ignore + # IOError's here so that SCons can continue and shutdown + # properly so that the .sconsign is correctly written + # before SCons exits. + pass + def __getattr__(self, attr): + return getattr(self.file, attr) + +def make_path_relative(path): + """ makes an absolute path name to a relative pathname. + """ + if os.path.isabs(path): + drive_s,path = os.path.splitdrive(path) + + import re + if not drive_s: + path=re.compile("/*(.*)").findall(path)[0] + else: + path=path[1:] + + assert( not os.path.isabs( path ) ), path + return path + + + +# The original idea for AddMethod() and RenameFunction() come from the +# following post to the ActiveState Python Cookbook: +# +# ASPN: Python Cookbook : Install bound methods in an instance +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 +# +# That code was a little fragile, though, so the following changes +# have been wrung on it: +# +# * Switched the installmethod() "object" and "function" arguments, +# so the order reflects that the left-hand side is the thing being +# "assigned to" and the right-hand side is the value being assigned. +# +# * Changed explicit type-checking to the "try: klass = object.__class__" +# block in installmethod() below so that it still works with the +# old-style classes that SCons uses. +# +# * Replaced the by-hand creation of methods and functions with use of +# the "new" module, as alluded to in Alex Martelli's response to the +# following Cookbook post: +# +# ASPN: Python Cookbook : Dynamically added methods to a class +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 + +def AddMethod(obj, function, name=None): + """ + Adds either a bound method to an instance or an unbound method to + a class. If name is ommited the name of the specified function + is used by default. + Example: + a = A() + def f(self, x, y): + self.z = x + y + AddMethod(f, A, "add") + a.add(2, 4) + print a.z + AddMethod(lambda self, i: self.l[i], a, "listIndex") + print a.listIndex(5) + """ + if name is None: + name = function.func_name + else: + function = RenameFunction(function, name) + + if hasattr(obj, '__class__') and obj.__class__ is not type: + # "obj" is an instance, so it gets a bound method. + setattr(obj, name, MethodType(function, obj, obj.__class__)) + else: + # "obj" is a class, so it gets an unbound method. + setattr(obj, name, MethodType(function, None, obj)) + +def RenameFunction(function, name): + """ + Returns a function identical to the specified function, but with + the specified name. + """ + return FunctionType(function.func_code, + function.func_globals, + name, + function.func_defaults) + + +md5 = False +def MD5signature(s): + return str(s) + +def MD5filesignature(fname, chunksize=65536): + f = open(fname, "rb") + result = f.read() + f.close() + return result + +try: + import hashlib +except ImportError: + pass +else: + if hasattr(hashlib, 'md5'): + md5 = True + def MD5signature(s): + m = hashlib.md5() + m.update(str(s)) + return m.hexdigest() + + def MD5filesignature(fname, chunksize=65536): + m = hashlib.md5() + f = open(fname, "rb") + while True: + blck = f.read(chunksize) + if not blck: + break + m.update(str(blck)) + f.close() + return m.hexdigest() + +def MD5collect(signatures): + """ + Collects a list of signatures into an aggregate signature. + + signatures - a list of signatures + returns - the aggregate signature + """ + if len(signatures) == 1: + return signatures[0] + else: + return MD5signature(', '.join(signatures)) + + + +def silent_intern(x): + """ + Perform sys.intern() on the passed argument and return the result. + If the input is ineligible (e.g. a unicode string) the original argument is + returned and no exception is thrown. + """ + try: + return sys.intern(x) + except TypeError: + return x + + + +# From Dinu C. Gherman, +# Python Cookbook, second edition, recipe 6.17, p. 277. +# Also: +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 +# ASPN: Python Cookbook: Null Object Design Pattern + +#TODO??? class Null(object): +class Null(object): + """ Null objects always and reliably "do nothing." """ + def __new__(cls, *args, **kwargs): + if not '_instance' in vars(cls): + cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) + return cls._instance + def __init__(self, *args, **kwargs): + pass + def __call__(self, *args, **kwargs): + return self + def __repr__(self): + return "Null(0x%08X)" % id(self) + def __nonzero__(self): + return False + def __getattr__(self, name): + return self + def __setattr__(self, name, value): + return self + def __delattr__(self, name): + return self + +class NullSeq(Null): + def __len__(self): + return 0 + def __iter__(self): + return iter(()) + def __getitem__(self, i): + return self + def __delitem__(self, i): + return self + def __setitem__(self, i, v): + return self + + +del __revision__ + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Variables/BoolVariable.py b/engine/SCons/Variables/BoolVariable.py new file mode 100644 index 0000000..dc2bf40 --- /dev/null +++ b/engine/SCons/Variables/BoolVariable.py @@ -0,0 +1,89 @@ +"""engine.SCons.Variables.BoolVariable + +This file defines the option type for SCons implementing true/false values. + +Usage example: + + opts = Variables() + opts.Add(BoolVariable('embedded', 'build for an embedded system', 0)) + ... + if env['embedded'] == 1: + ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Variables/BoolVariable.py 5357 2011/09/09 21:31:03 bdeegan" + +__all__ = ['BoolVariable',] + +import SCons.Errors + +__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' ) +__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none') + + +def _text2bool(val): + """ + Converts strings to True/False depending on the 'truth' expressed by + the string. If the string can't be converted, the original value + will be returned. + + See '__true_strings' and '__false_strings' for values considered + 'true' or 'false respectivly. + + This is usable as 'converter' for SCons' Variables. + """ + lval = val.lower() + if lval in __true_strings: return True + if lval in __false_strings: return False + raise ValueError("Invalid value for boolean option: %s" % val) + + +def _validator(key, val, env): + """ + Validates the given value to be either '0' or '1'. + + This is usable as 'validator' for SCons' Variables. + """ + if not env[key] in (True, False): + raise SCons.Errors.UserError( + 'Invalid value for boolean option %s: %s' % (key, env[key])) + + +def BoolVariable(key, help, default): + """ + The input parameters describe a boolen option, thus they are + returned with the correct converter and validator appended. The + 'help' text will by appended by '(yes|no) to show the valid + valued. The result is usable for input to opts.Add(). + """ + return (key, '%s (yes|no)' % help, default, + _validator, _text2bool) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Variables/EnumVariable.py b/engine/SCons/Variables/EnumVariable.py new file mode 100644 index 0000000..60a3c5d --- /dev/null +++ b/engine/SCons/Variables/EnumVariable.py @@ -0,0 +1,103 @@ +"""engine.SCons.Variables.EnumVariable + +This file defines the option type for SCons allowing only specified +input-values. + +Usage example: + + opts = Variables() + opts.Add(EnumVariable('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=2)) + ... + if env['debug'] == 'full': + ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Variables/EnumVariable.py 5357 2011/09/09 21:31:03 bdeegan" + +__all__ = ['EnumVariable',] + + +import SCons.Errors + +def _validator(key, val, env, vals): + if not val in vals: + raise SCons.Errors.UserError( + 'Invalid value for option %s: %s. Valid values are: %s' % (key, val, vals)) + + +def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0): + """ + The input parameters describe a option with only certain values + allowed. They are returned with an appropriate converter and + validator appended. The result is usable for input to + Variables.Add(). + + 'key' and 'default' are the values to be passed on to Variables.Add(). + + 'help' will be appended by the allowed values automatically + + 'allowed_values' is a list of strings, which are allowed as values + for this option. + + The 'map'-dictionary may be used for converting the input value + into canonical values (eg. for aliases). + + 'ignorecase' defines the behaviour of the validator: + + If ignorecase == 0, the validator/converter are case-sensitive. + If ignorecase == 1, the validator/converter are case-insensitive. + If ignorecase == 2, the validator/converter is case-insensitive and + the converted value will always be lower-case. + + The 'validator' tests whether the value is in the list of allowed + values. The 'converter' converts input values according to the + given 'map'-dictionary (unmapped input values are returned + unchanged). + """ + help = '%s (%s)' % (help, '|'.join(allowed_values)) + # define validator + if ignorecase >= 1: + validator = lambda key, val, env: \ + _validator(key, val.lower(), env, allowed_values) + else: + validator = lambda key, val, env: \ + _validator(key, val, env, allowed_values) + # define converter + if ignorecase == 2: + converter = lambda val: map.get(val.lower(), val).lower() + elif ignorecase == 1: + converter = lambda val: map.get(val.lower(), val) + else: + converter = lambda val: map.get(val, val) + return (key, help, default, validator, converter) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Variables/ListVariable.py b/engine/SCons/Variables/ListVariable.py new file mode 100644 index 0000000..eb3ad8a --- /dev/null +++ b/engine/SCons/Variables/ListVariable.py @@ -0,0 +1,135 @@ +"""engine.SCons.Variables.ListVariable + +This file defines the option type for SCons implementing 'lists'. + +A 'list' option may either be 'all', 'none' or a list of names +separated by comma. After the option has been processed, the option +value holds either the named list elements, all list elemens or no +list elements at all. + +Usage example: + + list_of_libs = Split('x11 gl qt ical') + + opts = Variables() + opts.Add(ListVariable('shared', + 'libraries to build as shared libraries', + 'all', + elems = list_of_libs)) + ... + for lib in list_of_libs: + if lib in env['shared']: + env.SharedObject(...) + else: + env.Object(...) +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Variables/ListVariable.py 5357 2011/09/09 21:31:03 bdeegan" + +# Know Bug: This should behave like a Set-Type, but does not really, +# since elements can occur twice. + +__all__ = ['ListVariable',] + +import collections + +import SCons.Util + + +class _ListVariable(collections.UserList): + def __init__(self, initlist=[], allowedElems=[]): + collections.UserList.__init__(self, [_f for _f in initlist if _f]) + self.allowedElems = sorted(allowedElems) + + def __cmp__(self, other): + raise NotImplementedError + def __eq__(self, other): + raise NotImplementedError + def __ge__(self, other): + raise NotImplementedError + def __gt__(self, other): + raise NotImplementedError + def __le__(self, other): + raise NotImplementedError + def __lt__(self, other): + raise NotImplementedError + def __str__(self): + if len(self) == 0: + return 'none' + self.data.sort() + if self.data == self.allowedElems: + return 'all' + else: + return ','.join(self) + def prepare_to_store(self): + return self.__str__() + +def _converter(val, allowedElems, mapdict): + """ + """ + if val == 'none': + val = [] + elif val == 'all': + val = allowedElems + else: + val = [_f for _f in val.split(',') if _f] + val = [mapdict.get(v, v) for v in val] + notAllowed = [v for v in val if not v in allowedElems] + if notAllowed: + raise ValueError("Invalid value(s) for option: %s" % + ','.join(notAllowed)) + return _ListVariable(val, allowedElems) + + +## def _validator(key, val, env): +## """ +## """ +## # todo: write validater for pgk list +## return 1 + + +def ListVariable(key, help, default, names, map={}): + """ + The input parameters describe a 'package list' option, thus they + are returned with the correct converter and validater appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (separated by space). + """ + names_str = 'allowed names: %s' % ' '.join(names) + if SCons.Util.is_List(default): + default = ','.join(default) + help = '\n '.join( + (help, '(all|none|comma-separated list of names)', names_str)) + return (key, help, default, + None, #_validator, + lambda val: _converter(val, names, map)) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Variables/PackageVariable.py b/engine/SCons/Variables/PackageVariable.py new file mode 100644 index 0000000..ffa5db5 --- /dev/null +++ b/engine/SCons/Variables/PackageVariable.py @@ -0,0 +1,106 @@ +"""engine.SCons.Variables.PackageVariable + +This file defines the option type for SCons implementing 'package +activation'. + +To be used whenever a 'package' may be enabled/disabled and the +package path may be specified. + +Usage example: + + Examples: + x11=no (disables X11 support) + x11=yes (will search for the package installation dir) + x11=/usr/local/X11 (will check this path for existance) + + To replace autoconf's --with-xxx=yyy + + opts = Variables() + opts.Add(PackageVariable('x11', + 'use X11 installed here (yes = search some places', + 'yes')) + ... + if env['x11'] == True: + dir = ... search X11 in some standard places ... + env['x11'] = dir + if env['x11']: + ... build with x11 ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Variables/PackageVariable.py 5357 2011/09/09 21:31:03 bdeegan" + +__all__ = ['PackageVariable',] + +import SCons.Errors + +__enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search') +__disable_strings = ('0', 'no', 'false', 'off', 'disable') + +def _converter(val): + """ + """ + lval = val.lower() + if lval in __enable_strings: return True + if lval in __disable_strings: return False + #raise ValueError("Invalid value for boolean option: %s" % val) + return val + + +def _validator(key, val, env, searchfunc): + # NB: searchfunc is currenty undocumented and unsupported + """ + """ + # todo: write validator, check for path + import os + if env[key] is True: + if searchfunc: + env[key] = searchfunc(key, val) + elif env[key] and not os.path.exists(val): + raise SCons.Errors.UserError( + 'Path does not exist for option %s: %s' % (key, val)) + + +def PackageVariable(key, help, default, searchfunc=None): + # NB: searchfunc is currenty undocumented and unsupported + """ + The input parameters describe a 'package list' option, thus they + are returned with the correct converter and validator appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (seperated by space). + """ + help = '\n '.join( + (help, '( yes | no | /path/to/%s )' % key)) + return (key, help, default, + lambda k, v, e: _validator(k,v,e,searchfunc), + _converter) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Variables/PathVariable.py b/engine/SCons/Variables/PathVariable.py new file mode 100644 index 0000000..1076a8d --- /dev/null +++ b/engine/SCons/Variables/PathVariable.py @@ -0,0 +1,147 @@ +"""SCons.Variables.PathVariable + +This file defines an option type for SCons implementing path settings. + +To be used whenever a a user-specified path override should be allowed. + +Arguments to PathVariable are: + option-name = name of this option on the command line (e.g. "prefix") + option-help = help string for option + option-dflt = default value for this option + validator = [optional] validator for option value. Predefined + validators are: + + PathAccept -- accepts any path setting; no validation + PathIsDir -- path must be an existing directory + PathIsDirCreate -- path must be a dir; will create + PathIsFile -- path must be a file + PathExists -- path must exist (any type) [default] + + The validator is a function that is called and which + should return True or False to indicate if the path + is valid. The arguments to the validator function + are: (key, val, env). The key is the name of the + option, the val is the path specified for the option, + and the env is the env to which the Otions have been + added. + +Usage example: + + Examples: + prefix=/usr/local + + opts = Variables() + + opts = Variables() + opts.Add(PathVariable('qtdir', + 'where the root of Qt is installed', + qtdir, PathIsDir)) + opts.Add(PathVariable('qt_includes', + 'where the Qt includes are installed', + '$qtdir/includes', PathIsDirCreate)) + opts.Add(PathVariable('qt_libraries', + 'where the Qt library is installed', + '$qtdir/lib')) + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Variables/PathVariable.py 5357 2011/09/09 21:31:03 bdeegan" + +__all__ = ['PathVariable',] + +import os +import os.path + +import SCons.Errors + +class _PathVariableClass(object): + + def PathAccept(self, key, val, env): + """Accepts any path, no checking done.""" + pass + + def PathIsDir(self, key, val, env): + """Validator to check if Path is a directory.""" + if not os.path.isdir(val): + if os.path.isfile(val): + m = 'Directory path for option %s is a file: %s' + else: + m = 'Directory path for option %s does not exist: %s' + raise SCons.Errors.UserError(m % (key, val)) + + def PathIsDirCreate(self, key, val, env): + """Validator to check if Path is a directory, + creating it if it does not exist.""" + if os.path.isfile(val): + m = 'Path for option %s is a file, not a directory: %s' + raise SCons.Errors.UserError(m % (key, val)) + if not os.path.isdir(val): + os.makedirs(val) + + def PathIsFile(self, key, val, env): + """validator to check if Path is a file""" + if not os.path.isfile(val): + if os.path.isdir(val): + m = 'File path for option %s is a directory: %s' + else: + m = 'File path for option %s does not exist: %s' + raise SCons.Errors.UserError(m % (key, val)) + + def PathExists(self, key, val, env): + """validator to check if Path exists""" + if not os.path.exists(val): + m = 'Path for option %s does not exist: %s' + raise SCons.Errors.UserError(m % (key, val)) + + def __call__(self, key, help, default, validator=None): + # NB: searchfunc is currenty undocumented and unsupported + """ + The input parameters describe a 'path list' option, thus they + are returned with the correct converter and validator appended. The + result is usable for input to opts.Add() . + + The 'default' option specifies the default path to use if the + user does not specify an override with this option. + + validator is a validator, see this file for examples + """ + if validator is None: + validator = self.PathExists + + if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): + return (key, '%s ( /path/to/%s )' % (help, key[0]), default, + validator, None) + else: + return (key, '%s ( /path/to/%s )' % (help, key), default, + validator, None) + +PathVariable = _PathVariableClass() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Variables/__init__.py b/engine/SCons/Variables/__init__.py new file mode 100644 index 0000000..3c5ca53 --- /dev/null +++ b/engine/SCons/Variables/__init__.py @@ -0,0 +1,312 @@ +"""engine.SCons.Variables + +This file defines the Variables class that is used to add user-friendly +customizable variables to an SCons build. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/engine/SCons/Variables/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +import os.path +import sys + +import SCons.Environment +import SCons.Errors +import SCons.Util +import SCons.Warnings + +from BoolVariable import BoolVariable # okay +from EnumVariable import EnumVariable # okay +from ListVariable import ListVariable # naja +from PackageVariable import PackageVariable # naja +from PathVariable import PathVariable # okay + + +class Variables(object): + instance=None + + """ + Holds all the options, updates the environment with the variables, + and renders the help text. + """ + def __init__(self, files=[], args={}, is_global=1): + """ + files - [optional] List of option configuration files to load + (backward compatibility) If a single string is passed it is + automatically placed in a file list + """ + self.options = [] + self.args = args + if not SCons.Util.is_List(files): + if files: + files = [ files ] + else: + files = [] + self.files = files + self.unknown = {} + + # create the singleton instance + if is_global: + self=Variables.instance + + if not Variables.instance: + Variables.instance=self + + def _do_add(self, key, help="", default=None, validator=None, converter=None): + class Variable(object): + pass + + option = Variable() + + # if we get a list or a tuple, we take the first element as the + # option key and store the remaining in aliases. + if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): + option.key = key[0] + option.aliases = key[1:] + else: + option.key = key + option.aliases = [ key ] + option.help = help + option.default = default + option.validator = validator + option.converter = converter + + self.options.append(option) + + # options might be added after the 'unknown' dict has been set up, + # so we remove the key and all its aliases from that dict + for alias in list(option.aliases) + [ option.key ]: + if alias in self.unknown: + del self.unknown[alias] + + def keys(self): + """ + Returns the keywords for the options + """ + return [o.key for o in self.options] + + def Add(self, key, help="", default=None, validator=None, converter=None, **kw): + """ + Add an option. + + key - the name of the variable, or a list or tuple of arguments + help - optional help text for the options + default - optional default value + validator - optional function that is called to validate the option's value + Called with (key, value, environment) + converter - optional function that is called to convert the option's value before + putting it in the environment. + """ + + if SCons.Util.is_List(key) or isinstance(key, tuple): + self._do_add(*key) + return + + if not SCons.Util.is_String(key) or \ + not SCons.Environment.is_valid_construction_var(key): + raise SCons.Errors.UserError("Illegal Variables.Add() key `%s'" % str(key)) + + self._do_add(key, help, default, validator, converter) + + def AddVariables(self, *optlist): + """ + Add a list of options. + + Each list element is a tuple/list of arguments to be passed on + to the underlying method for adding options. + + Example: + opt.AddVariables( + ('debug', '', 0), + ('CC', 'The C compiler'), + ('VALIDATE', 'An option for testing validation', 'notset', + validator, None), + ) + """ + for o in optlist: + self._do_add(*o) + + + def Update(self, env, args=None): + """ + Update an environment with the option variables. + + env - the environment to update. + """ + + values = {} + + # first set the defaults: + for option in self.options: + if not option.default is None: + values[option.key] = option.default + + # next set the value specified in the options file + for filename in self.files: + if os.path.exists(filename): + dir = os.path.split(os.path.abspath(filename))[0] + if dir: + sys.path.insert(0, dir) + try: + values['__name__'] = filename + exec open(filename, 'rU').read() in {}, values + finally: + if dir: + del sys.path[0] + del values['__name__'] + + # set the values specified on the command line + if args is None: + args = self.args + + for arg, value in args.items(): + added = False + for option in self.options: + if arg in list(option.aliases) + [ option.key ]: + values[option.key] = value + added = True + if not added: + self.unknown[arg] = value + + # put the variables in the environment: + # (don't copy over variables that are not declared as options) + for option in self.options: + try: + env[option.key] = values[option.key] + except KeyError: + pass + + # Call the convert functions: + for option in self.options: + if option.converter and option.key in values: + value = env.subst('${%s}'%option.key) + try: + try: + env[option.key] = option.converter(value) + except TypeError: + env[option.key] = option.converter(value, env) + except ValueError, x: + raise SCons.Errors.UserError('Error converting option: %s\n%s'%(option.key, x)) + + + # Finally validate the values: + for option in self.options: + if option.validator and option.key in values: + option.validator(option.key, env.subst('${%s}'%option.key), env) + + def UnknownVariables(self): + """ + Returns any options in the specified arguments lists that + were not known, declared options in this object. + """ + return self.unknown + + def Save(self, filename, env): + """ + Saves all the options in the given file. This file can + then be used to load the options next run. This can be used + to create an option cache file. + + filename - Name of the file to save into + env - the environment get the option values from + """ + + # Create the file and write out the header + try: + fh = open(filename, 'w') + + try: + # Make an assignment in the file for each option + # within the environment that was assigned a value + # other than the default. + for option in self.options: + try: + value = env[option.key] + try: + prepare = value.prepare_to_store + except AttributeError: + try: + eval(repr(value)) + except KeyboardInterrupt: + raise + except: + # Convert stuff that has a repr() that + # cannot be evaluated into a string + value = SCons.Util.to_String(value) + else: + value = prepare() + + defaultVal = env.subst(SCons.Util.to_String(option.default)) + if option.converter: + defaultVal = option.converter(defaultVal) + + if str(env.subst('${%s}' % option.key)) != str(defaultVal): + fh.write('%s = %s\n' % (option.key, repr(value))) + except KeyError: + pass + finally: + fh.close() + + except IOError, x: + raise SCons.Errors.UserError('Error writing options to file: %s\n%s' % (filename, x)) + + def GenerateHelpText(self, env, sort=None): + """ + Generate the help text for the options. + + env - an environment that is used to get the current values + of the options. + """ + + if sort: + options = sorted(self.options, key=lambda x: x.key) + else: + options = self.options + + def format(opt, self=self, env=env): + if opt.key in env: + actual = env.subst('${%s}' % opt.key) + else: + actual = None + return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases) + lines = [_f for _f in map(format, options) if _f] + + return ''.join(lines) + + format = '\n%s: %s\n default: %s\n actual: %s\n' + format_ = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n' + + def FormatVariableHelpText(self, env, key, help, default, actual, aliases=[]): + # Don't display the key name itself as an alias. + aliases = [a for a in aliases if a != key] + if len(aliases)==0: + return self.format % (key, help, default, actual) + else: + return self.format_ % (key, help, default, actual, aliases) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Warnings.py b/engine/SCons/Warnings.py new file mode 100644 index 0000000..f20e65e --- /dev/null +++ b/engine/SCons/Warnings.py @@ -0,0 +1,246 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +"""SCons.Warnings + +This file implements the warnings framework for SCons. + +""" + +__revision__ = "src/engine/SCons/Warnings.py 5357 2011/09/09 21:31:03 bdeegan" + +import sys + +import SCons.Errors + +class Warning(SCons.Errors.UserError): + pass + +class WarningOnByDefault(Warning): + pass + + +# NOTE: If you add a new warning class, add it to the man page, too! + +class CacheWriteErrorWarning(Warning): + pass + +class CorruptSConsignWarning(WarningOnByDefault): + pass + +class DependencyWarning(Warning): + pass + +class DuplicateEnvironmentWarning(WarningOnByDefault): + pass + +class FutureReservedVariableWarning(WarningOnByDefault): + pass + +class LinkWarning(WarningOnByDefault): + pass + +class MisleadingKeywordsWarning(WarningOnByDefault): + pass + +class MissingSConscriptWarning(WarningOnByDefault): + pass + +class NoMD5ModuleWarning(WarningOnByDefault): + pass + +class NoMetaclassSupportWarning(WarningOnByDefault): + pass + +class NoObjectCountWarning(WarningOnByDefault): + pass + +class NoParallelSupportWarning(WarningOnByDefault): + pass + +class ReservedVariableWarning(WarningOnByDefault): + pass + +class StackSizeWarning(WarningOnByDefault): + pass + +class VisualCMissingWarning(WarningOnByDefault): + pass + +# Used when MSVC_VERSION and MSVS_VERSION do not point to the +# same version (MSVS_VERSION is deprecated) +class VisualVersionMismatch(WarningOnByDefault): + pass + +class VisualStudioMissingWarning(Warning): + pass + +class FortranCxxMixWarning(LinkWarning): + pass + + +# Deprecation warnings + +class FutureDeprecatedWarning(Warning): + pass + +class DeprecatedWarning(Warning): + pass + +class MandatoryDeprecatedWarning(DeprecatedWarning): + pass + + +# Special case; base always stays DeprecatedWarning +class PythonVersionWarning(DeprecatedWarning): + pass + +class DeprecatedSourceCodeWarning(FutureDeprecatedWarning): + pass + +class DeprecatedBuildDirWarning(DeprecatedWarning): + pass + +class TaskmasterNeedsExecuteWarning(DeprecatedWarning): + pass + +class DeprecatedCopyWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedOptionsWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedSourceSignaturesWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedTargetSignaturesWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedDebugOptionsWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedSigModuleWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedBuilderKeywordsWarning(MandatoryDeprecatedWarning): + pass + + +# The below is a list of 2-tuples. The first element is a class object. +# The second element is true if that class is enabled, false if it is disabled. +_enabled = [] + +# If set, raise the warning as an exception +_warningAsException = 0 + +# If not None, a function to call with the warning +_warningOut = None + +def suppressWarningClass(clazz): + """Suppresses all warnings that are of type clazz or + derived from clazz.""" + _enabled.insert(0, (clazz, 0)) + +def enableWarningClass(clazz): + """Enables all warnings that are of type clazz or + derived from clazz.""" + _enabled.insert(0, (clazz, 1)) + +def warningAsException(flag=1): + """Turn warnings into exceptions. Returns the old value of the flag.""" + global _warningAsException + old = _warningAsException + _warningAsException = flag + return old + +def warn(clazz, *args): + global _enabled, _warningAsException, _warningOut + + warning = clazz(args) + for clazz, flag in _enabled: + if isinstance(warning, clazz): + if flag: + if _warningAsException: + raise warning + + if _warningOut: + _warningOut(warning) + break + +def process_warn_strings(arguments): + """Process string specifications of enabling/disabling warnings, + as passed to the --warn option or the SetOption('warn') function. + + + An argument to this option should be of the form + or no-. The warning class is munged in order + to get an actual class name from the classes above, which we + need to pass to the {enable,disable}WarningClass() functions. + The supplied is split on hyphens, each element + is capitalized, then smushed back together. Then the string + "Warning" is appended to get the class name. + + For example, 'deprecated' will enable the DeprecatedWarning + class. 'no-dependency' will disable the DependencyWarning class. + + As a special case, --warn=all and --warn=no-all will enable or + disable (respectively) the base Warning class of all warnings. + + """ + + def _capitalize(s): + if s[:5] == "scons": + return "SCons" + s[5:] + else: + return s.capitalize() + + for arg in arguments: + + elems = arg.lower().split('-') + enable = 1 + if elems[0] == 'no': + enable = 0 + del elems[0] + + if len(elems) == 1 and elems[0] == 'all': + class_name = "Warning" + else: + class_name = ''.join(map(_capitalize, elems)) + "Warning" + try: + clazz = globals()[class_name] + except KeyError: + sys.stderr.write("No warning type: '%s'\n" % arg) + else: + if enable: + enableWarningClass(clazz) + elif issubclass(clazz, MandatoryDeprecatedWarning): + fmt = "Can not disable mandataory warning: '%s'\n" + sys.stderr.write(fmt % arg) + else: + suppressWarningClass(clazz) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/__init__.py b/engine/SCons/__init__.py new file mode 100644 index 0000000..950b314 --- /dev/null +++ b/engine/SCons/__init__.py @@ -0,0 +1,49 @@ +"""SCons + +The main package for the SCons software construction utility. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +__version__ = "2.1.0" + +__build__ = "r5357[MODIFIED]" + +__buildsys__ = "ubuntu" + +__date__ = "2011/09/09 21:31:03" + +__developer__ = "bdeegan" + +# make sure compatibility is always in place +import SCons.compat + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/compat/__init__.py b/engine/SCons/compat/__init__.py new file mode 100644 index 0000000..39edff2 --- /dev/null +++ b/engine/SCons/compat/__init__.py @@ -0,0 +1,237 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__doc__ = """ +SCons compatibility package for old Python versions + +This subpackage holds modules that provide backwards-compatible +implementations of various things that we'd like to use in SCons but which +only show up in later versions of Python than the early, old version(s) +we still support. + +Other code will not generally reference things in this package through +the SCons.compat namespace. The modules included here add things to +the builtins namespace or the global module list so that the rest +of our code can use the objects and names imported here regardless of +Python version. + +Simply enough, things that go in the builtins name space come from +our _scons_builtins module. + +The rest of the things here will be in individual compatibility modules +that are either: 1) suitably modified copies of the future modules that +we want to use; or 2) backwards compatible re-implementations of the +specific portions of a future module's API that we want to use. + +GENERAL WARNINGS: Implementations of functions in the SCons.compat +modules are *NOT* guaranteed to be fully compliant with these functions in +later versions of Python. We are only concerned with adding functionality +that we actually use in SCons, so be wary if you lift this code for +other uses. (That said, making these more nearly the same as later, +official versions is still a desirable goal, we just don't need to be +obsessive about it.) + +We name the compatibility modules with an initial '_scons_' (for example, +_scons_subprocess.py is our compatibility module for subprocess) so +that we can still try to import the real module name and fall back to +our compatibility module if we get an ImportError. The import_as() +function defined below loads the module as the "real" name (without the +'_scons'), after which all of the "import {module}" statements in the +rest of our code will find our pre-loaded compatibility module. +""" + +__revision__ = "src/engine/SCons/compat/__init__.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import sys +import imp # Use the "imp" module to protect imports from fixers. + +def import_as(module, name): + """ + Imports the specified module (from our local directory) as the + specified name, returning the loaded module object. + """ + dir = os.path.split(__file__)[0] + return imp.load_module(name, *imp.find_module(module, [dir])) + +def rename_module(new, old): + """ + Attempts to import the old module and load it under the new name. + Used for purely cosmetic name changes in Python 3.x. + """ + try: + sys.modules[new] = imp.load_module(old, *imp.find_module(old)) + return True + except ImportError: + return False + + +rename_module('builtins', '__builtin__') +import _scons_builtins + + +try: + import hashlib +except ImportError: + # Pre-2.5 Python has no hashlib module. + try: + import_as('_scons_hashlib', 'hashlib') + except ImportError: + # If we failed importing our compatibility module, it probably + # means this version of Python has no md5 module. Don't do + # anything and let the higher layer discover this fact, so it + # can fall back to using timestamp. + pass + +try: + set +except NameError: + # Pre-2.4 Python has no native set type + import_as('_scons_sets', 'sets') + import builtins, sets + builtins.set = sets.Set + + +try: + import collections +except ImportError: + # Pre-2.4 Python has no collections module. + import_as('_scons_collections', 'collections') +else: + try: + collections.UserDict + except AttributeError: + exec('from UserDict import UserDict as _UserDict') + collections.UserDict = _UserDict + del _UserDict + try: + collections.UserList + except AttributeError: + exec('from UserList import UserList as _UserList') + collections.UserList = _UserList + del _UserList + try: + collections.UserString + except AttributeError: + exec('from UserString import UserString as _UserString') + collections.UserString = _UserString + del _UserString + + +try: + import io +except ImportError: + # Pre-2.6 Python has no io module. + import_as('_scons_io', 'io') + + +try: + os.devnull +except AttributeError: + # Pre-2.4 Python has no os.devnull attribute + _names = sys.builtin_module_names + if 'posix' in _names: + os.devnull = '/dev/null' + elif 'nt' in _names: + os.devnull = 'nul' + os.path.devnull = os.devnull +try: + os.path.lexists +except AttributeError: + # Pre-2.4 Python has no os.path.lexists function + def lexists(path): + return os.path.exists(path) or os.path.islink(path) + os.path.lexists = lexists + + +# When we're using the '-3' option during regression tests, importing +# cPickle gives a warning no matter how it's done, so always use the +# real profile module, whether it's fast or not. +if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is None: + # Not a regression test with '-3', so try to use faster version. + # In 3.x, 'pickle' automatically loads the fast version if available. + rename_module('pickle', 'cPickle') + + +# In 3.x, 'profile' automatically loads the fast version if available. +rename_module('profile', 'cProfile') + + +# Before Python 3.0, the 'queue' module was named 'Queue'. +rename_module('queue', 'Queue') + + +# Before Python 3.0, the 'winreg' module was named '_winreg' +rename_module('winreg', '_winreg') + + +try: + import subprocess +except ImportError: + # Pre-2.4 Python has no subprocess module. + import_as('_scons_subprocess', 'subprocess') + +try: + sys.intern +except AttributeError: + # Pre-2.6 Python has no sys.intern() function. + import builtins + try: + sys.intern = builtins.intern + except AttributeError: + # Pre-2.x Python has no builtin intern() function. + def intern(x): + return x + sys.intern = intern + del intern +try: + sys.maxsize +except AttributeError: + # Pre-2.6 Python has no sys.maxsize attribute + # Wrapping sys in () is silly, but protects it from 2to3 renames fixer + sys.maxsize = (sys).maxint + + +if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: + # We can't apply the 'callable' fixer until the floor is 2.6, but the + # '-3' option to Python 2.6 and 2.7 generates almost ten thousand + # warnings. This hack allows us to run regression tests with the '-3' + # option by replacing the callable() built-in function with a hack + # that performs the same function but doesn't generate the warning. + # Note that this hack is ONLY intended to be used for regression + # testing, and should NEVER be used for real runs. + from types import ClassType + def callable(obj): + if hasattr(obj, '__call__'): return True + if isinstance(obj, (ClassType, type)): return True + return False + import builtins + builtins.callable = callable + del callable + + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/compat/_scons_builtins.py b/engine/SCons/compat/_scons_builtins.py new file mode 100644 index 0000000..63dde01 --- /dev/null +++ b/engine/SCons/compat/_scons_builtins.py @@ -0,0 +1,150 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +# Portions of the following are derived from the compat.py file in +# Twisted, under the following copyright: +# +# Copyright (c) 2001-2004 Twisted Matrix Laboratories + +__doc__ = """ +Compatibility idioms for builtins names + +This module adds names to the builtins module for things that we want +to use in SCons but which don't show up until later Python versions than +the earliest ones we support. + +This module checks for the following builtins names: + + all() + any() + sorted() + memoryview() + +Implementations of functions are *NOT* guaranteed to be fully compliant +with these functions in later versions of Python. We are only concerned +with adding functionality that we actually use in SCons, so be wary +if you lift this code for other uses. (That said, making these more +nearly the same as later, official versions is still a desirable goal, +we just don't need to be obsessive about it.) + +If you're looking at this with pydoc and various names don't show up in +the FUNCTIONS or DATA output, that means those names are already built in +to this version of Python and we don't need to add them from this module. +""" + +__revision__ = "src/engine/SCons/compat/_scons_builtins.py 5357 2011/09/09 21:31:03 bdeegan" + +import builtins + +try: + all +except NameError: + # Pre-2.5 Python has no all() function. + def all(iterable): + """ + Returns True if all elements of the iterable are true. + """ + for element in iterable: + if not element: + return False + return True + builtins.all = all + all = all + +try: + any +except NameError: + # Pre-2.5 Python has no any() function. + def any(iterable): + """ + Returns True if any element of the iterable is true. + """ + for element in iterable: + if element: + return True + return False + builtins.any = any + any = any + +try: + memoryview +except NameError: + # Pre-2.7 doesn't have the memoryview() built-in. + class memoryview(object): + def __init__(self, obj): + # wrapping buffer in () keeps the fixer from changing it + self.obj = (buffer)(obj) + def __getitem__(self, indx): + if isinstance(indx, slice): + return self.obj[indx.start:indx.stop] + else: + return self.obj[indx] + builtins.memoryview = memoryview + +try: + sorted +except NameError: + # Pre-2.4 Python has no sorted() function. + # + # The pre-2.4 Python list.sort() method does not support + # list.sort(key=) nor list.sort(reverse=) keyword arguments, so + # we must implement the functionality of those keyword arguments + # by hand instead of passing them to list.sort(). + def sorted(iterable, cmp=None, key=None, reverse=False): + if key is not None: + result = [(key(x), x) for x in iterable] + else: + result = iterable[:] + if cmp is None: + # Pre-2.3 Python does not support list.sort(None). + result.sort() + else: + result.sort(cmp) + if key is not None: + result = [t1 for t0,t1 in result] + if reverse: + result.reverse() + return result + builtins.sorted = sorted + +#if sys.version_info[:3] in ((2, 2, 0), (2, 2, 1)): +# def lstrip(s, c=string.whitespace): +# while s and s[0] in c: +# s = s[1:] +# return s +# def rstrip(s, c=string.whitespace): +# while s and s[-1] in c: +# s = s[:-1] +# return s +# def strip(s, c=string.whitespace, l=lstrip, r=rstrip): +# return l(r(s, c), c) +# +# object.__setattr__(str, 'lstrip', lstrip) +# object.__setattr__(str, 'rstrip', rstrip) +# object.__setattr__(str, 'strip', strip) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/compat/_scons_collections.py b/engine/SCons/compat/_scons_collections.py new file mode 100644 index 0000000..8975b44 --- /dev/null +++ b/engine/SCons/compat/_scons_collections.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__doc__ = """ +collections compatibility module for older (pre-2.4) Python versions + +This does not not NOT (repeat, *NOT*) provide complete collections +functionality. It only wraps the portions of collections functionality +used by SCons, in an interface that looks enough like collections for +our purposes. +""" + +__revision__ = "src/engine/SCons/compat/_scons_collections.py 5357 2011/09/09 21:31:03 bdeegan" + +# Use exec to hide old names from fixers. +exec("""if True: + from UserDict import UserDict + from UserList import UserList + from UserString import UserString""") + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/compat/_scons_dbm.py b/engine/SCons/compat/_scons_dbm.py new file mode 100644 index 0000000..e8f7e4b --- /dev/null +++ b/engine/SCons/compat/_scons_dbm.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__doc__ = """ +dbm compatibility module for Python versions that don't have dbm. + +This does not not NOT (repeat, *NOT*) provide complete dbm functionality. +It's just a stub on which to hang just enough pieces of dbm functionality +that the whichdb.whichdb() implementstation in the various 2.X versions of +Python won't blow up even if dbm wasn't compiled in. +""" + +__revision__ = "src/engine/SCons/compat/_scons_dbm.py 5357 2011/09/09 21:31:03 bdeegan" + +class error(Exception): + pass + +def open(*args, **kw): + raise error() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/compat/_scons_hashlib.py b/engine/SCons/compat/_scons_hashlib.py new file mode 100644 index 0000000..991e29a --- /dev/null +++ b/engine/SCons/compat/_scons_hashlib.py @@ -0,0 +1,76 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__doc__ = """ +hashlib backwards-compatibility module for older (pre-2.5) Python versions + +This does not not NOT (repeat, *NOT*) provide complete hashlib +functionality. It only wraps the portions of MD5 functionality used +by SCons, in an interface that looks like hashlib (or enough for our +purposes, anyway). In fact, this module will raise an ImportError if +the underlying md5 module isn't available. +""" + +__revision__ = "src/engine/SCons/compat/_scons_hashlib.py 5357 2011/09/09 21:31:03 bdeegan" + +import md5 +from string import hexdigits + +class md5obj(object): + + md5_module = md5 + + def __init__(self, name, string=''): + if not name in ('MD5', 'md5'): + raise ValueError("unsupported hash type") + self.name = 'md5' + self.m = self.md5_module.md5() + + def __repr__(self): + return '<%s HASH object @ %#x>' % (self.name, id(self)) + + def copy(self): + import copy + result = copy.copy(self) + result.m = self.m.copy() + return result + + def digest(self): + return self.m.digest() + + def update(self, arg): + return self.m.update(arg) + + def hexdigest(self): + return self.m.hexdigest() + +new = md5obj + +def md5(string=''): + return md5obj('md5', string) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/compat/_scons_io.py b/engine/SCons/compat/_scons_io.py new file mode 100644 index 0000000..9b04e98 --- /dev/null +++ b/engine/SCons/compat/_scons_io.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__doc__ = """ +io compatibility module for older (pre-2.6) Python versions + +This does not not NOT (repeat, *NOT*) provide complete io +functionality. It only wraps the portions of io functionality used +by SCons, in an interface that looks enough like io for our purposes. +""" + +__revision__ = "src/engine/SCons/compat/_scons_io.py 5357 2011/09/09 21:31:03 bdeegan" + +# Use the "imp" module to protect the imports below from fixers. +import imp + +_cStringIO = imp.load_module('cStringIO', *imp.find_module('cStringIO')) +StringIO = _cStringIO.StringIO +del _cStringIO + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/compat/_scons_sets.py b/engine/SCons/compat/_scons_sets.py new file mode 100644 index 0000000..0fde994 --- /dev/null +++ b/engine/SCons/compat/_scons_sets.py @@ -0,0 +1,563 @@ +"""Classes to represent arbitrary sets (including sets of sets). + +This module implements sets using dictionaries whose values are +ignored. The usual operations (union, intersection, deletion, etc.) +are provided as both methods and operators. + +Important: sets are not sequences! While they support 'x in s', +'len(s)', and 'for x in s', none of those operations are unique for +sequences; for example, mappings support all three as well. The +characteristic operation for sequences is subscripting with small +integers: s[i], for i in range(len(s)). Sets don't support +subscripting at all. Also, sequences allow multiple occurrences and +their elements have a definite order; sets on the other hand don't +record multiple occurrences and don't remember the order of element +insertion (which is why they don't support s[i]). + +The following classes are provided: + +BaseSet -- All the operations common to both mutable and immutable + sets. This is an abstract class, not meant to be directly + instantiated. + +Set -- Mutable sets, subclass of BaseSet; not hashable. + +ImmutableSet -- Immutable sets, subclass of BaseSet; hashable. + An iterable argument is mandatory to create an ImmutableSet. + +_TemporarilyImmutableSet -- A wrapper around a Set, hashable, + giving the same hash value as the immutable set equivalent + would have. Do not use this class directly. + +Only hashable objects can be added to a Set. In particular, you cannot +really add a Set as an element to another Set; if you try, what is +actually added is an ImmutableSet built from it (it compares equal to +the one you tried adding). + +When you ask if `x in y' where x is a Set and y is a Set or +ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and +what's tested is actually `z in y'. + +""" + +# Code history: +# +# - Greg V. Wilson wrote the first version, using a different approach +# to the mutable/immutable problem, and inheriting from dict. +# +# - Alex Martelli modified Greg's version to implement the current +# Set/ImmutableSet approach, and make the data an attribute. +# +# - Guido van Rossum rewrote much of the code, made some API changes, +# and cleaned up the docstrings. +# +# - Raymond Hettinger added a number of speedups and other +# improvements. + +# protect this import from the fixers... +exec('from itertools import ifilterfalse as filterfalse') + +__all__ = ['BaseSet', 'Set', 'ImmutableSet'] + +class BaseSet(object): + """Common base class for mutable and immutable sets.""" + + __slots__ = ['_data'] + + # Constructor + + def __init__(self): + """This is an abstract class.""" + # Don't call this from a concrete subclass! + if self.__class__ is BaseSet: + raise TypeError("BaseSet is an abstract class. " + "Use Set or ImmutableSet.") + + # Standard protocols: __len__, __repr__, __str__, __iter__ + + def __len__(self): + """Return the number of elements of a set.""" + return len(self._data) + + def __repr__(self): + """Return string representation of a set. + + This looks like 'Set([])'. + """ + return self._repr() + + # __str__ is the same as __repr__ + __str__ = __repr__ + + def _repr(self, sort_them=False): + elements = list(self._data.keys()) + if sort_them: + elements.sort() + return '%s(%r)' % (self.__class__.__name__, elements) + + def __iter__(self): + """Return an iterator over the elements or a set. + + This is the keys iterator for the underlying dict. + """ + # Wrapping name in () prevents fixer from "fixing" this + return (self._data.iterkeys)() + + # Three-way comparison is not supported. However, because __eq__ is + # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and + # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this + # case). + + def __cmp__(self, other): + raise TypeError("can't compare sets using cmp()") + + # Equality comparisons using the underlying dicts. Mixed-type comparisons + # are allowed here, where Set == z for non-Set z always returns False, + # and Set != z always True. This allows expressions like "x in y" to + # give the expected result when y is a sequence of mixed types, not + # raising a pointless TypeError just because y contains a Set, or x is + # a Set and y contain's a non-set ("in" invokes only __eq__). + # Subtle: it would be nicer if __eq__ and __ne__ could return + # NotImplemented instead of True or False. Then the other comparand + # would get a chance to determine the result, and if the other comparand + # also returned NotImplemented then it would fall back to object address + # comparison (which would always return False for __eq__ and always + # True for __ne__). However, that doesn't work, because this type + # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented, + # Python tries __cmp__ next, and the __cmp__ here then raises TypeError. + + def __eq__(self, other): + if isinstance(other, BaseSet): + return self._data == other._data + else: + return False + + def __ne__(self, other): + if isinstance(other, BaseSet): + return self._data != other._data + else: + return True + + # Copying operations + + def copy(self): + """Return a shallow copy of a set.""" + result = self.__class__() + result._data.update(self._data) + return result + + __copy__ = copy # For the copy module + + def __deepcopy__(self, memo): + """Return a deep copy of a set; used by copy module.""" + # This pre-creates the result and inserts it in the memo + # early, in case the deep copy recurses into another reference + # to this same set. A set can't be an element of itself, but + # it can certainly contain an object that has a reference to + # itself. + from copy import deepcopy + result = self.__class__() + memo[id(self)] = result + data = result._data + value = True + for elt in self: + data[deepcopy(elt, memo)] = value + return result + + # Standard set operations: union, intersection, both differences. + # Each has an operator version (e.g. __or__, invoked with |) and a + # method version (e.g. union). + # Subtle: Each pair requires distinct code so that the outcome is + # correct when the type of other isn't suitable. For example, if + # we did "union = __or__" instead, then Set().union(3) would return + # NotImplemented instead of raising TypeError (albeit that *why* it + # raises TypeError as-is is also a bit subtle). + + def __or__(self, other): + """Return the union of two sets as a new set. + + (I.e. all elements that are in either set.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.union(other) + + def union(self, other): + """Return the union of two sets as a new set. + + (I.e. all elements that are in either set.) + """ + result = self.__class__(self) + result._update(other) + return result + + def __and__(self, other): + """Return the intersection of two sets as a new set. + + (I.e. all elements that are in both sets.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.intersection(other) + + def intersection(self, other): + """Return the intersection of two sets as a new set. + + (I.e. all elements that are in both sets.) + """ + if not isinstance(other, BaseSet): + other = Set(other) + if len(self) <= len(other): + little, big = self, other + else: + little, big = other, self + common = iter(filter(big._data.has_key, little)) + return self.__class__(common) + + def __xor__(self, other): + """Return the symmetric difference of two sets as a new set. + + (I.e. all elements that are in exactly one of the sets.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.symmetric_difference(other) + + def symmetric_difference(self, other): + """Return the symmetric difference of two sets as a new set. + + (I.e. all elements that are in exactly one of the sets.) + """ + result = self.__class__() + data = result._data + value = True + selfdata = self._data + try: + otherdata = other._data + except AttributeError: + otherdata = Set(other)._data + for elt in filterfalse(otherdata.has_key, selfdata): + data[elt] = value + for elt in filterfalse(selfdata.has_key, otherdata): + data[elt] = value + return result + + def __sub__(self, other): + """Return the difference of two sets as a new Set. + + (I.e. all elements that are in this set and not in the other.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.difference(other) + + def difference(self, other): + """Return the difference of two sets as a new Set. + + (I.e. all elements that are in this set and not in the other.) + """ + result = self.__class__() + data = result._data + try: + otherdata = other._data + except AttributeError: + otherdata = Set(other)._data + value = True + for elt in filterfalse(otherdata.has_key, self): + data[elt] = value + return result + + # Membership test + + def __contains__(self, element): + """Report whether an element is a member of a set. + + (Called in response to the expression `element in self'.) + """ + try: + return element in self._data + except TypeError: + transform = getattr(element, "__as_temporarily_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + return transform() in self._data + + # Subset and superset test + + def issubset(self, other): + """Report whether another set contains this set.""" + self._binary_sanity_check(other) + if len(self) > len(other): # Fast check for obvious cases + return False + for elt in filterfalse(other._data.has_key, self): + return False + return True + + def issuperset(self, other): + """Report whether this set contains another set.""" + self._binary_sanity_check(other) + if len(self) < len(other): # Fast check for obvious cases + return False + for elt in filterfalse(self._data.has_key, other): + return False + return True + + # Inequality comparisons using the is-subset relation. + __le__ = issubset + __ge__ = issuperset + + def __lt__(self, other): + self._binary_sanity_check(other) + return len(self) < len(other) and self.issubset(other) + + def __gt__(self, other): + self._binary_sanity_check(other) + return len(self) > len(other) and self.issuperset(other) + + # Assorted helpers + + def _binary_sanity_check(self, other): + # Check that the other argument to a binary operation is also + # a set, raising a TypeError otherwise. + if not isinstance(other, BaseSet): + raise TypeError("Binary operation only permitted between sets") + + def _compute_hash(self): + # Calculate hash code for a set by xor'ing the hash codes of + # the elements. This ensures that the hash code does not depend + # on the order in which elements are added to the set. This is + # not called __hash__ because a BaseSet should not be hashable; + # only an ImmutableSet is hashable. + result = 0 + for elt in self: + result ^= hash(elt) + return result + + def _update(self, iterable): + # The main loop for update() and the subclass __init__() methods. + data = self._data + + # Use the fast update() method when a dictionary is available. + if isinstance(iterable, BaseSet): + data.update(iterable._data) + return + + value = True + + if type(iterable) in (list, tuple, xrange): + # Optimized: we know that __iter__() and next() can't + # raise TypeError, so we can move 'try:' out of the loop. + it = iter(iterable) + while True: + try: + for element in it: + data[element] = value + return + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + data[transform()] = value + else: + # Safe: only catch TypeError where intended + for element in iterable: + try: + data[element] = value + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + data[transform()] = value + + +class ImmutableSet(BaseSet): + """Immutable set class.""" + + __slots__ = ['_hashcode'] + + # BaseSet + hashing + + def __init__(self, iterable=None): + """Construct an immutable set from an optional iterable.""" + self._hashcode = None + self._data = {} + if iterable is not None: + self._update(iterable) + + def __hash__(self): + if self._hashcode is None: + self._hashcode = self._compute_hash() + return self._hashcode + + def __getstate__(self): + return self._data, self._hashcode + + def __setstate__(self, state): + self._data, self._hashcode = state + +class Set(BaseSet): + """ Mutable set class.""" + + __slots__ = [] + + # BaseSet + operations requiring mutability; no hashing + + def __init__(self, iterable=None): + """Construct a set from an optional iterable.""" + self._data = {} + if iterable is not None: + self._update(iterable) + + def __getstate__(self): + # getstate's results are ignored if it is not + return self._data, + + def __setstate__(self, data): + self._data, = data + + def __hash__(self): + """A Set cannot be hashed.""" + # We inherit object.__hash__, so we must deny this explicitly + raise TypeError("Can't hash a Set, only an ImmutableSet.") + + # In-place union, intersection, differences. + # Subtle: The xyz_update() functions deliberately return None, + # as do all mutating operations on built-in container types. + # The __xyz__ spellings have to return self, though. + + def __ior__(self, other): + """Update a set with the union of itself and another.""" + self._binary_sanity_check(other) + self._data.update(other._data) + return self + + def union_update(self, other): + """Update a set with the union of itself and another.""" + self._update(other) + + def __iand__(self, other): + """Update a set with the intersection of itself and another.""" + self._binary_sanity_check(other) + self._data = (self & other)._data + return self + + def intersection_update(self, other): + """Update a set with the intersection of itself and another.""" + if isinstance(other, BaseSet): + self &= other + else: + self._data = (self.intersection(other))._data + + def __ixor__(self, other): + """Update a set with the symmetric difference of itself and another.""" + self._binary_sanity_check(other) + self.symmetric_difference_update(other) + return self + + def symmetric_difference_update(self, other): + """Update a set with the symmetric difference of itself and another.""" + data = self._data + value = True + if not isinstance(other, BaseSet): + other = Set(other) + if self is other: + self.clear() + for elt in other: + if elt in data: + del data[elt] + else: + data[elt] = value + + def __isub__(self, other): + """Remove all elements of another set from this set.""" + self._binary_sanity_check(other) + self.difference_update(other) + return self + + def difference_update(self, other): + """Remove all elements of another set from this set.""" + data = self._data + if not isinstance(other, BaseSet): + other = Set(other) + if self is other: + self.clear() + for elt in filter(data.has_key, other): + del data[elt] + + # Python dict-like mass mutations: update, clear + + def update(self, iterable): + """Add all values from an iterable (such as a list or file).""" + self._update(iterable) + + def clear(self): + """Remove all elements from this set.""" + self._data.clear() + + # Single-element mutations: add, remove, discard + + def add(self, element): + """Add an element to a set. + + This has no effect if the element is already present. + """ + try: + self._data[element] = True + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + self._data[transform()] = True + + def remove(self, element): + """Remove an element from a set; it must be a member. + + If the element is not a member, raise a KeyError. + """ + try: + del self._data[element] + except TypeError: + transform = getattr(element, "__as_temporarily_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + del self._data[transform()] + + def discard(self, element): + """Remove an element from a set if it is a member. + + If the element is not a member, do nothing. + """ + try: + self.remove(element) + except KeyError: + pass + + def pop(self): + """Remove and return an arbitrary set element.""" + return self._data.popitem()[0] + + def __as_immutable__(self): + # Return a copy of self as an immutable set + return ImmutableSet(self) + + def __as_temporarily_immutable__(self): + # Return self wrapped in a temporarily immutable set + return _TemporarilyImmutableSet(self) + + +class _TemporarilyImmutableSet(BaseSet): + # Wrap a mutable set as if it was temporarily immutable. + # This only supplies hashing and equality comparisons. + + def __init__(self, set): + self._set = set + self._data = set._data # Needed by ImmutableSet.__eq__() + + def __hash__(self): + return self._set._compute_hash() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/compat/_scons_subprocess.py b/engine/SCons/compat/_scons_subprocess.py new file mode 100644 index 0000000..eebe53d --- /dev/null +++ b/engine/SCons/compat/_scons_subprocess.py @@ -0,0 +1,1281 @@ +# subprocess - Subprocesses with accessible I/O streams +# +# For more information about this module, see PEP 324. +# +# This module should remain compatible with Python 2.2, see PEP 291. +# +# Copyright (c) 2003-2005 by Peter Astrand +# +# Licensed to PSF under a Contributor Agreement. +# See http://www.python.org/2.4/license for licensing details. + +r"""subprocess - Subprocesses with accessible I/O streams + +This module allows you to spawn processes, connect to their +input/output/error pipes, and obtain their return codes. This module +intends to replace several other, older modules and functions, like: + +os.system +os.spawn* +os.popen* +popen2.* +commands.* + +Information about how the subprocess module can be used to replace these +modules and functions can be found below. + + + +Using the subprocess module +=========================== +This module defines one class called Popen: + +class Popen(args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0): + + +Arguments are: + +args should be a string, or a sequence of program arguments. The +program to execute is normally the first item in the args sequence or +string, but can be explicitly set by using the executable argument. + +On UNIX, with shell=False (default): In this case, the Popen class +uses os.execvp() to execute the child program. args should normally +be a sequence. A string will be treated as a sequence with the string +as the only item (the program to execute). + +On UNIX, with shell=True: If args is a string, it specifies the +command string to execute through the shell. If args is a sequence, +the first item specifies the command string, and any additional items +will be treated as additional shell arguments. + +On Windows: the Popen class uses CreateProcess() to execute the child +program, which operates on strings. If args is a sequence, it will be +converted to a string using the list2cmdline method. Please note that +not all MS Windows applications interpret the command line the same +way: The list2cmdline is designed for applications using the same +rules as the MS C runtime. + +bufsize, if given, has the same meaning as the corresponding argument +to the built-in open() function: 0 means unbuffered, 1 means line +buffered, any other positive value means use a buffer of +(approximately) that size. A negative bufsize means to use the system +default, which usually means fully buffered. The default value for +bufsize is 0 (unbuffered). + +stdin, stdout and stderr specify the executed programs' standard +input, standard output and standard error file handles, respectively. +Valid values are PIPE, an existing file descriptor (a positive +integer), an existing file object, and None. PIPE indicates that a +new pipe to the child should be created. With None, no redirection +will occur; the child's file handles will be inherited from the +parent. Additionally, stderr can be STDOUT, which indicates that the +stderr data from the applications should be captured into the same +file handle as for stdout. + +If preexec_fn is set to a callable object, this object will be called +in the child process just before the child is executed. + +If close_fds is true, all file descriptors except 0, 1 and 2 will be +closed before the child process is executed. + +if shell is true, the specified command will be executed through the +shell. + +If cwd is not None, the current directory will be changed to cwd +before the child is executed. + +If env is not None, it defines the environment variables for the new +process. + +If universal_newlines is true, the file objects stdout and stderr are +opened as a text files, but lines may be terminated by any of '\n', +the Unix end-of-line convention, '\r', the Macintosh convention or +'\r\n', the Windows convention. All of these external representations +are seen as '\n' by the Python program. Note: This feature is only +available if Python is built with universal newline support (the +default). Also, the newlines attribute of the file objects stdout, +stdin and stderr are not updated by the communicate() method. + +The startupinfo and creationflags, if given, will be passed to the +underlying CreateProcess() function. They can specify things such as +appearance of the main window and priority for the new process. +(Windows only) + + +This module also defines two shortcut functions: + +call(*popenargs, **kwargs): + Run command with arguments. Wait for command to complete, then + return the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + retcode = call(["ls", "-l"]) + +check_call(*popenargs, **kwargs): + Run command with arguments. Wait for command to complete. If the + exit code was zero then return, otherwise raise + CalledProcessError. The CalledProcessError object will have the + return code in the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + check_call(["ls", "-l"]) + +Exceptions +---------- +Exceptions raised in the child process, before the new program has +started to execute, will be re-raised in the parent. Additionally, +the exception object will have one extra attribute called +'child_traceback', which is a string containing traceback information +from the childs point of view. + +The most common exception raised is OSError. This occurs, for +example, when trying to execute a non-existent file. Applications +should prepare for OSErrors. + +A ValueError will be raised if Popen is called with invalid arguments. + +check_call() will raise CalledProcessError, if the called process +returns a non-zero return code. + + +Security +-------- +Unlike some other popen functions, this implementation will never call +/bin/sh implicitly. This means that all characters, including shell +metacharacters, can safely be passed to child processes. + + +Popen objects +============= +Instances of the Popen class have the following methods: + +poll() + Check if child process has terminated. Returns returncode + attribute. + +wait() + Wait for child process to terminate. Returns returncode attribute. + +communicate(input=None) + Interact with process: Send data to stdin. Read data from stdout + and stderr, until end-of-file is reached. Wait for process to + terminate. The optional stdin argument should be a string to be + sent to the child process, or None, if no data should be sent to + the child. + + communicate() returns a tuple (stdout, stderr). + + Note: The data read is buffered in memory, so do not use this + method if the data size is large or unlimited. + +The following attributes are also available: + +stdin + If the stdin argument is PIPE, this attribute is a file object + that provides input to the child process. Otherwise, it is None. + +stdout + If the stdout argument is PIPE, this attribute is a file object + that provides output from the child process. Otherwise, it is + None. + +stderr + If the stderr argument is PIPE, this attribute is file object that + provides error output from the child process. Otherwise, it is + None. + +pid + The process ID of the child process. + +returncode + The child return code. A None value indicates that the process + hasn't terminated yet. A negative value -N indicates that the + child was terminated by signal N (UNIX only). + + +Replacing older functions with the subprocess module +==================================================== +In this section, "a ==> b" means that b can be used as a replacement +for a. + +Note: All functions in this section fail (more or less) silently if +the executed program cannot be found; this module raises an OSError +exception. + +In the following examples, we assume that the subprocess module is +imported with "from subprocess import *". + + +Replacing /bin/sh shell backquote +--------------------------------- +output=`mycmd myarg` +==> +output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] + + +Replacing shell pipe line +------------------------- +output=`dmesg | grep hda` +==> +p1 = Popen(["dmesg"], stdout=PIPE) +p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) +output = p2.communicate()[0] + + +Replacing os.system() +--------------------- +sts = os.system("mycmd" + " myarg") +==> +p = Popen("mycmd" + " myarg", shell=True) +pid, sts = os.waitpid(p.pid, 0) + +Note: + +* Calling the program through the shell is usually not required. + +* It's easier to look at the returncode attribute than the + exitstatus. + +A more real-world example would look like this: + +try: + retcode = call("mycmd" + " myarg", shell=True) + if retcode < 0: + print >>sys.stderr, "Child was terminated by signal", -retcode + else: + print >>sys.stderr, "Child returned", retcode +except OSError, e: + print >>sys.stderr, "Execution failed:", e + + +Replacing os.spawn* +------------------- +P_NOWAIT example: + +pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") +==> +pid = Popen(["/bin/mycmd", "myarg"]).pid + + +P_WAIT example: + +retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") +==> +retcode = call(["/bin/mycmd", "myarg"]) + + +Vector example: + +os.spawnvp(os.P_NOWAIT, path, args) +==> +Popen([path] + args[1:]) + + +Environment example: + +os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) +==> +Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) + + +Replacing os.popen* +------------------- +pipe = os.popen(cmd, mode='r', bufsize) +==> +pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout + +pipe = os.popen(cmd, mode='w', bufsize) +==> +pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin + + +(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) +==> +p = Popen(cmd, shell=True, bufsize=bufsize, + stdin=PIPE, stdout=PIPE, close_fds=True) +(child_stdin, child_stdout) = (p.stdin, p.stdout) + + +(child_stdin, + child_stdout, + child_stderr) = os.popen3(cmd, mode, bufsize) +==> +p = Popen(cmd, shell=True, bufsize=bufsize, + stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) +(child_stdin, + child_stdout, + child_stderr) = (p.stdin, p.stdout, p.stderr) + + +(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) +==> +p = Popen(cmd, shell=True, bufsize=bufsize, + stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) +(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) + + +Replacing popen2.* +------------------ +Note: If the cmd argument to popen2 functions is a string, the command +is executed through /bin/sh. If it is a list, the command is directly +executed. + +(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) +==> +p = Popen(["somestring"], shell=True, bufsize=bufsize + stdin=PIPE, stdout=PIPE, close_fds=True) +(child_stdout, child_stdin) = (p.stdout, p.stdin) + + +(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) +==> +p = Popen(["mycmd", "myarg"], bufsize=bufsize, + stdin=PIPE, stdout=PIPE, close_fds=True) +(child_stdout, child_stdin) = (p.stdout, p.stdin) + +The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen, +except that: + +* subprocess.Popen raises an exception if the execution fails +* the capturestderr argument is replaced with the stderr argument. +* stdin=PIPE and stdout=PIPE must be specified. +* popen2 closes all filedescriptors by default, but you have to specify + close_fds=True with subprocess.Popen. + + +""" + +import sys +mswindows = (sys.platform == "win32") + +import os +import types +import traceback + +# Exception classes used by this module. +class CalledProcessError(Exception): + """This exception is raised when a process run by check_call() returns + a non-zero exit status. The exit status will be stored in the + returncode attribute.""" + def __init__(self, returncode, cmd): + self.returncode = returncode + self.cmd = cmd + def __str__(self): + return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) + + +if mswindows: + try: + import threading + except ImportError: + # SCons: the threading module is only used by the communicate() + # method, which we don't actually use, so don't worry if we + # can't import it. + pass + import msvcrt + try: + # Try to get _subprocess + from _subprocess import * + class STARTUPINFO(object): + dwFlags = 0 + hStdInput = None + hStdOutput = None + hStdError = None + wShowWindow = 0 + class pywintypes(object): + error = IOError + except ImportError: + # If not there, then drop back to requiring pywin32 + # TODO: Should this be wrapped in try as well? To notify user to install + # pywin32 ? With URL to it? + import pywintypes + from win32api import GetStdHandle, STD_INPUT_HANDLE, \ + STD_OUTPUT_HANDLE, STD_ERROR_HANDLE + from win32api import GetCurrentProcess, DuplicateHandle, \ + GetModuleFileName, GetVersion + from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE + from win32pipe import CreatePipe + from win32process import CreateProcess, STARTUPINFO, \ + GetExitCodeProcess, STARTF_USESTDHANDLES, \ + STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE + from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 + + +else: + import select + import errno + import fcntl + import pickle + + try: + fcntl.F_GETFD + except AttributeError: + fcntl.F_GETFD = 1 + + try: + fcntl.F_SETFD + except AttributeError: + fcntl.F_SETFD = 2 + +__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] + +try: + MAXFD = os.sysconf("SC_OPEN_MAX") +except KeyboardInterrupt: + raise # SCons: don't swallow keyboard interrupts +except: + MAXFD = 256 + +try: + isinstance(1, int) +except TypeError: + def is_int(obj): + return isinstance(obj, type(1)) + def is_int_or_long(obj): + return type(obj) in (type(1), type(1L)) +else: + def is_int(obj): + return isinstance(obj, int) + def is_int_or_long(obj): + return isinstance(obj, (int, long)) + +try: + types.StringTypes +except AttributeError: + try: + types.StringTypes = (str, unicode) + except NameError: + types.StringTypes = (str,) +def is_string(obj): + return isinstance(obj, types.StringTypes) + +_active = [] + +def _cleanup(): + for inst in _active[:]: + if inst.poll(_deadstate=sys.maxsize) >= 0: + try: + _active.remove(inst) + except ValueError: + # This can happen if two threads create a new Popen instance. + # It's harmless that it was already removed, so ignore. + pass + +PIPE = -1 +STDOUT = -2 + + +def call(*popenargs, **kwargs): + """Run command with arguments. Wait for command to complete, then + return the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + retcode = call(["ls", "-l"]) + """ + return apply(Popen, popenargs, kwargs).wait() + + +def check_call(*popenargs, **kwargs): + """Run command with arguments. Wait for command to complete. If + the exit code was zero then return, otherwise raise + CalledProcessError. The CalledProcessError object will have the + return code in the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + check_call(["ls", "-l"]) + """ + retcode = call(*popenargs, **kwargs) + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + if retcode: + raise CalledProcessError(retcode, cmd) + return retcode + + +def list2cmdline(seq): + """ + Translate a sequence of arguments into a command line + string, using the same rules as the MS C runtime: + + 1) Arguments are delimited by white space, which is either a + space or a tab. + + 2) A string surrounded by double quotation marks is + interpreted as a single argument, regardless of white space + contained within. A quoted string can be embedded in an + argument. + + 3) A double quotation mark preceded by a backslash is + interpreted as a literal double quotation mark. + + 4) Backslashes are interpreted literally, unless they + immediately precede a double quotation mark. + + 5) If backslashes immediately precede a double quotation mark, + every pair of backslashes is interpreted as a literal + backslash. If the number of backslashes is odd, the last + backslash escapes the next double quotation mark as + described in rule 3. + """ + + # See + # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp + result = [] + needquote = False + for arg in seq: + bs_buf = [] + + # Add a space to separate this argument from the others + if result: + result.append(' ') + + needquote = (" " in arg) or ("\t" in arg) + if needquote: + result.append('"') + + for c in arg: + if c == '\\': + # Don't know if we need to double yet. + bs_buf.append(c) + elif c == '"': + # Double backspaces. + result.append('\\' * len(bs_buf)*2) + bs_buf = [] + result.append('\\"') + else: + # Normal char + if bs_buf: + result.extend(bs_buf) + bs_buf = [] + result.append(c) + + # Add remaining backspaces, if any. + if bs_buf: + result.extend(bs_buf) + + if needquote: + result.extend(bs_buf) + result.append('"') + + return ''.join(result) + +class Popen(object): + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0): + """Create new Popen instance.""" + _cleanup() + + self._child_created = False + if not is_int_or_long(bufsize): + raise TypeError("bufsize must be an integer") + + if mswindows: + if preexec_fn is not None: + raise ValueError("preexec_fn is not supported on Windows " + "platforms") + if close_fds: + raise ValueError("close_fds is not supported on Windows " + "platforms") + else: + # POSIX + if startupinfo is not None: + raise ValueError("startupinfo is only supported on Windows " + "platforms") + if creationflags != 0: + raise ValueError("creationflags is only supported on Windows " + "platforms") + + self.stdin = None + self.stdout = None + self.stderr = None + self.pid = None + self.returncode = None + self.universal_newlines = universal_newlines + + # Input and output objects. The general principle is like + # this: + # + # Parent Child + # ------ ----- + # p2cwrite ---stdin---> p2cread + # c2pread <--stdout--- c2pwrite + # errread <--stderr--- errwrite + # + # On POSIX, the child objects are file descriptors. On + # Windows, these are Windows file handles. The parent objects + # are file descriptors on both platforms. The parent objects + # are None when not using PIPEs. The child objects are None + # when not redirecting. + + (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) = self._get_handles(stdin, stdout, stderr) + + self._execute_child(args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + if p2cwrite: + self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) + if c2pread: + if universal_newlines: + self.stdout = os.fdopen(c2pread, 'rU', bufsize) + else: + self.stdout = os.fdopen(c2pread, 'rb', bufsize) + if errread: + if universal_newlines: + self.stderr = os.fdopen(errread, 'rU', bufsize) + else: + self.stderr = os.fdopen(errread, 'rb', bufsize) + + + def _translate_newlines(self, data): + data = data.replace("\r\n", "\n") + data = data.replace("\r", "\n") + return data + + + def __del__(self): + if not self._child_created: + # We didn't get to successfully create a child process. + return + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxsize) + if self.returncode is None and _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + + def communicate(self, input=None): + """Interact with process: Send data to stdin. Read data from + stdout and stderr, until end-of-file is reached. Wait for + process to terminate. The optional input argument should be a + string to be sent to the child process, or None, if no data + should be sent to the child. + + communicate() returns a tuple (stdout, stderr).""" + + # Optimization: If we are only using one pipe, or no pipe at + # all, using select() or threads is unnecessary. + if [self.stdin, self.stdout, self.stderr].count(None) >= 2: + stdout = None + stderr = None + if self.stdin: + if input: + self.stdin.write(input) + self.stdin.close() + elif self.stdout: + stdout = self.stdout.read() + elif self.stderr: + stderr = self.stderr.read() + self.wait() + return (stdout, stderr) + + return self._communicate(input) + + + if mswindows: + # + # Windows methods + # + def _get_handles(self, stdin, stdout, stderr): + """Construct and return tupel with IO objects: + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite + """ + if stdin is None and stdout is None and stderr is None: + return (None, None, None, None, None, None) + + p2cread, p2cwrite = None, None + c2pread, c2pwrite = None, None + errread, errwrite = None, None + + if stdin is None: + p2cread = GetStdHandle(STD_INPUT_HANDLE) + elif stdin == PIPE: + p2cread, p2cwrite = CreatePipe(None, 0) + # Detach and turn into fd + p2cwrite = p2cwrite.Detach() + p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) + elif is_int(stdin): + p2cread = msvcrt.get_osfhandle(stdin) + else: + # Assuming file-like object + p2cread = msvcrt.get_osfhandle(stdin.fileno()) + p2cread = self._make_inheritable(p2cread) + + if stdout is None: + c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) + elif stdout == PIPE: + c2pread, c2pwrite = CreatePipe(None, 0) + # Detach and turn into fd + c2pread = c2pread.Detach() + c2pread = msvcrt.open_osfhandle(c2pread, 0) + elif is_int(stdout): + c2pwrite = msvcrt.get_osfhandle(stdout) + else: + # Assuming file-like object + c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) + c2pwrite = self._make_inheritable(c2pwrite) + + if stderr is None: + errwrite = GetStdHandle(STD_ERROR_HANDLE) + elif stderr == PIPE: + errread, errwrite = CreatePipe(None, 0) + # Detach and turn into fd + errread = errread.Detach() + errread = msvcrt.open_osfhandle(errread, 0) + elif stderr == STDOUT: + errwrite = c2pwrite + elif is_int(stderr): + errwrite = msvcrt.get_osfhandle(stderr) + else: + # Assuming file-like object + errwrite = msvcrt.get_osfhandle(stderr.fileno()) + errwrite = self._make_inheritable(errwrite) + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + + def _make_inheritable(self, handle): + """Return a duplicate of handle, which is inheritable""" + return DuplicateHandle(GetCurrentProcess(), handle, + GetCurrentProcess(), 0, 1, + DUPLICATE_SAME_ACCESS) + + + def _find_w9xpopen(self): + """Find and return absolut path to w9xpopen.exe""" + w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), + "w9xpopen.exe") + if not os.path.exists(w9xpopen): + # Eeek - file-not-found - possibly an embedding + # situation - see if we can locate it in sys.exec_prefix + w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), + "w9xpopen.exe") + if not os.path.exists(w9xpopen): + raise RuntimeError("Cannot locate w9xpopen.exe, which is " + "needed for Popen to work with your " + "shell or platform.") + return w9xpopen + + + def _execute_child(self, args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite): + """Execute program (MS Windows version)""" + + if not isinstance(args, types.StringTypes): + args = list2cmdline(args) + + # Process startup details + if startupinfo is None: + startupinfo = STARTUPINFO() + if None not in (p2cread, c2pwrite, errwrite): + startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESTDHANDLES + startupinfo.hStdInput = p2cread + startupinfo.hStdOutput = c2pwrite + startupinfo.hStdError = errwrite + + if shell: + startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESHOWWINDOW + startupinfo.wShowWindow = SW_HIDE + comspec = os.environ.get("COMSPEC", "cmd.exe") + args = comspec + " /c " + args + if (GetVersion() >= 0x80000000L or + os.path.basename(comspec).lower() == "command.com"): + # Win9x, or using command.com on NT. We need to + # use the w9xpopen intermediate program. For more + # information, see KB Q150956 + # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp) + w9xpopen = self._find_w9xpopen() + args = '"%s" %s' % (w9xpopen, args) + # Not passing CREATE_NEW_CONSOLE has been known to + # cause random failures on win9x. Specifically a + # dialog: "Your program accessed mem currently in + # use at xxx" and a hopeful warning about the + # stability of your system. Cost is Ctrl+C wont + # kill children. + creationflags = creationflags | CREATE_NEW_CONSOLE + + # Start the process + try: + hp, ht, pid, tid = CreateProcess(executable, args, + # no special security + None, None, + # must inherit handles to pass std + # handles + 1, + creationflags, + env, + cwd, + startupinfo) + except pywintypes.error, e: + # Translate pywintypes.error to WindowsError, which is + # a subclass of OSError. FIXME: We should really + # translate errno using _sys_errlist (or simliar), but + # how can this be done from Python? + raise WindowsError(*e.args) + + # Retain the process handle, but close the thread handle + self._child_created = True + self._handle = hp + self.pid = pid + ht.Close() + + # Child is launched. Close the parent's copy of those pipe + # handles that only the child should have open. You need + # to make sure that no handles to the write end of the + # output pipe are maintained in this process or else the + # pipe will not close when the child process exits and the + # ReadFile will hang. + if p2cread is not None: + p2cread.Close() + if c2pwrite is not None: + c2pwrite.Close() + if errwrite is not None: + errwrite.Close() + + + def poll(self, _deadstate=None): + """Check if child process has terminated. Returns returncode + attribute.""" + if self.returncode is None: + if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: + self.returncode = GetExitCodeProcess(self._handle) + return self.returncode + + + def wait(self): + """Wait for child process to terminate. Returns returncode + attribute.""" + if self.returncode is None: + obj = WaitForSingleObject(self._handle, INFINITE) + self.returncode = GetExitCodeProcess(self._handle) + return self.returncode + + + def _readerthread(self, fh, buffer): + buffer.append(fh.read()) + + + def _communicate(self, input): + stdout = None # Return + stderr = None # Return + + if self.stdout: + stdout = [] + stdout_thread = threading.Thread(target=self._readerthread, + args=(self.stdout, stdout)) + stdout_thread.setDaemon(True) + stdout_thread.start() + if self.stderr: + stderr = [] + stderr_thread = threading.Thread(target=self._readerthread, + args=(self.stderr, stderr)) + stderr_thread.setDaemon(True) + stderr_thread.start() + + if self.stdin: + if input is not None: + self.stdin.write(input) + self.stdin.close() + + if self.stdout: + stdout_thread.join() + if self.stderr: + stderr_thread.join() + + # All data exchanged. Translate lists into strings. + if stdout is not None: + stdout = stdout[0] + if stderr is not None: + stderr = stderr[0] + + # Translate newlines, if requested. We cannot let the file + # object do the translation: It is based on stdio, which is + # impossible to combine with select (unless forcing no + # buffering). + if self.universal_newlines and hasattr(file, 'newlines'): + if stdout: + stdout = self._translate_newlines(stdout) + if stderr: + stderr = self._translate_newlines(stderr) + + self.wait() + return (stdout, stderr) + + else: + # + # POSIX methods + # + def _get_handles(self, stdin, stdout, stderr): + """Construct and return tupel with IO objects: + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite + """ + p2cread, p2cwrite = None, None + c2pread, c2pwrite = None, None + errread, errwrite = None, None + + if stdin is None: + pass + elif stdin == PIPE: + p2cread, p2cwrite = os.pipe() + elif is_int(stdin): + p2cread = stdin + else: + # Assuming file-like object + p2cread = stdin.fileno() + + if stdout is None: + pass + elif stdout == PIPE: + c2pread, c2pwrite = os.pipe() + elif is_int(stdout): + c2pwrite = stdout + else: + # Assuming file-like object + c2pwrite = stdout.fileno() + + if stderr is None: + pass + elif stderr == PIPE: + errread, errwrite = os.pipe() + elif stderr == STDOUT: + errwrite = c2pwrite + elif is_int(stderr): + errwrite = stderr + else: + # Assuming file-like object + errwrite = stderr.fileno() + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + + def _set_cloexec_flag(self, fd): + try: + cloexec_flag = fcntl.FD_CLOEXEC + except AttributeError: + cloexec_flag = 1 + + old = fcntl.fcntl(fd, fcntl.F_GETFD) + fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) + + + def _close_fds(self, but): + for i in range(3, MAXFD): + if i == but: + continue + try: + os.close(i) + except KeyboardInterrupt: + raise # SCons: don't swallow keyboard interrupts + except: + pass + + + def _execute_child(self, args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite): + """Execute program (POSIX version)""" + + if is_string(args): + args = [args] + + if shell: + args = ["/bin/sh", "-c"] + args + + if executable is None: + executable = args[0] + + # For transferring possible exec failure from child to parent + # The first char specifies the exception type: 0 means + # OSError, 1 means some other error. + errpipe_read, errpipe_write = os.pipe() + self._set_cloexec_flag(errpipe_write) + + self.pid = os.fork() + self._child_created = True + if self.pid == 0: + # Child + try: + # Close parent's pipe ends + if p2cwrite: + os.close(p2cwrite) + if c2pread: + os.close(c2pread) + if errread: + os.close(errread) + os.close(errpipe_read) + + # Dup fds for child + if p2cread: + os.dup2(p2cread, 0) + if c2pwrite: + os.dup2(c2pwrite, 1) + if errwrite: + os.dup2(errwrite, 2) + + # Close pipe fds. Make sure we don't close the same + # fd more than once, or standard fds. + try: + set + except NameError: + # Fall-back for earlier Python versions, so epydoc + # can use this module directly to execute things. + if p2cread: + os.close(p2cread) + if c2pwrite and c2pwrite not in (p2cread,): + os.close(c2pwrite) + if errwrite and errwrite not in (p2cread, c2pwrite): + os.close(errwrite) + else: + for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)): + if fd: os.close(fd) + + # Close all other fds, if asked for + if close_fds: + self._close_fds(but=errpipe_write) + + if cwd is not None: + os.chdir(cwd) + + if preexec_fn: + apply(preexec_fn) + + if env is None: + os.execvp(executable, args) + else: + os.execvpe(executable, args, env) + + except KeyboardInterrupt: + raise # SCons: don't swallow keyboard interrupts + + except: + exc_type, exc_value, tb = sys.exc_info() + # Save the traceback and attach it to the exception object + exc_lines = traceback.format_exception(exc_type, + exc_value, + tb) + exc_value.child_traceback = ''.join(exc_lines) + os.write(errpipe_write, pickle.dumps(exc_value)) + + # This exitcode won't be reported to applications, so it + # really doesn't matter what we return. + os._exit(255) + + # Parent + os.close(errpipe_write) + if p2cread and p2cwrite: + os.close(p2cread) + if c2pwrite and c2pread: + os.close(c2pwrite) + if errwrite and errread: + os.close(errwrite) + + # Wait for exec to fail or succeed; possibly raising exception + data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB + os.close(errpipe_read) + if data != "": + os.waitpid(self.pid, 0) + child_exception = pickle.loads(data) + raise child_exception + + + def _handle_exitstatus(self, sts): + if os.WIFSIGNALED(sts): + self.returncode = -os.WTERMSIG(sts) + elif os.WIFEXITED(sts): + self.returncode = os.WEXITSTATUS(sts) + else: + # Should never happen + raise RuntimeError("Unknown child exit status!") + + + def poll(self, _deadstate=None): + """Check if child process has terminated. Returns returncode + attribute.""" + if self.returncode is None: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + if pid == self.pid: + self._handle_exitstatus(sts) + except os.error: + if _deadstate is not None: + self.returncode = _deadstate + return self.returncode + + + def wait(self): + """Wait for child process to terminate. Returns returncode + attribute.""" + if self.returncode is None: + pid, sts = os.waitpid(self.pid, 0) + self._handle_exitstatus(sts) + return self.returncode + + + def _communicate(self, input): + read_set = [] + write_set = [] + stdout = None # Return + stderr = None # Return + + if self.stdin: + # Flush stdio buffer. This might block, if the user has + # been writing to .stdin in an uncontrolled fashion. + self.stdin.flush() + if input: + write_set.append(self.stdin) + else: + self.stdin.close() + if self.stdout: + read_set.append(self.stdout) + stdout = [] + if self.stderr: + read_set.append(self.stderr) + stderr = [] + + input_offset = 0 + while read_set or write_set: + rlist, wlist, xlist = select.select(read_set, write_set, []) + + if self.stdin in wlist: + # When select has indicated that the file is writable, + # we can write up to PIPE_BUF bytes without risk + # blocking. POSIX defines PIPE_BUF >= 512 + m = memoryview(input)[input_offset:input_offset+512] + bytes_written = os.write(self.stdin.fileno(), m) + input_offset = input_offset + bytes_written + if input_offset >= len(input): + self.stdin.close() + write_set.remove(self.stdin) + + if self.stdout in rlist: + data = os.read(self.stdout.fileno(), 1024) + if data == "": + self.stdout.close() + read_set.remove(self.stdout) + stdout.append(data) + + if self.stderr in rlist: + data = os.read(self.stderr.fileno(), 1024) + if data == "": + self.stderr.close() + read_set.remove(self.stderr) + stderr.append(data) + + # All data exchanged. Translate lists into strings. + if stdout is not None: + stdout = ''.join(stdout) + if stderr is not None: + stderr = ''.join(stderr) + + # Translate newlines, if requested. We cannot let the file + # object do the translation: It is based on stdio, which is + # impossible to combine with select (unless forcing no + # buffering). + if self.universal_newlines and hasattr(file, 'newlines'): + if stdout: + stdout = self._translate_newlines(stdout) + if stderr: + stderr = self._translate_newlines(stderr) + + self.wait() + return (stdout, stderr) + + +def _demo_posix(): + # + # Example 1: Simple redirection: Get process list + # + plist = Popen(["ps"], stdout=PIPE).communicate()[0] + print "Process list:" + print plist + + # + # Example 2: Change uid before executing child + # + if os.getuid() == 0: + p = Popen(["id"], preexec_fn=lambda: os.setuid(100)) + p.wait() + + # + # Example 3: Connecting several subprocesses + # + print "Looking for 'hda'..." + p1 = Popen(["dmesg"], stdout=PIPE) + p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) + print repr(p2.communicate()[0]) + + # + # Example 4: Catch execution error + # + print + print "Trying a weird file..." + try: + print Popen(["/this/path/does/not/exist"]).communicate() + except OSError, e: + if e.errno == errno.ENOENT: + print "The file didn't exist. I thought so..." + print "Child traceback:" + print e.child_traceback + else: + print "Error", e.errno + else: + sys.stderr.write( "Gosh. No error.\n" ) + + +def _demo_windows(): + # + # Example 1: Connecting several subprocesses + # + print "Looking for 'PROMPT' in set output..." + p1 = Popen("set", stdout=PIPE, shell=True) + p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE) + print repr(p2.communicate()[0]) + + # + # Example 2: Simple execution of program + # + print "Executing calc..." + p = Popen("calc") + p.wait() + + +if __name__ == "__main__": + if mswindows: + _demo_windows() + else: + _demo_posix() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/cpp.py b/engine/SCons/cpp.py new file mode 100644 index 0000000..3015b53 --- /dev/null +++ b/engine/SCons/cpp.py @@ -0,0 +1,589 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/cpp.py 5357 2011/09/09 21:31:03 bdeegan" + +__doc__ = """ +SCons C Pre-Processor module +""" +#TODO 2.3 and before has no sorted() +import SCons.compat + +import os +import re + +# +# First "subsystem" of regular expressions that we set up: +# +# Stuff to turn the C preprocessor directives in a file's contents into +# a list of tuples that we can process easily. +# + +# A table of regular expressions that fetch the arguments from the rest of +# a C preprocessor line. Different directives have different arguments +# that we want to fetch, using the regular expressions to which the lists +# of preprocessor directives map. +cpp_lines_dict = { + # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument, + # separated from the keyword by white space. + ('if', 'elif', 'ifdef', 'ifndef',) + : '\s+(.+)', + + # Fetch the rest of a #import/#include/#include_next line as one + # argument, with white space optional. + ('import', 'include', 'include_next',) + : '\s*(.+)', + + # We don't care what comes after a #else or #endif line. + ('else', 'endif',) : '', + + # Fetch three arguments from a #define line: + # 1) The #defined keyword. + # 2) The optional parentheses and arguments (if it's a function-like + # macro, '' if it's not). + # 3) The expansion value. + ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)', + + # Fetch the #undefed keyword from a #undef line. + ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)', +} + +# Create a table that maps each individual C preprocessor directive to +# the corresponding compiled regular expression that fetches the arguments +# we care about. +Table = {} +for op_list, expr in cpp_lines_dict.items(): + e = re.compile(expr) + for op in op_list: + Table[op] = e +del e +del op +del op_list + +# Create a list of the expressions we'll use to match all of the +# preprocessor directives. These are the same as the directives +# themselves *except* that we must use a negative lookahead assertion +# when matching "if" so it doesn't match the "if" in "ifdef." +override = { + 'if' : 'if(?!def)', +} +l = [override.get(x, x) for x in Table.keys()] + + +# Turn the list of expressions into one big honkin' regular expression +# that will match all the preprocessor lines at once. This will return +# a list of tuples, one for each preprocessor line. The preprocessor +# directive will be the first element in each tuple, and the rest of +# the line will be the second element. +e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$' + +# And last but not least, compile the expression. +CPP_Expression = re.compile(e, re.M) + + + + +# +# Second "subsystem" of regular expressions that we set up: +# +# Stuff to translate a C preprocessor expression (as found on a #if or +# #elif line) into an equivalent Python expression that we can eval(). +# + +# A dictionary that maps the C representation of Boolean operators +# to their Python equivalents. +CPP_to_Python_Ops_Dict = { + '!' : ' not ', + '!=' : ' != ', + '&&' : ' and ', + '||' : ' or ', + '?' : ' and ', + ':' : ' or ', + '\r' : '', +} + +CPP_to_Python_Ops_Sub = lambda m: CPP_to_Python_Ops_Dict[m.group(0)] + +# We have to sort the keys by length so that longer expressions +# come *before* shorter expressions--in particular, "!=" must +# come before "!" in the alternation. Without this, the Python +# re module, as late as version 2.2.2, empirically matches the +# "!" in "!=" first, instead of finding the longest match. +# What's up with that? +l = sorted(CPP_to_Python_Ops_Dict.keys(), key=lambda a: len(a), reverse=True) + +# Turn the list of keys into one regular expression that will allow us +# to substitute all of the operators at once. +expr = '|'.join(map(re.escape, l)) + +# ...and compile the expression. +CPP_to_Python_Ops_Expression = re.compile(expr) + +# A separate list of expressions to be evaluated and substituted +# sequentially, not all at once. +CPP_to_Python_Eval_List = [ + ['defined\s+(\w+)', '"\\1" in __dict__'], + ['defined\s*\((\w+)\)', '"\\1" in __dict__'], + ['/\*.*\*/', ''], + ['/\*.*', ''], + ['//.*', ''], + ['(0x[0-9A-Fa-f]*)[UL]+', '\\1'], +] + +# Replace the string representations of the regular expressions in the +# list with compiled versions. +for l in CPP_to_Python_Eval_List: + l[0] = re.compile(l[0]) + +# Wrap up all of the above into a handy function. +def CPP_to_Python(s): + """ + Converts a C pre-processor expression into an equivalent + Python expression that can be evaluated. + """ + s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s) + for expr, repl in CPP_to_Python_Eval_List: + s = expr.sub(repl, s) + return s + + + +del expr +del l +del override + + + +class FunctionEvaluator(object): + """ + Handles delayed evaluation of a #define function call. + """ + def __init__(self, name, args, expansion): + """ + Squirrels away the arguments and expansion value of a #define + macro function for later evaluation when we must actually expand + a value that uses it. + """ + self.name = name + self.args = function_arg_separator.split(args) + try: + expansion = expansion.split('##') + except AttributeError: + pass + self.expansion = expansion + def __call__(self, *values): + """ + Evaluates the expansion of a #define macro function called + with the specified values. + """ + if len(self.args) != len(values): + raise ValueError("Incorrect number of arguments to `%s'" % self.name) + # Create a dictionary that maps the macro arguments to the + # corresponding values in this "call." We'll use this when we + # eval() the expansion so that arguments will get expanded to + # the right values. + locals = {} + for k, v in zip(self.args, values): + locals[k] = v + + parts = [] + for s in self.expansion: + if not s in self.args: + s = repr(s) + parts.append(s) + statement = ' + '.join(parts) + + return eval(statement, globals(), locals) + + + +# Find line continuations. +line_continuations = re.compile('\\\\\r?\n') + +# Search for a "function call" macro on an expansion. Returns the +# two-tuple of the "function" name itself, and a string containing the +# arguments within the call parentheses. +function_name = re.compile('(\S+)\(([^)]*)\)') + +# Split a string containing comma-separated function call arguments into +# the separate arguments. +function_arg_separator = re.compile(',\s*') + + + +class PreProcessor(object): + """ + The main workhorse class for handling C pre-processing. + """ + def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0): + global Table + + cpppath = tuple(cpppath) + + self.searchpath = { + '"' : (current,) + cpppath, + '<' : cpppath + (current,), + } + + # Initialize our C preprocessor namespace for tracking the + # values of #defined keywords. We use this namespace to look + # for keywords on #ifdef/#ifndef lines, and to eval() the + # expressions on #if/#elif lines (after massaging them from C to + # Python). + self.cpp_namespace = dict.copy() + self.cpp_namespace['__dict__'] = self.cpp_namespace + + if all: + self.do_include = self.all_include + + # For efficiency, a dispatch table maps each C preprocessor + # directive (#if, #define, etc.) to the method that should be + # called when we see it. We accomodate state changes (#if, + # #ifdef, #ifndef) by pushing the current dispatch table on a + # stack and changing what method gets called for each relevant + # directive we might see next at this level (#else, #elif). + # #endif will simply pop the stack. + d = { + 'scons_current_file' : self.scons_current_file + } + for op in Table.keys(): + d[op] = getattr(self, 'do_' + op) + self.default_table = d + + # Controlling methods. + + def tupleize(self, contents): + """ + Turns the contents of a file into a list of easily-processed + tuples describing the CPP lines in the file. + + The first element of each tuple is the line's preprocessor + directive (#if, #include, #define, etc., minus the initial '#'). + The remaining elements are specific to the type of directive, as + pulled apart by the regular expression. + """ + global CPP_Expression, Table + contents = line_continuations.sub('', contents) + cpp_tuples = CPP_Expression.findall(contents) + return [(m[0],) + Table[m[0]].match(m[1]).groups() for m in cpp_tuples] + + def __call__(self, file): + """ + Pre-processes a file. + + This is the main public entry point. + """ + self.current_file = file + return self.process_contents(self.read_file(file), file) + + def process_contents(self, contents, fname=None): + """ + Pre-processes a file contents. + + This is the main internal entry point. + """ + self.stack = [] + self.dispatch_table = self.default_table.copy() + self.current_file = fname + self.tuples = self.tupleize(contents) + + self.initialize_result(fname) + while self.tuples: + t = self.tuples.pop(0) + # Uncomment to see the list of tuples being processed (e.g., + # to validate the CPP lines are being translated correctly). + #print t + self.dispatch_table[t[0]](t) + return self.finalize_result(fname) + + # Dispatch table stack manipulation methods. + + def save(self): + """ + Pushes the current dispatch table on the stack and re-initializes + the current dispatch table to the default. + """ + self.stack.append(self.dispatch_table) + self.dispatch_table = self.default_table.copy() + + def restore(self): + """ + Pops the previous dispatch table off the stack and makes it the + current one. + """ + try: self.dispatch_table = self.stack.pop() + except IndexError: pass + + # Utility methods. + + def do_nothing(self, t): + """ + Null method for when we explicitly want the action for a + specific preprocessor directive to do nothing. + """ + pass + + def scons_current_file(self, t): + self.current_file = t[1] + + def eval_expression(self, t): + """ + Evaluates a C preprocessor expression. + + This is done by converting it to a Python equivalent and + eval()ing it in the C preprocessor namespace we use to + track #define values. + """ + t = CPP_to_Python(' '.join(t[1:])) + try: return eval(t, self.cpp_namespace) + except (NameError, TypeError): return 0 + + def initialize_result(self, fname): + self.result = [fname] + + def finalize_result(self, fname): + return self.result[1:] + + def find_include_file(self, t): + """ + Finds the #include file for a given preprocessor tuple. + """ + fname = t[2] + for d in self.searchpath[t[1]]: + if d == os.curdir: + f = fname + else: + f = os.path.join(d, fname) + if os.path.isfile(f): + return f + return None + + def read_file(self, file): + return open(file).read() + + # Start and stop processing include lines. + + def start_handling_includes(self, t=None): + """ + Causes the PreProcessor object to start processing #import, + #include and #include_next lines. + + This method will be called when a #if, #ifdef, #ifndef or #elif + evaluates True, or when we reach the #else in a #if, #ifdef, + #ifndef or #elif block where a condition already evaluated + False. + + """ + d = self.dispatch_table + d['import'] = self.do_import + d['include'] = self.do_include + d['include_next'] = self.do_include + + def stop_handling_includes(self, t=None): + """ + Causes the PreProcessor object to stop processing #import, + #include and #include_next lines. + + This method will be called when a #if, #ifdef, #ifndef or #elif + evaluates False, or when we reach the #else in a #if, #ifdef, + #ifndef or #elif block where a condition already evaluated True. + """ + d = self.dispatch_table + d['import'] = self.do_nothing + d['include'] = self.do_nothing + d['include_next'] = self.do_nothing + + # Default methods for handling all of the preprocessor directives. + # (Note that what actually gets called for a given directive at any + # point in time is really controlled by the dispatch_table.) + + def _do_if_else_condition(self, condition): + """ + Common logic for evaluating the conditions on #if, #ifdef and + #ifndef lines. + """ + self.save() + d = self.dispatch_table + if condition: + self.start_handling_includes() + d['elif'] = self.stop_handling_includes + d['else'] = self.stop_handling_includes + else: + self.stop_handling_includes() + d['elif'] = self.do_elif + d['else'] = self.start_handling_includes + + def do_ifdef(self, t): + """ + Default handling of a #ifdef line. + """ + self._do_if_else_condition(t[1] in self.cpp_namespace) + + def do_ifndef(self, t): + """ + Default handling of a #ifndef line. + """ + self._do_if_else_condition(t[1] not in self.cpp_namespace) + + def do_if(self, t): + """ + Default handling of a #if line. + """ + self._do_if_else_condition(self.eval_expression(t)) + + def do_elif(self, t): + """ + Default handling of a #elif line. + """ + d = self.dispatch_table + if self.eval_expression(t): + self.start_handling_includes() + d['elif'] = self.stop_handling_includes + d['else'] = self.stop_handling_includes + + def do_else(self, t): + """ + Default handling of a #else line. + """ + pass + + def do_endif(self, t): + """ + Default handling of a #endif line. + """ + self.restore() + + def do_define(self, t): + """ + Default handling of a #define line. + """ + _, name, args, expansion = t + try: + expansion = int(expansion) + except (TypeError, ValueError): + pass + if args: + evaluator = FunctionEvaluator(name, args[1:-1], expansion) + self.cpp_namespace[name] = evaluator + else: + self.cpp_namespace[name] = expansion + + def do_undef(self, t): + """ + Default handling of a #undef line. + """ + try: del self.cpp_namespace[t[1]] + except KeyError: pass + + def do_import(self, t): + """ + Default handling of a #import line. + """ + # XXX finish this -- maybe borrow/share logic from do_include()...? + pass + + def do_include(self, t): + """ + Default handling of a #include line. + """ + t = self.resolve_include(t) + include_file = self.find_include_file(t) + if include_file: + #print "include_file =", include_file + self.result.append(include_file) + contents = self.read_file(include_file) + new_tuples = [('scons_current_file', include_file)] + \ + self.tupleize(contents) + \ + [('scons_current_file', self.current_file)] + self.tuples[:] = new_tuples + self.tuples + + # Date: Tue, 22 Nov 2005 20:26:09 -0500 + # From: Stefan Seefeld + # + # By the way, #include_next is not the same as #include. The difference + # being that #include_next starts its search in the path following the + # path that let to the including file. In other words, if your system + # include paths are ['/foo', '/bar'], and you are looking at a header + # '/foo/baz.h', it might issue an '#include_next ' which would + # correctly resolve to '/bar/baz.h' (if that exists), but *not* see + # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html + # for more reasoning. + # + # I have no idea in what context 'import' might be used. + + # XXX is #include_next really the same as #include ? + do_include_next = do_include + + # Utility methods for handling resolution of include files. + + def resolve_include(self, t): + """Resolve a tuple-ized #include line. + + This handles recursive expansion of values without "" or <> + surrounding the name until an initial " or < is found, to handle + #include FILE + where FILE is a #define somewhere else. + """ + s = t[1] + while not s[0] in '<"': + #print "s =", s + try: + s = self.cpp_namespace[s] + except KeyError: + m = function_name.search(s) + s = self.cpp_namespace[m.group(1)] + if callable(s): + args = function_arg_separator.split(m.group(2)) + s = s(*args) + if not s: + return None + return (t[0], s[0], s[1:-1]) + + def all_include(self, t): + """ + """ + self.result.append(self.resolve_include(t)) + +class DumbPreProcessor(PreProcessor): + """A preprocessor that ignores all #if/#elif/#else/#endif directives + and just reports back *all* of the #include files (like the classic + SCons scanner did). + + This is functionally equivalent to using a regular expression to + find all of the #include lines, only slower. It exists mainly as + an example of how the main PreProcessor class can be sub-classed + to tailor its behavior. + """ + def __init__(self, *args, **kw): + PreProcessor.__init__(self, *args, **kw) + d = self.default_table + for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']: + d[func] = d[func] = self.do_nothing + +del __revision__ + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/dblite.py b/engine/SCons/dblite.py new file mode 100644 index 0000000..f4ba90a --- /dev/null +++ b/engine/SCons/dblite.py @@ -0,0 +1,254 @@ +# dblite.py module contributed by Ralf W. Grosse-Kunstleve. +# Extended for Unicode by Steven Knight. + +import SCons.compat + +import builtins +import os +# compat layer imports "cPickle" for us if it's available. +import pickle +import shutil +import time + +keep_all_files = 00000 +ignore_corrupt_dbfiles = 0 + +def corruption_warning(filename): + print "Warning: Discarding corrupt database:", filename + +try: unicode +except NameError: + def is_string(s): + return isinstance(s, str) +else: + def is_string(s): + return type(s) in (str, unicode) + +try: + unicode('a') +except NameError: + def unicode(s): return s + +dblite_suffix = '.dblite' +tmp_suffix = '.tmp' + +class dblite(object): + + # Squirrel away references to the functions in various modules + # that we'll use when our __del__() method calls our sync() method + # during shutdown. We might get destroyed when Python is in the midst + # of tearing down the different modules we import in an essentially + # arbitrary order, and some of the various modules's global attributes + # may already be wiped out from under us. + # + # See the discussion at: + # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html + + _open = builtins.open + _pickle_dump = staticmethod(pickle.dump) + _os_chmod = os.chmod + try: + _os_chown = os.chown + except AttributeError: + _os_chown = None + _os_rename = os.rename + _os_unlink = os.unlink + _shutil_copyfile = shutil.copyfile + _time_time = time.time + + def __init__(self, file_base_name, flag, mode): + assert flag in (None, "r", "w", "c", "n") + if (flag is None): flag = "r" + base, ext = os.path.splitext(file_base_name) + if ext == dblite_suffix: + # There's already a suffix on the file name, don't add one. + self._file_name = file_base_name + self._tmp_name = base + tmp_suffix + else: + self._file_name = file_base_name + dblite_suffix + self._tmp_name = file_base_name + tmp_suffix + self._flag = flag + self._mode = mode + self._dict = {} + self._needs_sync = 00000 + if self._os_chown is not None and (os.geteuid()==0 or os.getuid()==0): + # running as root; chown back to current owner/group when done + try: + statinfo = os.stat(self._file_name) + self._chown_to = statinfo.st_uid + self._chgrp_to = statinfo.st_gid + except OSError, e: + # db file doesn't exist yet. + # Check os.environ for SUDO_UID, use if set + self._chown_to = int(os.environ.get('SUDO_UID', -1)) + self._chgrp_to = int(os.environ.get('SUDO_GID', -1)) + else: + self._chown_to = -1 # don't chown + self._chgrp_to = -1 # don't chgrp + if (self._flag == "n"): + self._open(self._file_name, "wb", self._mode) + else: + try: + f = self._open(self._file_name, "rb") + except IOError, e: + if (self._flag != "c"): + raise e + self._open(self._file_name, "wb", self._mode) + else: + p = f.read() + if (len(p) > 0): + try: + self._dict = pickle.loads(p) + except (pickle.UnpicklingError, EOFError): + if (ignore_corrupt_dbfiles == 0): raise + if (ignore_corrupt_dbfiles == 1): + corruption_warning(self._file_name) + + def close(self): + if (self._needs_sync): + self.sync() + + def __del__(self): + self.close() + + def sync(self): + self._check_writable() + f = self._open(self._tmp_name, "wb", self._mode) + self._pickle_dump(self._dict, f, 1) + f.close() + # Windows doesn't allow renaming if the file exists, so unlink + # it first, chmod'ing it to make sure we can do so. On UNIX, we + # may not be able to chmod the file if it's owned by someone else + # (e.g. from a previous run as root). We should still be able to + # unlink() the file if the directory's writable, though, so ignore + # any OSError exception thrown by the chmod() call. + try: self._os_chmod(self._file_name, 0777) + except OSError: pass + self._os_unlink(self._file_name) + self._os_rename(self._tmp_name, self._file_name) + if self._os_chown is not None and self._chown_to > 0: # don't chown to root or -1 + try: + self._os_chown(self._file_name, self._chown_to, self._chgrp_to) + except OSError: + pass + self._needs_sync = 00000 + if (keep_all_files): + self._shutil_copyfile( + self._file_name, + self._file_name + "_" + str(int(self._time_time()))) + + def _check_writable(self): + if (self._flag == "r"): + raise IOError("Read-only database: %s" % self._file_name) + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + self._check_writable() + if (not is_string(key)): + raise TypeError("key `%s' must be a string but is %s" % (key, type(key))) + if (not is_string(value)): + raise TypeError("value `%s' must be a string but is %s" % (value, type(value))) + self._dict[key] = value + self._needs_sync = 0001 + + def keys(self): + return list(self._dict.keys()) + + def has_key(self, key): + return key in self._dict + + def __contains__(self, key): + return key in self._dict + + def iterkeys(self): + # Wrapping name in () prevents fixer from "fixing" this + return (self._dict.iterkeys)() + + __iter__ = iterkeys + + def __len__(self): + return len(self._dict) + +def open(file, flag=None, mode=0666): + return dblite(file, flag, mode) + +def _exercise(): + db = open("tmp", "n") + assert len(db) == 0 + db["foo"] = "bar" + assert db["foo"] == "bar" + db[unicode("ufoo")] = unicode("ubar") + assert db[unicode("ufoo")] == unicode("ubar") + db.sync() + db = open("tmp", "c") + assert len(db) == 2, len(db) + assert db["foo"] == "bar" + db["bar"] = "foo" + assert db["bar"] == "foo" + db[unicode("ubar")] = unicode("ufoo") + assert db[unicode("ubar")] == unicode("ufoo") + db.sync() + db = open("tmp", "r") + assert len(db) == 4, len(db) + assert db["foo"] == "bar" + assert db["bar"] == "foo" + assert db[unicode("ufoo")] == unicode("ubar") + assert db[unicode("ubar")] == unicode("ufoo") + try: + db.sync() + except IOError, e: + assert str(e) == "Read-only database: tmp.dblite" + else: + raise RuntimeError("IOError expected.") + db = open("tmp", "w") + assert len(db) == 4 + db["ping"] = "pong" + db.sync() + try: + db[(1,2)] = "tuple" + except TypeError, e: + assert str(e) == "key `(1, 2)' must be a string but is ", str(e) + else: + raise RuntimeError("TypeError exception expected") + try: + db["list"] = [1,2] + except TypeError, e: + assert str(e) == "value `[1, 2]' must be a string but is ", str(e) + else: + raise RuntimeError("TypeError exception expected") + db = open("tmp", "r") + assert len(db) == 5 + db = open("tmp", "n") + assert len(db) == 0 + dblite._open("tmp.dblite", "w") + db = open("tmp", "r") + dblite._open("tmp.dblite", "w").write("x") + try: + db = open("tmp", "r") + except pickle.UnpicklingError: + pass + else: + raise RuntimeError("pickle exception expected.") + global ignore_corrupt_dbfiles + ignore_corrupt_dbfiles = 2 + db = open("tmp", "r") + assert len(db) == 0 + os.unlink("tmp.dblite") + try: + db = open("tmp", "w") + except IOError, e: + assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e) + else: + raise RuntimeError("IOError expected.") + print "OK" + +if (__name__ == "__main__"): + _exercise() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/exitfuncs.py b/engine/SCons/exitfuncs.py new file mode 100644 index 0000000..7dd2bfe --- /dev/null +++ b/engine/SCons/exitfuncs.py @@ -0,0 +1,77 @@ +"""SCons.exitfuncs + +Register functions which are executed when SCons exits for any reason. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/exitfuncs.py 5357 2011/09/09 21:31:03 bdeegan" + + + +_exithandlers = [] +def _run_exitfuncs(): + """run any registered exit functions + + _exithandlers is traversed in reverse order so functions are executed + last in, first out. + """ + + while _exithandlers: + func, targs, kargs = _exithandlers.pop() + func(*targs, **kargs) + +def register(func, *targs, **kargs): + """register a function to be executed upon normal program termination + + func - function to be called at exit + targs - optional arguments to pass to func + kargs - optional keyword arguments to pass to func + """ + _exithandlers.append((func, targs, kargs)) + +import sys + +try: + x = sys.exitfunc + + # if x isn't our own exit func executive, assume it's another + # registered exit function - append it to our list... + if x != _run_exitfuncs: + register(x) + +except AttributeError: + pass + +# make our exit function get run by python when it exits: +sys.exitfunc = _run_exitfuncs + +del sys + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/os_spawnv_fix.diff b/os_spawnv_fix.diff new file mode 100644 index 0000000..926f896 --- /dev/null +++ b/os_spawnv_fix.diff @@ -0,0 +1,83 @@ +? dist/src/Mac/IDE scripts/Hold option to open a script +? dist/src/Mac/IDE scripts/Insert file name +? dist/src/Mac/IDE scripts/Insert folder name +? dist/src/Mac/IDE scripts/Search Python Documentation +? dist/src/Mac/IDE scripts/Hack/Remove .pyc files +? dist/src/Mac/IDE scripts/Hack/Toolbox Assistant +Index: dist/src/Modules/posixmodule.c +=================================================================== +RCS file: /cvsroot/python/python/dist/src/Modules/posixmodule.c,v +retrieving revision 2.213 +diff -c -c -r2.213 posixmodule.c +*** dist/src/Modules/posixmodule.c 2001/12/03 20:41:00 2.213 +--- dist/src/Modules/posixmodule.c 2001/12/05 00:52:58 +*************** +*** 1668,1674 **** + #ifdef HAVE_SPAWNV + static char posix_spawnv__doc__[] = + "spawnv(mode, path, args)\n\ +! Execute an executable path with arguments, replacing current process.\n\ + \n\ + mode: mode of process creation\n\ + path: path of executable file\n\ +--- 1668,1674 ---- + #ifdef HAVE_SPAWNV + static char posix_spawnv__doc__[] = + "spawnv(mode, path, args)\n\ +! Execute the program 'path' in a new process.\n\ + \n\ + mode: mode of process creation\n\ + path: path of executable file\n\ +*************** +*** 1717,1724 **** + + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; + spawnval = _spawnv(mode, path, argvlist); +! + PyMem_DEL(argvlist); + + if (spawnval == -1) +--- 1717,1727 ---- + + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; ++ ++ Py_BEGIN_ALLOW_THREADS + spawnval = _spawnv(mode, path, argvlist); +! Py_END_ALLOW_THREADS +! + PyMem_DEL(argvlist); + + if (spawnval == -1) +*************** +*** 1734,1740 **** + + static char posix_spawnve__doc__[] = + "spawnve(mode, path, args, env)\n\ +! Execute a path with arguments and environment, replacing current process.\n\ + \n\ + mode: mode of process creation\n\ + path: path of executable file\n\ +--- 1737,1743 ---- + + static char posix_spawnve__doc__[] = + "spawnve(mode, path, args, env)\n\ +! Execute the program 'path' in a new process.\n\ + \n\ + mode: mode of process creation\n\ + path: path of executable file\n\ +*************** +*** 1830,1836 **** +--- 1833,1843 ---- + + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; ++ ++ Py_BEGIN_ALLOW_THREADS + spawnval = _spawnve(mode, path, argvlist, envlist); ++ Py_END_ALLOW_THREADS ++ + if (spawnval == -1) + (void) posix_error(); + else diff --git a/scons-time.1 b/scons-time.1 new file mode 100644 index 0000000..69db49a --- /dev/null +++ b/scons-time.1 @@ -0,0 +1,1017 @@ +.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining +.\" a copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, sublicense, and/or sell copies of the Software, and to +.\" permit persons to whom the Software is furnished to do so, subject to +.\" the following conditions: +.\" +.\" The above copyright notice and this permission notice shall be included +.\" in all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +.\" KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +.\" WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +.\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +.\" +.\" doc/man/scons-time.1 5357 2011/09/09 21:31:03 bdeegan +.\" +.\" ES - Example Start - indents and turns off line fill +.de ES +.RS +.nf +.. +.\" EE - Example End - ends indent and turns line fill back on +.de EE +.RE +.fi +.. +'\"========================================================================== +.de SF +.B scons-time func +[\fB-h\fR] +[\fB--chdir=\fIDIR\fR] +[\fB-f \fIFILE\fR] +[\fB--fmt=\fIFORMAT\fR] +[\fB--func=\fINAME\fR] +[\fB-p \fISTRING\fR] +[\fB-t \fINUMBER\fR] +[\fB--title= TITLE\fR] +[\fIARGUMENTS\fR] +.. +'\"-------------------------------------------------------------------------- +.de SY +.B scons-time mem +[\fB-h\fR] +[\fB--chdir=\fIDIR\fR] +[\fB-f \fIFILE\fR] +[\fB--fmt=\fIFORMAT\fR] +[\fB-p \fISTRING\fR] +[\fB--stage=\fISTAGE\fR] +[\fB-t \fINUMBER\fR] +[\fB--title=\fITITLE\fR] +[\fIARGUMENTS\fR] +.. +'\"-------------------------------------------------------------------------- +.de SO +.B scons-time obj +[\fB-h\fR] +[\fB--chdir=\fIDIR\fR] +[\fB-f \fIFILE\fR] +[\fB--fmt=\fIFORMAT\fR] +[\fB-p \fISTRING\fR] +[\fB--stage=\fISTAGE\fR] +[\fB-t \fINUMBER\fR] +[\fB--title=\fITITLE\fR] +[\fIARGUMENTS\fR] +.. +'\"-------------------------------------------------------------------------- +.de SR +.B scons-time run +[\fB-hnqv\fR] +[\fB--aegis=\fIPROJECT\fR] +[\fB-f \fIFILE\fR] +[\fB--number=\fINUMBER\fR] +[\fB--outdir=\fIOUTDIR\fR] +[\fB-p \fISTRING\fR] +[\fB--python=\fIPYTHON\fR] +[\fB-s \fIDIR\fR] +[\fB--scons=\fISCONS\fR] +[\fB--svn=\fIURL\fR] +[\fIARGUMENTS\fR] +.. +'\"-------------------------------------------------------------------------- +.de ST +.B scons-time time +[\fB-h\fR] +[\fB--chdir=\fIDIR\fR] +[\fB-f \fIFILE\fR] +[\fB--fmt=\fIFORMAT\fR] +[\fB-p \fISTRING\fR] +[\fB-t \fINUMBER\fR] +[\fB--title=\fITITLE\fR] +[\fB--which=\fIWHICH\fR] +[\fIARGUMENTS\fR] +.. +.TH SCONS-TIME 1 "September 2011" +.SH NAME +scons-time \- generate and display SCons timing information +'\"========================================================================== +.SH SYNOPSIS +.B scons-time +.IR subcommand +[ +.IR options ... +] +[ +.IR arguments ... +] +'\"-------------------------------------------------------------------------- +.SS "Generating Timing Information" +.SR +'\"-------------------------------------------------------------------------- +.SS "Extracting Function Timings" +.SF +'\"-------------------------------------------------------------------------- +.SS "Extracting Memory Statistics" +.SY +'\"-------------------------------------------------------------------------- +.SS "Extracting Object Counts" +.SO +'\"-------------------------------------------------------------------------- +.SS "Extracting Execution Times" +.ST +'\"-------------------------------------------------------------------------- +.SS "Help Text" +.B scons-time help +.I SUBCOMMAND +[...] +'\"========================================================================== +.SH DESCRIPTION +The +.B scons-time +command runs an SCons configuration +through a standard set of profiled timings +and can extract and graph information from the +resulting profiles and log files of those timings. +The action to be performed by the +.B scons-time +script is specified +by a subcommand, the first argument on the command line. +See the +.B SUBCOMMANDS +section below for information about the operation +of specific subcommands. +.P +The basic way to use +.B scons-time +is to run the +.B scons-time run +subcommand +(possibly multiple times) +to generate profile and log file output, +and then use one of the other +subcommands to display the results +captured in the profiles and log files +for a particular kind of information: +function timings +(the +.B scons-time func +subcommand), +total memory used +(the +.B scons-time mem +subcommand), +object counts +(the +.B scons-time obj +subcommand) +and overall execution time +(the +.B scons-time time +subcommand). +Options exist to place and find the +profiles and log files in separate directories, +to generate the output in a format suitable +for graphing with the +.BR gnuplot (1) +program, +and so on. +.P +There are two basic ways the +.B scons-time run +subcommand +is intended to be used +to gather timing statistics +for a configuration. +One is to use the +.B --svn= +option to test a configuration against +a list of revisions from the SCons Subversion repository. +This will generate a profile and timing log file +for every revision listed with the +.B --number= +option, +and can be used to look at the +impact of commited changes to the +SCons code base on a particular +configuration over time. +.P +The other way is to profile incremental changes to a +local SCons code base during a development cycle--that is, +to look at the performance impact of changes +you're making in the local tree. +In this mode, +you run the +.B scons-time run +subcommand +.I without +the +.B --svn= +option, +in which case it simply looks in the profile/log file output directory +(the current directory by default) +and automatically figures out the +.I next +run number for the output profile and log file. +Used in this way, +the development cycle goes something like: +make a change to SCons; +run +.B scons-time run +to profile it against a specific configuration; +make another change to SCons; +run +.B scons-time run +again to profile it; +etc. +'\"========================================================================== +.SH OPTIONS +The +.B scons-time +command only supports a few global options: +.TP +-h, --help +Displays the global help text and exits, +identical to the +.B scons-time help +subcommand. +.TP +-V, --version +Displays the +.B scons-time +version and exits. +.P +Most functionality is controlled by options +to the individual subcommands. +See the next section for information +about individual subcommand options. +'\"========================================================================== +.SH SUBCOMMANDS +The +.B scons-time +command supports the following +individual subcommands. +'\"-------------------------------------------------------------------------- +.SS "The func Subcommand" +.SF +.P +The +.B scons-time func +subcommand displays timing information +for a specific Python function within SCons. +By default, it extracts information about the +.BR _main () +function, +which includes the Python profiler timing +for all of SCons. +.P +The +.B scons-time func +subcommand extracts function timing information +from all the specified file arguments, +which should be Python profiler output files. +(Normally, these would be +.B *.prof +files generated by the +.B scons-time run +subcommand, +but they can actually be generated +by any Python profiler invocation.) +All file name arguments will be +globbed for on-disk files. +.P +If no arguments are specified, +then function timing information +will be extracted from all +.B *.prof +files, +or the subset of them +with a prefix specified by the +.B -p +option. +.P +Options include: +.TP +-C DIRECTORY, --chdir=DIRECTORY +Changes to the specified +.I DIRECTORY +before looking for the specified files +(or files that match the specified patterns). +.TP +-f FILE, --file=FILE +Reads configuration information from the specified +.IR FILE . +.TP +-fmt=FORMAT, --format=FORMAT +Reports the output in the specified +.IR FORMAT . +The formats currently supported are +.B ascii +(the default) +and +.BR gnuplot . +.TP +--func=NAME +Extracts timings for the specified function +.IR NAME . +The default is to report cumulative timings for the +.BR _main () +function, +which contains the entire SCons run. +.TP +-h, --help +Displays help text for the +.B scons-time func +subcommand. +.TP +-p STRING, --prefix=STRING +Specifies the prefix string for profiles +from which to extract function timing information. +This will be used to search for profiles +if no arguments are specified on the command line. +.TP +-t NUMBER, --tail=NUMBER +Only extracts function timings from the last +.I NUMBER +files. +'\"-------------------------------------------------------------------------- +.SS "The help Subcommand" +.B scons-time help +.I SUBCOMMAND +[...] +The +.B help +subcommand prints help text for any +other subcommands listed as later arguments on the command line. +'\"-------------------------------------------------------------------------- +.SS "The mem Subcommand" +.SY +.P +The +.B scons-time mem +subcommand displays how much memory SCons uses. +.P +The +.B scons-time mem +subcommand extracts memory use information +from all the specified file arguments, +which should be files containing output from +running SCons with the +.B --debug=memory +option. +(Normally, these would be +.B *.log +files generated by the +.B scons-time run +subcommand.) +All file name arguments will be +globbed for on-disk files. +.P +If no arguments are specified, +then memory information +will be extracted from all +.B *.log +files, +or the subset of them +with a prefix specified by the +.B -p +option. +.P +.TP +-C DIR, --chdir=DIR +Changes to the specified +.I DIRECTORY +before looking for the specified files +(or files that match the specified patterns). +.TP +-f FILE, --file=FILE +Reads configuration information from the specified +.IR FILE . +.TP +-fmt=FORMAT, --format=FORMAT +Reports the output in the specified +.IR FORMAT . +The formats currently supported are +.B ascii +(the default) +and +.BR gnuplot . +.TP +-h, --help +Displays help text for the +.B scons-time mem +subcommand. +.TP +-p STRING, --prefix=STRING +Specifies the prefix string for log files +from which to extract memory usage information. +This will be used to search for log files +if no arguments are specified on the command line. +.TP +--stage=STAGE +Prints the memory used at the end of the specified +.IR STAGE : +.B pre-read +(before the SConscript files are read), +.B post-read , +(after the SConscript files are read), +.B pre-build +(before any targets are built) +or +.B post-build +(after any targets are built). +If no +.B --stage +option is specified, +the default behavior is +.BR post-build , +which reports the final amount of memory +used by SCons during each run. +.TP +-t NUMBER, --tail=NUMBER +Only reports memory statistics from the last +.I NUMBER +files. +'\"-------------------------------------------------------------------------- +.SS "The obj Subcommand" +.SO +.P +The +.B scons-time obj +subcommand displays how many objects of a specific named type +are created by SCons. +.P +The +.B scons-time obj +subcommand extracts object counts +from all the specified file arguments, +which should be files containing output from +running SCons with the +.B --debug=count +option. +(Normally, these would be +.B *.log +files generated by the +.B scons-time run +subcommand.) +All file name arguments will be +globbed for on-disk files. +.P +If no arguments are specified, +then object counts +will be extracted from all +.B *.log +files, +or the subset of them +with a prefix specified by the +.B -p +option. +.TP +-C DIR, --chdir=DIR +Changes to the specified +.I DIRECTORY +before looking for the specified files +(or files that match the specified patterns). +.TP +-f FILE, --file=FILE +Reads configuration information from the specified +.IR FILE . +.TP +-fmt=FORMAT, --format=FORMAT +Reports the output in the specified +.IR FORMAT . +The formats currently supported are +.B ascii +(the default) +and +.BR gnuplot . +.TP +-h, --help +Displays help text for the +.B scons-time obj +subcommand. +.TP +-p STRING, --prefix=STRING +Specifies the prefix string for log files +from which to extract object counts. +This will be used to search for log files +if no arguments are specified on the command line. +.TP +--stage=STAGE +Prints the object count at the end of the specified +.IR STAGE : +.B pre-read +(before the SConscript files are read), +.B post-read , +(after the SConscript files are read), +.B pre-build +(before any targets are built) +or +.B post-build +(after any targets are built). +If no +.B --stage +option is specified, +the default behavior is +.BR post-build , +which reports the final object count during each run. +.TP +-t NUMBER, --tail=NUMBER +Only reports object counts from the last +.I NUMBER +files. +'\"-------------------------------------------------------------------------- +.SS "The run Subcommand" +.SR +The +.B scons-time run +subcommand is the basic subcommand +for profiling a specific configuration +against a version of SCons. +.P +The configuration to be tested +is specified as a list of files +or directories that will be unpacked or copied +into a temporary directory +in which SCons will be invoked. +The +.B scons-time run +subcommand understands file suffixes like +.BR .tar , +.BR .tar.gz , +.BR .tgz +and +.BR .zip +and will unpack their contents into a temporary directory. +If more than one argument is specified, +each one will be unpacked or copied +into the temporary directory "on top of" +the previous archives or directories, +so the expectation is that multiple +specified archives share the same directory layout. +.P +Once the file or directory arguments are unpacked or +copied to the temporary directory, +the +.B scons-time run +subcommand runs the +requested version of SCons +against the configuration +three times: +.TP +Startup +SCons is run with the +.B --help +option so that just the SConscript files are read, +and then the default help text is printed. +This profiles just the perceived "overhead" of starting up SCons +and processing the SConscript files. +.TP +Full build +SCons is run to build everything specified in the configuration. +Specific targets to be passed in on the command l ine +may be specified by the +.B targets +keyword in a configuration file; see below for details. +.TP +Rebuild +SCons is run again on the same just-built directory. +If the dependencies in the SCons configuration are correct, +this should be an up-to-date, "do nothing" rebuild. +.P +Each invocation captures the output log file and a profile. +.P +The +.B scons-time run +subcommand supports the following options: +.TP +--aegis=PROJECT +Specifies the Aegis +.I PROJECT +from which the +version(s) of +.B scons +being timed will be extracted. +When +.B --aegis +is specified, the +.BI --number= NUMBER +option specifies delta numbers +that will be tested. +Output from each invocation run will be placed in file +names that match the Aegis delta numbers. +If the +.B --number= +option is not specified, +then the default behavior is to time the +tip of the specified +.IR PROJECT . +.TP +-f FILE, --file=FILE +Reads configuration information from the specified +.IR FILE . +This often provides a more convenient way to specify and +collect parameters associated with a specific timing configuration +than specifying them on the command line. +See the +.B CONFIGURATION FILE +section below +for information about the configuration file parameters. +.TP +-h, --help +Displays help text for the +.B scons-time run +subcommand. +.TP +-n, --no-exec +Do not execute commands, +just printing the command-line equivalents of what would be executed. +Note that the +.B scons-time +script actually executes its actions in Python, +where possible, +for portability. +The commands displayed are UNIX +.I equivalents +of what it's doing. +.TP +--number=NUMBER +Specifies the run number to be used in the names of +the log files and profile outputs generated by this run. +.IP +When used in conjuction with the +.BI --aegis= PROJECT +option, +.I NUMBER +specifies one or more comma-separated Aegis delta numbers +that will be retrieved automatically from the specified Aegis +.IR PROJECT . +.IP +When used in conjuction with the +.BI --svn= URL +option, +.I NUMBER +specifies one or more comma-separated Subversion revision numbers +that will be retrieved automatically from the Subversion +repository at the specified +.IR URL . +Ranges of delta or revision numbers +may be specified be separating two numbers +with a hyphen +.RB ( \- ). +.P +Example: +.ES +% scons-time run --svn=http://scons.tigris.org/svn/trunk --num=1247,1249-1252 . +.EE +.TP +-p STRING, --prefix=STRING +Specifies the prefix string to be used for all of the log files +and profiles generated by this run. +The default is derived from the first +specified argument: +if the first argument is a directory, +the default prefix is the name of the directory; +if the first argument is an archive +(tar or zip file), +the default prefix is the the base name of the archive, +that is, what remains after stripping the archive suffix +.RB ( .tgz ", " .tar.gz " or " .zip ). +.TP +--python=PYTHON +Specifies a path to the Python executable to be used +for the timing runs. +The default is to use the same Python executable that +is running the +.B scons-time +command itself. +.TP +-q, --quiet +Suppresses display of the command lines being executed. +.TP +-s DIR, --subdir=DIR +Specifies the name of directory or subdirectory +from which the commands should be executed. +The default is XXX +.TP +--scons=SCONS +Specifies a path to the SCons script to be used +for the timing runs. +The default is XXX +.TP +--svn=URL, --subversion=URL +Specifies the +.I URL +of the Subversion repository from which the +version(s) of +.B scons +being timed will be extracted. +When +.B --svn +is specified, the +.BI --number= NUMBER +option specifies revision numbers +that will be tested. +Output from each invocation run will be placed in file +names that match the Subversion revision numbers. +If the +.B --number= +option is not specified, +then the default behavior is to time the +.B HEAD +of the specified +.IR URL . +.TP +-v, --verbose +Displays the output from individual commands to the screen +(in addition to capturing the output in log files). +'\"-------------------------------------------------------------------------- +.SS "The time Subcommand" +.ST +.P +The +.B scons-time time +subcommand displays SCons execution times +as reported by the +.B scons --debug=time +option. +.P +The +.B scons-time time +subcommand extracts SCons timing +from all the specified file arguments, +which should be files containing output from +running SCons with the +.B --debug=time +option. +(Normally, these would be +.B *.log +files generated by the +.B scons-time run +subcommand.) +All file name arguments will be +globbed for on-disk files. +.P +If no arguments are specified, +then execution timings +will be extracted from all +.B *.log +files, +or the subset of them +with a prefix specified by the +.B -p +option. +.TP +-C DIR, --chdir=DIR +Changes to the specified +.I DIRECTORY +before looking for the specified files +(or files that match the specified patterns). +.TP +-f FILE, --file=FILE +Reads configuration information from the specified +.IR FILE . +.TP +-fmt=FORMAT, --format=FORMAT +Reports the output in the specified +.IR FORMAT . +The formats currently supported are +.B ascii +(the default) +and +.BR gnuplot . +.TP +-h, --help +Displays help text for the +.B scons-time time +subcommand. +.TP +-p STRING, --prefix=STRING +Specifies the prefix string for log files +from which to extract execution timings. +This will be used to search for log files +if no arguments are specified on the command line. +.TP +-t NUMBER, --tail=NUMBER +Only reports object counts from the last +.I NUMBER +files. +.TP +--which=WHICH +Prints the execution time for the specified +.IR WHICH +value: +.B total +(the total execution time), +.B SConscripts +(total execution time for the SConscript files themselves), +.B SCons +(exectuion time in SCons code itself) +or +.B commands +(execution time of the commands and other actions +used to build targets). +If no +.B --which +option is specified, +the default behavior is +.BR total , +which reports the total execution time for each run. +'\"========================================================================== +.SH CONFIGURATION FILE +Various +.B scons-time +subcommands can read information from a specified +configuration file when passed the +.B \-f +or +.B \--file +options. +The configuration file is actually executed as a Python script. +Setting Python variables in the configuration file +controls the behavior of the +.B scons-time +script more conveniently than having to specify +command-line options or arguments for every run, +and provides a handy way to "shrink-wrap" +the necessary information for producing (and reporting) +consistent timing runs for a given configuration. +.TP +.B aegis +The Aegis executable for extracting deltas. +The default is simply +.BR aegis . +.TP +.B aegis_project +The Aegis project from which deltas should be extracted. +The default is whatever is specified +with the +.B --aegis= +command-line option. +.TP +.B archive_list +A list of archives (files or directories) +that will be copied to the temporary directory +in which SCons will be invoked. +.BR .tar , +.BR .tar.gz , +.BR .tgz +and +.BR .zip +files will have their contents unpacked in +the temporary directory. +Directory trees and files will be copied as-is. +.TP +.B initial_commands +A list of commands that will be executed +before the actual timed +.B scons +runs. +This can be used for commands that are necessary +to prepare the source tree\-for example, +creating a configuration file +that should not be part of the timed run. +.TP +.B key_location +The location of the key on Gnuplot graphing information +generated with the +.BR --format=gnuplot +option. +The default is +.BR "bottom left" . +.TP +.B prefix +The file name prefix to be used when +running or extracting timing for this configuration. +.TP +.B python +The path name of the Python executable +to be used when running or extracting information +for this configuration. +The default is the same version of Python +used to run the SCons +.TP +.B scons +The path name of the SCons script to be used +when running or extracting information +for this configuration. +The default is simply +.BR scons . +.TP +.B scons_flags +The +.B scons +flags used when running SCons to collect timing information. +The default value is +.BR "--debug=count --debug=memory --debug=time --debug=memoizer" . +.TP +.B scons_lib_dir +.TP +.B scons_wrapper +.TP +.B startup_targets +.TP +.B subdir +The subdirectory of the project into which the +.B scons-time +script should change +before executing the SCons commands to time. +.TP +.B subversion_url +The Subversion URL from +.TP +.B svn +The subversion executable used to +check out revisions of SCons to be timed. +The default is simple +.BR svn . +.TP +.B svn_co_flag +.TP +.B tar +.TP +.B targets +A string containing the targets that should be added to +the command line of every timed +.B scons +run. +This can be used to restrict what's being timed to a +subset of the full build for the configuration. +.TP +.B targets0 +.TP +.B targets1 +.TP +.B targets2 +.TP +.B title +.TP +.B unzip +.TP +.B verbose +.TP +.B vertical_bars +'\"-------------------------------------------------------------------------- +.SS Example +Here is an example +.B scons-time +configuration file +for a hypothetical sample project: +.P +.ES +# The project doesn't use SCons natively (yet), so we're +# timing a separate set of SConscript files that we lay +# on top of the vanilla unpacked project tarball. +arguments = ['project-1.2.tgz', 'project-SConscripts.tar'] + +# The subdirectory name contains the project version number, +# so tell scons-time to chdir there before building. +subdir = 'project-1.2' + +# Set the prefix so output log files and profiles are named: +# project-000-[012].{log,prof} +# project-001-[012].{log,prof} +# etc. +prefix = 'project' + +# The SConscript files being tested don't do any SConf +# configuration, so run their normal ./configure script +# before we invoke SCons. +initial_commands = [ + './configure', +] + +# Only time building the bin/project executable. +targets = 'bin/project' + +# Time against SCons revisions of the branches/core branch +subversion_url = 'http://scons.tigris.org/svn/scons/branches/core' +.EE +'\"========================================================================== +.SH ENVIRONMENT +The +.B scons-time +script uses the following environment variables: +.TP +.B PRESERVE +If this value is set, +the +.B scons-time +script will +.I not +remove the temporary directory or directories +in which it builds the specified configuration +or downloads a specific version of SCons. +'\"========================================================================== +.SH "SEE ALSO" +.BR gnuplot (1), +.BR scons (1) + +.SH AUTHORS +Steven Knight diff --git a/scons.1 b/scons.1 new file mode 100644 index 0000000..ee321d7 --- /dev/null +++ b/scons.1 @@ -0,0 +1,15225 @@ +.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining +.\" a copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, sublicense, and/or sell copies of the Software, and to +.\" permit persons to whom the Software is furnished to do so, subject to +.\" the following conditions: +.\" +.\" The above copyright notice and this permission notice shall be included +.\" in all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +.\" KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +.\" WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +.\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +.\" +.\" doc/man/scons.1 5357 2011/09/09 21:31:03 bdeegan +.\" +.TH SCONS 1 "September 2011" +.\" ES - Example Start - indents and turns off line fill +.rm ES +.de ES +.RS +.nf +.. +.\" EE - Example End - ends indent and turns line fill back on +.rm EE +.de EE +.fi +.RE +.. +.SH NAME +scons \- a software construction tool +.SH SYNOPSIS +.B scons +[ +.IR options ... +] +[ +.IR name = val ... +] +[ +.IR targets ... +] +.SH DESCRIPTION + +The +.B scons +utility builds software (or other files) by determining which +component pieces must be rebuilt and executing the necessary commands to +rebuild them. + +By default, +.B scons +searches for a file named +.IR SConstruct , +.IR Sconstruct , +or +.I sconstruct +(in that order) in the current directory and reads its +configuration from the first file found. +An alternate file name may be +specified via the +.B -f +option. + +The +.I SConstruct +file can specify subsidiary +configuration files using the +.BR SConscript () +function. +By convention, +these subsidiary files are named +.IR SConscript , +although any name may be used. +(Because of this naming convention, +the term "SConscript files" +is sometimes used to refer +generically to all +.B scons +configuration files, +regardless of actual file name.) + +The configuration files +specify the target files to be built, and +(optionally) the rules to build those targets. Reasonable default +rules exist for building common software components (executable +programs, object files, libraries), so that for most software +projects, only the target and input files need be specified. + +Before reading the +.I SConstruct +file, +.B scons +looks for a directory named +.I site_scons +in various system directories (see below) and the directory containing the +.I SConstruct +file; for each of those dirs which exists, +.I site_scons +is prepended to sys.path, +the file +.IR site_scons/site_init.py , +is evaluated if it exists, +and the directory +.I site_scons/site_tools +is prepended to the default toolpath if it exists. +See the +.I --no-site-dir +and +.I --site-dir +options for more details. + +.B scons +reads and executes the SConscript files as Python scripts, +so you may use normal Python scripting capabilities +(such as flow control, data manipulation, and imported Python libraries) +to handle complicated build situations. +.BR scons , +however, reads and executes all of the SConscript files +.I before +it begins building any targets. +To make this obvious, +.B scons +prints the following messages about what it is doing: + +.ES +$ scons foo.out +scons: Reading SConscript files ... +scons: done reading SConscript files. +scons: Building targets ... +cp foo.in foo.out +scons: done building targets. +$ +.EE + +The status messages +(everything except the line that reads "cp foo.in foo.out") +may be suppressed using the +.B -Q +option. + +.B scons +does not automatically propagate +the external environment used to execute +.B scons +to the commands used to build target files. +This is so that builds will be guaranteed +repeatable regardless of the environment +variables set at the time +.B scons +is invoked. +This also means that if the compiler or other commands +that you want to use to build your target files +are not in standard system locations, +.B scons +will not find them unless +you explicitly set the PATH +to include those locations. +Whenever you create an +.B scons +construction environment, +you can propagate the value of PATH +from your external environment as follows: + +.ES +import os +env = Environment(ENV = {'PATH' : os.environ['PATH']}) +.EE + +Similarly, if the commands use external environment variables +like $PATH, $HOME, $JAVA_HOME, $LANG, $SHELL, $TERM, etc., +these variables can also be explicitly propagated: + +.ES +import os +env = Environment(ENV = {'PATH' : os.environ['PATH'], + 'HOME' : os.environ['HOME']}) +.EE + +Or you may explicitly propagate the invoking user's +complete external environment: + +.ES +import os +env = Environment(ENV = os.environ) +.EE + +This comes at the expense of making your build +dependent on the user's environment being set correctly, +but it may be more convenient for many configurations. + +.B scons +can scan known input files automatically for dependency +information (for example, #include statements +in C or C++ files) and will rebuild dependent files appropriately +whenever any "included" input file changes. +.B scons +supports the +ability to define new scanners for unknown input file types. + +.B scons +knows how to fetch files automatically from +SCCS or RCS subdirectories +using SCCS, RCS or BitKeeper. + +.B scons +is normally executed in a top-level directory containing a +.I SConstruct +file, optionally specifying +as command-line arguments +the target file or files to be built. + +By default, the command + +.ES +scons +.EE + +will build all target files in or below the current directory. +Explicit default targets +(to be built when no targets are specified on the command line) +may be defined the SConscript file(s) +using the +.B Default() +function, described below. + +Even when +.B Default() +targets are specified in the SConscript file(s), +all target files in or below the current directory +may be built by explicitly specifying +the current directory (.) +as a command-line target: + +.ES +scons . +.EE + +Building all target files, +including any files outside of the current directory, +may be specified by supplying a command-line target +of the root directory (on POSIX systems): + +.ES +scons / +.EE + +or the path name(s) of the volume(s) in which all the targets +should be built (on Windows systems): + +.ES +scons C:\\ D:\\ +.EE + +To build only specific targets, +supply them as command-line arguments: + +.ES +scons foo bar +.EE + +in which case only the specified targets will be built +(along with any derived files on which they depend). + +Specifying "cleanup" targets in SConscript files is not usually necessary. +The +.B -c +flag removes all files +necessary to build the specified target: + +.ES +scons -c . +.EE + +to remove all target files, or: + +.ES +scons -c build export +.EE + +to remove target files under build and export. +Additional files or directories to remove can be specified using the +.BR Clean() +function. +Conversely, targets that would normally be removed by the +.B -c +invocation +can be prevented from being removed by using the +.BR NoClean () +function. + +A subset of a hierarchical tree may be built by +remaining at the top-level directory (where the +.I SConstruct +file lives) and specifying the subdirectory as the target to be +built: + +.ES +scons src/subdir +.EE + +or by changing directory and invoking scons with the +.B -u +option, which traverses up the directory +hierarchy until it finds the +.I SConstruct +file, and then builds +targets relatively to the current subdirectory: + +.ES +cd src/subdir +scons -u . +.EE + +.B scons +supports building multiple targets in parallel via a +.B -j +option that takes, as its argument, the number +of simultaneous tasks that may be spawned: + +.ES +scons -j 4 +.EE + +builds four targets in parallel, for example. + +.B scons +can maintain a cache of target (derived) files that can +be shared between multiple builds. When caching is enabled in a +SConscript file, any target files built by +.B scons +will be copied +to the cache. If an up-to-date target file is found in the cache, it +will be retrieved from the cache instead of being rebuilt locally. +Caching behavior may be disabled and controlled in other ways by the +.BR --cache-force , +.BR --cache-disable , +and +.B --cache-show +command-line options. The +.B --random +option is useful to prevent multiple builds +from trying to update the cache simultaneously. + +Values of variables to be passed to the SConscript file(s) +may be specified on the command line: + +.ES +scons debug=1 . +.EE + +These variables are available in SConscript files +through the ARGUMENTS dictionary, +and can be used in the SConscript file(s) to modify +the build in any way: + +.ES +if ARGUMENTS.get('debug', 0): + env = Environment(CCFLAGS = '-g') +else: + env = Environment() +.EE + +The command-line variable arguments are also available +in the ARGLIST list, +indexed by their order on the command line. +This allows you to process them in order rather than by name, +if necessary. +ARGLIST[0] returns a tuple +containing (argname, argvalue). +A Python exception is thrown if you +try to access a list member that +does not exist. + +.B scons +requires Python version 2.4 or later. +There should be no other dependencies or requirements to run +.B scons. + +.\" The following paragraph reflects the default tool search orders +.\" currently in SCons/Tool/__init__.py. If any of those search orders +.\" change, this documentation should change, too. +By default, +.B scons +knows how to search for available programming tools +on various systems. +On Windows systems, +.B scons +searches in order for the +Microsoft Visual C++ tools, +the MinGW tool chain, +the Intel compiler tools, +and the PharLap ETS compiler. +On OS/2 systems, +.B scons +searches in order for the +OS/2 compiler, +the GCC tool chain, +and the Microsoft Visual C++ tools, +On SGI IRIX, IBM AIX, Hewlett Packard HP-UX, and Sun Solaris systems, +.B scons +searches for the native compiler tools +(MIPSpro, Visual Age, aCC, and Forte tools respectively) +and the GCC tool chain. +On all other platforms, +including POSIX (Linux and UNIX) platforms, +.B scons +searches in order +for the GCC tool chain, +the Microsoft Visual C++ tools, +and the Intel compiler tools. +You may, of course, override these default values +by appropriate configuration of +Environment construction variables. + +.SH OPTIONS +In general, +.B scons +supports the same command-line options as GNU +.BR make , +and many of those supported by +.BR cons . + +.TP +-b +Ignored for compatibility with non-GNU versions of +.BR make. + +.TP +-c, --clean, --remove +Clean up by removing all target files for which a construction +command is specified. +Also remove any files or directories associated to the construction command +using the +.BR Clean () +function. +Will not remove any targets specified by the +.BR NoClean () +function. + +.TP +.RI --cache-debug= file +Print debug information about the +.BR CacheDir () +derived-file caching +to the specified +.IR file . +If +.I file +is +.B \- +(a hyphen), +the debug information are printed to the standard output. +The printed messages describe what signature file names are +being looked for in, retrieved from, or written to the +.BR CacheDir () +directory tree. + +.TP +--cache-disable, --no-cache +Disable the derived-file caching specified by +.BR CacheDir (). +.B scons +will neither retrieve files from the cache +nor copy files to the cache. + +.TP +--cache-force, --cache-populate +When using +.BR CacheDir (), +populate a cache by copying any already-existing, up-to-date +derived files to the cache, +in addition to files built by this invocation. +This is useful to populate a new cache with +all the current derived files, +or to add to the cache any derived files +recently built with caching disabled via the +.B --cache-disable +option. + +.TP +--cache-show +When using +.BR CacheDir () +and retrieving a derived file from the cache, +show the command +that would have been executed to build the file, +instead of the usual report, +"Retrieved `file' from cache." +This will produce consistent output for build logs, +regardless of whether a target +file was rebuilt or retrieved from the cache. + +.TP +.RI --config= mode +This specifies how the +.B Configure +call should use or generate the +results of configuration tests. +The option should be specified from +among the following choices: + +.TP +--config=auto +scons will use its normal dependency mechanisms +to decide if a test must be rebuilt or not. +This saves time by not running the same configuration tests +every time you invoke scons, +but will overlook changes in system header files +or external commands (such as compilers) +if you don't specify those dependecies explicitly. +This is the default behavior. + +.TP +--config=force +If this option is specified, +all configuration tests will be re-run +regardless of whether the +cached results are out of date. +This can be used to explicitly +force the configuration tests to be updated +in response to an otherwise unconfigured change +in a system header file or compiler. + +.TP +--config=cache +If this option is specified, +no configuration tests will be rerun +and all results will be taken from cache. +Note that scons will still consider it an error +if --config=cache is specified +and a necessary test does not +yet have any results in the cache. + +.TP +.RI "-C" " directory" ", --directory=" directory +Change to the specified +.I directory +before searching for the +.IR SConstruct , +.IR Sconstruct , +or +.I sconstruct +file, or doing anything +else. Multiple +.B -C +options are interpreted +relative to the previous one, and the right-most +.B -C +option wins. (This option is nearly +equivalent to +.BR "-f directory/SConstruct" , +except that it will search for +.IR SConstruct , +.IR Sconstruct , +or +.I sconstruct +in the specified directory.) + +.\" .TP +.\" -d +.\" Display dependencies while building target files. Useful for +.\" figuring out why a specific file is being rebuilt, as well as +.\" general debugging of the build process. + +.TP +-D +Works exactly the same way as the +.B -u +option except for the way default targets are handled. +When this option is used and no targets are specified on the command line, +all default targets are built, whether or not they are below the current +directory. + +.TP +.RI --debug= type +Debug the build process. +.I type +specifies what type of debugging: + +.TP +--debug=count +Print how many objects are created +of the various classes used internally by SCons +before and after reading the SConscript files +and before and after building targets. +This is not supported when SCons is executed with the Python +.B -O +(optimized) option +or when the SCons modules +have been compiled with optimization +(that is, when executing from +.B *.pyo +files). + +.TP +--debug=duplicate +Print a line for each unlink/relink (or copy) of a variant file from +its source file. Includes debugging info for unlinking stale variant +files, as well as unlinking old targets before building them. + +.TP +--debug=dtree +A synonym for the newer +.B --tree=derived +option. +This will be deprecated in some future release +and ultimately removed. + +.TP +--debug=explain +Print an explanation of precisely why +.B scons +is deciding to (re-)build any targets. +(Note: this does not print anything +for targets that are +.I not +rebuilt.) + +.TP +--debug=findlibs +Instruct the scanner that searches for libraries +to print a message about each potential library +name it is searching for, +and about the actual libraries it finds. + +.TP +--debug=includes +Print the include tree after each top-level target is built. +This is generally used to find out what files are included by the sources +of a given derived file: + +.ES +$ scons --debug=includes foo.o +.EE + +.TP +--debug=memoizer +Prints a summary of hits and misses using the Memoizer, +an internal subsystem that counts +how often SCons uses cached values in memory +instead of recomputing them each time they're needed. + +.TP +--debug=memory +Prints how much memory SCons uses +before and after reading the SConscript files +and before and after building targets. + +.TP +--debug=nomemoizer +A deprecated option preserved for backwards compatibility. + +.TP +--debug=objects +Prints a list of the various objects +of the various classes used internally by SCons. + +.TP +--debug=pdb +Re-run SCons under the control of the +.RI pdb +Python debugger. + +.TP +--debug=prepare +Print a line each time any target (internal or external) +is prepared for building. +.B scons +prints this for each target it considers, even if that +target is up to date (see also --debug=explain). +This can help debug problems with targets that aren't being +built; it shows whether +.B scons +is at least considering them or not. + +.TP +--debug=presub +Print the raw command line used to build each target +before the construction environment variables are substituted. +Also shows which targets are being built by this command. +Output looks something like this: +.ES +$ scons --debug=presub +Building myprog.o with action(s): + $SHCC $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES +\&... +.EE + +.TP +--debug=stacktrace +Prints an internal Python stack trace +when encountering an otherwise unexplained error. + +.TP +--debug=stree +A synonym for the newer +.B --tree=all,status +option. +This will be deprecated in some future release +and ultimately removed. + +.TP +--debug=time +Prints various time profiling information: +the time spent executing each individual build command; +the total build time (time SCons ran from beginning to end); +the total time spent reading and executing SConscript files; +the total time spent SCons itself spend running +(that is, not counting reading and executing SConscript files); +and both the total time spent executing all build commands +and the elapsed wall-clock time spent executing those build commands. +(When +.B scons +is executed without the +.B -j +option, +the elapsed wall-clock time will typically +be slightly longer than the total time spent +executing all the build commands, +due to the SCons processing that takes place +in between executing each command. +When +.B scons +is executed +.I with +the +.B -j +option, +and your build configuration allows good parallelization, +the elapsed wall-clock time should +be significantly smaller than the +total time spent executing all the build commands, +since multiple build commands and +intervening SCons processing +should take place in parallel.) + +.TP +--debug=tree +A synonym for the newer +.B --tree=all +option. +This will be deprecated in some future release +and ultimately removed. + +.TP +.RI --diskcheck= types +Enable specific checks for +whether or not there is a file on disk +where the SCons configuration expects a directory +(or vice versa), +and whether or not RCS or SCCS sources exist +when searching for source and include files. +The +.I types +argument can be set to: +.BR all , +to enable all checks explicitly +(the default behavior); +.BR none , +to disable all such checks; +.BR match , +to check that files and directories on disk +match SCons' expected configuration; +.BR rcs , +to check for the existence of an RCS source +for any missing source or include files; +.BR sccs , +to check for the existence of an SCCS source +for any missing source or include files. +Multiple checks can be specified separated by commas; +for example, +.B --diskcheck=sccs,rcs +would still check for SCCS and RCS sources, +but disable the check for on-disk matches of files and directories. +Disabling some or all of these checks +can provide a performance boost for large configurations, +or when the configuration will check for files and/or directories +across networked or shared file systems, +at the slight increased risk of an incorrect build +or of not handling errors gracefully +(if include files really should be +found in SCCS or RCS, for example, +or if a file really does exist +where the SCons configuration expects a directory). + +.TP +.RI --duplicate= ORDER +There are three ways to duplicate files in a build tree: hard links, +soft (symbolic) links and copies. The default behaviour of SCons is to +prefer hard links to soft links to copies. You can specify different +behaviours with this option. +.IR ORDER +must be one of +.IR hard-soft-copy +(the default), +.IR soft-hard-copy , +.IR hard-copy , +.IR soft-copy +or +.IR copy . +SCons will attempt to duplicate files using +the mechanisms in the specified order. + +.\" .TP +.\" -e, --environment-overrides +.\" Variables from the execution environment override construction +.\" variables from the SConscript files. + +.TP +.RI -f " file" ", --file=" file ", --makefile=" file ", --sconstruct=" file +Use +.I file +as the initial SConscript file. +Multiple +.B -f +options may be specified, +in which case +.B scons +will read all of the specified files. + +.TP +-h, --help +Print a local help message for this build, if one is defined in +the SConscript file(s), plus a line that describes the +.B -H +option for command-line option help. If no local help message +is defined, prints the standard help message about command-line +options. Exits after displaying the appropriate message. + +.TP +-H, --help-options +Print the standard help message about command-line options and +exit. + +.TP +-i, --ignore-errors +Ignore all errors from commands executed to rebuild files. + +.TP +.RI -I " directory" ", --include-dir=" directory +Specifies a +.I directory +to search for +imported Python modules. If several +.B -I +options +are used, the directories are searched in the order specified. + +.TP +--implicit-cache +Cache implicit dependencies. +This causes +.B scons +to use the implicit (scanned) dependencies +from the last time it was run +instead of scanning the files for implicit dependencies. +This can significantly speed up SCons, +but with the following limitations: +.IP +.B scons +will not detect changes to implicit dependency search paths +(e.g. +.BR CPPPATH ", " LIBPATH ) +that would ordinarily +cause different versions of same-named files to be used. +.IP +.B scons +will miss changes in the implicit dependencies +in cases where a new implicit +dependency is added earlier in the implicit dependency search path +(e.g. +.BR CPPPATH ", " LIBPATH ) +than a current implicit dependency with the same name. + +.TP +--implicit-deps-changed +Forces SCons to ignore the cached implicit dependencies. This causes the +implicit dependencies to be rescanned and recached. This implies +.BR --implicit-cache . + +.TP +--implicit-deps-unchanged +Force SCons to ignore changes in the implicit dependencies. +This causes cached implicit dependencies to always be used. +This implies +.BR --implicit-cache . + +.TP +--interactive +Starts SCons in interactive mode. +The SConscript files are read once and a +.B "scons>>>" +prompt is printed. +Targets may now be rebuilt by typing commands at interactive prompt +without having to re-read the SConscript files +and re-initialize the dependency graph from scratch. + +SCons interactive mode supports the following commands: + +.RS 10 +.TP 6 +.BI build "[OPTIONS] [TARGETS] ..." +Builds the specified +.I TARGETS +(and their dependencies) +with the specified +SCons command-line +.IR OPTIONS . +.B b +and +.B scons +are synonyms. + +The following SCons command-line options affect the +.B build +command: + +.ES +--cache-debug=FILE +--cache-disable, --no-cache +--cache-force, --cache-populate +--cache-show +--debug=TYPE +-i, --ignore-errors +-j N, --jobs=N +-k, --keep-going +-n, --no-exec, --just-print, --dry-run, --recon +-Q +-s, --silent, --quiet +--taskmastertrace=FILE +--tree=OPTIONS +.EE + +.IP "" 6 +Any other SCons command-line options that are specified +do not cause errors +but have no effect on the +.B build +command +(mainly because they affect how the SConscript files are read, +which only happens once at the beginning of interactive mode). + +.TP 6 +.BI clean "[OPTIONS] [TARGETS] ..." +Cleans the specified +.I TARGETS +(and their dependencies) +with the specified options. +.B c +is a synonym. +This command is itself a synonym for +.B "build --clean" + +.TP 6 +.BI exit +Exits SCons interactive mode. +You can also exit by terminating input +(CTRL+D on UNIX or Linux systems, +CTRL+Z on Windows systems). + +.TP 6 +.BI help "[COMMAND]" +Provides a help message about +the commands available in SCons interactive mode. +If +.I COMMAND +is specified, +.B h +and +.B ? +are synonyms. + +.TP 6 +.BI shell "[COMMANDLINE]" +Executes the specified +.I COMMANDLINE +in a subshell. +If no +.I COMMANDLINE +is specified, +executes the interactive command interpreter +specified in the +.B SHELL +environment variable +(on UNIX and Linux systems) +or the +.B COMSPEC +environment variable +(on Windows systems). +.B sh +and +.B ! +are synonyms. + +.TP 6 +.B version +Prints SCons version information. +.RE + +.IP +An empty line repeats the last typed command. +Command-line editing can be used if the +.B readline +module is available. + +.ES +$ scons --interactive +scons: Reading SConscript files ... +scons: done reading SConscript files. +scons>>> build -n prog +scons>>> exit +.EE + +.TP +.RI -j " N" ", --jobs=" N +Specifies the number of jobs (commands) to run simultaneously. +If there is more than one +.B -j +option, the last one is effective. +.\" ??? If the +.\" .B -j +.\" option +.\" is specified without an argument, +.\" .B scons +.\" will not limit the number of +.\" simultaneous jobs. + +.TP +-k, --keep-going +Continue as much as possible after an error. The target that +failed and those that depend on it will not be remade, but other +targets specified on the command line will still be processed. + +.\" .TP +.\" .RI -l " N" ", --load-average=" N ", --max-load=" N +.\" No new jobs (commands) will be started if +.\" there are other jobs running and the system load +.\" average is at least +.\" .I N +.\" (a floating-point number). + +.\" +.\" .TP +.\" --list-derived +.\" List derived files (targets, dependencies) that would be built, +.\" but do not build them. +.\" [XXX This can probably go away with the right +.\" combination of other options. Revisit this issue.] +.\" +.\" .TP +.\" --list-actions +.\" List derived files that would be built, with the actions +.\" (commands) that build them. Does not build the files. +.\" [XXX This can probably go away with the right +.\" combination of other options. Revisit this issue.] +.\" +.\" .TP +.\" --list-where +.\" List derived files that would be built, plus where the file is +.\" defined (file name and line number). Does not build the files. +.\" [XXX This can probably go away with the right +.\" combination of other options. Revisit this issue.] + +.TP +-m +Ignored for compatibility with non-GNU versions of +.BR make . + +.TP +.RI --max-drift= SECONDS +Set the maximum expected drift in the modification time of files to +.IR SECONDS . +This value determines how long a file must be unmodified +before its cached content signature +will be used instead of +calculating a new content signature (MD5 checksum) +of the file's contents. +The default value is 2 days, which means a file must have a +modification time of at least two days ago in order to have its +cached content signature used. +A negative value means to never cache the content +signature and to ignore the cached value if there already is one. A value +of 0 means to always use the cached signature, +no matter how old the file is. + +.TP +.RI --md5-chunksize= KILOBYTES +Set the block size used to compute MD5 signatures to +.IR KILOBYTES . +This value determines the size of the chunks which are read in at once when +computing MD5 signatures. Files below that size are fully stored in memory +before performing the signature computation while bigger files are read in +block-by-block. A huge block-size leads to high memory consumption while a very +small block-size slows down the build considerably. + +The default value is to use a chunk size of 64 kilobytes, which should +be appropriate for most uses. + +.TP +-n, --just-print, --dry-run, --recon +No execute. Print the commands that would be executed to build +any out-of-date target files, but do not execute the commands. + +.TP +.RI --no-site-dir +Prevents the automatic addition of the standard +.I site_scons +dirs to +.IR sys.path . +Also prevents loading the +.I site_scons/site_init.py +modules if they exist, and prevents adding their +.I site_scons/site_tools +dirs to the toolpath. + +.\" .TP +.\" .RI -o " file" ", --old-file=" file ", --assume-old=" file +.\" Do not rebuild +.\" .IR file , +.\" and do +.\" not rebuild anything due to changes in the contents of +.\" .IR file . +.\" .TP +.\" .RI --override " file" +.\" Read values to override specific build environment variables +.\" from the specified +.\" .IR file . +.\" .TP +.\" -p +.\" Print the data base (construction environments, +.\" Builder and Scanner objects) that are defined +.\" after reading the SConscript files. +.\" After printing, a normal build is performed +.\" as usual, as specified by other command-line options. +.\" This also prints version information +.\" printed by the +.\" .B -v +.\" option. +.\" +.\" To print the database without performing a build do: +.\" +.\" .ES +.\" scons -p -q +.\" .EE + +.TP +.RI --profile= file +Run SCons under the Python profiler +and save the results in the specified +.IR file . +The results may be analyzed using the Python +pstats module. + +.TP +-q, --question +Do not run any commands, or print anything. Just return an exit +status that is zero if the specified targets are already up to +date, non-zero otherwise. +.TP +-Q +Quiets SCons status messages about +reading SConscript files, +building targets +and entering directories. +Commands that are executed +to rebuild target files are still printed. + +.\" .TP +.\" -r, -R, --no-builtin-rules, --no-builtin-variables +.\" Clear the default construction variables. Construction +.\" environments that are created will be completely empty. + +.TP +--random +Build dependencies in a random order. This is useful when +building multiple trees simultaneously with caching enabled, +to prevent multiple builds from simultaneously trying to build +or retrieve the same target files. + +.TP +-s, --silent, --quiet +Silent. Do not print commands that are executed to rebuild +target files. +Also suppresses SCons status messages. + +.TP +-S, --no-keep-going, --stop +Ignored for compatibility with GNU +.BR make . + +.TP +.RI --site-dir= dir +Uses the named dir as the site dir rather than the default +.I site_scons +dirs. This dir will get prepended to +.IR sys.path , +the module +.IR dir /site_init.py +will get loaded if it exists, and +.IR dir /site_tools +will get added to the default toolpath. + +The default set of +.I site_scons +dirs used when +.I --site-dir +is not specified depends on the system platform, as follows. Note +that the directories are examined in the order given, from most +generic to most specific, so the last-executed site_init.py file is +the most specific one (which gives it the chance to override +everything else), and the dirs are prepended to the paths, again so +the last dir examined comes first in the resulting path. + +.IP "Windows:" +.nf + %ALLUSERSPROFILE/Application Data/scons/site_scons + %USERPROFILE%/Local Settings/Application Data/scons/site_scons + %APPDATA%/scons/site_scons + %HOME%/.scons/site_scons + ./site_scons +.fi +.IP "Mac OS X:" +.nf + /Library/Application Support/SCons/site_scons + /opt/local/share/scons/site_scons (for MacPorts) + /sw/share/scons/site_scons (for Fink) + $HOME/Library/Application Support/SCons/site_scons + $HOME/.scons/site_scons + ./site_scons +.fi +.IP "Solaris:" +.nf + /opt/sfw/scons/site_scons + /usr/share/scons/site_scons + $HOME/.scons/site_scons + ./site_scons +.fi +.IP "Linux, HPUX, and other Posix-like systems:" +.nf + /usr/share/scons/site_scons + $HOME/.scons/site_scons + ./site_scons +.fi + +.TP +.RI --stack-size= KILOBYTES +Set the size stack used to run threads to +.IR KILOBYTES . +This value determines the stack size of the threads used to run jobs. +These are the threads that execute the actions of the builders for the +nodes that are out-of-date. +Note that this option has no effect unless the +.B num_jobs +option, which corresponds to -j and --jobs, is larger than one. Using +a stack size that is too small may cause stack overflow errors. This +usually shows up as segmentation faults that cause scons to abort +before building anything. Using a stack size that is too large will +cause scons to use more memory than required and may slow down the entire +build process. + +The default value is to use a stack size of 256 kilobytes, which should +be appropriate for most uses. You should not need to increase this value +unless you encounter stack overflow errors. + +.TP +-t, --touch +Ignored for compatibility with GNU +.BR make . +(Touching a file to make it +appear up-to-date is unnecessary when using +.BR scons .) + +.TP +.RI --taskmastertrace= file +Prints trace information to the specified +.I file +about how the internal Taskmaster object +evaluates and controls the order in which Nodes are built. +A file name of +.B - +may be used to specify the standard output. + +.TP +.RI -tree= options +Prints a tree of the dependencies +after each top-level target is built. +This prints out some or all of the tree, +in various formats, +depending on the +.I options +specified: + +.TP +--tree=all +Print the entire dependency tree +after each top-level target is built. +This prints out the complete dependency tree, +including implicit dependencies and ignored dependencies. + +.TP +--tree=derived +Restricts the tree output to only derived (target) files, +not source files. + +.TP +--tree=status +Prints status information for each displayed node. + +.TP +--tree=prune +Prunes the tree to avoid repeating dependency information +for nodes that have already been displayed. +Any node that has already been displayed +will have its name printed in +.BR "[square brackets]" , +as an indication that the dependencies +for that node can be found by searching +for the relevant output higher up in the tree. + +.IP +Multiple options may be specified, +separated by commas: + +.ES +# Prints only derived files, with status information: +scons --tree=derived,status + +# Prints all dependencies of target, with status information +# and pruning dependencies of already-visited Nodes: +scons --tree=all,prune,status target +.EE + +.TP +-u, --up, --search-up +Walks up the directory structure until an +.I SConstruct , +.I Sconstruct +or +.I sconstruct +file is found, and uses that +as the top of the directory tree. +If no targets are specified on the command line, +only targets at or below the +current directory will be built. + +.TP +-U +Works exactly the same way as the +.B -u +option except for the way default targets are handled. +When this option is used and no targets are specified on the command line, +all default targets that are defined in the SConscript(s) in the current +directory are built, regardless of what directory the resultant targets end +up in. + +.TP +-v, --version +Print the +.B scons +version, copyright information, +list of authors, and any other relevant information. +Then exit. + +.TP +-w, --print-directory +Print a message containing the working directory before and +after other processing. + +.TP +--no-print-directory +Turn off -w, even if it was turned on implicitly. + +.TP +.RI --warn= type ", --warn=no-" type +Enable or disable warnings. +.I type +specifies the type of warnings to be enabled or disabled: + +.TP +--warn=all, --warn=no-all +Enables or disables all warnings. + +.TP +--warn=cache-write-error, --warn=no-cache-write-error +Enables or disables warnings about errors trying to +write a copy of a built file to a specified +.BR CacheDir (). +These warnings are disabled by default. + +.TP +--warn=corrupt-sconsign, --warn=no-corrupt-sconsign +Enables or disables warnings about unfamiliar signature data in +.B .sconsign +files. +These warnings are enabled by default. + +.TP +--warn=dependency, --warn=no-dependency +Enables or disables warnings about dependencies. +These warnings are disabled by default. + +.TP +--warn=deprecated, --warn=no-deprecated +Enables or disables all warnings about use of +currently deprecated features. +These warnings are enabled by default. +Note that the +.B --warn=no-deprecated +option does not disable warnings about absolutely all deprecated features. +Warnings for some deprecated features that have already been through +several releases with deprecation warnings +may be mandatory for a release or two +before they are officially no longer supported by SCons. +Warnings for some specific deprecated features +may be enabled or disabled individually; +see below. + +.RS +.TP +--warn=deprecated-copy, --warn=no-deprecated-copy +Enables or disables warnings about use of the deprecated +.B env.Copy() +method. + +.TP +--warn=deprecated-source-signatures, --warn=no-deprecated-source-signatures +Enables or disables warnings about use of the deprecated +.B SourceSignatures() +function or +.B env.SourceSignatures() +method. + +.TP +--warn=deprecated-target-signatures, --warn=no-deprecated-target-signatures +Enables or disables warnings about use of the deprecated +.B TargetSignatures() +function or +.B env.TargetSignatures() +method. +.RE + +.TP +--warn=duplicate-environment, --warn=no-duplicate-environment +Enables or disables warnings about attempts to specify a build +of a target with two different construction environments +that use the same action. +These warnings are enabled by default. + +.TP +--warn=fortran-cxx-mix, --warn=no-fortran-cxx-mix +Enables or disables the specific warning about linking +Fortran and C++ object files in a single executable, +which can yield unpredictable behavior with some compilers. + +.TP +--warn=future-deprecated, --warn=no-future-deprecated +Enables or disables warnings about features +that will be deprecated in the future. +These warnings are disabled by default. +Enabling this warning is especially +recommended for projects that redistribute +SCons configurations for other users to build, +so that the project can be warned as soon as possible +about to-be-deprecated features +that may require changes to the configuration. + +.TP +--warn=link, --warn=no-link +Enables or disables warnings about link steps. + +.TP +--warn=misleading-keywords, --warn=no-misleading-keywords +Enables or disables warnings about use of the misspelled keywords +.B targets +and +.B sources +when calling Builders. +(Note the last +.B s +characters, the correct spellings are +.B target +and +.B source.) +These warnings are enabled by default. + +.TP +--warn=missing-sconscript, --warn=no-missing-sconscript +Enables or disables warnings about missing SConscript files. +These warnings are enabled by default. + +.TP +--warn=no-md5-module, --warn=no-no-md5-module +Enables or disables warnings about the version of Python +not having an MD5 checksum module available. +These warnings are enabled by default. + +.TP +--warn=no-metaclass-support, --warn=no-no-metaclass-support +Enables or disables warnings about the version of Python +not supporting metaclasses when the +.B --debug=memoizer +option is used. +These warnings are enabled by default. + +.TP +--warn=no-object-count, --warn=no-no-object-count +Enables or disables warnings about the +.B --debug=object +feature not working when +.B scons +is run with the python +.B \-O +option or from optimized Python (.pyo) modules. + +.TP +--warn=no-parallel-support, --warn=no-no-parallel-support +Enables or disables warnings about the version of Python +not being able to support parallel builds when the +.B -j +option is used. +These warnings are enabled by default. + +.TP +--warn=python-version, --warn=no-python-version +Enables or disables the warning about running +SCons with a deprecated version of Python. +These warnings are enabled by default. + +.TP +--warn=reserved-variable, --warn=no-reserved-variable +Enables or disables warnings about attempts to set the +reserved construction variable names +.BR CHANGED_SOURCES , +.BR CHANGED_TARGETS , +.BR TARGET , +.BR TARGETS , +.BR SOURCE , +.BR SOURCES , +.BR UNCHANGED_SOURCES +or +.BR UNCHANGED_TARGETS . +These warnings are disabled by default. + +.TP +--warn=stack-size, --warn=no-stack-size +Enables or disables warnings about requests to set the stack size +that could not be honored. +These warnings are enabled by default. + +.\" .TP +.\" .RI --write-filenames= file +.\" Write all filenames considered into +.\" .IR file . +.\" +.\" .TP +.\" .RI -W " file" ", --what-if=" file ", --new-file=" file ", --assume-new=" file +.\" Pretend that the target +.\" .I file +.\" has been +.\" modified. When used with the +.\" .B -n +.\" option, this +.\" show you what would be rebuilt if you were to modify that file. +.\" Without +.\" .B -n +.\" ... what? XXX +.\" +.\" .TP +.\" --warn-undefined-variables +.\" Warn when an undefined variable is referenced. + +.TP +.RI -Y " repository" ", --repository=" repository ", --srcdir=" repository +Search the specified repository for any input and target +files not found in the local directory hierarchy. Multiple +.B -Y +options may be specified, in which case the +repositories are searched in the order specified. + +.SH CONFIGURATION FILE REFERENCE +.\" .SS Python Basics +.\" XXX Adding this in the future would be a help. +.SS Construction Environments +A construction environment is the basic means by which the SConscript +files communicate build information to +.BR scons . +A new construction environment is created using the +.B Environment +function: + +.ES +env = Environment() +.EE + +Variables, called +.I construction +.IR variables , +may be set in a construction environment +either by specifying them as keywords when the object is created +or by assigning them a value after the object is created: + +.ES +env = Environment(FOO = 'foo') +env['BAR'] = 'bar' +.EE + +As a convenience, +construction variables may also be set or modified by the +.I parse_flags +keyword argument, which applies the +.B ParseFlags +method (described below) to the argument value +after all other processing is completed. +This is useful either if the exact content of the flags is unknown +(for example, read from a control file) +or if the flags are distributed to a number of construction variables. + +.ES +env = Environment(parse_flags = '-Iinclude -DEBUG -lm') +.EE + +This example adds 'include' to +.BR CPPPATH , +\&'EBUG' to +.BR CPPDEFINES , +and 'm' to +.BR LIBS . + +By default, a new construction environment is +initialized with a set of builder methods +and construction variables that are appropriate +for the current platform. +An optional platform keyword argument may be +used to specify that an environment should +be initialized for a different platform: + +.ES +env = Environment(platform = 'cygwin') +env = Environment(platform = 'os2') +env = Environment(platform = 'posix') +env = Environment(platform = 'win32') +.EE + +Specifying a platform initializes the appropriate +construction variables in the environment +to use and generate file names with prefixes +and suffixes appropriate for the platform. + +Note that the +.B win32 +platform adds the +.B SystemDrive +and +.B SystemRoot +variables from the user's external environment +to the construction environment's +.B ENV +dictionary. +This is so that any executed commands +that use sockets to connect with other systems +(such as fetching source files from +external CVS repository specifications like +.BR :pserver:anonymous@cvs.sourceforge.net:/cvsroot/scons ) +will work on Windows systems. + +The platform argument may be function or callable object, +in which case the Environment() method +will call the specified argument to update +the new construction environment: + +.ES +def my_platform(env): + env['VAR'] = 'xyzzy' + +env = Environment(platform = my_platform) +.EE + +Additionally, a specific set of tools +with which to initialize the environment +may be specified as an optional keyword argument: + +.ES +env = Environment(tools = ['msvc', 'lex']) +.EE + +Non-built-in tools may be specified using the toolpath argument: + +.ES +env = Environment(tools = ['default', 'foo'], toolpath = ['tools']) +.EE + +This looks for a tool specification in tools/foo.py (as well as +using the ordinary default tools for the platform). foo.py should +have two functions: generate(env, **kw) and exists(env). +The +.B generate() +function +modifies the passed-in environment +to set up variables so that the tool +can be executed; +it may use any keyword arguments +that the user supplies (see below) +to vary its initialization. +The +.B exists() +function should return a true +value if the tool is available. +Tools in the toolpath are used before +any of the built-in ones. For example, adding gcc.py to the toolpath +would override the built-in gcc tool. +Also note that the toolpath is +stored in the environment for use +by later calls to +.BR Clone () +and +.BR Tool () +methods: + +.ES +base = Environment(toolpath=['custom_path']) +derived = base.Clone(tools=['custom_tool']) +derived.CustomBuilder() +.EE + +The elements of the tools list may also +be functions or callable objects, +in which case the Environment() method +will call the specified elements +to update the new construction environment: + +.ES +def my_tool(env): + env['XYZZY'] = 'xyzzy' + +env = Environment(tools = [my_tool]) +.EE + +The individual elements of the tools list +may also themselves be two-element lists of the form +.RI ( toolname ", " kw_dict ). +SCons searches for the +.I toolname +specification file as described above, and +passes +.IR kw_dict , +which must be a dictionary, as keyword arguments to the tool's +.B generate +function. +The +.B generate +function can use the arguments to modify the tool's behavior +by setting up the environment in different ways +or otherwise changing its initialization. + +.ES +# in tools/my_tool.py: +def generate(env, **kw): + # Sets MY_TOOL to the value of keyword argument 'arg1' or 1. + env['MY_TOOL'] = kw.get('arg1', '1') +def exists(env): + return 1 + +# in SConstruct: +env = Environment(tools = ['default', ('my_tool', {'arg1': 'abc'})], + toolpath=['tools']) +.EE + +The tool definition (i.e. my_tool()) can use the PLATFORM variable from +the environment it receives to customize the tool for different platforms. + +If no tool list is specified, then SCons will auto-detect the installed +tools using the PATH variable in the ENV construction variable and the +platform name when the Environment is constructed. Changing the PATH +variable after the Environment is constructed will not cause the tools to +be redetected. + +SCons supports the following tool specifications out of the box: + +.ES +386asm +aixc++ +aixcc +aixf77 +aixlink +ar +as +bcc32 +c++ +cc +cvf +dmd +dvipdf +dvips +f77 +f90 +f95 +fortran +g++ +g77 +gas +gcc +gfortran +gnulink +gs +hpc++ +hpcc +hplink +icc +icl +ifl +ifort +ilink +ilink32 +intelc +jar +javac +javah +latex +lex +link +linkloc +m4 +masm +midl +mingw +mslib +mslink +mssdk +msvc +msvs +mwcc +mwld +nasm +pdflatex +pdftex +qt +rmic +rpcgen +sgiar +sgic++ +sgicc +sgilink +sunar +sunc++ +suncc +sunf77 +sunf90 +sunf95 +sunlink +swig +tar +tex +textfile +tlib +yacc +zip +.EE + +Additionally, there is a "tool" named +.B default +which configures the +environment with a default set of tools for the current platform. + +On posix and cygwin platforms +the GNU tools (e.g. gcc) are preferred by SCons, +on Windows the Microsoft tools (e.g. msvc) +followed by MinGW are preferred by SCons, +and in OS/2 the IBM tools (e.g. icc) are preferred by SCons. + +.SS Builder Methods + +Build rules are specified by calling a construction +environment's builder methods. +The arguments to the builder methods are +.B target +(a list of targets to be built, +usually file names) +and +.B source +(a list of sources to be built, +usually file names). + +Because long lists of file names +can lead to a lot of quoting, +.B scons +supplies a +.B Split() +global function +and a same-named environment method +that split a single string +into a list, separated on +strings of white-space characters. +(These are similar to the split() member function of Python strings +but work even if the input isn't a string.) + +Like all Python arguments, +the target and source arguments to a builder method +can be specified either with or without +the "target" and "source" keywords. +When the keywords are omitted, +the target is first, +followed by the source. +The following are equivalent examples of calling the Program builder method: + +.ES +env.Program('bar', ['bar.c', 'foo.c']) +env.Program('bar', Split('bar.c foo.c')) +env.Program('bar', env.Split('bar.c foo.c')) +env.Program(source = ['bar.c', 'foo.c'], target = 'bar') +env.Program(target = 'bar', Split('bar.c foo.c')) +env.Program(target = 'bar', env.Split('bar.c foo.c')) +env.Program('bar', source = 'bar.c foo.c'.split()) +.EE + +Target and source file names +that are not absolute path names +(that is, do not begin with +.B / +on POSIX systems +or +.B \\ +on Windows systems, +with or without +an optional drive letter) +are interpreted relative to the directory containing the +.B SConscript +file being read. +An initial +.B # +(hash mark) +on a path name means that the rest of the file name +is interpreted relative to +the directory containing +the top-level +.B SConstruct +file, +even if the +.B # +is followed by a directory separator character +(slash or backslash). + +Examples: + +.ES +# The comments describing the targets that will be built +# assume these calls are in a SConscript file in the +# a subdirectory named "subdir". + +# Builds the program "subdir/foo" from "subdir/foo.c": +env.Program('foo', 'foo.c') + +# Builds the program "/tmp/bar" from "subdir/bar.c": +env.Program('/tmp/bar', 'bar.c') + +# An initial '#' or '#/' are equivalent; the following +# calls build the programs "foo" and "bar" (in the +# top-level SConstruct directory) from "subdir/foo.c" and +# "subdir/bar.c", respectively: +env.Program('#foo', 'foo.c') +env.Program('#/bar', 'bar.c') + +# Builds the program "other/foo" (relative to the top-level +# SConstruct directory) from "subdir/foo.c": +env.Program('#other/foo', 'foo.c') +.EE + +When the target shares the same base name +as the source and only the suffix varies, +and if the builder method has a suffix defined for the target file type, +then the target argument may be omitted completely, +and +.B scons +will deduce the target file name from +the source file name. +The following examples all build the +executable program +.B bar +(on POSIX systems) +or +.B bar.exe +(on Windows systems) +from the bar.c source file: + +.ES +env.Program(target = 'bar', source = 'bar.c') +env.Program('bar', source = 'bar.c') +env.Program(source = 'bar.c') +env.Program('bar.c') +.EE + +As a convenience, a +.B srcdir +keyword argument may be specified +when calling a Builder. +When specified, +all source file strings that are not absolute paths +will be interpreted relative to the specified +.BR srcdir . +The following example will build the +.B build/prog +(or +.B build/prog.exe +on Windows) +program from the files +.B src/f1.c +and +.BR src/f2.c : + +.ES +env.Program('build/prog', ['f1.c', 'f2.c'], srcdir='src') +.EE + +It is possible to override or add construction variables when calling a +builder method by passing additional keyword arguments. +These overridden or added +variables will only be in effect when building the target, so they will not +affect other parts of the build. For example, if you want to add additional +libraries for just one program: + +.ES +env.Program('hello', 'hello.c', LIBS=['gl', 'glut']) +.EE + +or generate a shared library with a non-standard suffix: + +.ES +env.SharedLibrary('word', 'word.cpp', + SHLIBSUFFIX='.ocx', + LIBSUFFIXES=['.ocx']) +.EE + +(Note that both the $SHLIBSUFFIX and $LIBSUFFIXES variables must be set +if you want SCons to search automatically +for dependencies on the non-standard library names; +see the descriptions of these variables, below, for more information.) + +It is also possible to use the +.I parse_flags +keyword argument in an override: + +.ES +env = Program('hello', 'hello.c', parse_flags = '-Iinclude -DEBUG -lm') +.EE + +This example adds 'include' to +.BR CPPPATH , +\&'EBUG' to +.BR CPPDEFINES , +and 'm' to +.BR LIBS . + +Although the builder methods defined by +.B scons +are, in fact, +methods of a construction environment object, +they may also be called without an explicit environment: + +.ES +Program('hello', 'hello.c') +SharedLibrary('word', 'word.cpp') +.EE + +In this case, +the methods are called internally using a default construction +environment that consists of the tools and values that +.B scons +has determined are appropriate for the local system. + +Builder methods that can be called without an explicit +environment may be called from custom Python modules that you +import into an SConscript file by adding the following +to the Python module: + +.ES +from SCons.Script import * +.EE + +All builder methods return a list-like object +containing Nodes that +represent the target or targets that will be built. +A +.I Node +is an internal SCons object +which represents +build targets or sources. + +The returned Node-list object +can be passed to other builder methods as source(s) +or passed to any SCons function or method +where a filename would normally be accepted. +For example, if it were necessary +to add a specific +.B -D +flag when compiling one specific object file: + +.ES +bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR') +env.Program(source = ['foo.c', bar_obj_list, 'main.c']) +.EE + +Using a Node in this way +makes for a more portable build +by avoiding having to specify +a platform-specific object suffix +when calling the Program() builder method. + +Note that Builder calls will automatically "flatten" +the source and target file lists, +so it's all right to have the bar_obj list +return by the StaticObject() call +in the middle of the source file list. +If you need to manipulate a list of lists returned by Builders +directly using Python, +you can either build the list by hand: + +.ES +foo = Object('foo.c') +bar = Object('bar.c') +objects = ['begin.o'] + foo + ['middle.o'] + bar + ['end.o'] +for object in objects: + print str(object) +.EE + +Or you can use the +.BR Flatten () +function supplied by scons +to create a list containing just the Nodes, +which may be more convenient: + +.ES +foo = Object('foo.c') +bar = Object('bar.c') +objects = Flatten(['begin.o', foo, 'middle.o', bar, 'end.o']) +for object in objects: + print str(object) +.EE + +Note also that because Builder calls return +a list-like object, not an actual Python list, +you should +.I not +use the Python +.B += +operator to append Builder results to a Python list. +Because the list and the object are different types, +Python will not update the original list in place, +but will instead create a new Node-list object +containing the concatenation of the list +elements and the Builder results. +This will cause problems for any other Python variables +in your SCons configuration +that still hold on to a reference to the original list. +Instead, use the Python +.B .extend() +method to make sure the list is updated in-place. +Example: + +.ES +object_files = [] + +# Do NOT use += as follows: +# +# object_files += Object('bar.c') +# +# It will not update the object_files list in place. +# +# Instead, use the .extend() method: +object_files.extend(Object('bar.c')) + +.EE + +The path name for a Node's file may be used +by passing the Node to the Python-builtin +.B str() +function: + +.ES +bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR') +print "The path to bar_obj is:", str(bar_obj_list[0]) +.EE + +Note again that because the Builder call returns a list, +we have to access the first element in the list +.B (bar_obj_list[0]) +to get at the Node that actually represents +the object file. + +Builder calls support a +.B chdir +keyword argument that +specifies that the Builder's action(s) +should be executed +after changing directory. +If the +.B chdir +argument is +a string or a directory Node, +scons will change to the specified directory. +If the +.B chdir +is not a string or Node +and is non-zero, +then scons will change to the +target file's directory. + +.ES +# scons will change to the "sub" subdirectory +# before executing the "cp" command. +env.Command('sub/dir/foo.out', 'sub/dir/foo.in', + "cp dir/foo.in dir/foo.out", + chdir='sub') + +# Because chdir is not a string, scons will change to the +# target's directory ("sub/dir") before executing the +# "cp" command. +env.Command('sub/dir/foo.out', 'sub/dir/foo.in', + "cp foo.in foo.out", + chdir=1) +.EE + +Note that scons will +.I not +automatically modify +its expansion of +construction variables like +.B $TARGET +and +.B $SOURCE +when using the chdir +keyword argument--that is, +the expanded file names +will still be relative to +the top-level SConstruct directory, +and consequently incorrect +relative to the chdir directory. +If you use the chdir keyword argument, +you will typically need to supply a different +command line using +expansions like +.B ${TARGET.file} +and +.B ${SOURCE.file} +to use just the filename portion of the +targets and source. + +.B scons +provides the following builder methods: + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +'\" BEGIN GENERATED BUILDER DESCRIPTIONS +'\" +'\" The descriptions below of the various SCons Builders are generated +'\" from the .xml files that live next to the various Python modules in +'\" the build enginer library. If you're reading this [gnt]roff file +'\" with an eye towards patching this man page, you can still submit +'\" a diff against this text, but it will have to be translated to a +'\" diff against the underlying .xml file before the patch is actually +'\" accepted. If you do that yourself, it will make it easier to +'\" integrate the patch. +'\" +'\" BEGIN GENERATED BUILDER DESCRIPTIONS +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP CFile() +.IP env.CFile() +Builds a C source file given a lex (\fB.l\fP) +or yacc (\fB.y\fP) input file. +The suffix specified by the \fB$CFILESUFFIX\fP construction variable +(\fB.c\fP by default) +is automatically added to the target +if it is not already present. +Example: + +.ES +# builds foo.c +env.CFile(target = 'foo.c', source = 'foo.l') +# builds bar.c +env.CFile(target = 'bar', source = 'bar.y') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Command() +.IP env.Command() +The \fBCommand\fP() "Builder" is actually implemented +as a function that looks like a Builder, +but actually takes an additional argument of the action +from which the Builder should be made. +See the \fBCommand\fP() function description +for the calling syntax and details. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP CXXFile() +.IP env.CXXFile() +Builds a C++ source file given a lex (\fB.ll\fP) +or yacc (\fB.yy\fP) +input file. +The suffix specified by the \fB$CXXFILESUFFIX\fP construction variable +(\fB.cc\fP by default) +is automatically added to the target +if it is not already present. +Example: + +.ES +# builds foo.cc +env.CXXFile(target = 'foo.cc', source = 'foo.ll') +# builds bar.cc +env.CXXFile(target = 'bar', source = 'bar.yy') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP DVI() +.IP env.DVI() +Builds a \fB.dvi\fP file +from a \fB.tex\fP, +\fB.ltx\fP or \fB.latex\fP input file. +If the source file suffix is \fB.tex\fP, +.B scons +will examine the contents of the file; +if the string +.B \\documentclass +or +.B \\documentstyle +is found, the file is assumed to be a LaTeX file and +the target is built by invoking the \fB$LATEXCOM\fP command line; +otherwise, the \fB$TEXCOM\fP command line is used. +If the file is a LaTeX file, +the +.BR DVI () +builder method will also examine the contents +of the +.B .aux +file and invoke the \fB$BIBTEX\fP command line +if the string +.B bibdata +is found, +start \fB$MAKEINDEX\fP to generate an index if a +.B .ind +file is found +and will examine the contents +.B .log +file and re-run the \fB$LATEXCOM\fP command +if the log file says it is necessary. + +The suffix \fB.dvi\fP +(hard-coded within TeX itself) +is automatically added to the target +if it is not already present. +Examples: + +.ES +# builds from aaa.tex +env.DVI(target = 'aaa.dvi', source = 'aaa.tex') +# builds bbb.dvi +env.DVI(target = 'bbb', source = 'bbb.ltx') +# builds from ccc.latex +env.DVI(target = 'ccc.dvi', source = 'ccc.latex') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Install() +.IP env.Install() +Installs one or more source files or directories +in the specified target, +which must be a directory. +The names of the specified source files or directories +remain the same within the destination directory. + +.ES +env.Install('/usr/local/bin', source = ['foo', 'bar']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP InstallAs() +.IP env.InstallAs() +Installs one or more source files or directories +to specific names, +allowing changing a file or directory name +as part of the installation. +It is an error if the +target +and +source +arguments list different numbers of files or directories. + +.ES +env.InstallAs(target = '/usr/local/bin/foo', + source = 'foo_debug') +env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], + source = ['libFOO.a', 'libBAR.a']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Jar() +.IP env.Jar() +Builds a Java archive (\fB.jar\fP) file +from the specified list of sources. +Any directories in the source list +will be searched for \fB.class\fP files). +Any \fB.java\fP files in the source list +will be compiled to \fB.class\fP files +by calling the \fBJava\fP() Builder. + +If the \fB$JARCHDIR\fP value is set, the +.B jar +command will change to the specified directory using the +.B \-C +option. +If \fB$JARCHDIR\fP is not set explicitly, +&SCons; will use the top of any subdirectory tree +in which Java \fB.class\fP +were built by the \fBJava\fP() Builder. + +If the contents any of the source files begin with the string +.BR Manifest-Version , +the file is assumed to be a manifest +and is passed to the +.B jar +command with the +.B m +option set. + +.ES +env.Jar(target = 'foo.jar', source = 'classes') + +env.Jar(target = 'bar.jar', + source = ['bar1.java', 'bar2.java']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Java() +.IP env.Java() +Builds one or more Java class files. +The sources may be any combination of explicit +\fB.java\fP files, +or directory trees which will be scanned +for \fB.java\fP files. + +SCons will parse each source \fB.java\fP file +to find the classes +(including inner classes) +defined within that file, +and from that figure out the +target \fB.class\fP files that will be created. +The class files will be placed underneath +the specified target directory. + +SCons will also search each Java file +for the Java package name, +which it assumes can be found on a line +beginning with the string +.B package +in the first column; +the resulting \fB.class\fP files +will be placed in a directory reflecting +the specified package name. +For example, +the file +.B Foo.java +defining a single public +.I Foo +class and +containing a package name of +.I sub.dir +will generate a corresponding +.B sub/dir/Foo.class +class file. + +Examples: + +.ES +env.Java(target = 'classes', source = 'src') +env.Java(target = 'classes', source = ['src1', 'src2']) +env.Java(target = 'classes', source = ['File1.java', 'File2.java']) +.EE +.IP +Java source files can use the native encoding for the underlying OS. +Since SCons compiles in simple ASCII mode by default, +the compiler will generate warnings about unmappable characters, +which may lead to errors as the file is processed further. +In this case, the user must specify the \fBLANG\fP +environment variable to tell the compiler what encoding is used. +For portibility, it's best if the encoding is hard-coded +so that the compile will work if it is done on a system +with a different encoding. + +.ES +env = Environment() +env['ENV']['LANG'] = 'en_GB.UTF-8' +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP JavaH() +.IP env.JavaH() +Builds C header and source files for +implementing Java native methods. +The target can be either a directory +in which the header files will be written, +or a header file name which +will contain all of the definitions. +The source can be the names of \fB.class\fP files, +the names of \fB.java\fP files +to be compiled into \fB.class\fP files +by calling the \fBJava\fP() builder method, +or the objects returned from the +.BR Java () +builder method. + +If the construction variable +.B $JAVACLASSDIR +is set, either in the environment +or in the call to the +.BR JavaH () +builder method itself, +then the value of the variable +will be stripped from the +beginning of any \fB.class\fP file names. + +Examples: + +.ES +# builds java_native.h +classes = env.Java(target = 'classdir', source = 'src') +env.JavaH(target = 'java_native.h', source = classes) + +# builds include/package_foo.h and include/package_bar.h +env.JavaH(target = 'include', + source = ['package/foo.class', 'package/bar.class']) + +# builds export/foo.h and export/bar.h +env.JavaH(target = 'export', + source = ['classes/foo.class', 'classes/bar.class'], + JAVACLASSDIR = 'classes') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Library() +.IP env.Library() +A synonym for the +.BR StaticLibrary () +builder method. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP LoadableModule() +.IP env.LoadableModule() +On most systems, +this is the same as +.BR SharedLibrary (). +On Mac OS X (Darwin) platforms, +this creates a loadable module bundle. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP M4() +.IP env.M4() +Builds an output file from an M4 input file. +This uses a default \fB$M4FLAGS\fP value of +.BR \-E , +which considers all warnings to be fatal +and stops on the first warning +when using the GNU version of m4. +Example: + +.ES +env.M4(target = 'foo.c', source = 'foo.c.m4') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Moc() +.IP env.Moc() +Builds an output file from a moc input file. Moc input files are either +header files or cxx files. This builder is only available after using the +tool 'qt'. See the \fB$QTDIR\fP variable for more information. +Example: + +.ES +env.Moc('foo.h') # generates moc_foo.cc +env.Moc('foo.cpp') # generates foo.moc +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP MSVSProject() +.IP env.MSVSProject() +Builds a Microsoft Visual Studio project file, +and by default builds a solution file as well. + +This builds a Visual Studio project file, based on the version of +Visual Studio that is configured (either the latest installed version, +or the version specified by +.B $MSVS_VERSION +in the Environment constructor). +For Visual Studio 6, it will generate a +.B .dsp +file. +For Visual Studio 7 (.NET) and later versions, it will generate a +.B .vcproj +file. + +By default, +this also generates a solution file +for the specified project, +a +.B .dsw +file for Visual Studio 6 +or a +.B .sln +file for Visual Studio 7 (.NET). +This behavior may be disabled by specifying +.B auto_build_solution=0 +when you call +.BR MSVSProject (), +in which case you presumably want to +build the solution file(s) +by calling the +.BR MSVSSolution () +Builder (see below). + +The \fBMSVSProject\fP() builder +takes several lists of filenames +to be placed into the project file. +These are currently limited to +.BR srcs , +.BR incs , +.BR localincs , +.BR resources , +and +.BR misc . +These are pretty self-explanatory, but it should be noted that these +lists are added to the \fB$SOURCES\fP construction variable as strings, +NOT as SCons File Nodes. This is because they represent file +names to be added to the project file, not the source files used to +build the project file. + +The above filename lists are all optional, +although at least one must be specified +for the resulting project file to be non-empty. + +In addition to the above lists of values, +the following values may be specified: + +.BR target : +The name of the target +.B .dsp +or +.B .vcproj +file. +The correct +suffix for the version of Visual Studio must be used, +but the +.B $MSVSPROJECTSUFFIX +construction variable +will be defined to the correct value (see example below). + +.BR variant : +The name of this particular variant. +For Visual Studio 7 projects, +this can also be a list of variant names. +These are typically things like "Debug" or "Release", but really +can be anything you want. +For Visual Studio 7 projects, +they may also specify a target platform +separated from the variant name by a +.B | +(vertical pipe) +character: +.BR Debug|Xbox . +The default target platform is Win32. +Multiple calls to +.BR MSVSProject () +with different variants are allowed; +all variants will be added to the project file with their appropriate +build targets and sources. + +.BR buildtarget : +An optional string, node, or list of strings or nodes +(one per build variant), to tell the Visual Studio debugger +what output target to use in what build variant. +The number of +.B buildtarget +entries must match the number of +.B variant +entries. + +.BR runfile : +The name of the file that Visual Studio 7 and later +will run and debug. +This appears as the value of the +.B Output +field in the resutling Visual Studio project file. +If this is not specified, +the default is the same as the specified +.B buildtarget +value. + +Note that because &SCons; always executes its build commands +from the directory in which the \fBSConstruct\fP file is located, +if you generate a project file in a different directory +than the \fBSConstruct\fP directory, +users will not be able to double-click +on the file name in compilation error messages +displayed in the Visual Studio console output window. +This can be remedied by adding the +Visual C/C++ +.B /FC +compiler option to the \fB$CCFLAGS\fP variable +so that the compiler will print +the full path name of any +files that cause compilation errors. + +Example usage: + +.ES +barsrcs = ['bar.cpp'], +barincs = ['bar.h'], +barlocalincs = ['StdAfx.h'] +barresources = ['bar.rc','resource.h'] +barmisc = ['bar_readme.txt'] + +dll = env.SharedLibrary(target = 'bar.dll', + source = barsrcs) + +env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'], + srcs = barsrcs, + incs = barincs, + localincs = barlocalincs, + resources = barresources, + misc = barmisc, + buildtarget = dll, + variant = 'Release') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP MSVSSolution() +.IP env.MSVSSolution() +Builds a Microsoft Visual Studio solution file. + +This builds a Visual Studio solution file, +based on the version of Visual Studio that is configured +(either the latest installed version, +or the version specified by +.B $MSVS_VERSION +in the construction environment). +For Visual Studio 6, it will generate a +.B .dsw +file. +For Visual Studio 7 (.NET), it will +generate a +.B .sln +file. + +The following values must be specified: + +.BR target : +The name of the target .dsw or .sln file. The correct +suffix for the version of Visual Studio must be used, but the value +.B $MSVSSOLUTIONSUFFIX +will be defined to the correct value (see example below). + +.BR variant : +The name of this particular variant, or a list of variant +names (the latter is only supported for MSVS 7 solutions). These are +typically things like "Debug" or "Release", but really can be anything +you want. For MSVS 7 they may also specify target platform, like this +"Debug|Xbox". Default platform is Win32. + +.BR projects : +A list of project file names, or Project nodes returned by calls to the +.BR MSVSProject () +Builder, +to be placed into the solution file. +It should be noted that these file names are NOT added to the $SOURCES +environment variable in form of files, but rather as strings. This +is because they represent file names to be added to the solution file, +not the source files used to build the solution file. + +Example Usage: + +.ES +env.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'], + projects = ['bar' + env['MSVSPROJECTSUFFIX']], + variant = 'Release') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Object() +.IP env.Object() +A synonym for the +.BR StaticObject () +builder method. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Package() +.IP env.Package() +Builds software distribution packages. +Packages consist of files to install and packaging information. +The former may be specified with the \fIsource\fP parameter and may be left out, +in which case the &FindInstalledFiles; function will collect +all files that have an \fBInstall\fP() or \fBInstallAs\fP() Builder attached. +If the \fItarget\fP is not specified +it will be deduced from additional information given to this Builder. + +The packaging information is specified +with the help of construction variables documented below. +This information is called a tag to stress that +some of them can also be attached to files with the &Tag; function. +The mandatory ones will complain if they were not specified. +They vary depending on chosen target packager. + +The target packager may be selected with the "PACKAGETYPE" command line +option or with the \fB$PACKAGETYPE\fP construction variable. Currently +the following packagers available: + + * msi - Microsoft Installer + * rpm - Redhat Package Manger + * ipkg - Itsy Package Management System + * tarbz2 - compressed tar + * targz - compressed tar + * zip - zip file + * src_tarbz2 - compressed tar source + * src_targz - compressed tar source + * src_zip - zip file source + +An updated list is always available under the "package_type" option when +running "scons --help" on a project that has packaging activated. +.ES +env = Environment(tools=['default', 'packaging']) +env.Install('/bin/', 'my_program') +env.Package( NAME = 'foo', + VERSION = '1.2.3', + PACKAGEVERSION = 0, + PACKAGETYPE = 'rpm', + LICENSE = 'gpl', + SUMMARY = 'balalalalal', + DESCRIPTION = 'this should be really really long', + X_RPM_GROUP = 'Application/fu', + SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + ) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP PCH() +.IP env.PCH() +Builds a Microsoft Visual C++ precompiled header. +Calling this builder method +returns a list of two targets: the PCH as the first element, and the object +file as the second element. Normally the object file is ignored. +This builder method is only +provided when Microsoft Visual C++ is being used as the compiler. +The PCH builder method is generally used in +conjuction with the PCH construction variable to force object files to use +the precompiled header: + +.ES +env['PCH'] = env.PCH('StdAfx.cpp')[0] +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP PDF() +.IP env.PDF() +Builds a \fB.pdf\fP file +from a \fB.dvi\fP input file +(or, by extension, a \fB.tex\fP, +.BR .ltx , +or +\fB.latex\fP input file). +The suffix specified by the \fB$PDFSUFFIX\fP construction variable +(\fB.pdf\fP by default) +is added automatically to the target +if it is not already present. Example: + +.ES +# builds from aaa.tex +env.PDF(target = 'aaa.pdf', source = 'aaa.tex') +# builds bbb.pdf from bbb.dvi +env.PDF(target = 'bbb', source = 'bbb.dvi') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP PostScript() +.IP env.PostScript() +Builds a \fB.ps\fP file +from a \fB.dvi\fP input file +(or, by extension, a \fB.tex\fP, +.BR .ltx , +or +\fB.latex\fP input file). +The suffix specified by the \fB$PSSUFFIX\fP construction variable +(\fB.ps\fP by default) +is added automatically to the target +if it is not already present. Example: + +.ES +# builds from aaa.tex +env.PostScript(target = 'aaa.ps', source = 'aaa.tex') +# builds bbb.ps from bbb.dvi +env.PostScript(target = 'bbb', source = 'bbb.dvi') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Program() +.IP env.Program() +Builds an executable given one or more object files +or C, C++, D, or Fortran source files. +If any C, C++, D or Fortran source files are specified, +then they will be automatically +compiled to object files using the +.BR Object () +builder method; +see that builder method's description for +a list of legal source file suffixes +and how they are interpreted. +The target executable file prefix +(specified by the \fB$PROGPREFIX\fP construction variable; nothing by default) +and suffix +(specified by the \fB$PROGSUFFIX\fP construction variable; +by default, \fB.exe\fP on Windows systems, +nothing on POSIX systems) +are automatically added to the target if not already present. +Example: + +.ES +env.Program(target = 'foo', source = ['foo.o', 'bar.c', 'baz.f']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP RES() +.IP env.RES() +Builds a Microsoft Visual C++ resource file. +This builder method is only provided +when Microsoft Visual C++ or MinGW is being used as the compiler. The +.B .res +(or +.B .o +for MinGW) suffix is added to the target name if no other suffix is given. +The source +file is scanned for implicit dependencies as though it were a C file. +Example: + +.ES +env.RES('resource.rc') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP RMIC() +.IP env.RMIC() +Builds stub and skeleton class files +for remote objects +from Java \fB.class\fP files. +The target is a directory +relative to which the stub +and skeleton class files will be written. +The source can be the names of \fB.class\fP files, +or the objects return from the +.BR Java () +builder method. + +If the construction variable +.B $JAVACLASSDIR +is set, either in the environment +or in the call to the +.BR RMIC () +builder method itself, +then the value of the variable +will be stripped from the +beginning of any \fB.class \fP +file names. + +.ES +classes = env.Java(target = 'classdir', source = 'src') +env.RMIC(target = 'outdir1', source = classes) + +env.RMIC(target = 'outdir2', + source = ['package/foo.class', 'package/bar.class']) + +env.RMIC(target = 'outdir3', + source = ['classes/foo.class', 'classes/bar.class'], + JAVACLASSDIR = 'classes') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP RPCGenClient() +.IP env.RPCGenClient() +Generates an RPC client stub (\fB_clnt.c\fP) file +from a specified RPC (\fB.x\fP) source file. +Because rpcgen only builds output files +in the local directory, +the command will be executed +in the source file's directory by default. + +.ES +# Builds src/rpcif_clnt.c +env.RPCGenClient('src/rpcif.x') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP RPCGenHeader() +.IP env.RPCGenHeader() +Generates an RPC header (\fB.h\fP) file +from a specified RPC (\fB.x\fP) source file. +Because rpcgen only builds output files +in the local directory, +the command will be executed +in the source file's directory by default. + +.ES +# Builds src/rpcif.h +env.RPCGenHeader('src/rpcif.x') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP RPCGenService() +.IP env.RPCGenService() +Generates an RPC server-skeleton (\fB_svc.c\fP) file +from a specified RPC (\fB.x\fP) source file. +Because rpcgen only builds output files +in the local directory, +the command will be executed +in the source file's directory by default. + +.ES +# Builds src/rpcif_svc.c +env.RPCGenClient('src/rpcif.x') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP RPCGenXDR() +.IP env.RPCGenXDR() +Generates an RPC XDR routine (\fB_xdr.c\fP) file +from a specified RPC (\fB.x\fP) source file. +Because rpcgen only builds output files +in the local directory, +the command will be executed +in the source file's directory by default. + +.ES +# Builds src/rpcif_xdr.c +env.RPCGenClient('src/rpcif.x') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP SharedLibrary() +.IP env.SharedLibrary() +Builds a shared library +(\fB.so\fP on a POSIX system, +\fB.dll\fP on Windows) +given one or more object files +or C, C++, D or Fortran source files. +If any source files are given, +then they will be automatically +compiled to object files. +The static library prefix and suffix (if any) +are automatically added to the target. +The target library file prefix +(specified by the \fB$SHLIBPREFIX\fP construction variable; +by default, \fBlib\fP on POSIX systems, +nothing on Windows systems) +and suffix +(specified by the \fB$SHLIBSUFFIX\fP construction variable; +by default, \fB.dll\fP on Windows systems, +\fB.so\fP on POSIX systems) +are automatically added to the target if not already present. +Example: + +.ES +env.SharedLibrary(target = 'bar', source = ['bar.c', 'foo.o']) +.EE +.IP +On Windows systems, the +.BR SharedLibrary () +builder method will always build an import +(\fB.lib\fP) library +in addition to the shared (\fB.dll\fP) library, +adding a \fB.lib\fP library with the same basename +if there is not already a \fB.lib\fP file explicitly +listed in the targets. + +Any object files listed in the +.B source +must have been built for a shared library +(that is, using the +.BR SharedObject () +builder method). +.B scons +will raise an error if there is any mismatch. + +On some platforms, there is a distinction between a shared library +(loaded automatically by the system to resolve external references) +and a loadable module (explicitly loaded by user action). +For maximum portability, use the \fBLoadableModule\fP() builder for the latter. + +On Windows systems, specifying +.B register=1 +will cause the \fB.dll\fP to be +registered after it is built using REGSVR32. +The command that is run +("regsvr32" by default) is determined by \fB$REGSVR\fP construction +variable, and the flags passed are determined by \fB$REGSVRFLAGS\fP. By +default, \fB$REGSVRFLAGS\fP includes the \fB/s\fP option, +to prevent dialogs from popping +up and requiring user attention when it is run. If you change +\fB$REGSVRFLAGS\fP, be sure to include the \fB/s\fP option. +For example, + +.ES +env.SharedLibrary(target = 'bar', + source = ['bar.cxx', 'foo.obj'], + register=1) +.EE +.IP +will register \fBbar.dll\fP as a COM object +when it is done linking it. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP SharedObject() +.IP env.SharedObject() +Builds an object file for +inclusion in a shared library. +Source files must have one of the same set of extensions +specified above for the +.BR StaticObject () +builder method. +On some platforms building a shared object requires additional +compiler option +(e.g. \fB\-fPIC\fP for gcc) +in addition to those needed to build a +normal (static) object, but on some platforms there is no difference between a +shared object and a normal (static) one. When there is a difference, SCons +will only allow shared objects to be linked into a shared library, and will +use a different suffix for shared objects. On platforms where there is no +difference, SCons will allow both normal (static) +and shared objects to be linked into a +shared library, and will use the same suffix for shared and normal +(static) objects. +The target object file prefix +(specified by the \fB$SHOBJPREFIX\fP construction variable; +by default, the same as \fB$OBJPREFIX\fP) +and suffix +(specified by the \fB$SHOBJSUFFIX\fP construction variable) +are automatically added to the target if not already present. +Examples: + +.ES +env.SharedObject(target = 'ddd', source = 'ddd.c') +env.SharedObject(target = 'eee.o', source = 'eee.cpp') +env.SharedObject(target = 'fff.obj', source = 'fff.for') +.EE +.IP +Note that the source files will be scanned +according to the suffix mappings in the +.B SourceFileScanner +object. +See the section "Scanner Objects," +below, for more information. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP StaticLibrary() +.IP env.StaticLibrary() +Builds a static library given one or more object files +or C, C++, D or Fortran source files. +If any source files are given, +then they will be automatically +compiled to object files. +The static library prefix and suffix (if any) +are automatically added to the target. +The target library file prefix +(specified by the \fB$LIBPREFIX\fP construction variable; +by default, \fBlib\fP on POSIX systems, +nothing on Windows systems) +and suffix +(specified by the \fB$LIBSUFFIX\fP construction variable; +by default, \fB.lib\fP on Windows systems, +\fB.a\fP on POSIX systems) +are automatically added to the target if not already present. +Example: + +.ES +env.StaticLibrary(target = 'bar', source = ['bar.c', 'foo.o']) +.EE +.IP +Any object files listed in the +.B source +must have been built for a static library +(that is, using the +.BR StaticObject () +builder method). +.B scons +will raise an error if there is any mismatch. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP StaticObject() +.IP env.StaticObject() +Builds a static object file +from one or more C, C++, D, or Fortran source files. +Source files must have one of the following extensions: + +.ES + .asm assembly language file + .ASM assembly language file + .c C file + .C Windows: C file + POSIX: C++ file + .cc C++ file + .cpp C++ file + .cxx C++ file + .cxx C++ file + .c++ C++ file + .C++ C++ file + .d D file + .f Fortran file + .F Windows: Fortran file + POSIX: Fortran file + C pre-processor + .for Fortran file + .FOR Fortran file + .fpp Fortran file + C pre-processor + .FPP Fortran file + C pre-processor + .m Object C file + .mm Object C++ file + .s assembly language file + .S Windows: assembly language file + ARM: CodeSourcery Sourcery Lite + .sx assembly language file + C pre-processor + POSIX: assembly language file + C pre-processor + .spp assembly language file + C pre-processor + .SPP assembly language file + C pre-processor +.EE +.IP +The target object file prefix +(specified by the \fB$OBJPREFIX\fP construction variable; nothing by default) +and suffix +(specified by the \fB$OBJSUFFIX\fP construction variable; +\fB.obj\fP on Windows systems, +\fB.o\fP on POSIX systems) +are automatically added to the target if not already present. +Examples: + +.ES +env.StaticObject(target = 'aaa', source = 'aaa.c') +env.StaticObject(target = 'bbb.o', source = 'bbb.c++') +env.StaticObject(target = 'ccc.obj', source = 'ccc.f') +.EE +.IP +Note that the source files will be scanned +according to the suffix mappings in +.B SourceFileScanner +object. +See the section "Scanner Objects," +below, for more information. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Substfile() +.IP env.Substfile() +The \fBSubstfile\fP() builder generates a single text file +by concatenating the source files. +Nested lists of sources are flattened. +\fB$LINESEPARATOR\fP is used to separate the source files; +see the description of \fBTextfile\fP() for details. + +If a single source file is present with an \fB.in\fP suffix, +the suffix is stripped and the remainder is used as the default target name. + +The prefix and suffix specified by the \fB$SUBSTFILEPREFIX\fP +and \fB$SUBSTFILESUFFIX\fP construction variables +(the null string by default in both cases) +are automatically added to the target if they are not already present. + +If a construction variable named \fB$SUBST_DICT\fP is present, +it may be either a Python dictionary or a sequence of (key,value) tuples. +If the former, +the dictionary is converted into a list of tuples in an arbitrary order, +so if one key is a prefix of another key +or if one substitution could be further expanded by another subsitition, +it is unpredictible whether the expansion will occur. + +Any occurences in the source of a key +are replaced by the corresponding value, +which may be a Python callable function or a string. +If a value is a function, +it is first called (with no arguments) to produce a string. +The string is \fIsubst\fP-expanded +and the result replaces the key. + +.ES +env = Environment(tools = ['default', 'textfile']) + +env['prefix'] = '/usr/bin' +script_dict = {'@prefix@': '/bin', @exec_prefix@: '$prefix'} +env.Substfile('script.in', SUBST_DICT = script_dict) + +conf_dict = {'%VERSION%': '1.2.3', '%BASE%': 'MyProg'} +env.Substfile('config.h.in', conf_dict, SUBST_DICT = conf_dict) + +# UNPREDICTABLE - one key is a prefix of another +bad_foo = {'$foo': '$foo', '$foobar': '$foobar'} +env.Substfile('foo.in', SUBST_DICT = bad_foo) + +# PREDICTABLE - keys are applied longest first +good_foo = [('$foobar', '$foobar'), ('$foo', '$foo')] +env.Substfile('foo.in', SUBST_DICT = good_foo) + +# UNPREDICTABLE - one substitution could be futher expanded +bad_bar = {'@bar@': '@soap@', '@soap@': 'lye'} +env.Substfile('bar.in', SUBST_DICT = bad_bar) + +# PREDICTABLE - substitutions are expanded in order +good_bar = (('@bar@', '@soap@'), ('@soap@', 'lye')) +env.Substfile('bar.in', SUBST_DICT = good_bar) + +# the SUBST_DICT may be in common (and not an override) +substutions = {} +subst = Environment(tools = ['textfile', SUBST_DICT = substitutions) +substitutions['@foo@'] = 'foo' +subst['SUBST_DICT']['@bar@'] = 'bar' +subst.Substfile('pgm1.c', [Value('#include "@foo@.h"'), + Value('#include "@bar@.h"'), + "common.in", + "pgm1.in" + ]) +subst.Substfile('pgm2.c', [Value('#include "@foo@.h"'), + Value('#include "@bar@.h"'), + "common.in", + "pgm2.in" + ]) + +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Tar() +.IP env.Tar() +Builds a tar archive of the specified files +and/or directories. +Unlike most builder methods, +the +.BR Tar () +builder method may be called multiple times +for a given target; +each additional call +adds to the list of entries +that will be built into the archive. +Any source directories will +be scanned for changes to +any on-disk files, +regardless of whether or not +.B scons +knows about them from other Builder or function calls. + +.ES +env.Tar('src.tar', 'src') + +# Create the stuff.tar file. +env.Tar('stuff', ['subdir1', 'subdir2']) +# Also add "another" to the stuff.tar file. +env.Tar('stuff', 'another') + +# Set TARFLAGS to create a gzip-filtered archive. +env = Environment(TARFLAGS = '-c -z') +env.Tar('foo.tar.gz', 'foo') + +# Also set the suffix to .tgz. +env = Environment(TARFLAGS = '-c -z', + TARSUFFIX = '.tgz') +env.Tar('foo') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Textfile() +.IP env.Textfile() +The \fBTextfile\fP() builder generates a single text file. +The source strings constitute the lines; +nested lists of sources are flattened. +\fB$LINESEPARATOR\fP is used to separate the strings. + +If present, the \fB$SUBST_DICT\fP construction variable +is used to modify the strings before they are written; +see the \fBSubstfile\fP() description for details. + +The prefix and suffix specified by the \fB$TEXTFILEPREFIX\fP +and \fB$TEXTFILESUFFIX\fP construction variables +(the null string and \fB.txt\fP by default, respectively) +are automatically added to the target if they are not already present. +Examples: + +.ES +# builds/writes foo.txt +env.Textfile(target = 'foo.txt', source = ['Goethe', 42, 'Schiller']) + +# builds/writes bar.txt +env.Textfile(target = 'bar', + source = ['lalala', 'tanteratei'], + LINESEPARATOR='|*') + +# nested lists are flattened automatically +env.Textfile(target = 'blob', + source = ['lalala', ['Goethe', 42 'Schiller'], 'tanteratei']) + +# files may be used as input by wraping them in File() +env.Textfile(target = 'concat', # concatenate files with a marker between + source = [File('concat1'), File('concat2')], + LINESEPARATOR = '====================\\n') + +Results are: +foo.txt + ....8<---- + Goethe + 42 + Schiller + ....8<---- (no linefeed at the end) + +bar.txt: + ....8<---- + lalala|*tanteratei + ....8<---- (no linefeed at the end) + +blob.txt + ....8<---- + lalala + Goethe + 42 + Schiller + tanteratei + ....8<---- (no linefeed at the end) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP TypeLibrary() +.IP env.TypeLibrary() +Builds a Windows type library (\fB.tlb\fP) +file from an input IDL file (\fB.idl\fP). +In addition, it will build the associated inteface stub and +proxy source files, +naming them according to the base name of the \fB.idl\fP file. +For example, + +.ES +env.TypeLibrary(source="foo.idl") +.EE +.IP +Will create \fBfoo.tlb\fP, +.BR foo.h , +.BR foo_i.c , +.B foo_p.c +and +.B foo_data.c +files. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Uic() +.IP env.Uic() +Builds a header file, an implementation file and a moc file from an ui file. +and returns the corresponding nodes in the above order. +This builder is only available after using the tool 'qt'. Note: you can +specify \fB.ui\fP files directly as source +files to the \fBProgram\fP(), +\fBLibrary\fP() and \fBSharedLibrary\fP() builders +without using this builder. Using this builder lets you override the standard +naming conventions (be careful: prefixes are always prepended to names of +built files; if you don't want prefixes, you may set them to ``). +See the \fB$QTDIR\fP variable for more information. +Example: + +.ES +env.Uic('foo.ui') # -> ['foo.h', 'uic_foo.cc', 'moc_foo.cc'] +env.Uic(target = Split('include/foo.h gen/uicfoo.cc gen/mocfoo.cc'), + source = 'foo.ui') # -> ['include/foo.h', 'gen/uicfoo.cc', 'gen/mocfoo.cc'] +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.IP Zip() +.IP env.Zip() +Builds a zip archive of the specified files +and/or directories. +Unlike most builder methods, +the +.BR Zip () +builder method may be called multiple times +for a given target; +each additional call +adds to the list of entries +that will be built into the archive. +Any source directories will +be scanned for changes to +any on-disk files, +regardless of whether or not +.B scons +knows about them from other Builder or function calls. + +.ES +env.Zip('src.zip', 'src') + +# Create the stuff.zip file. +env.Zip('stuff', ['subdir1', 'subdir2']) +# Also add "another" to the stuff.tar file. +env.Zip('stuff', 'another') +.EE +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +'\" END GENERATED BUILDER DESCRIPTIONS +'\" +'\" The descriptions above of the various SCons Builders are generated +'\" from the .xml files that live next to the various Python modules in +'\" the build enginer library. If you're reading this [gnt]roff file +'\" with an eye towards patching this man page, you can still submit +'\" a diff against this text, but it will have to be translated to a +'\" diff against the underlying .xml file before the patch is actually +'\" accepted. If you do that yourself, it will make it easier to +'\" integrate the patch. +'\" +'\" END GENERATED BUILDER DESCRIPTIONS +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.P +All +targets of builder methods automatically depend on their sources. +An explicit dependency can +be specified using the +.B Depends +method of a construction environment (see below). + +In addition, +.B scons +automatically scans +source files for various programming languages, +so the dependencies do not need to be specified explicitly. +By default, SCons can +C source files, +C++ source files, +Fortran source files with +.B .F +(POSIX systems only), +.B .fpp, +or +.B .FPP +file extensions, +and assembly language files with +.B .S +(POSIX systems only), +.B .spp, +or +.B .SPP +files extensions +for C preprocessor dependencies. +SCons also has default support +for scanning D source files, +You can also write your own Scanners +to add support for additional source file types. +These can be added to the default +Scanner object used by the +.BR Object (), +.BR StaticObject (), +and +.BR SharedObject () +Builders by adding them +to the +.B SourceFileScanner +object. +See the section "Scanner Objects" +below, for more information about +defining your own Scanner objects +and using the +.B SourceFileScanner +object. + +.SS Methods and Functions to Do Things +In addition to Builder methods, +.B scons +provides a number of other construction environment methods +and global functions to +manipulate the build configuration. + +Usually, a construction environment method +and global function with the same name both exist +so that you don't have to remember whether +to a specific bit of functionality +must be called with or without a construction environment. +In the following list, +if you call something as a global function +it looks like: +.ES +.RI Function( arguments ) +.EE +and if you call something through a construction +environment it looks like: +.ES +.RI env.Function( arguments ) +.EE +If you can call the functionality in both ways, +then both forms are listed. + +Global functions may be called from custom Python modules that you +import into an SConscript file by adding the following +to the Python module: + +.ES +from SCons.Script import * +.EE + +Except where otherwise noted, +the same-named +construction environment method +and global function +provide the exact same functionality. +The only difference is that, +where appropriate, +calling the functionality through a construction environment will +substitute construction variables into +any supplied strings. +For example: + +.ES +env = Environment(FOO = 'foo') +Default('$FOO') +env.Default('$FOO') +.EE + +In the above example, +the first call to the global +.B Default() +function will actually add a target named +.B $FOO +to the list of default targets, +while the second call to the +.B env.Default() +construction environment method +will expand the value +and add a target named +.B foo +to the list of default targets. +For more on construction variable expansion, +see the next section on +construction variables. + +Construction environment methods +and global functions supported by +.B scons +include: + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +'\" BEGIN GENERATED FUNCTION DESCRIPTIONS +'\" +'\" The descriptions below of the various SCons functions are generated +'\" from the .xml files that live next to the various Python modules in +'\" the build enginer library. If you're reading this [gnt]roff file +'\" with an eye towards patching this man page, you can still submit +'\" a diff against this text, but it will have to be translated to a +'\" diff against the underlying .xml file before the patch is actually +'\" accepted. If you do that yourself, it will make it easier to +'\" integrate the patch. +'\" +'\" BEGIN GENERATED FUNCTION DESCRIPTIONS +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Action( action ", [" cmd/str/fun ", [" var ", ...]] [option=" value ", ...])" +.TP +.IR env .Action( action ", [" cmd/str/fun ", [" var ", ...]] [option=" value ", ...])" +Creates an Action object for +the specified +.IR action . +See the section "Action Objects," +below, for a complete explanation of the arguments and behavior. + +Note that the +.BR env.Action () +form of the invocation will expand +construction variables in any argument strings, +including the +.I action +argument, at the time it is called +using the construction variables in the +.I env +construction environment through which +.BR env.Action () +was called. +The +.BR Action () +form delays all variable expansion +until the Action object is actually used. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI AddMethod( object ", " function ", [" name ]) +.TP +.IR env .AddMethod( function ", [" name ]) +When called with the +.BR AddMethod () +form, +adds the specified +.I function +to the specified +.I object +as the specified method +.IR name . +When called with the +.BR env.AddMethod () +form, +adds the specified +.I function +to the construction environment +.I env +as the specified method +.IR name . +In both cases, if +.I name +is omitted or +.BR None , +the name of the +specified +.I function +itself is used for the method name. + +Examples: + +.ES +# Note that the first argument to the function to +# be attached as a method must be the object through +# which the method will be called; the Python +# convention is to call it 'self'. +def my_method(self, arg): + print "my_method() got", arg + +# Use the global AddMethod() function to add a method +# to the Environment class. This +AddMethod(Environment, my_method) +env = Environment() +env.my_method('arg') + +# Add the function as a method, using the function +# name for the method call. +env = Environment() +env.AddMethod(my_method, 'other_method_name') +env.other_method_name('another arg') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI AddOption( arguments ) +This function adds a new command-line option to be recognized. +The specified +.I arguments +are the same as supported by the standard Python +.BR optparse.add_option () +method (with a few additional capabilities noted below); +see the documentation for +.B optparse +for a thorough discussion of its option-processing capabities. + +In addition to the arguments and values supported by the +.BR optparse.add_option () +method, +the SCons +.BR AddOption () +function allows you to set the +.B nargs +keyword value to +.B '?' +(a string with just the question mark) +to indicate that the specified long option(s) take(s) an +.I optional +argument. +When +.B "nargs = '?'" +is passed to the +.BR AddOption () +function, the +.B const +keyword argument +may be used to supply the "default" +value that should be used when the +option is specified on the command line +without an explicit argument. + +If no +.B default= +keyword argument is supplied when calling +.BR AddOption (), +the option will have a default value of +.BR None . + +Once a new command-line option has been added with +.BR AddOption (), +the option value may be accessed using +.BR GetOption () +or +.BR env.GetOption (). +The value may also be set, using +.BR SetOption () +or +.BR env.SetOption (), +if conditions in a +.B SConscript +require overriding any default value. +Note, however, that a +value specified on the command line will +.I always +override a value set by any SConscript file. + +Any specified +.B help= +strings for the new option(s) +will be displayed by the +.B \-H +or +.B \-h +options +(the latter only if no other help text is +specified in the SConscript files). +The help text for the local options specified by +.BR AddOption () +will appear below the SCons options themselves, +under a separate +.B "Local Options" +heading. +The options will appear in the help text +in the order in which the +.BR AddOption () +calls occur. + +Example: + +.ES +AddOption('--prefix', + dest='prefix', + nargs=1, type='string', + action='store', + metavar='DIR', + help='installation prefix') +env = Environment(PREFIX = GetOption('prefix')) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI AddPostAction( target ", " action ) +.TP +.IR env .AddPostAction( target ", " action ) +Arranges for the specified +.I action +to be performed +after the specified +.I target +has been built. +The specified action(s) may be +an Action object, or anything that +can be converted into an Action object +(see below). + +When multiple targets are supplied, +the action may be called multiple times, +once after each action that generates +one or more targets in the list. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI AddPreAction( target ", " action ) +.TP +.IR env .AddPreAction( target ", " action ) +Arranges for the specified +.I action +to be performed +before the specified +.I target +is built. +The specified action(s) may be +an Action object, or anything that +can be converted into an Action object +(see below). + +When multiple targets are specified, +the action(s) may be called multiple times, +once before each action that generates +one or more targets in the list. + +Note that if any of the targets are built in multiple steps, +the action will be invoked just +before the "final" action that specifically +generates the specified target(s). +For example, when building an executable program +from a specified source +.B .c +file via an intermediate object file: + +.ES +foo = Program('foo.c') +AddPreAction(foo, 'pre_action') +.EE +.IP +The specified +.B pre_action +would be executed before +.B scons +calls the link command that actually +generates the executable program binary +.BR foo , +not before compiling the +.B foo.c +file into an object file. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Alias( alias ", [" targets ", [" action ]]) +.TP +.IR env .Alias( alias ", [" targets ", [" action ]]) +Creates one or more phony targets that +expand to one or more other targets. +An optional +.I action +(command) +or list of actions +can be specified that will be executed +whenever the any of the alias targets are out-of-date. +Returns the Node object representing the alias, +which exists outside of any file system. +This Node object, or the alias name, +may be used as a dependency of any other target, +including another alias. +.BR Alias () +can be called multiple times for the same +alias to add additional targets to the alias, +or additional actions to the list for this alias. + +Examples: + +.ES +Alias('install') +Alias('install', '/usr/bin') +Alias(['install', 'install-lib'], '/usr/local/lib') + +env.Alias('install', ['/usr/local/bin', '/usr/local/lib']) +env.Alias('install', ['/usr/local/man']) + +env.Alias('update', ['file1', 'file2'], "update_database $SOURCES") +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI AllowSubstExceptions([ exception ", ...])" +Specifies the exceptions that will be allowed +when expanding construction variables. +By default, +any construction variable expansions that generate a +.B NameError +or +.B IndexError +exception will expand to a +.B '' +(a null string) and not cause scons to fail. +All exceptions not in the specified list +will generate an error message +and terminate processing. + +If +.BR AllowSubstExceptions () +is called multiple times, +each call completely overwrites the previous list +of allowed exceptions. + +Example: + +.ES +# Requires that all construction variable names exist. +# (You may wish to do this if you want to enforce strictly +# that all construction variables must be defined before use.) +AllowSubstExceptions() + +# Also allow a string containing a zero-division expansion +# like '${1 / 0}' to evalute to ''. +AllowSubstExceptions(IndexError, NameError, ZeroDivisionError) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI AlwaysBuild( target ", ...)" +.TP +.IR env .AlwaysBuild( target ", ...)" +Marks each given +.I target +so that it is always assumed to be out of date, +and will always be rebuilt if needed. +Note, however, that +.BR AlwaysBuild () +does not add its target(s) to the default target list, +so the targets will only be built +if they are specified on the command line, +or are a dependent of a target specified on the command line--but +they will +.I always +be built if so specified. +Multiple targets can be passed in to a single call to +.BR AlwaysBuild (). + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .Append(key= val ", [...])" +Appends the specified keyword arguments +to the end of construction variables in the environment. +If the Environment does not have +the specified construction variable, +it is simply added to the environment. +If the values of the construction variable +and the keyword argument are the same type, +then the two values will be simply added together. +Otherwise, the construction variable +and the value of the keyword argument +are both coerced to lists, +and the lists are added together. +(See also the Prepend method, below.) + +Example: + +.ES +env.Append(CCFLAGS = ' -g', FOO = ['foo.yyy']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .AppendENVPath( name ", " newpath ", [" envname ", " sep ", " delete_existing ]) +This appends new path elements to the given path in the +specified external environment +.RB ( ENV +by default). +This will only add +any particular path once (leaving the last one it encounters and +ignoring the rest, to preserve path order), +and to help assure this, +will normalize all paths (using +.B os.path.normpath +and +.BR os.path.normcase ). +This can also handle the +case where the given old path variable is a list instead of a +string, in which case a list will be returned instead of a string. + +If +.I delete_existing +is 0, then adding a path that already exists +will not move it to the end; it will stay where it is in the list. + +Example: + +.ES +print 'before:',env['ENV']['INCLUDE'] +include_path = '/foo/bar:/foo' +env.AppendENVPath('INCLUDE', include_path) +print 'after:',env['ENV']['INCLUDE'] + +yields: +before: /foo:/biz +after: /biz:/foo/bar:/foo +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .AppendUnique(key= val ", [...], delete_existing=" 0) +Appends the specified keyword arguments +to the end of construction variables in the environment. +If the Environment does not have +the specified construction variable, +it is simply added to the environment. +If the construction variable being appended to is a list, +then any value(s) that already exist in the +construction variable will +.I not +be added again to the list. +However, if delete_existing is 1, +existing matching values are removed first, so +existing values in the arg list move to the end of the list. + +Example: + +.ES +env.AppendUnique(CCFLAGS = '-g', FOO = ['foo.yyy']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .BitKeeper() +A factory function that +returns a Builder object +to be used to fetch source files +using BitKeeper. +The returned Builder +is intended to be passed to the +.BR SourceCode () +function. + +This function is deprecated. For details, see the entry for the +.BR SourceCode () +function. + +Example: + +.ES +env.SourceCode('.', env.BitKeeper()) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI BuildDir( build_dir ", " src_dir ", [" duplicate ]) +.TP +.IR env .BuildDir( build_dir ", " src_dir ", [" duplicate ]) +Deprecated synonyms for +.BR VariantDir () +and +.BR env.VariantDir (). +The +.I build_dir +argument becomes the +.I variant_dir +argument of +.BR VariantDir () +or +.BR env.VariantDir (). + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Builder( action ", [" arguments ]) +.TP +.IR env .Builder( action ", [" arguments ]) +Creates a Builder object for +the specified +.IR action . +See the section "Builder Objects," +below, for a complete explanation of the arguments and behavior. + +Note that the +.BR env.Builder () +form of the invocation will expand +construction variables in any arguments strings, +including the +.I action +argument, +at the time it is called +using the construction variables in the +.I env +construction environment through which +.BR env.Builder () +was called. +The +.BR Builder () +form delays all variable expansion +until after the Builder object is actually called. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI CacheDir( cache_dir ) +.TP +.IR env .CacheDir( cache_dir ) +Specifies that +.B scons +will maintain a cache of derived files in +.IR cache_dir . +The derived files in the cache will be shared +among all the builds using the same +.BR CacheDir () +call. +Specifying a +.I cache_dir +of +.B None +disables derived file caching. + +Calling +.BR env.CacheDir () +will only affect targets built +through the specified construction environment. +Calling +.BR CacheDir () +sets a global default +that will be used by all targets built +through construction environments +that do +.I not +have an +.BR env.CacheDir () +specified. + +When a +.BR CacheDir () +is being used and +.B scons +finds a derived file that needs to be rebuilt, +it will first look in the cache to see if a +derived file has already been built +from identical input files and an identical build action +(as incorporated into the MD5 build signature). +If so, +.B scons +will retrieve the file from the cache. +If the derived file is not present in the cache, +.B scons +will rebuild it and +then place a copy of the built file in the cache +(identified by its MD5 build signature), +so that it may be retrieved by other +builds that need to build the same derived file +from identical inputs. + +Use of a specified +.BR CacheDir () +may be disabled for any invocation +by using the +.B \-\-cache-disable +option. + +If the +.B \-\-cache-force +option is used, +.B scons +will place a copy of +.I all +derived files in the cache, +even if they already existed +and were not built by this invocation. +This is useful to populate a cache +the first time +.BR CacheDir () +is added to a build, +or after using the +.B \-\-cache-disable +option. + +When using +.BR CacheDir (), +.B scons +will report, +"Retrieved `file' from cache," +unless the +.B \-\-cache-show +option is being used. +When the +.B \-\-cache-show +option is used, +.B scons +will print the action that +.I would +have been used to build the file, +without any indication that +the file was actually retrieved from the cache. +This is useful to generate build logs +that are equivalent regardless of whether +a given derived file has been built in-place +or retrieved from the cache. + +The +.BR NoCache () +method can be used to disable caching of specific files. This can be +useful if inputs and/or outputs of some tool are impossible to +predict or prohibitively large. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Clean( targets ", " files_or_dirs ) +.TP +.IR env .Clean( targets ", " files_or_dirs ) +This specifies a list of files or directories which should be removed +whenever the targets are specified with the +.B \-c +command line option. +The specified targets may be a list +or an individual target. +Multiple calls to +.BR Clean () +are legal, +and create new targets or add files and directories to the +clean list for the specified targets. + +Multiple files or directories should be specified +either as separate arguments to the +.BR Clean () +method, or as a list. +.BR Clean () +will also accept the return value of any of the construction environment +Builder methods. +Examples: + +The related +.BR NoClean () +function overrides calling +.BR Clean () +for the same target, +and any targets passed to both functions will +.I not +be removed by the +.B \-c +option. + +Examples: + +.ES +Clean('foo', ['bar', 'baz']) +Clean('dist', env.Program('hello', 'hello.c')) +Clean(['foo', 'bar'], 'something_else_to_clean') +.EE +.IP +In this example, +installing the project creates a subdirectory for the documentation. +This statement causes the subdirectory to be removed +if the project is deinstalled. +.ES +Clean(docdir, os.path.join(docdir, projectname)) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .Clone([key= val ", ...])" +Returns a separate copy of a construction environment. +If there are any keyword arguments specified, +they are added to the returned copy, +overwriting any existing values +for the keywords. + +Example: + +.ES +env2 = env.Clone() +env3 = env.Clone(CCFLAGS = '-g') +.EE +.IP +Additionally, a list of tools and a toolpath may be specified, as in +the Environment constructor: + +.ES +def MyTool(env): env['FOO'] = 'bar' +env4 = env.Clone(tools = ['msvc', MyTool]) +.EE +.IP +The +.I parse_flags +keyword argument is also recognized: + +.ES +# create an environment for compiling programs that use wxWidgets +wx_env = env.Clone(parse_flags = '!wx-config --cflags --cxxflags') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Command( target ", " source ", " action ", [key=" val ", ...])" +.TP +.IR env .Command( target ", " source ", " action ", [key=" val ", ...])" +Executes a specific action +(or list of actions) +to build a target file or files. +This is more convenient +than defining a separate Builder object +for a single special-case build. + +As a special case, the +.I source_scanner +keyword argument can +be used to specify +a Scanner object +that will be used to scan the sources. +(The global +.B DirScanner +object can be used +if any of the sources will be directories +that must be scanned on-disk for +changes to files that aren't +already specified in other Builder of function calls.) + +Any other keyword arguments specified override any +same-named existing construction variables. + +An action can be an external command, +specified as a string, +or a callable Python object; +see "Action Objects," below, +for more complete information. +Also note that a string specifying an external command +may be preceded by an +.B @ +(at-sign) +to suppress printing the command in question, +or by a +.B \- +(hyphen) +to ignore the exit status of the external command. + +Examples: + +.ES +env.Command('foo.out', 'foo.in', + "$FOO_BUILD < $SOURCES > $TARGET") + +env.Command('bar.out', 'bar.in', + ["rm -f $TARGET", + "$BAR_BUILD < $SOURCES > $TARGET"], + ENV = {'PATH' : '/usr/local/bin/'}) + +def rename(env, target, source): + import os + os.rename('.tmp', str(target[0])) + +env.Command('baz.out', 'baz.in', + ["$BAZ_BUILD < $SOURCES > .tmp", + rename ]) +.EE +.IP +Note that the +.BR Command () +function will usually assume, by default, +that the specified targets and/or sources are Files, +if no other part of the configuration +identifies what type of entry it is. +If necessary, you can explicitly specify +that targets or source nodes should +be treated as directoriese +by using the +.BR Dir () +or +.BR env.Dir () +functions. + +Examples: + +.ES +env.Command('ddd.list', Dir('ddd'), 'ls -l $SOURCE > $TARGET') + +env['DISTDIR'] = 'destination/directory' +env.Command(env.Dir('$DISTDIR')), None, make_distdir) +.EE +.IP +(Also note that SCons will usually +automatically create any directory necessary to hold a target file, +so you normally don't need to create directories by hand.) + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Configure( env ", [" custom_tests ", " conf_dir ", " log_file ", " config_h ]) +.TP +.IR env .Configure([ custom_tests ", " conf_dir ", " log_file ", " config_h ]) +Creates a Configure object for integrated +functionality similar to GNU autoconf. +See the section "Configure Contexts," +below, for a complete explanation of the arguments and behavior. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .Copy([key= val ", ...])" +A now-deprecated synonym for +.BR env.Clone (). + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .CVS( repository ", " module ) +A factory function that +returns a Builder object +to be used to fetch source files +from the specified +CVS +.IR repository . +The returned Builder +is intended to be passed to the +.BR SourceCode () +function. + +This function is deprecated. For details, see the entry for the +.BR SourceCode () +function. + +The optional specified +.I module +will be added to the beginning +of all repository path names; +this can be used, in essence, +to strip initial directory names +from the repository path names, +so that you only have to +replicate part of the repository +directory hierarchy in your +local build directory. + +Examples: + +.ES +# Will fetch foo/bar/src.c +# from /usr/local/CVSROOT/foo/bar/src.c. +env.SourceCode('.', env.CVS('/usr/local/CVSROOT')) + +# Will fetch bar/src.c +# from /usr/local/CVSROOT/foo/bar/src.c. +env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo')) + +# Will fetch src.c +# from /usr/local/CVSROOT/foo/bar/src.c. +env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo/bar')) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Decider( function ) +.TP +.IR env .Decider( function ) +Specifies that all up-to-date decisions for +targets built through this construction environment +will be handled by the specified +.IR function . +The +.I function +can be one of the following strings +that specify the type of decision function +to be performed: + +.RS 10 +.TP 6 +.B timestamp-newer +Specifies that a target shall be considered out of date and rebuilt +if the dependency's timestamp is newer than the target file's timestamp. +This is the behavior of the classic Make utility, +and +.B make +can be used a synonym for +.BR timestamp-newer . +.TP 6 +.B timestamp-match +Specifies that a target shall be considered out of date and rebuilt +if the dependency's timestamp is different than the +timestamp recorded the last time the target was built. +This provides behavior very similar to the classic Make utility +(in particular, files are not opened up so that their +contents can be checksummed) +except that the target will also be rebuilt if a +dependency file has been restored to a version with an +.I earlier +timestamp, such as can happen when restoring files from backup archives. +.TP 6 +.B MD5 +Specifies that a target shall be considered out of date and rebuilt +if the dependency's content has changed sine the last time +the target was built, +as determined be performing an MD5 checksum +on the dependency's contents +and comparing it to the checksum recorded the +last time the target was built. +.B content +can be used as a synonym for +.BR MD5 . +.TP 6 +.B MD5-timestamp +Specifies that a target shall be considered out of date and rebuilt +if the dependency's content has changed sine the last time +the target was built, +except that dependencies with a timestamp that matches +the last time the target was rebuilt will be +assumed to be up-to-date and +.I not +rebuilt. +This provides behavior very similar +to the +.B MD5 +behavior of always checksumming file contents, +with an optimization of not checking +the contents of files whose timestamps haven't changed. +The drawback is that SCons will +.I not +detect if a file's content has changed +but its timestamp is the same, +as might happen in an automated script +that runs a build, +updates a file, +and runs the build again, +all within a single second. +.RE + +Examples: + +.ES +# Use exact timestamp matches by default. +Decider('timestamp-match') + +# Use MD5 content signatures for any targets built +# with the attached construction environment. +env.Decider('content') +.EE +.IP +In addition to the above already-available functions, +the +.I function +argument may be an actual Python function +that takes the following three arguments: + +.RS 10 +.IP dependency +The Node (file) which +should cause the +.I target +to be rebuilt +if it has "changed" since the last tme +.I target +was built. +.IP target +The Node (file) being built. +In the normal case, +this is what should get rebuilt +if the +.I dependency +has "changed." +.IP prev_ni +Stored information about the state of the +.I dependency +the last time the +.I target +was built. +This can be consulted to match various +file characteristics +such as the timestamp, +size, or content signature. +.RE + +The +.I function +should return a +.B True +(non-zero) +value if the +.I dependency +has "changed" since the last time +the +.I target +was built +(indicating that the target +.I should +be rebuilt), +and +.B False +(zero) +otherwise +(indicating that the target should +.I not +be rebuilt). +Note that the decision can be made +using whatever criteria are appopriate. +Ignoring some or all of the function arguments +is perfectly normal. + +Example: + +.ES +def my_decider(dependency, target, prev_ni): + return not os.path.exists(str(target)) + +env.Decider(my_decider) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Default( targets ) +.TP +.IR env .Default( targets ) +This specifies a list of default targets, +which will be built by +.B scons +if no explicit targets are given on the command line. +Multiple calls to +.BR Default () +are legal, +and add to the list of default targets. + +Multiple targets should be specified as +separate arguments to the +.BR Default () +method, or as a list. +.BR Default () +will also accept the Node returned by any +of a construction environment's +builder methods. + +Examples: + +.ES +Default('foo', 'bar', 'baz') +env.Default(['a', 'b', 'c']) +hello = env.Program('hello', 'hello.c') +env.Default(hello) +.EE +.IP +An argument to +.BR Default () +of +.B None +will clear all default targets. +Later calls to +.BR Default () +will add to the (now empty) default-target list +like normal. + +The current list of targets added using the +.BR Default () +function or method is available in the +.B DEFAULT_TARGETS +list; +see below. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI DefaultEnvironment([ args ]) +Creates and returns a default construction environment object. +This construction environment is used internally by SCons +in order to execute many of the global functions in this list, +and to fetch source files transparently +from source code management systems. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Depends( target ", " dependency ) +.TP +.IR env .Depends( target ", " dependency ) +Specifies an explicit dependency; +the +.I target +will be rebuilt +whenever the +.I dependency +has changed. +Both the specified +.I target +and +.I dependency +can be a string +(usually the path name of a file or directory) +or Node objects, +or a list of strings or Node objects +(such as returned by a Builder call). +This should only be necessary +for cases where the dependency +is not caught by a Scanner +for the file. + +Example: + +.ES +env.Depends('foo', 'other-input-file-for-foo') + +mylib = env.Library('mylib.c') +installed_lib = env.Install('lib', mylib) +bar = env.Program('bar.c') + +# Arrange for the library to be copied into the installation +# directory before trying to build the "bar" program. +# (Note that this is for example only. A "real" library +# dependency would normally be configured through the $LIBS +# and $LIBPATH variables, not using an env.Depends() call.) + +env.Depends(bar, installed_lib) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .Dictionary([ vars ]) +Returns a dictionary object +containing copies of all of the +construction variables in the environment. +If there are any variable names specified, +only the specified construction +variables are returned in the dictionary. + +Example: + +.ES +dict = env.Dictionary() +cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Dir( name ", [" directory ]) +.TP +.IR env .Dir( name ", [" directory ]) +This returns a Directory Node, +an object that represents the specified directory +.IR name . +.I name +can be a relative or absolute path. +.I directory +is an optional directory that will be used as the parent directory. +If no +.I directory +is specified, the current script's directory is used as the parent. + +If +.I name +is a list, SCons returns a list of Dir nodes. +Construction variables are expanded in +.IR name . + +Directory Nodes can be used anywhere you +would supply a string as a directory name +to a Builder method or function. +Directory Nodes have attributes and methods +that are useful in many situations; +see "File and Directory Nodes," below. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .Dump([ key ]) +Returns a pretty printable representation of the environment. +.IR key , +if not +.BR None , +should be a string containing the name of the variable of interest. + +This SConstruct: + +.ES +env=Environment() +print env.Dump('CCCOM') +.EE +.IP +will print: + +.ES +'$CC -c -o $TARGET $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES' +.EE +.IP +While this SConstruct: + +.ES +env=Environment() +print env.Dump() +.EE +.IP +will print: +.ES +{ 'AR': 'ar', + 'ARCOM': '$AR $ARFLAGS $TARGET $SOURCES\\n$RANLIB $RANLIBFLAGS $TARGET', + 'ARFLAGS': ['r'], + 'AS': 'as', + 'ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES', + 'ASFLAGS': [], + ... +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI EnsurePythonVersion( major ", " minor ) +.TP +.IR env .EnsurePythonVersion( major ", " minor ) +Ensure that the Python version is at least +.IR major\fP.\fIminor . +This function will +print out an error message and exit SCons with a non-zero exit code if the +actual Python version is not late enough. + +Example: + +.ES +EnsurePythonVersion(2,2) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI EnsureSConsVersion( major ", " minor ", [" revision ]) +.TP +.IR env .EnsureSConsVersion( major ", " minor ", [" revision ]) +Ensure that the SCons version is at least +.IR major.minor , +or +.IR major.minor.revision . +if +.I revision +is specified. +This function will +print out an error message and exit SCons with a non-zero exit code if the +actual SCons version is not late enough. + +Examples: + +.ES +EnsureSConsVersion(0,14) + +EnsureSConsVersion(0,96,90) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Environment([key= value ", ...])" +.TP +.IR env .Environment([key= value ", ...])" +Return a new construction environment +initialized with the specified +.IR key = value +pairs. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Execute( action ", [" strfunction ", " varlist ]) +.TP +.IR env .Execute( action ", [" strfunction ", " varlist ]) +Executes an Action object. +The specified +.I action +may be an Action object +(see the section "Action Objects," +below, for a complete explanation of the arguments and behavior), +or it may be a command-line string, +list of commands, +or executable Python function, +each of which will be converted +into an Action object +and then executed. +The exit value of the command +or return value of the Python function +will be returned. + +Note that +.B scons +will print an error message if the executed +.I action +fails--that is, +exits with or returns a non-zero value. +.B scons +will +.IR not , +however, +automatically terminate the build +if the specified +.I action +fails. +If you want the build to stop in response to a failed +.BR Execute () +call, +you must explicitly check for a non-zero return value: + +.ES +Execute(Copy('file.out', 'file.in')) + +if Execute("mkdir sub/dir/ectory"): + # The mkdir failed, don't try to build. + Exit(1) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Exit([ value ]) +.TP +.IR env .Exit([ value ]) +This tells +.B scons +to exit immediately +with the specified +.IR value . +A default exit value of +.B 0 +(zero) +is used if no value is specified. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Export( vars ) +.TP +.IR env .Export( vars ) +This tells +.B scons +to export a list of variables from the current +SConscript file to all other SConscript files. +The exported variables are kept in a global collection, +so subsequent calls to +.BR Export () +will over-write previous exports that have the same name. +Multiple variable names can be passed to +.BR Export () +as separate arguments or as a list. +Keyword arguments can be used to provide names and their values. +A dictionary can be used to map variables to a different name when exported. +Both local variables and global variables can be exported. + +Examples: + +.ES +env = Environment() +# Make env available for all SConscript files to Import(). +Export("env") + +package = 'my_name' +# Make env and package available for all SConscript files:. +Export("env", "package") + +# Make env and package available for all SConscript files: +Export(["env", "package"]) + +# Make env available using the name debug: +Export(debug = env) + +# Make env available using the name debug: +Export({"debug":env}) +.EE +.IP +Note that the +.BR SConscript () +function supports an +.I exports +argument that makes it easier to to export a variable or +set of variables to a single SConscript file. +See the description of the +.BR SConscript () +function, below. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI File( name ", [" directory ]) +.TP +.IR env .File( name ", [" directory ]) +This returns a +File Node, +an object that represents the specified file +.IR name . +.I name +can be a relative or absolute path. +.I directory +is an optional directory that will be used as the parent directory. + +If +.I name +is a list, SCons returns a list of File nodes. +Construction variables are expanded in +.IR name . + +File Nodes can be used anywhere you +would supply a string as a file name +to a Builder method or function. +File Nodes have attributes and methods +that are useful in many situations; +see "File and Directory Nodes," below. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI FindFile( file ", " dirs ) +.TP +.IR env .FindFile( file ", " dirs ) +Search for +.I file +in the path specified by +.IR dirs . +.I dirs +may be a list of directory names or a single directory name. +In addition to searching for files that exist in the filesystem, +this function also searches for derived files +that have not yet been built. + +Example: + +.ES +foo = env.FindFile('foo', ['dir1', 'dir2']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI FindInstalledFiles() +.TP +.IR env .FindInstalledFiles() +Returns the list of targets set up by the +.BR Install () +or +.BR InstallAs () +builders. + +This function serves as a convenient method to select the contents of +a binary package. + +Example: + +.ES +Install( '/bin', [ 'executable_a', 'executable_b' ] ) + +# will return the file node list +# [ '/bin/executable_a', '/bin/executable_b' ] +FindInstalledFiles() + +Install( '/lib', [ 'some_library' ] ) + +# will return the file node list +# [ '/bin/executable_a', '/bin/executable_b', '/lib/some_library' ] +FindInstalledFiles() +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI FindPathDirs( variable ) +Returns a function +(actually a callable Python object) +intended to be used as the +.I path_function +of a Scanner object. +The returned object will look up the specified +.I variable +in a construction environment +and treat the construction variable's value as a list of +directory paths that should be searched +(like +.BR $CPPPATH , +.BR $LIBPATH , +etc.). + +Note that use of +.BR FindPathDirs () +is generally preferable to +writing your own +.I path_function +for the following reasons: +1) The returned list will contain all appropriate directories +found in source trees +(when +.BR VariantDir () +is used) +or in code repositories +(when +.BR Repository () +or the +.B \-Y +option are used). +2) scons will identify expansions of +.I variable +that evaluate to the same list of directories as, +in fact, the same list, +and avoid re-scanning the directories for files, +when possible. + +Example: + +.ES +def my_scan(node, env, path, arg): + # Code to scan file contents goes here... + return include_files + +scanner = Scanner(name = 'myscanner', + function = my_scan, + path_function = FindPathDirs('MYPATH')) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI FindSourceFiles(node= '"."') +.TP +.IR env .FindSourceFiles(node= '"."') +Returns the list of nodes which serve as the source of the built files. +It does so by inspecting the dependency tree starting at the optional +argument +.I node +which defaults to the '"."'-node. It will then return all leaves of +.IR node . +These are all children which have no further children. + +This function is a convenient method to select the contents of a Source +Package. + +Example: + +.ES +Program( 'src/main_a.c' ) +Program( 'src/main_b.c' ) +Program( 'main_c.c' ) + +# returns ['main_c.c', 'src/main_a.c', 'SConstruct', 'src/main_b.c'] +FindSourceFiles() + +# returns ['src/main_b.c', 'src/main_a.c' ] +FindSourceFiles( 'src' ) +.EE +.IP +As you can see build support files (SConstruct in the above example) +will also be returned by this function. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Flatten( sequence ) +.TP +.IR env .Flatten( sequence ) +Takes a sequence (that is, a Python list or tuple) +that may contain nested sequences +and returns a flattened list containing +all of the individual elements in any sequence. +This can be helpful for collecting +the lists returned by calls to Builders; +other Builders will automatically +flatten lists specified as input, +but direct Python manipulation of +these lists does not. + +Examples: + +.ES +foo = Object('foo.c') +bar = Object('bar.c') + +# Because `foo' and `bar' are lists returned by the Object() Builder, +# `objects' will be a list containing nested lists: +objects = ['f1.o', foo, 'f2.o', bar, 'f3.o'] + +# Passing such a list to another Builder is all right because +# the Builder will flatten the list automatically: +Program(source = objects) + +# If you need to manipulate the list directly using Python, you need to +# call Flatten() yourself, or otherwise handle nested lists: +for object in Flatten(objects): + print str(object) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI GetBuildFailures() +Returns a list of exceptions for the +actions that failed while +attempting to build targets. +Each element in the returned list is a +.I BuildError +object +with the following attributes +that record various aspects +of the build failure: + +.B .node +The node that was being built +when the build failure occurred. + +.B .status +The numeric exit status +returned by the command or Python function +that failed when trying to build the +specified Node. + +.B .errstr +The SCons error string +describing the build failure. +(This is often a generic +message like "Error 2" +to indicate that an executed +command exited with a status of 2.) + +.B .filename +The name of the file or +directory that actually caused the failure. +This may be different from the +.B .node +attribute. +For example, +if an attempt to build a target named +.B sub/dir/target +fails because the +.B sub/dir +directory could not be created, +then the +.B .node +attribute will be +.B sub/dir/target +but the +.B .filename +attribute will be +.BR sub/dir . + +.B .executor +The SCons Executor object +for the target Node +being built. +This can be used to retrieve +the construction environment used +for the failed action. + +.B .action +The actual SCons Action object that failed. +This will be one specific action +out of the possible list of +actions that would have been +executed to build the target. + +.B .command +The actual expanded command that was executed and failed, +after expansion of +.BR $TARGET , +.BR $SOURCE , +and other construction variables. + +Note that the +.BR GetBuildFailures () +function +will always return an empty list +until any build failure has occurred, +which means that +.BR GetBuildFailures () +will always return an empty list +while the +.B SConscript +files are being read. +Its primary intended use is +for functions that will be +executed before SCons exits +by passing them to the +standard Python +.BR atexit.register () +function. +Example: + +.ES +import atexit + +def print_build_failures(): + from SCons.Script import GetBuildFailures + for bf in GetBuildFailures(): + print "%s failed: %s" % (bf.node, bf.errstr) + +atexit.register(print_build_failures) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI GetBuildPath( file ", [...])" +.TP +.IR env .GetBuildPath( file ", [...])" +Returns the +.B scons +path name (or names) for the specified +.I file +(or files). +The specified +.I file +or files +may be +.B scons +Nodes or strings representing path names. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI GetLaunchDir() +.TP +.IR env .GetLaunchDir() +Returns the absolute path name of the directory from which +.B scons +was initially invoked. +This can be useful when using the +.BR \-u , +.B \-U +or +.B \-D +options, which internally +change to the directory in which the +.B SConstruct +file is found. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI GetOption( name ) +.TP +.IR env .GetOption( name ) +This function provides a way to query the value of +SCons options set on scons command line +(or set using the +.BR SetOption () +function). +The options supported are: + +.RS 10 +.TP 6 +.B cache_debug +which corresponds to --cache-debug; +.TP 6 +.B cache_disable +which corresponds to --cache-disable; +.TP 6 +.B cache_force +which corresponds to --cache-force; +.TP 6 +.B cache_show +which corresponds to --cache-show; +.TP 6 +.B clean +which corresponds to -c, --clean and --remove; +.TP 6 +.B config +which corresponds to --config; +.TP 6 +.B directory +which corresponds to -C and --directory; +.TP 6 +.B diskcheck +which corresponds to --diskcheck +.TP 6 +.B duplicate +which corresponds to --duplicate; +.TP 6 +.B file +which corresponds to -f, --file, --makefile and --sconstruct; +.TP 6 +.B help +which corresponds to -h and --help; +.TP 6 +.B ignore_errors +which corresponds to --ignore-errors; +.TP 6 +.B implicit_cache +which corresponds to --implicit-cache; +.TP 6 +.B implicit_deps_changed +which corresponds to --implicit-deps-changed; +.TP 6 +.B implicit_deps_unchanged +which corresponds to --implicit-deps-unchanged; +.TP 6 +.B interactive +which corresponds to --interact and --interactive; +.TP 6 +.B keep_going +which corresponds to -k and --keep-going; +.TP 6 +.B max_drift +which corresponds to --max-drift; +.TP 6 +.B no_exec +which corresponds to -n, --no-exec, --just-print, --dry-run and --recon; +.TP 6 +.B no_site_dir +which corresponds to --no-site-dir; +.TP 6 +.B num_jobs +which corresponds to -j and --jobs; +.TP 6 +.B profile_file +which corresponds to --profile; +.TP 6 +.B question +which corresponds to -q and --question; +.TP 6 +.B random +which corresponds to --random; +.TP 6 +.B repository +which corresponds to -Y, --repository and --srcdir; +.TP 6 +.B silent +which corresponds to -s, --silent and --quiet; +.TP 6 +.B site_dir +which corresponds to --site-dir; +.TP 6 +.B stack_size +which corresponds to --stack-size; +.TP 6 +.B taskmastertrace_file +which corresponds to --taskmastertrace; and +.TP 6 +.B warn +which corresponds to --warn and --warning. +.RE + +See the documentation for the +corresponding command line object for information about each specific +option. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Glob( pattern ", [" ondisk ", " source ", " strings ]) +.TP +.IR env .Glob( pattern ", [" ondisk ", " source ", " strings ]) +Returns Nodes (or strings) that match the specified +.IR pattern , +relative to the directory of the current +.B SConscript +file. +The +.BR env.Glob () +form performs string substition on +.I pattern +and returns whatever matches +the resulting expanded pattern. + +The specified +.I pattern +uses Unix shell style metacharacters for matching: + +.ES + * matches everything + ? matches any single character + [seq] matches any character in seq + [!seq] matches any char not in seq +.EE +.IP +If the first character of a filename is a dot, +it must be matched explicitly. +Character matches do +.I not +span directory separators. + +The +.BR Glob () +knows about +repositories +(see the +.BR Repository () +function) +and source directories +(see the +.BR VariantDir () +function) +and +returns a Node (or string, if so configured) +in the local (SConscript) directory +if matching Node is found +anywhere in a corresponding +repository or source directory. + +The +.I ondisk +argument may be set to +.B False +(or any other non-true value) +to disable the search for matches on disk, +thereby only returning matches among +already-configured File or Dir Nodes. +The default behavior is to +return corresponding Nodes +for any on-disk matches found. + +The +.I source +argument may be set to +.B True +(or any equivalent value) +to specify that, +when the local directory is a +.BR VariantDir (), +the returned Nodes should be from the +corresponding source directory, +not the local directory. + +The +.I strings +argument may be set to +.B True +(or any equivalent value) +to have the +.BR Glob () +function return strings, not Nodes, +that represent the matched files or directories. +The returned strings will be relative to +the local (SConscript) directory. +(Note that This may make it easier to perform +arbitrary manipulation of file names, +but if the returned strings are +passed to a different +.B SConscript +file, +any Node translation will be relative +to the other +.B SConscript +directory, +not the original +.B SConscript +directory.) + +Examples: + +.ES +Program('foo', Glob('*.c')) +Zip('/tmp/everything', Glob('.??*') + Glob('*')) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Help( text ) +.TP +.IR env .Help( text ) +This specifies help text to be printed if the +.B \-h +argument is given to +.BR scons . +If +.BR Help () +is called multiple times, the text is appended together in the order +that +.BR Help () +is called. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Ignore( target ", " dependency ) +.TP +.IR env .Ignore( target ", " dependency ) +The specified dependency file(s) +will be ignored when deciding if +the target file(s) need to be rebuilt. + +You can also use +.BR Ignore () +to remove a target from the default build. +In order to do this you must specify the directory the target will +be built in as the target, and the file you want to skip building +as the dependency. + +Note that this will only remove the dependencies listed from +the files built by default. It will still be built if that +dependency is needed by another object being built. +See the third and forth examples below. + +Examples: + +.ES +env.Ignore('foo', 'foo.c') +env.Ignore('bar', ['bar1.h', 'bar2.h']) +env.Ignore('.','foobar.obj') +env.Ignore('bar','bar/foobar.obj') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Import( vars ) +.TP +.IR env .Import( vars ) +This tells +.B scons +to import a list of variables into the current SConscript file. This +will import variables that were exported with +.BR Export () +or in the +.I exports +argument to +.BR SConscript (). +Variables exported by +.BR SConscript () +have precedence. +Multiple variable names can be passed to +.BR Import () +as separate arguments or as a list. The variable "*" can be used +to import all variables. + +Examples: + +.ES +Import("env") +Import("env", "variable") +Import(["env", "variable"]) +Import("*") +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Literal( string ) +.TP +.IR env .Literal( string ) +The specified +.I string +will be preserved as-is +and not have construction variables expanded. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Local( targets ) +.TP +.IR env .Local( targets ) +The specified +.I targets +will have copies made in the local tree, +even if an already up-to-date copy +exists in a repository. +Returns a list of the target Node or Nodes. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .MergeFlags( arg ", [" unique ]) +Merges the specified +.I arg +values to the construction environment's construction variables. +If the +.I arg +argument is not a dictionary, +it is converted to one by calling +.BR env.ParseFlags () +on the argument +before the values are merged. +Note that +.I arg +must be a single value, +so multiple strings must +be passed in as a list, +not as separate arguments to +.BR env.MergeFlags (). + +By default, +duplicate values are eliminated; +you can, however, specify +.B unique=0 +to allow duplicate +values to be added. +When eliminating duplicate values, +any construction variables that end with +the string +.B PATH +keep the left-most unique value. +All other construction variables keep +the right-most unique value. + +Examples: + +.ES +# Add an optimization flag to $CCFLAGS. +env.MergeFlags('-O3') + +# Combine the flags returned from running pkg-config with an optimization +# flag and merge the result into the construction variables. +env.MergeFlags(['!pkg-config gtk+-2.0 --cflags', '-O3']) + +# Combine an optimization flag with the flags returned from running pkg-config +# twice and merge the result into the construction variables. +env.MergeFlags(['-O3', + '!pkg-config gtk+-2.0 --cflags --libs', + '!pkg-config libpng12 --cflags --libs']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI NoCache( target ", ...)" +.TP +.IR env .NoCache( target ", ...)" +Specifies a list of files which should +.I not +be cached whenever the +.BR CacheDir () +method has been activated. +The specified targets may be a list +or an individual target. + +Multiple files should be specified +either as separate arguments to the +.BR NoCache () +method, or as a list. +.BR NoCache () +will also accept the return value of any of the construction environment +Builder methods. + +Calling +.BR NoCache () +on directories and other non-File Node types has no effect because +only File Nodes are cached. + +Examples: + +.ES +NoCache('foo.elf') +NoCache(env.Program('hello', 'hello.c')) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI NoClean( target ", ...)" +.TP +.IR env .NoClean( target ", ...)" +Specifies a list of files or directories which should +.I not +be removed whenever the targets (or their dependencies) +are specified with the +.B \-c +command line option. +The specified targets may be a list +or an individual target. +Multiple calls to +.BR NoClean () +are legal, +and prevent each specified target +from being removed by calls to the +.B \-c +option. + +Multiple files or directories should be specified +either as separate arguments to the +.BR NoClean () +method, or as a list. +.BR NoClean () +will also accept the return value of any of the construction environment +Builder methods. + +Calling +.BR NoClean () +for a target overrides calling +.BR Clean () +for the same target, +and any targets passed to both functions will +.I not +be removed by the +.B \-c +option. + +Examples: + +.ES +NoClean('foo.elf') +NoClean(env.Program('hello', 'hello.c')) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .ParseConfig( command ", [" function ", " unique ]) +Calls the specified +.I function +to modify the environment as specified by the output of +.IR command . +The default +.I function +is +.BR env.MergeFlags (), +which expects the output of a typical +.B *-config +command +(for example, +.BR gtk-config ) +and adds the options +to the appropriate construction variables. +By default, +duplicate values are not +added to any construction variables; +you can specify +.B unique=0 +to allow duplicate +values to be added. + +Interpreted options +and the construction variables they affect +are as specified for the +.BR env.ParseFlags () +method (which this method calls). +See that method's description, below, +for a table of options and construction variables. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI ParseDepends( filename ", [" must_exist ", " only_one ]) +.TP +.IR env .ParseDepends( filename ", [" must_exist ", " only_one ]) +Parses the contents of the specified +.I filename +as a list of dependencies in the style of +.B Make +or +.BR mkdep , +and explicitly establishes all of the listed dependencies. + +By default, +it is not an error +if the specified +.I filename +does not exist. +The optional +.I must_exist +argument may be set to a non-zero +value to have +scons +throw an exception and +generate an error if the file does not exist, +or is otherwise inaccessible. + +The optional +.I only_one +argument may be set to a non-zero +value to have +scons +thrown an exception and +generate an error +if the file contains dependency +information for more than one target. +This can provide a small sanity check +for files intended to be generated +by, for example, the +.B "gcc -M" +flag, +which should typically only +write dependency information for +one output file into a corresponding +.B .d +file. + +The +.I filename +and all of the files listed therein +will be interpreted relative to +the directory of the +.B SConscript +file which calls the +.BR ParseDepends () +function. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .ParseFlags( flags ", ...)" +Parses one or more strings containing +typical command-line flags for GCC tool chains +and returns a dictionary with the flag values +separated into the appropriate SCons construction variables. +This is intended as a companion to the +.BR env.MergeFlags () +method, but allows for the values in the returned dictionary +to be modified, if necessary, +before merging them into the construction environment. +(Note that +.BR env.MergeFlags () +will call this method if its argument is not a dictionary, +so it is usually not necessary to call +.BR env.ParseFlags () +directly unless you want to manipulate the values.) + +If the first character in any string is +an exclamation mark (!), +the rest of the string is executed as a command, +and the output from the command is +parsed as GCC tool chain command-line flags +and added to the resulting dictionary. + +Flag values are translated accordig to the prefix found, +and added to the following construction variables: + +.ES +-arch CCFLAGS, LINKFLAGS +-D CPPDEFINES +-framework FRAMEWORKS +-frameworkdir= FRAMEWORKPATH +-include CCFLAGS +-isysroot CCFLAGS, LINKFLAGS +-I CPPPATH +-l LIBS +-L LIBPATH +-mno-cygwin CCFLAGS, LINKFLAGS +-mwindows LINKFLAGS +-pthread CCFLAGS, LINKFLAGS +-std= CFLAGS +-Wa, ASFLAGS, CCFLAGS +-Wl,-rpath= RPATH +-Wl,-R, RPATH +-Wl,-R RPATH +-Wl, LINKFLAGS +-Wp, CPPFLAGS +- CCFLAGS ++ CCFLAGS, LINKFLAGS +.EE +.IP +Any other strings not associated with options +are assumed to be the names of libraries +and added to the +.B $LIBS +construction variable. + +Examples (all of which produce the same result): + +.ES +dict = env.ParseFlags('-O2 -Dfoo -Dbar=1') +dict = env.ParseFlags('-O2', '-Dfoo', '-Dbar=1') +dict = env.ParseFlags(['-O2', '-Dfoo -Dbar=1']) +dict = env.ParseFlags('-O2', '!echo -Dfoo -Dbar=1') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .Perforce() +A factory function that +returns a Builder object +to be used to fetch source files +from the Perforce source code management system. +The returned Builder +is intended to be passed to the +.BR SourceCode () +function. + +This function is deprecated. For details, see the entry for the +.BR SourceCode () +function. + +Example: + +.ES +env.SourceCode('.', env.Perforce()) +.EE +.IP +Perforce uses a number of external +environment variables for its operation. +Consequently, this function adds the +following variables from the user's external environment +to the construction environment's +ENV dictionary: +P4CHARSET, +P4CLIENT, +P4LANGUAGE, +P4PASSWD, +P4PORT, +P4USER, +SystemRoot, +USER, +and +USERNAME. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Platform( string ) +The +.BR Platform () +form returns a callable object +that can be used to initialize +a construction environment using the +platform keyword of the +.BR Environment () +function. + +Example: + +.ES +env = Environment(platform = Platform('win32')) +.EE +.IP +The +.BR env.Platform () +form applies the callable object for the specified platform +.I string +to the environment through which the method was called. + +.ES +env.Platform('posix') +.EE +.IP +Note that the +.B win32 +platform adds the +.B SystemDrive +and +.B SystemRoot +variables from the user's external environment +to the construction environment's +.B $ENV +dictionary. +This is so that any executed commands +that use sockets to connect with other systems +(such as fetching source files from +external CVS repository specifications like +.BR :pserver:anonymous@cvs.sourceforge.net:/cvsroot/scons ) +will work on Windows systems. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Precious( target ", ...)" +.TP +.IR env .Precious( target ", ...)" +Marks each given +.I target +as precious so it is not deleted before it is rebuilt. Normally +.B scons +deletes a target before building it. +Multiple targets can be passed in to a single call to +.BR Precious (). + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .Prepend(key= val ", [...])" +Appends the specified keyword arguments +to the beginning of construction variables in the environment. +If the Environment does not have +the specified construction variable, +it is simply added to the environment. +If the values of the construction variable +and the keyword argument are the same type, +then the two values will be simply added together. +Otherwise, the construction variable +and the value of the keyword argument +are both coerced to lists, +and the lists are added together. +(See also the Append method, above.) + +Example: + +.ES +env.Prepend(CCFLAGS = '-g ', FOO = ['foo.yyy']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .PrependENVPath( name ", " newpath ", [" envname ", " sep ", " delete_existing ]) +This appends new path elements to the given path in the +specified external environment +.RB ( $ENV +by default). +This will only add +any particular path once (leaving the first one it encounters and +ignoring the rest, to preserve path order), +and to help assure this, +will normalize all paths (using +.B os.path.normpath +and +.BR os.path.normcase ). +This can also handle the +case where the given old path variable is a list instead of a +string, in which case a list will be returned instead of a string. + +If +.I delete_existing +is 0, then adding a path that already exists +will not move it to the beginning; +it will stay where it is in the list. + +Example: + +.ES +print 'before:',env['ENV']['INCLUDE'] +include_path = '/foo/bar:/foo' +env.PrependENVPath('INCLUDE', include_path) +print 'after:',env['ENV']['INCLUDE'] +.EE +.IP +The above example will print: + +.ES +before: /biz:/foo +after: /foo/bar:/foo:/biz +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .PrependUnique(key= val ", delete_existing=" "0, [...])" +Appends the specified keyword arguments +to the beginning of construction variables in the environment. +If the Environment does not have +the specified construction variable, +it is simply added to the environment. +If the construction variable being appended to is a list, +then any value(s) that already exist in the +construction variable will +.I not +be added again to the list. +However, if delete_existing is 1, +existing matching values are removed first, so +existing values in the arg list move to the front of the list. + +Example: + +.ES +env.PrependUnique(CCFLAGS = '-g', FOO = ['foo.yyy']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Progress( callable ", [" interval ]) +.TP +.RI Progress( string ", [" interval ", " file ", " overwrite ]) +.TP +.RI Progress( list_of_strings ", [" interval ", " file ", " overwrite ]) +Allows SCons to show progress made during the build +by displaying a string or calling a function while +evaluating Nodes (e.g. files). + +If the first specified argument is a Python callable +(a function or an object that has a +.BR __call__ () +method), +the function will be called +once every +.I interval +times a Node is evaluated. +The callable will be passed the evaluated Node +as its only argument. +(For future compatibility, +it's a good idea to also add +.B *args +and +.B **kw +as arguments to your function or method. +This will prevent the code from breaking +if SCons ever changes the interface +to call the function with additional arguments in the future.) + +An example of a simple custom progress function +that prints a string containing the Node name +every 10 Nodes: + +.ES +def my_progress_function(node, *args, **kw): + print 'Evaluating node %s!' % node +Progress(my_progress_function, interval=10) +.EE +.IP +A more complicated example of a custom progress display object +that prints a string containing a count +every 100 evaluated Nodes. +Note the use of +.B \\r +(a carriage return) +at the end so that the string +will overwrite itself on a display: + +.ES +import sys +class ProgressCounter(object): + count = 0 + def __call__(self, node, *args, **kw): + self.count += 100 + sys.stderr.write('Evaluated %s nodes\\r' % self.count) +Progress(ProgressCounter(), interval=100) +.EE +.IP +If the first argument +.BR Progress () +is a string, +the string will be displayed +every +.I interval +evaluated Nodes. +The default is to print the string on standard output; +an alternate output stream +may be specified with the +.B file= +argument. +The following will print a series of dots +on the error output, +one dot for every 100 evaluated Nodes: + +.ES +import sys +Progress('.', interval=100, file=sys.stderr) +.EE +.IP +If the string contains the verbatim substring +.BR $TARGET , +it will be replaced with the Node. +Note that, for performance reasons, this is +.I not +a regular SCons variable substition, +so you can not use other variables +or use curly braces. +The following example will print the name of +every evaluated Node, +using a +.B \\r +(carriage return) to cause each line to overwritten by the next line, +and the +.B overwrite= +keyword argument to make sure the previously-printed +file name is overwritten with blank spaces: + +.ES +import sys +Progress('$TARGET\\r', overwrite=True) +.EE +.IP +If the first argument to +.BR Progress () +is a list of strings, +then each string in the list will be displayed +in rotating fashion every +.I interval +evaluated Nodes. +This can be used to implement a "spinner" +on the user's screen as follows: + +.ES +Progress(['-\\r', '\\\\\\r', '|\\r', '/\\r'], interval=5) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .RCS() +A factory function that +returns a Builder object +to be used to fetch source files +from RCS. +The returned Builder +is intended to be passed to the +.BR SourceCode () +function: + +This function is deprecated. For details, see the entry for the +.BR SourceCode () +function. + +Examples: + +.ES +env.SourceCode('.', env.RCS()) +.EE +.IP +Note that +.B scons +will fetch source files +from RCS subdirectories automatically, +so configuring RCS +as demonstrated in the above example +should only be necessary if +you are fetching from +RCS,v +files in the same +directory as the source files, +or if you need to explicitly specify RCS +for a specific subdirectory. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .Replace(key= val ", [...])" +Replaces construction variables in the Environment +with the specified keyword arguments. + +Example: + +.ES +env.Replace(CCFLAGS = '-g', FOO = 'foo.xxx') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Repository( directory ) +.TP +.IR env .Repository( directory ) +Specifies that +.I directory +is a repository to be searched for files. +Multiple calls to +.BR Repository () +are legal, +and each one adds to the list of +repositories that will be searched. + +To +.BR scons , +a repository is a copy of the source tree, +from the top-level directory on down, +which may contain +both source files and derived files +that can be used to build targets in +the local source tree. +The canonical example would be an +official source tree maintained by an integrator. +If the repository contains derived files, +then the derived files should have been built using +.BR scons , +so that the repository contains the necessary +signature information to allow +.B scons +to figure out when it is appropriate to +use the repository copy of a derived file, +instead of building one locally. + +Note that if an up-to-date derived file +already exists in a repository, +.B scons +will +.I not +make a copy in the local directory tree. +In order to guarantee that a local copy +will be made, +use the +.BR Local () +method. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Requires( target ", " prerequisite ) +.TP +.IR env .Requires( target ", " prerequisite ) +Specifies an order-only relationship +between the specified target file(s) +and the specified prerequisite file(s). +The prerequisite file(s) +will be (re)built, if necessary, +.I before +the target file(s), +but the target file(s) do not actually +depend on the prerequisites +and will not be rebuilt simply because +the prerequisite file(s) change. + +Example: + +.ES +env.Requires('foo', 'file-that-must-be-built-before-foo') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Return([ vars "..., stop=" ]) +By default, +this stops processing the current SConscript +file and returns to the calling SConscript file +the values of the variables named in the +.I vars +string arguments. +Multiple strings contaning variable names may be passed to +.BR Return (). +Any strings that contain white space + +The optional +.B stop= +keyword argument may be set to a false value +to continue processing the rest of the SConscript +file after the +.BR Return () +call. +This was the default behavior prior to SCons 0.98. +However, the values returned +are still the values of the variables in the named +.I vars +at the point +.BR Return () +is called. + +Examples: + +.ES +# Returns without returning a value. +Return() + +# Returns the value of the 'foo' Python variable. +Return("foo") + +# Returns the values of the Python variables 'foo' and 'bar'. +Return("foo", "bar") + +# Returns the values of Python variables 'val1' and 'val2'. +Return('val1 val2') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Scanner( function ", [" argument ", " keys ", " path_function ", " node_class ", " node_factory ", " scan_check ", " recursive ]) +.TP +.IR env .Scanner( function ", [" argument ", " keys ", " path_function ", " node_class ", " node_factory ", " scan_check ", " recursive ]) +Creates a Scanner object for +the specified +.IR function . +See the section "Scanner Objects," +below, for a complete explanation of the arguments and behavior. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .SCCS() +A factory function that +returns a Builder object +to be used to fetch source files +from SCCS. +The returned Builder +is intended to be passed to the +.BR SourceCode () +function. + +Example: + +.ES +env.SourceCode('.', env.SCCS()) +.EE +.IP +Note that +.B scons +will fetch source files +from SCCS subdirectories automatically, +so configuring SCCS +as demonstrated in the above example +should only be necessary if +you are fetching from +.B s.SCCS +files in the same +directory as the source files, +or if you need to explicitly specify SCCS +for a specific subdirectory. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI SConscript( scripts ", [" exports ", " variant_dir ", " duplicate ]) +.TP +.IR env .SConscript( scripts ", [" exports ", " variant_dir ", " duplicate ]) +.TP +.RI SConscript(dirs= subdirs ", [name=" script ", " exports ", " variant_dir ", " duplicate ]) +.TP +.IR env .SConscript(dirs= subdirs ", [name=" script ", " exports ", " variant_dir ", " duplicate ]) +This tells +.B scons +to execute +one or more subsidiary SConscript (configuration) files. +Any variables returned by a called script using +.BR Return () +will be returned by the call to +.BR SConscript (). +There are two ways to call the +.BR SConscript () +function. + +The first way you can call +.BR SConscript () +is to explicitly specify one or more +.I scripts +as the first argument. +A single script may be specified as a string; +multiple scripts must be specified as a list +(either explicitly or as created by +a function like +.BR Split ()). +Examples: +.ES +SConscript('SConscript') # run SConscript in the current directory +SConscript('src/SConscript') # run SConscript in the src directory +SConscript(['src/SConscript', 'doc/SConscript']) +config = SConscript('MyConfig.py') +.EE +.IP +The second way you can call +.BR SConscript () +is to specify a list of (sub)directory names +as a +.BI dirs= subdirs +keyword argument. +In this case, +.B scons +will, by default, +execute a subsidiary configuration file named +.B SConscript +in each of the specified directories. +You may specify a name other than +.B SConscript +by supplying an optional +.BI name= script +keyword argument. +The first three examples below have the same effect +as the first three examples above: +.ES +SConscript(dirs='.') # run SConscript in the current directory +SConscript(dirs='src') # run SConscript in the src directory +SConscript(dirs=['src', 'doc']) +SConscript(dirs=['sub1', 'sub2'], name='MySConscript') +.EE +.IP +The optional +.I exports +argument provides a list of variable names or a dictionary of +named values to export to the +.IR script(s) . +These variables are locally exported only to the specified +.IR script(s) , +and do not affect the global pool of variables used by the +.BR Export () +function. +The subsidiary +.I script(s) +must use the +.BR Import () +function to import the variables. +Examples: +.ES +foo = SConscript('sub/SConscript', exports='env') +SConscript('dir/SConscript', exports=['env', 'variable']) +SConscript(dirs='subdir', exports='env variable') +SConscript(dirs=['one', 'two', 'three'], exports='shared_info') +.EE +.IP +If the optional +.I variant_dir +argument is present, it causes an effect equivalent to the +.BR VariantDir () +method described below. +(If +.I variant_dir +is not present, the +.I duplicate +argument is ignored.) +The +.I variant_dir +argument is interpreted relative to the directory of the calling +.B SConscript +file. +See the description of the +.BR VariantDir () +function below for additional details and restrictions. + +If +.I variant_dir +is present, +the source directory is the directory in which the +.B SConscript +file resides and the +.B SConscript +file is evaluated as if it were in the +.I variant_dir +directory: +.ES +SConscript('src/SConscript', variant_dir = 'build') +.EE +.IP +is equivalent to + +.ES +VariantDir('build', 'src') +SConscript('build/SConscript') +.EE +.IP +This later paradigm is often used when the sources are +in the same directory as the +.BR SConstruct : + +.ES +SConscript('SConscript', variant_dir = 'build') +.EE +.IP +is equivalent to + +.ES +VariantDir('build', '.') +SConscript('build/SConscript') +.EE +.IP +Here are some composite examples: + +.ES +# collect the configuration information and use it to build src and doc +shared_info = SConscript('MyConfig.py') +SConscript('src/SConscript', exports='shared_info') +SConscript('doc/SConscript', exports='shared_info') +.EE + +.ES +# build debugging and production versions. SConscript +# can use Dir('.').path to determine variant. +SConscript('SConscript', variant_dir='debug', duplicate=0) +SConscript('SConscript', variant_dir='prod', duplicate=0) +.EE + +.ES +# build debugging and production versions. SConscript +# is passed flags to use. +opts = { 'CPPDEFINES' : ['DEBUG'], 'CCFLAGS' : '-pgdb' } +SConscript('SConscript', variant_dir='debug', duplicate=0, exports=opts) +opts = { 'CPPDEFINES' : ['NODEBUG'], 'CCFLAGS' : '-O' } +SConscript('SConscript', variant_dir='prod', duplicate=0, exports=opts) +.EE + +.ES +# build common documentation and compile for different architectures +SConscript('doc/SConscript', variant_dir='build/doc', duplicate=0) +SConscript('src/SConscript', variant_dir='build/x86', duplicate=0) +SConscript('src/SConscript', variant_dir='build/ppc', duplicate=0) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI SConscriptChdir( value ) +.TP +.IR env .SConscriptChdir( value ) +By default, +.B scons +changes its working directory +to the directory in which each +subsidiary SConscript file lives. +This behavior may be disabled +by specifying either: + +.ES +SConscriptChdir(0) +env.SConscriptChdir(0) +.EE +.IP +in which case +.B scons +will stay in the top-level directory +while reading all SConscript files. +(This may be necessary when building from repositories, +when all the directories in which SConscript files may be found +don't necessarily exist locally.) +You may enable and disable +this ability by calling +SConscriptChdir() +multiple times. + +Example: + +.ES +env = Environment() +SConscriptChdir(0) +SConscript('foo/SConscript') # will not chdir to foo +env.SConscriptChdir(1) +SConscript('bar/SConscript') # will chdir to bar +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI SConsignFile([ file ", " dbm_module ]) +.TP +.IR env .SConsignFile([ file ", " dbm_module ]) +This tells +.B scons +to store all file signatures +in the specified database +.IR file . +If the +.I file +name is omitted, +.B .sconsign +is used by default. +(The actual file name(s) stored on disk +may have an appropriated suffix appended +by the +.IR dbm_module .) +If +.I file +is not an absolute path name, +the file is placed in the same directory as the top-level +.B SConstruct +file. + +If +.I file +is +.BR None , +then +.B scons +will store file signatures +in a separate +.B .sconsign +file in each directory, +not in one global database file. +(This was the default behavior +prior to SCons 0.96.91 and 0.97.) + +The optional +.I dbm_module +argument can be used to specify +which Python database module +The default is to use a custom +.B SCons.dblite +module that uses pickled +Python data structures, +and which works on all Python versions. + +Examples: + +.ES +# Explicitly stores signatures in ".sconsign.dblite" +# in the top-level SConstruct directory (the +# default behavior). +SConsignFile() + +# Stores signatures in the file "etc/scons-signatures" +# relative to the top-level SConstruct directory. +SConsignFile("etc/scons-signatures") + +# Stores signatures in the specified absolute file name. +SConsignFile("/home/me/SCons/signatures") + +# Stores signatures in a separate .sconsign file +# in each directory. +SConsignFile(None) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .SetDefault(key= val ", [...])" +Sets construction variables to default values specified with the keyword +arguments if (and only if) the variables are not already set. +The following statements are equivalent: + +.ES +env.SetDefault(FOO = 'foo') + +if 'FOO' not in env: env['FOO'] = 'foo' +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI SetOption( name ", " value ) +.TP +.IR env .SetOption( name ", " value ) +This function provides a way to set a select subset of the scons command +line options from a SConscript file. The options supported are: + +.RS 10 +.TP 6 +.B clean +which corresponds to -c, --clean and --remove; +.TP 6 +.B duplicate +which corresponds to --duplicate; +.TP 6 +.B help +which corresponds to -h and --help; +.TP 6 +.B implicit_cache +which corresponds to --implicit-cache; +.TP 6 +.B max_drift +which corresponds to --max-drift; +.TP 6 +.B no_exec +which corresponds to -n, --no-exec, --just-print, --dry-run and --recon; +.TP 6 +.B num_jobs +which corresponds to -j and --jobs; +.TP 6 +.B random +which corresponds to --random; and +.TP 6 +.B stack_size +which corresponds to --stack-size. +.RE + +See the documentation for the +corresponding command line object for information about each specific +option. + +Example: + +.ES +SetOption('max_drift', 1) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI SideEffect( side_effect ", " target ) +.TP +.IR env .SideEffect( side_effect ", " target ) +Declares +.I side_effect +as a side effect of building +.IR target . +Both +.I side_effect +and +.I target +can be a list, a file name, or a node. +A side effect is a target file that is created or updated +as a side effect of building other targets. +For example, a Windows PDB +file is created as a side effect of building the .obj +files for a static library, +and various log files are created updated +as side effects of various TeX commands. +If a target is a side effect of multiple build commands, +.B scons +will ensure that only one set of commands +is executed at a time. +Consequently, you only need to use this method +for side-effect targets that are built as a result of +multiple build commands. + +Because multiple build commands may update +the same side effect file, +by default the +.I side_effect +target is +.I not +automatically removed +when the +.I target +is removed by the +.B \-c +option. +(Note, however, that the +.I side_effect +might be removed as part of +cleaning the directory in which it lives.) +If you want to make sure the +.I side_effect +is cleaned whenever a specific +.I target +is cleaned, +you must specify this explicitly +with the +.BR Clean () +or +.BR env.Clean () +function. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI SourceCode( entries ", " builder ) +.TP +.IR env .SourceCode( entries ", " builder ) +This function and its associate factory functions are deprecated. +There is no replacement. +The intended use was to keep a local tree in sync with an archive, +but in actuality the function only causes the archive +to be fetched on the first run. +Synchronizing with the archive is best done external to &SCons;. + +Arrange for non-existent source files to +be fetched from a source code management system +using the specified +.IR builder . +The specified +.I entries +may be a Node, string or list of both, +and may represent either individual +source files or directories in which +source files can be found. + +For any non-existent source files, +.B scons +will search up the directory tree +and use the first +.BR SourceCode () +builder it finds. +The specified +.I builder +may be +.BR None , +in which case +.B scons +will not use a builder to fetch +source files for the specified +.IR entries , +even if a +.BR SourceCode () +builder has been specified +for a directory higher up the tree. + +.B scons +will, by default, +fetch files from SCCS or RCS subdirectories +without explicit configuration. +This takes some extra processing time +to search for the necessary +source code management files on disk. +You can avoid these extra searches +and speed up your build a little +by disabling these searches as follows: + +.ES +env.SourceCode('.', None) +.EE +.IP +Note that if the specified +.I builder +is one you create by hand, +it must have an associated +construction environment to use +when fetching a source file. + +.B scons +provides a set of canned factory +functions that return appropriate +Builders for various popular +source code management systems. +Canonical examples of invocation include: + +.ES +env.SourceCode('.', env.BitKeeper('/usr/local/BKsources')) +env.SourceCode('src', env.CVS('/usr/local/CVSROOT')) +env.SourceCode('/', env.RCS()) +env.SourceCode(['f1.c', 'f2.c'], env.SCCS()) +env.SourceCode('no_source.c', None) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI SourceSignatures( type ) +.TP +.IR env .SourceSignatures( type ) +Note: Although it is not yet officially deprecated, +use of this function is discouraged. +See the +.BR Decider () +function for a more flexible and straightforward way +to configure SCons' decision-making. + +The +.BR SourceSignatures () +function tells +.B scons +how to decide if a source file +(a file that is not built from any other files) +has changed since the last time it +was used to build a particular target file. +Legal values are +.B MD5 +or +.BR timestamp . + +If the environment method is used, +the specified type of source signature +is only used when deciding whether targets +built with that environment are up-to-date or must be rebuilt. +If the global function is used, +the specified type of source signature becomes the default +used for all decisions +about whether targets are up-to-date. + +.B MD5 +means +.B scons +decides that a source file has changed +if the MD5 checksum of its contents has changed since +the last time it was used to rebuild a particular target file. + +.B timestamp +means +.B scons +decides that a source file has changed +if its timestamp (modification time) has changed since +the last time it was used to rebuild a particular target file. +(Note that although this is similar to the behavior of Make, +by default it will also rebuild if the dependency is +.I older +than the last time it was used to rebuild the target file.) + +There is no different between the two behaviors +for Python +.BR Value () +node objects. + +.B MD5 +signatures take longer to compute, +but are more accurate than +.B timestamp +signatures. +The default value is +.BR MD5 . + +Note that the default +.BR TargetSignatures () +setting (see below) +is to use this +.BR SourceSignatures () +setting for any target files that are used +to build other target files. +Consequently, changing the value of +.BR SourceSignatures () +will, by default, +affect the up-to-date decision for all files in the build +(or all files built with a specific construction environment +when +.BR env.SourceSignatures () +is used). + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Split( arg ) +.TP +.IR env .Split( arg ) +Returns a list of file names or other objects. +If arg is a string, +it will be split on strings of white-space characters +within the string, +making it easier to write long lists of file names. +If arg is already a list, +the list will be returned untouched. +If arg is any other type of object, +it will be returned as a list +containing just the object. + +Example: + +.ES +files = Split("f1.c f2.c f3.c") +files = env.Split("f4.c f5.c f6.c") +files = Split(""" + f7.c + f8.c + f9.c +""") +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.IR env .subst( input ", [" raw ", " target ", " source ", " conv ]) +Performs construction variable interpolation +on the specified string or sequence argument +.IR input . + +By default, +leading or trailing white space will +be removed from the result. +and all sequences of white space +will be compressed to a single space character. +Additionally, any +.B $( +and +.B $) +character sequences will be stripped from the returned string, +The optional +.I raw +argument may be set to +.B 1 +if you want to preserve white space and +.BR $( - $) +sequences. +The +.I raw +argument may be set to +.B 2 +if you want to strip +all characters between +any +.B $( +and +.B $) +pairs +(as is done for signature calculation). + +If the input is a sequence +(list or tuple), +the individual elements of +the sequence will be expanded, +and the results will be returned as a list. + +The optional +.I target +and +.I source +keyword arguments +must be set to lists of +target and source nodes, respectively, +if you want the +.BR $TARGET , +.BR $TARGETS , +.B $SOURCE +and +.B $SOURCES +to be available for expansion. +This is usually necessary if you are +calling +.BR env.subst () +from within a Python function used +as an SCons action. + +Returned string values or sequence elements +are converted to their string representation by default. +The optional +.I conv +argument +may specify a conversion function +that will be used in place of +the default. +For example, if you want Python objects +(including SCons Nodes) +to be returned as Python objects, +you can use the Python +.B lambda +idiom to pass in an unnamed function +that simply returns its unconverted argument. + +Example: + +.ES +print env.subst("The C compiler is: $CC") + +def compile(target, source, env): + sourceDir = env.subst("${SOURCE.srcdir}", + target=target, + source=source) + +source_nodes = env.subst('$EXPAND_TO_NODELIST', + conv=lambda x: x) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Tag( node ", " tags ) +Annotates file or directory Nodes with +information about how the +.BR Package () +Builder should package those files or directories. +All tags are optional. + +Examples: + +.ES +# makes sure the built library will be installed with 0644 file +# access mode +Tag( Library( 'lib.c' ), UNIX_ATTR="0644" ) + +# marks file2.txt to be a documentation file +Tag( 'file2.txt', DOC ) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI TargetSignatures( type ) +.TP +.IR env .TargetSignatures( type ) +Note: Although it is not yet officially deprecated, +use of this function is discouraged. +See the +.BR Decider () +function for a more flexible and straightforward way +to configure SCons' decision-making. + +The +.BR TargetSignatures () +function tells +.B scons +how to decide if a target file +(a file that +.I is +built from any other files) +has changed since the last time it +was used to build some other target file. +Legal values are +.BR "build" ; +.B "content" +(or its synonym +.BR "MD5" ); +.BR "timestamp" ; +or +.BR "source" . + +If the environment method is used, +the specified type of target signature is only used +for targets built with that environment. +If the global function is used, +the specified type of signature becomes the default +used for all target files that +don't have an explicit target signature type +specified for their environments. + +.B "content" +(or its synonym +.BR "MD5" ) +means +.B scons +decides that a target file has changed +if the MD5 checksum of its contents has changed since +the last time it was used to rebuild some other target file. +This means +.B scons +will open up +MD5 sum the contents +of target files after they're built, +and may decide that it does not need to rebuild +"downstream" target files if a file was +rebuilt with exactly the same contents as the last time. + +.B "timestamp" +means +.B scons +decides that a target file has changed +if its timestamp (modification time) has changed since +the last time it was used to rebuild some other target file. +(Note that although this is similar to the behavior of Make, +by default it will also rebuild if the dependency is +.I older +than the last time it was used to rebuild the target file.) + +.B "source" +means +.B scons +decides that a target file has changed +as specified by the corresponding +.BR SourceSignatures () +setting +.RB ( "MD5" +or +.BR "timestamp" ). +This means that +.B scons +will treat all input files to a target the same way, +regardless of whether they are source files +or have been built from other files. + +.B "build" +means +.B scons +decides that a target file has changed +if it has been rebuilt in this invocation +or if its content or timestamp have changed +as specified by the corresponding +.BR SourceSignatures () +setting. +This "propagates" the status of a rebuilt file +so that other "downstream" target files +will always be rebuilt, +even if the contents or the timestamp +have not changed. + +.B "build" +signatures are fastest because +.B "content" +(or +.BR "MD5" ) +signatures take longer to compute, +but are more accurate than +.B "timestamp" +signatures, +and can prevent unnecessary "downstream" rebuilds +when a target file is rebuilt to the exact same contents +as the previous build. +The +.B "source" +setting provides the most consistent behavior +when other target files may be rebuilt from +both source and target input files. +The default value is +.BR "source" . + +Because the default setting is +.BR "source" , +using +.BR SourceSignatures () +is generally preferable to +.BR TargetSignatures (), +so that the up-to-date decision +will be consistent for all files +(or all files built with a specific construction environment). +Use of +.BR TargetSignatures () +provides specific control for how built target files +affect their "downstream" dependencies. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Tool( string ", [" toolpath ", **" kw ]) +.TP +.IR env .Tool( string ", [" toolpath ", **" kw ]) +The +.BR Tool () +form of the function +returns a callable object +that can be used to initialize +a construction environment using the +tools keyword of the Environment() method. +The object may be called with a construction +environment as an argument, +in which case the object will +add the necessary variables +to the construction environment +and the name of the tool will be added to the +.B $TOOLS +construction variable. + +Additional keyword arguments are passed to the tool's +.BR generate () +method. + +Examples: + +.ES +env = Environment(tools = [ Tool('msvc') ]) + +env = Environment() +t = Tool('msvc') +t(env) # adds 'msvc' to the TOOLS variable +u = Tool('opengl', toolpath = ['tools']) +u(env) # adds 'opengl' to the TOOLS variable +.EE +.IP +The +.BR env.Tool () +form of the function +applies the callable object for the specified tool +.I string +to the environment through which the method was called. + +Additional keyword arguments are passed to the tool's +.BR generate () +method. + +.ES +env.Tool('gcc') +env.Tool('opengl', toolpath = ['build/tools']) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI Value( value ", [" built_value ]) +.TP +.IR env .Value( value ", [" built_value ]) +Returns a Node object representing the specified Python value. Value +Nodes can be used as dependencies of targets. If the result of +calling +.BI str( value ) +changes between SCons runs, any targets depending on +.BI Value( value ) +will be rebuilt. +(This is true even when using timestamps to decide if +files are up-to-date.) +When using timestamp source signatures, Value Nodes' +timestamps are equal to the system time when the Node is created. + +The returned Value Node object has a +.BR write () +method that can be used to "build" a Value Node +by setting a new value. +The optional +.I built_value +argument can be specified +when the Value Node is created +to indicate the Node should already be considered +"built." +There is a corresponding +.BR read () +method that will return the built value of the Node. + +Examples: + +.ES +env = Environment() + +def create(target, source, env): + # A function that will write a 'prefix=$SOURCE' + # string into the file name specified as the + # $TARGET. + f = open(str(target[0]), 'wb') + f.write('prefix=' + source[0].get_contents()) + +# Fetch the prefix= argument, if any, from the command +# line, and use /usr/local as the default. +prefix = ARGUMENTS.get('prefix', '/usr/local') + +# Attach a .Config() builder for the above function action +# to the construction environment. +env['BUILDERS']['Config'] = Builder(action = create) +env.Config(target = 'package-config', source = Value(prefix)) + +def build_value(target, source, env): + # A function that "builds" a Python Value by updating + # the the Python value with the contents of the file + # specified as the source of the Builder call ($SOURCE). + target[0].write(source[0].get_contents()) + +output = env.Value('before') +input = env.Value('after') + +# Attach a .UpdateValue() builder for the above function +# action to the construction environment. +env['BUILDERS']['UpdateValue'] = Builder(action = build_value) +env.UpdateValue(target = Value(output), source = Value(input)) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI VariantDir( variant_dir ", " src_dir ", [" duplicate ]) +.TP +.IR env .VariantDir( variant_dir ", " src_dir ", [" duplicate ]) +Use the +.BR VariantDir () +function to create a copy of your sources in another location: +if a name under +.I variant_dir +is not found but exists under +.IR src_dir , +the file or directory is copied to +.IR variant_dir . +Target files can be built in a different directory +than the original sources by simply refering to the sources (and targets) +within the variant tree. + +.BR VariantDir () +can be called multiple times with the same +.I src_dir +to set up multiple builds with different options +.RI ( variants ). +The +.I src_dir +location must be in or underneath the SConstruct file's directory, and +.I variant_dir +may not be underneath +.IR src_dir . + +The default behavior is for +.B scons +to physically duplicate the source files in the variant tree. +Thus, a build performed in the variant tree is guaranteed to be identical +to a build performed in the source tree even if +intermediate source files are generated during the build, +or preprocessors or other scanners search for included files +relative to the source file, +or individual compilers or other invoked tools are hard-coded +to put derived files in the same directory as source files. + +If possible on the platform, +the duplication is performed by linking rather than copying; +see also the +.B \-\-duplicate +command-line option. +Moreover, only the files needed for the build are duplicated; +files and directories that are not used are not present in +.IR variant_dir . + +Duplicating the source tree may be disabled by setting the +.B duplicate +argument to +.B 0 +(zero). +This will cause +.B scons +to invoke Builders using the path names of source files in +.I src_dir +and the path names of derived files within +.IR variant_dir . +This is always more efficient than +.BR duplicate=1 , +and is usually safe for most builds +(but see above for cases that may cause problems). + +Note that +.BR VariantDir () +works most naturally with a subsidiary SConscript file. +However, you would then call the subsidiary SConscript file +not in the source directory, but in the +.IR variant_dir , +regardless of the value of +.BR duplicate . +This is how you tell +.B scons +which variant of a source tree to build: + +.ES +# run src/SConscript in two variant directories +VariantDir('build/variant1', 'src') +SConscript('build/variant1/SConscript') +VariantDir('build/variant2', 'src') +SConscript('build/variant2/SConscript') +.EE +.IP +See also the +.BR SConscript () +function, described above, +for another way to specify a variant directory +in conjunction with calling a subsidiary SConscript file. + +Examples: + +.ES +# use names in the build directory, not the source directory +VariantDir('build', 'src', duplicate=0) +Program('build/prog', 'build/source.c') +.EE + +.ES +# this builds both the source and docs in a separate subtree +VariantDir('build', '.', duplicate=0) +SConscript(dirs=['build/src','build/doc']) +.EE + +.ES +# same as previous example, but only uses SConscript +SConscript(dirs='src', variant_dir='build/src', duplicate=0) +SConscript(dirs='doc', variant_dir='build/doc', duplicate=0) +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI WhereIs( program ", [" path ", " pathext ", " reject ]) +.TP +.IR env .WhereIs( program ", [" path ", " pathext ", " reject ]) +Searches for the specified executable +.IR program , +returning the full path name to the program +if it is found, +and returning None if not. +Searches the specified +.IR path , +the value of the calling environment's PATH +.RB ( env['ENV']['PATH'] ), +or the user's current external PATH +.RB ( os.environ['PATH'] ) +by default. +On Windows systems, searches for executable +programs with any of the file extensions +listed in the specified +.IR pathext , +the calling environment's PATHEXT +.RB ( env['ENV']['PATHEXT'] ) +or the user's current PATHEXT +.RB ( os.environ['PATHEXT'] ) +by default. +Will not select any +path name or names +in the specified +.I reject +list, if any. +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +'\" END GENERATED FUNCTION DESCRIPTIONS +'\" +'\" The descriptions above of the various SCons functions are generated +'\" from the .xml files that live next to the various Python modules in +'\" the build enginer library. If you're reading this [gnt]roff file +'\" with an eye towards patching this man page, you can still submit +'\" a diff against this text, but it will have to be translated to a +'\" diff against the underlying .xml file before the patch is actually +'\" accepted. If you do that yourself, it will make it easier to +'\" integrate the patch. +'\" +'\" END GENERATED FUNCTION DESCRIPTIONS +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.SS SConscript Variables +In addition to the global functions and methods, +.B scons +supports a number of Python variables +that can be used in SConscript files +to affect how you want the build to be performed. +These variables may be accessed from custom Python modules that you +import into an SConscript file by adding the following +to the Python module: + +.ES +from SCons.Script import * +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +ARGLIST +A list +.IR keyword = value +arguments specified on the command line. +Each element in the list is a tuple +containing the +.RI ( keyword , value ) +of the argument. +The separate +.I keyword +and +.I value +elements of the tuple +can be accessed by +subscripting for element +.B [0] +and +.B [1] +of the tuple, respectively. + +Example: + +.ES +print "first keyword, value =", ARGLIST[0][0], ARGLIST[0][1] +print "second keyword, value =", ARGLIST[1][0], ARGLIST[1][1] +third_tuple = ARGLIST[2] +print "third keyword, value =", third_tuple[0], third_tuple[1] +for key, value in ARGLIST: + # process key and value +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +ARGUMENTS +A dictionary of all the +.IR keyword = value +arguments specified on the command line. +The dictionary is not in order, +and if a given keyword has +more than one value assigned to it +on the command line, +the last (right-most) value is +the one in the +.B ARGUMENTS +dictionary. + +Example: + +.ES +if ARGUMENTS.get('debug', 0): + env = Environment(CCFLAGS = '-g') +else: + env = Environment() +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +BUILD_TARGETS +A list of the targets which +.B scons +will actually try to build, +regardless of whether they were specified on +the command line or via the +.BR Default () +function or method. +The elements of this list may be strings +.I or +nodes, so you should run the list through the Python +.B str +function to make sure any Node path names +are converted to strings. + +Because this list may be taken from the +list of targets specified using the +.BR Default () +function or method, +the contents of the list may change +on each successive call to +.BR Default (). +See the +.B DEFAULT_TARGETS +list, below, +for additional information. + +Example: + +.ES +if 'foo' in BUILD_TARGETS: + print "Don't forget to test the `foo' program!" +if 'special/program' in BUILD_TARGETS: + SConscript('special') +.EE +.IP +Note that the +.B BUILD_TARGETS +list only contains targets expected listed +on the command line or via calls to the +.BR Default () +function or method. +It does +.I not +contain all dependent targets that will be built as +a result of making the sure the explicitly-specified +targets are up to date. + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +COMMAND_LINE_TARGETS +A list of the targets explicitly specified on +the command line. +If there are no targets specified on the command line, +the list is empty. +This can be used, for example, +to take specific actions only +when a certain target or targets +is explicitly being built. + +Example: + +.ES +if 'foo' in COMMAND_LINE_TARGETS: + print "Don't forget to test the `foo' program!" +if 'special/program' in COMMAND_LINE_TARGETS: + SConscript('special') +.EE + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +DEFAULT_TARGETS +A list of the target +.I nodes +that have been specified using the +.BR Default () +function or method. +The elements of the list are nodes, +so you need to run them through the Python +.B str +function to get at the path name for each Node. + +Example: + +.ES +print str(DEFAULT_TARGETS[0]) +if 'foo' in map(str, DEFAULT_TARGETS): + print "Don't forget to test the `foo' program!" +.EE +.IP +The contents of the +.B DEFAULT_TARGETS +list change on on each successive call to the +.BR Default () +function: + +.ES +print map(str, DEFAULT_TARGETS) # originally [] +Default('foo') +print map(str, DEFAULT_TARGETS) # now a node ['foo'] +Default('bar') +print map(str, DEFAULT_TARGETS) # now a node ['foo', 'bar'] +Default(None) +print map(str, DEFAULT_TARGETS) # back to [] +.EE +.IP +Consequently, be sure to use +.B DEFAULT_TARGETS +only after you've made all of your +.BR Default () +calls, +or else simply be careful of the order +of these statements in your SConscript files +so that you don't look for a specific +default target before it's actually been added to the list. + +.SS Construction Variables +.\" XXX From Gary Ruben, 23 April 2002: +.\" I think it would be good to have an example with each construction +.\" variable description in the documentation. +.\" eg. +.\" CC The C compiler +.\" Example: env["CC"] = "c68x" +.\" Default: env["CC"] = "cc" +.\" +.\" CCCOM The command line ... +.\" Example: +.\" To generate the compiler line c68x -ps -qq -mr -o $TARGET $SOURCES +.\" env["CC"] = "c68x" +.\" env["CFLAGS"] = "-ps -qq -mr" +.\" env["CCCOM"] = "$CC $CFLAGS -o $TARGET $SOURCES +.\" Default: +.\" (I dunno what this is ;-) +A construction environment has an associated dictionary of +.I construction variables +that are used by built-in or user-supplied build rules. +Construction variables must follow the same rules for +Python identifiers: +the initial character must be an underscore or letter, +followed by any number of underscores, letters, or digits. + +A number of useful construction variables are automatically defined by +scons for each supported platform, and additional construction variables +can be defined by the user. The following is a list of the automatically +defined construction variables: + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +'\" BEGIN GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS +'\" +'\" The descriptions below of the various SCons construction variables +'\" are generated from the .xml files that live next to the various +'\" Python modules in the build enginer library. If you're reading +'\" this [gnt]roff file with an eye towards patching this man page, +'\" you can still submit a diff against this text, but it will have to +'\" be translated to a diff against the underlying .xml file before the +'\" patch is actually accepted. If you do that yourself, it will make +'\" it easier to integrate the patch. +'\" +'\" BEGIN GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.IP AR +The static library archiver. + +.IP ARCHITECTURE +Specifies the system architecture for which +the package is being built. +The default is the system architecture +of the machine on which SCons is running. +This is used to fill in the +.B Architecture: +field in an Ipkg +\fBcontrol\fP file, +and as part of the name of a generated RPM file. + +.IP ARCOM +The command line used to generate a static library from object files. + +.IP ARCOMSTR +The string displayed when an object file +is generated from an assembly-language source file. +If this is not set, then \fB$ARCOM\fP (the command line) is displayed. + +.ES +env = Environment(ARCOMSTR = "Archiving $TARGET") +.EE + +.IP ARFLAGS +General options passed to the static library archiver. + +.IP AS +The assembler. + +.IP ASCOM +The command line used to generate an object file +from an assembly-language source file. + +.IP ASCOMSTR +The string displayed when an object file +is generated from an assembly-language source file. +If this is not set, then \fB$ASCOM\fP (the command line) is displayed. + +.ES +env = Environment(ASCOMSTR = "Assembling $TARGET") +.EE + +.IP ASFLAGS +General options passed to the assembler. + +.IP ASPPCOM +The command line used to assemble an assembly-language +source file into an object file +after first running the file through the C preprocessor. +Any options specified +in the \fB$ASFLAGS\fP and \fB$CPPFLAGS\fP construction variables +are included on this command line. + +.IP ASPPCOMSTR +The string displayed when an object file +is generated from an assembly-language source file +after first running the file through the C preprocessor. +If this is not set, then \fB$ASPPCOM\fP (the command line) is displayed. + +.ES +env = Environment(ASPPCOMSTR = "Assembling $TARGET") +.EE + +.IP ASPPFLAGS +General options when an assembling an assembly-language +source file into an object file +after first running the file through the C preprocessor. +The default is to use the value of \fB$ASFLAGS\fP. + +.IP BIBTEX +The bibliography generator for the TeX formatter and typesetter and the +LaTeX structured formatter and typesetter. + +.IP BIBTEXCOM +The command line used to call the bibliography generator for the +TeX formatter and typesetter and the LaTeX structured formatter and +typesetter. + +.IP BIBTEXCOMSTR +The string displayed when generating a bibliography +for TeX or LaTeX. +If this is not set, then \fB$BIBTEXCOM\fP (the command line) is displayed. + +.ES +env = Environment(BIBTEXCOMSTR = "Generating bibliography $TARGET") +.EE + +.IP BIBTEXFLAGS +General options passed to the bibliography generator for the TeX formatter +and typesetter and the LaTeX structured formatter and typesetter. + +.IP BITKEEPER +The BitKeeper executable. + +.IP BITKEEPERCOM +The command line for +fetching source files using BitKeeper. + +.IP BITKEEPERCOMSTR +The string displayed when fetching +a source file using BitKeeper. +If this is not set, then \fB$BITKEEPERCOM\fP +(the command line) is displayed. + +.IP BITKEEPERGET +The command (\fB$BITKEEPER\fP) and subcommand +for fetching source files using BitKeeper. + +.IP BITKEEPERGETFLAGS +Options that are passed to the BitKeeper +.B get +subcommand. + +.IP BUILDERS +A dictionary mapping the names of the builders +available through this environment +to underlying Builder objects. +Builders named +Alias, CFile, CXXFile, DVI, Library, Object, PDF, PostScript, and Program +are available by default. +If you initialize this variable when an +Environment is created: + +.ES +env = Environment(BUILDERS = {'NewBuilder' : foo}) +.EE +.IP +the default Builders will no longer be available. +To use a new Builder object in addition to the default Builders, +add your new Builder object like this: + +.ES +env = Environment() +env.Append(BUILDERS = {'NewBuilder' : foo}) +.EE +.IP +or this: + +.ES +env = Environment() +env['BUILDERS]['NewBuilder'] = foo +.EE + +.IP CC +The C compiler. + +.IP CCCOM +The command line used to compile a C source file to a (static) object +file. Any options specified in the \fB$CFLAGS\fP, \fB$CCFLAGS\fP and +\fB$CPPFLAGS\fP construction variables are included on this command +line. + +.IP CCCOMSTR +The string displayed when a C source file +is compiled to a (static) object file. +If this is not set, then \fB$CCCOM\fP (the command line) is displayed. + +.ES +env = Environment(CCCOMSTR = "Compiling static object $TARGET") +.EE + +.IP CCFLAGS +General options that are passed to the C and C++ compilers. + +.IP CCPCHFLAGS +Options added to the compiler command line +to support building with precompiled headers. +The default value expands expands to the appropriate +Microsoft Visual C++ command-line options +when the \fB$PCH\fP construction variable is set. + +.IP CCPDBFLAGS +Options added to the compiler command line +to support storing debugging information in a +Microsoft Visual C++ PDB file. +The default value expands expands to appropriate +Microsoft Visual C++ command-line options +when the \fB$PDB\fP construction variable is set. + +The Visual C++ compiler option that SCons uses by default +to generate PDB information is \fB/Z7\fP. +This works correctly with parallel (\fB\-j\fP) builds +because it embeds the debug information in the intermediate object files, +as opposed to sharing a single PDB file between multiple object files. +This is also the only way to get debug information +embedded into a static library. +Using the \fB/Zi\fP instead may yield improved +link-time performance, +although parallel builds will no longer work. + +You can generate PDB files with the \fB/Zi\fP +switch by overriding the default \fB$CCPDBFLAGS\fP variable as follows: + +.ES +env['CCPDBFLAGS'] = ['${(PDB and "/Zi /Fd%s" % File(PDB)) or ""}'] +.EE +.IP +An alternative would be to use the \fB/Zi\fP +to put the debugging information in a separate \fB.pdb\fP +file for each object file by overriding +the \fB$CCPDBFLAGS\fP variable as follows: + +.ES +env['CCPDBFLAGS'] = '/Zi /Fd${TARGET}.pdb' +.EE + +.IP CCVERSION +The version number of the C compiler. +This may or may not be set, +depending on the specific C compiler being used. + +.IP CFILESUFFIX +The suffix for C source files. +This is used by the internal CFile builder +when generating C files from Lex (.l) or YACC (.y) input files. +The default suffix, of course, is +.B .c +(lower case). +On case-insensitive systems (like Windows), +SCons also treats +.B .C +(upper case) files +as C files. + +.IP CFLAGS +General options that are passed to the C compiler (C only; not C++). + +.IP CHANGE_SPECFILE +A hook for modifying the file that controls the packaging build +(the \fB.spec\fP for RPM, +the \fBcontrol\fP for Ipkg, +the \fB.wxs\fP for MSI). +If set, the function will be called +after the SCons template for the file has been written. +XXX + +.IP CHANGED_SOURCES +A reserved variable name +that may not be set or used in a construction environment. +(See "Variable Substitution," below.) + +.IP CHANGED_TARGETS +A reserved variable name +that may not be set or used in a construction environment. +(See "Variable Substitution," below.) + +.IP CHANGELOG +The name of a file containing the change log text +to be included in the package. +This is included as the +.B %changelog +section of the RPM +\fB.spec\fP file. + +.IP _concat +A function used to produce variables like \fB$_CPPINCFLAGS\fP. It takes +four or five +arguments: a prefix to concatenate onto each element, a list of +elements, a suffix to concatenate onto each element, an environment +for variable interpolation, and an optional function that will be +called to transform the list before concatenation. + +.ES +env['_CPPINCFLAGS'] = '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs)} $)', +.EE + +.IP CONFIGUREDIR +The name of the directory in which +Configure context test files are written. +The default is +.B .sconf_temp +in the top-level directory +containing the +.B SConstruct +file. + +.IP CONFIGURELOG +The name of the Configure context log file. +The default is +.B config.log +in the top-level directory +containing the +.B SConstruct +file. + +.IP _CPPDEFFLAGS +An automatically-generated construction variable +containing the C preprocessor command-line options +to define values. +The value of \fB$_CPPDEFFLAGS\fP is created +by appending \fB$CPPDEFPREFIX\fP and \fB$CPPDEFSUFFIX\fP +to the beginning and end +of each definition in \fB$CPPDEFINES\fP. + +.IP CPPDEFINES +A platform independent specification of C preprocessor definitions. +The definitions will be added to command lines +through the automatically-generated +\fB$_CPPDEFFLAGS\fP construction variable (see above), +which is constructed according to +the type of value of \fB$CPPDEFINES\fP: + +If \fB$CPPDEFINES\fP is a string, +the values of the +.BR $CPPDEFPREFIX and $CPPDEFSUFFIX +construction variables +will be added to the beginning and end. + +.ES +# Will add -Dxyz to POSIX compiler command lines, +# and /Dxyz to Microsoft Visual C++ command lines. +env = Environment(CPPDEFINES='xyz') +.EE +.IP +If \fB$CPPDEFINES\fP is a list, +the values of the +.BR $CPPDEFPREFIX and $CPPDEFSUFFIX +construction variables +will be appended to the beginning and end +of each element in the list. +If any element is a list or tuple, +then the first item is the name being +defined and the second item is its value: + +.ES +# Will add -DB=2 -DA to POSIX compiler command lines, +# and /DB=2 /DA to Microsoft Visual C++ command lines. +env = Environment(CPPDEFINES=[('B', 2), 'A']) +.EE +.IP +If \fB$CPPDEFINES\fP is a dictionary, +the values of the +.BR $CPPDEFPREFIX and $CPPDEFSUFFIX +construction variables +will be appended to the beginning and end +of each item from the dictionary. +The key of each dictionary item +is a name being defined +to the dictionary item's corresponding value; +if the value is +.BR None , +then the name is defined without an explicit value. +Note that the resulting flags are sorted by keyword +to ensure that the order of the options on the +command line is consistent each time +.B scons +is run. + +.ES +# Will add -DA -DB=2 to POSIX compiler command lines, +# and /DA /DB=2 to Microsoft Visual C++ command lines. +env = Environment(CPPDEFINES={'B':2, 'A':None}) +.EE + +.IP CPPDEFPREFIX +The prefix used to specify preprocessor definitions +on the C compiler command line. +This will be appended to the beginning of each definition +in the \fB$CPPDEFINES\fP construction variable +when the \fB$_CPPDEFFLAGS\fP variable is automatically generated. + +.IP CPPDEFSUFFIX +The suffix used to specify preprocessor definitions +on the C compiler command line. +This will be appended to the end of each definition +in the \fB$CPPDEFINES\fP construction variable +when the \fB$_CPPDEFFLAGS\fP variable is automatically generated. + +.IP CPPFLAGS +User-specified C preprocessor options. +These will be included in any command that uses the C preprocessor, +including not just compilation of C and C++ source files +via the \fB$CCCOM\fP, +.BR $SHCCCOM , +\fB$CXXCOM\fP and +\fB$SHCXXCOM\fP command lines, +but also the \fB$FORTRANPPCOM\fP, +.BR $SHFORTRANPPCOM , +\fB$F77PPCOM\fP and +\fB$SHF77PPCOM\fP command lines +used to compile a Fortran source file, +and the \fB$ASPPCOM\fP command line +used to assemble an assembly language source file, +after first running each file through the C preprocessor. +Note that this variable does +.I not +contain +.B \-I +(or similar) include search path options +that scons generates automatically from \fB$CPPPATH\fP. +See \fB$_CPPINCFLAGS\fP, below, +for the variable that expands to those options. + +.IP _CPPINCFLAGS +An automatically-generated construction variable +containing the C preprocessor command-line options +for specifying directories to be searched for include files. +The value of \fB$_CPPINCFLAGS\fP is created +by appending \fB$INCPREFIX\fP and \fB$INCSUFFIX\fP +to the beginning and end +of each directory in \fB$CPPPATH\fP. + +.IP CPPPATH +The list of directories that the C preprocessor will search for include +directories. The C/C++ implicit dependency scanner will search these +directories for include files. Don't explicitly put include directory +arguments in CCFLAGS or CXXFLAGS because the result will be non-portable +and the directories will not be searched by the dependency scanner. Note: +directory names in CPPPATH will be looked-up relative to the SConscript +directory when they are used in a command. To force +.B scons +to look-up a directory relative to the root of the source tree use #: + +.ES +env = Environment(CPPPATH='#/include') +.EE +.IP +The directory look-up can also be forced using the +.BR Dir () +function: + +.ES +include = Dir('include') +env = Environment(CPPPATH=include) +.EE +.IP +The directory list will be added to command lines +through the automatically-generated +.B $_CPPINCFLAGS +construction variable, +which is constructed by +appending the values of the +.BR $INCPREFIX and $INCSUFFIX +construction variables +to the beginning and end +of each directory in \fB$CPPPATH\fP. +Any command lines you define that need +the CPPPATH directory list should +include \fB$_CPPINCFLAGS\fP: + +.ES +env = Environment(CCCOM="my_compiler $_CPPINCFLAGS -c -o $TARGET $SOURCE") +.EE + +.IP CPPSUFFIXES +The list of suffixes of files that will be scanned +for C preprocessor implicit dependencies +(#include lines). +The default list is: + +.ES +[".c", ".C", ".cxx", ".cpp", ".c++", ".cc", + ".h", ".H", ".hxx", ".hpp", ".hh", + ".F", ".fpp", ".FPP", + ".m", ".mm", + ".S", ".spp", ".SPP"] +.EE + +.IP CVS +The CVS executable. + +.IP CVSCOFLAGS +Options that are passed to the CVS checkout subcommand. + +.IP CVSCOM +The command line used to +fetch source files from a CVS repository. + +.IP CVSCOMSTR +The string displayed when fetching +a source file from a CVS repository. +If this is not set, then \fB$CVSCOM\fP +(the command line) is displayed. + +.IP CVSFLAGS +General options that are passed to CVS. +By default, this is set to +.B "-d $CVSREPOSITORY" +to specify from where the files must be fetched. + +.IP CVSREPOSITORY +The path to the CVS repository. +This is referenced in the default +\fB$CVSFLAGS\fP value. + +.IP CXX +The C++ compiler. + +.IP CXXCOM +The command line used to compile a C++ source file to an object file. +Any options specified in the \fB$CXXFLAGS\fP and +\fB$CPPFLAGS\fP construction variables +are included on this command line. + +.IP CXXCOMSTR +The string displayed when a C++ source file +is compiled to a (static) object file. +If this is not set, then \fB$CXXCOM\fP (the command line) is displayed. + +.ES +env = Environment(CXXCOMSTR = "Compiling static object $TARGET") +.EE + +.IP CXXFILESUFFIX +The suffix for C++ source files. +This is used by the internal CXXFile builder +when generating C++ files from Lex (.ll) or YACC (.yy) input files. +The default suffix is +.BR .cc . +SCons also treats files with the suffixes +.BR .cpp , +.BR .cxx , +.BR .c++ , +and +.B .C++ +as C++ files, +and files with +.B .mm +suffixes as Objective C++ files. +On case-sensitive systems (Linux, UNIX, and other POSIX-alikes), +SCons also treats +.B .C +(upper case) files +as C++ files. + +.IP CXXFLAGS +General options that are passed to the C++ compiler. +By default, this includes the value of \fB$CCFLAGS\fP, +so that setting \fB$CCFLAGS\fP affects both C and C++ compilation. +If you want to add C++-specific flags, +you must set or override the value of \fB$CXXFLAGS\fP. + +.IP CXXVERSION +The version number of the C++ compiler. +This may or may not be set, +depending on the specific C++ compiler being used. + +.IP DESCRIPTION +A long description of the project being packaged. +This is included in the relevant section +of the file that controls the packaging build. + +.IP DESCRIPTION_lang +A language-specific long description for +the specified \fIlang\fP. +This is used to populate a +.B "%description -l" +section of an RPM +\fB.spec\fP file. + +.IP Dir +A function that converts a string +into a Dir instance relative to the target being built. + +.IP Dirs +A function that converts a list of strings +into a list of Dir instances relative to the target being built. + +.IP DSUFFIXES +The list of suffixes of files that will be scanned +for imported D package files. +The default list is: + +.ES +['.d'] +.EE + +.IP DVIPDF +The TeX DVI file to PDF file converter. + +.IP DVIPDFCOM +The command line used to convert TeX DVI files into a PDF file. + +.IP DVIPDFCOMSTR +The string displayed when a TeX DVI file +is converted into a PDF file. +If this is not set, then \fB$DVIPDFCOM\fP (the command line) is displayed. + +.IP DVIPDFFLAGS +General options passed to the TeX DVI file to PDF file converter. + +.IP DVIPS +The TeX DVI file to PostScript converter. + +.IP DVIPSFLAGS +General options passed to the TeX DVI file to PostScript converter. + +.IP ENV +A dictionary of environment variables +to use when invoking commands. When +\fB$ENV\fP is used in a command all list +values will be joined using the path separator and any other non-string +values will simply be coerced to a string. +Note that, by default, +.B scons +does +.I not +propagate the environment in force when you +execute +.B scons +to the commands used to build target files. +This is so that builds will be guaranteed +repeatable regardless of the environment +variables set at the time +.B scons +is invoked. + +If you want to propagate your +environment variables +to the commands executed +to build target files, +you must do so explicitly: + +.ES +import os +env = Environment(ENV = os.environ) +.EE +.IP +Note that you can choose only to propagate +certain environment variables. +A common example is +the system +.B PATH +environment variable, +so that +.B scons +uses the same utilities +as the invoking shell (or other process): + +.ES +import os +env = Environment(ENV = {'PATH' : os.environ['PATH']}) +.EE + +.IP ESCAPE +A function that will be called to escape shell special characters in +command lines. The function should take one argument: the command line +string to escape; and should return the escaped command line. + +.IP F77 +The Fortran 77 compiler. +You should normally set the \fB$FORTRAN\fP variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set \fB$F77\fP if you need to use a specific compiler +or compiler version for Fortran 77 files. + +.IP F77COM +The command line used to compile a Fortran 77 source file to an object file. +You only need to set \fB$F77COM\fP if you need to use a specific +command line for Fortran 77 files. +You should normally set the \fB$FORTRANCOM\fP variable, +which specifies the default command line +for all Fortran versions. + +.IP F77COMSTR +The string displayed when a Fortran 77 source file +is compiled to an object file. +If this is not set, then \fB$F77COM\fP or \fB$FORTRANCOM\fP +(the command line) is displayed. + +.IP F77FILESUFFIXES +The list of file extensions for which the F77 dialect will be used. By +default, this is ['.f77'] + +.IP F77FLAGS +General user-specified options that are passed to the Fortran 77 compiler. +Note that this variable does +.I not +contain +.B \-I +(or similar) include search path options +that scons generates automatically from \fB$F77PATH\fP. +See +.B $_F77INCFLAGS +below, +for the variable that expands to those options. +You only need to set \fB$F77FLAGS\fP if you need to define specific +user options for Fortran 77 files. +You should normally set the \fB$FORTRANFLAGS\fP variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. + +.IP _F77INCFLAGS +An automatically-generated construction variable +containing the Fortran 77 compiler command-line options +for specifying directories to be searched for include files. +The value of \fB$_F77INCFLAGS\fP is created +by appending \fB$INCPREFIX\fP and \fB$INCSUFFIX\fP +to the beginning and end +of each directory in \fB$F77PATH\fP. + +.IP F77PATH +The list of directories that the Fortran 77 compiler will search for include +directories. The implicit dependency scanner will search these +directories for include files. Don't explicitly put include directory +arguments in \fB$F77FLAGS\fP because the result will be non-portable +and the directories will not be searched by the dependency scanner. Note: +directory names in \fB$F77PATH\fP will be looked-up relative to the SConscript +directory when they are used in a command. To force +.B scons +to look-up a directory relative to the root of the source tree use #: +You only need to set \fB$F77PATH\fP if you need to define a specific +include path for Fortran 77 files. +You should normally set the \fB$FORTRANPATH\fP variable, +which specifies the include path +for the default Fortran compiler +for all Fortran versions. + +.ES +env = Environment(F77PATH='#/include') +.EE +.IP +The directory look-up can also be forced using the +.BR Dir () +function: + +.ES +include = Dir('include') +env = Environment(F77PATH=include) +.EE +.IP +The directory list will be added to command lines +through the automatically-generated +.B $_F77INCFLAGS +construction variable, +which is constructed by +appending the values of the +.BR $INCPREFIX and $INCSUFFIX +construction variables +to the beginning and end +of each directory in \fB$F77PATH\fP. +Any command lines you define that need +the F77PATH directory list should +include \fB$_F77INCFLAGS\fP: + +.ES +env = Environment(F77COM="my_compiler $_F77INCFLAGS -c -o $TARGET $SOURCE") +.EE + +.IP F77PPCOM +The command line used to compile a Fortran 77 source file to an object file +after first running the file through the C preprocessor. +Any options specified in the \fB$F77FLAGS\fP and \fB$CPPFLAGS\fP construction variables +are included on this command line. +You only need to set \fB$F77PPCOM\fP if you need to use a specific +C-preprocessor command line for Fortran 77 files. +You should normally set the \fB$FORTRANPPCOM\fP variable, +which specifies the default C-preprocessor command line +for all Fortran versions. + +.IP F77PPCOMSTR +The string displayed when a Fortran 77 source file +is compiled to an object file +after first running the file through the C preprocessor. +If this is not set, then \fB$F77PPCOM\fP or \fB$FORTRANPPCOM\fP +(the command line) is displayed. + +.IP F77PPFILESUFFIXES +The list of file extensions for which the compilation + preprocessor pass for +F77 dialect will be used. By default, this is empty + +.IP F90 +The Fortran 90 compiler. +You should normally set the \fB$FORTRAN\fP variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set \fB$F90\fP if you need to use a specific compiler +or compiler version for Fortran 90 files. + +.IP F90COM +The command line used to compile a Fortran 90 source file to an object file. +You only need to set \fB$F90COM\fP if you need to use a specific +command line for Fortran 90 files. +You should normally set the \fB$FORTRANCOM\fP variable, +which specifies the default command line +for all Fortran versions. + +.IP F90COMSTR +The string displayed when a Fortran 90 source file +is compiled to an object file. +If this is not set, then \fB$F90COM\fP or \fB$FORTRANCOM\fP +(the command line) is displayed. + +.IP F90FILESUFFIXES +The list of file extensions for which the F90 dialect will be used. By +default, this is ['.f90'] + +.IP F90FLAGS +General user-specified options that are passed to the Fortran 90 compiler. +Note that this variable does +.I not +contain +.B \-I +(or similar) include search path options +that scons generates automatically from \fB$F90PATH\fP. +See +.B $_F90INCFLAGS +below, +for the variable that expands to those options. +You only need to set \fB$F90FLAGS\fP if you need to define specific +user options for Fortran 90 files. +You should normally set the \fB$FORTRANFLAGS\fP variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. + +.IP _F90INCFLAGS +An automatically-generated construction variable +containing the Fortran 90 compiler command-line options +for specifying directories to be searched for include files. +The value of \fB$_F90INCFLAGS\fP is created +by appending \fB$INCPREFIX\fP and \fB$INCSUFFIX\fP +to the beginning and end +of each directory in \fB$F90PATH\fP. + +.IP F90PATH +The list of directories that the Fortran 90 compiler will search for include +directories. The implicit dependency scanner will search these +directories for include files. Don't explicitly put include directory +arguments in \fB$F90FLAGS\fP because the result will be non-portable +and the directories will not be searched by the dependency scanner. Note: +directory names in \fB$F90PATH\fP will be looked-up relative to the SConscript +directory when they are used in a command. To force +.B scons +to look-up a directory relative to the root of the source tree use #: +You only need to set \fB$F90PATH\fP if you need to define a specific +include path for Fortran 90 files. +You should normally set the \fB$FORTRANPATH\fP variable, +which specifies the include path +for the default Fortran compiler +for all Fortran versions. + +.ES +env = Environment(F90PATH='#/include') +.EE +.IP +The directory look-up can also be forced using the +.BR Dir () +function: + +.ES +include = Dir('include') +env = Environment(F90PATH=include) +.EE +.IP +The directory list will be added to command lines +through the automatically-generated +.B $_F90INCFLAGS +construction variable, +which is constructed by +appending the values of the +.BR $INCPREFIX and $INCSUFFIX +construction variables +to the beginning and end +of each directory in \fB$F90PATH\fP. +Any command lines you define that need +the F90PATH directory list should +include \fB$_F90INCFLAGS\fP: + +.ES +env = Environment(F90COM="my_compiler $_F90INCFLAGS -c -o $TARGET $SOURCE") +.EE + +.IP F90PPCOM +The command line used to compile a Fortran 90 source file to an object file +after first running the file through the C preprocessor. +Any options specified in the \fB$F90FLAGS\fP and \fB$CPPFLAGS\fP construction variables +are included on this command line. +You only need to set \fB$F90PPCOM\fP if you need to use a specific +C-preprocessor command line for Fortran 90 files. +You should normally set the \fB$FORTRANPPCOM\fP variable, +which specifies the default C-preprocessor command line +for all Fortran versions. + +.IP F90PPCOMSTR +The string displayed when a Fortran 90 source file +is compiled after first running the file through the C preprocessor. +If this is not set, then \fB$F90PPCOM\fP or \fB$FORTRANPPCOM\fP +(the command line) is displayed. + +.IP F90PPFILESUFFIXES +The list of file extensions for which the compilation + preprocessor pass for +F90 dialect will be used. By default, this is empty + +.IP F95 +The Fortran 95 compiler. +You should normally set the \fB$FORTRAN\fP variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set \fB$F95\fP if you need to use a specific compiler +or compiler version for Fortran 95 files. + +.IP F95COM +The command line used to compile a Fortran 95 source file to an object file. +You only need to set \fB$F95COM\fP if you need to use a specific +command line for Fortran 95 files. +You should normally set the \fB$FORTRANCOM\fP variable, +which specifies the default command line +for all Fortran versions. + +.IP F95COMSTR +The string displayed when a Fortran 95 source file +is compiled to an object file. +If this is not set, then \fB$F95COM\fP or \fB$FORTRANCOM\fP +(the command line) is displayed. + +.IP F95FILESUFFIXES +The list of file extensions for which the F95 dialect will be used. By +default, this is ['.f95'] + +.IP F95FLAGS +General user-specified options that are passed to the Fortran 95 compiler. +Note that this variable does +.I not +contain +.B \-I +(or similar) include search path options +that scons generates automatically from \fB$F95PATH\fP. +See +.B $_F95INCFLAGS +below, +for the variable that expands to those options. +You only need to set \fB$F95FLAGS\fP if you need to define specific +user options for Fortran 95 files. +You should normally set the \fB$FORTRANFLAGS\fP variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. + +.IP _F95INCFLAGS +An automatically-generated construction variable +containing the Fortran 95 compiler command-line options +for specifying directories to be searched for include files. +The value of \fB$_F95INCFLAGS\fP is created +by appending \fB$INCPREFIX\fP and \fB$INCSUFFIX\fP +to the beginning and end +of each directory in \fB$F95PATH\fP. + +.IP F95PATH +The list of directories that the Fortran 95 compiler will search for include +directories. The implicit dependency scanner will search these +directories for include files. Don't explicitly put include directory +arguments in \fB$F95FLAGS\fP because the result will be non-portable +and the directories will not be searched by the dependency scanner. Note: +directory names in \fB$F95PATH\fP will be looked-up relative to the SConscript +directory when they are used in a command. To force +.B scons +to look-up a directory relative to the root of the source tree use #: +You only need to set \fB$F95PATH\fP if you need to define a specific +include path for Fortran 95 files. +You should normally set the \fB$FORTRANPATH\fP variable, +which specifies the include path +for the default Fortran compiler +for all Fortran versions. + +.ES +env = Environment(F95PATH='#/include') +.EE +.IP +The directory look-up can also be forced using the +.BR Dir () +function: + +.ES +include = Dir('include') +env = Environment(F95PATH=include) +.EE +.IP +The directory list will be added to command lines +through the automatically-generated +.B $_F95INCFLAGS +construction variable, +which is constructed by +appending the values of the +.BR $INCPREFIX and $INCSUFFIX +construction variables +to the beginning and end +of each directory in \fB$F95PATH\fP. +Any command lines you define that need +the F95PATH directory list should +include \fB$_F95INCFLAGS\fP: + +.ES +env = Environment(F95COM="my_compiler $_F95INCFLAGS -c -o $TARGET $SOURCE") +.EE + +.IP F95PPCOM +The command line used to compile a Fortran 95 source file to an object file +after first running the file through the C preprocessor. +Any options specified in the \fB$F95FLAGS\fP and \fB$CPPFLAGS\fP construction variables +are included on this command line. +You only need to set \fB$F95PPCOM\fP if you need to use a specific +C-preprocessor command line for Fortran 95 files. +You should normally set the \fB$FORTRANPPCOM\fP variable, +which specifies the default C-preprocessor command line +for all Fortran versions. + +.IP F95PPCOMSTR +The string displayed when a Fortran 95 source file +is compiled to an object file +after first running the file through the C preprocessor. +If this is not set, then \fB$F95PPCOM\fP or \fB$FORTRANPPCOM\fP +(the command line) is displayed. + +.IP F95PPFILESUFFIXES +The list of file extensions for which the compilation + preprocessor pass for +F95 dialect will be used. By default, this is empty + +.IP File +A function that converts a string into a File instance relative to the +target being built. + +.IP FORTRAN +The default Fortran compiler +for all versions of Fortran. + +.IP FORTRANCOM +The command line used to compile a Fortran source file to an object file. +By default, any options specified +in the \fB$FORTRANFLAGS\fP, +.BR $CPPFLAGS , +.BR $_CPPDEFFLAGS , +\fB$_FORTRANMODFLAG\fP, and +\fB$_FORTRANINCFLAGS\fP construction variables +are included on this command line. + +.IP FORTRANCOMSTR +The string displayed when a Fortran source file +is compiled to an object file. +If this is not set, then \fB$FORTRANCOM\fP +(the command line) is displayed. + +.IP FORTRANFILESUFFIXES +The list of file extensions for which the FORTRAN dialect will be used. By +default, this is ['.f', '.for', '.ftn'] + +.IP FORTRANFLAGS +General user-specified options that are passed to the Fortran compiler. +Note that this variable does +.I not +contain +.B \-I +(or similar) include or module search path options +that scons generates automatically from \fB$FORTRANPATH\fP. +See +.BR $_FORTRANINCFLAGS\fP and \fB$_FORTRANMODFLAG , +below, +for the variables that expand those options. + +.IP _FORTRANINCFLAGS +An automatically-generated construction variable +containing the Fortran compiler command-line options +for specifying directories to be searched for include +files and module files. +The value of \fB$_FORTRANINCFLAGS\fP is created +by prepending/appending \fB$INCPREFIX\fP and \fB$INCSUFFIX\fP +to the beginning and end +of each directory in \fB$FORTRANPATH\fP. + +.IP FORTRANMODDIR +Directory location where the Fortran compiler should place +any module files it generates. This variable is empty, by default. Some +Fortran compilers will internally append this directory in the search path +for module files, as well. + +.IP FORTRANMODDIRPREFIX +The prefix used to specify a module directory on the Fortran compiler command +line. +This will be appended to the beginning of the directory +in the \fB$FORTRANMODDIR\fP construction variables +when the \fB$_FORTRANMODFLAG\fP variables is automatically generated. + +.IP FORTRANMODDIRSUFFIX +The suffix used to specify a module directory on the Fortran compiler command +line. +This will be appended to the beginning of the directory +in the \fB$FORTRANMODDIR\fP construction variables +when the \fB$_FORTRANMODFLAG\fP variables is automatically generated. + +.IP _FORTRANMODFLAG +An automatically-generated construction variable +containing the Fortran compiler command-line option +for specifying the directory location where the Fortran +compiler should place any module files that happen to get +generated during compilation. +The value of \fB$_FORTRANMODFLAG\fP is created +by prepending/appending \fB$FORTRANMODDIRPREFIX\fP and +.B $FORTRANMODDIRSUFFIX +to the beginning and end of the directory in \fB$FORTRANMODDIR\fP. + +.IP FORTRANMODPREFIX +The module file prefix used by the Fortran compiler. SCons assumes that +the Fortran compiler follows the quasi-standard naming convention for +module files of +.BR module_name.mod . +As a result, this variable is left empty, by default. For situations in +which the compiler does not necessarily follow the normal convention, +the user may use this variable. Its value will be appended to every +module file name as scons attempts to resolve dependencies. + +.IP FORTRANMODSUFFIX +The module file suffix used by the Fortran compiler. SCons assumes that +the Fortran compiler follows the quasi-standard naming convention for +module files of +.BR module_name.mod . +As a result, this variable is set to ".mod", by default. For situations +in which the compiler does not necessarily follow the normal convention, +the user may use this variable. Its value will be appended to every +module file name as scons attempts to resolve dependencies. + +.IP FORTRANPATH +The list of directories that the Fortran compiler will search for +include files and (for some compilers) module files. The Fortran implicit +dependency scanner will search these directories for include files (but +not module files since they are autogenerated and, as such, may not +actually exist at the time the scan takes place). Don't explicitly put +include directory arguments in FORTRANFLAGS because the result will be +non-portable and the directories will not be searched by the dependency +scanner. Note: directory names in FORTRANPATH will be looked-up relative +to the SConscript directory when they are used in a command. To force +.B scons +to look-up a directory relative to the root of the source tree use #: + +.ES +env = Environment(FORTRANPATH='#/include') +.EE +.IP +The directory look-up can also be forced using the +.BR Dir () +function: + +.ES +include = Dir('include') +env = Environment(FORTRANPATH=include) +.EE +.IP +The directory list will be added to command lines +through the automatically-generated +.B $_FORTRANINCFLAGS +construction variable, +which is constructed by +appending the values of the +.BR $INCPREFIX and $INCSUFFIX +construction variables +to the beginning and end +of each directory in \fB$FORTRANPATH\fP. +Any command lines you define that need +the FORTRANPATH directory list should +include \fB$_FORTRANINCFLAGS\fP: + +.ES +env = Environment(FORTRANCOM="my_compiler $_FORTRANINCFLAGS -c -o $TARGET $SOURCE") +.EE + +.IP FORTRANPPCOM +The command line used to compile a Fortran source file to an object file +after first running the file through the C preprocessor. +By default, any options specified in the \fB$FORTRANFLAGS\fP, +.BR $CPPFLAGS , +.BR $_CPPDEFFLAGS , +\fB$_FORTRANMODFLAG\fP, and +.B $_FORTRANINCFLAGS +construction variables are included on this command line. + +.IP FORTRANPPCOMSTR +The string displayed when a Fortran source file +is compiled to an object file +after first running the file through the C preprocessor. +If this is not set, then \fB$FORTRANPPCOM\fP +(the command line) is displayed. + +.IP FORTRANPPFILESUFFIXES +The list of file extensions for which the compilation + preprocessor pass for +FORTRAN dialect will be used. By default, this is ['.fpp', '.FPP'] + +.IP FORTRANSUFFIXES +The list of suffixes of files that will be scanned +for Fortran implicit dependencies +(INCLUDE lines and USE statements). +The default list is: + +.ES +[".f", ".F", ".for", ".FOR", ".ftn", ".FTN", ".fpp", ".FPP", +".f77", ".F77", ".f90", ".F90", ".f95", ".F95"] +.EE + +.IP FRAMEWORKPATH +On Mac OS X with gcc, +a list containing the paths to search for frameworks. +Used by the compiler to find framework-style includes like +#include . +Used by the linker to find user-specified frameworks when linking (see +.BR $FRAMEWORKS ). +For example: + +.ES + env.AppendUnique(FRAMEWORKPATH='#myframeworkdir') +.EE +.IP +will add + +.ES + ... -Fmyframeworkdir +.EE +.IP +to the compiler and linker command lines. + +.IP _FRAMEWORKPATH +On Mac OS X with gcc, an automatically-generated construction variable +containing the linker command-line options corresponding to +.BR $FRAMEWORKPATH . + +.IP FRAMEWORKPATHPREFIX +On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries. +(see \fB$FRAMEWORKPATH\fP). +The default value is +.BR \-F . + +.IP FRAMEWORKPREFIX +On Mac OS X with gcc, +the prefix to be used for linking in frameworks +(see \fB$FRAMEWORKS\fP). +The default value is +.BR \-framework . + +.IP _FRAMEWORKS +On Mac OS X with gcc, +an automatically-generated construction variable +containing the linker command-line options +for linking with FRAMEWORKS. + +.IP FRAMEWORKS +On Mac OS X with gcc, a list of the framework names to be linked into a +program or shared library or bundle. +The default value is the empty list. +For example: + +.ES + env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration')) +.EE +.IP + +.IP FRAMEWORKSFLAGS +On Mac OS X with gcc, +general user-supplied frameworks options to be added at +the end of a command +line building a loadable module. +(This has been largely superseded by +the \fB$FRAMEWORKPATH\fP, \fB$FRAMEWORKPATHPREFIX\fP, +\fB$FRAMEWORKPREFIX\fP and \fB$FRAMEWORKS\fP variables +described above.) + +.IP GS +The Ghostscript program used to convert PostScript to PDF files. + +.IP GSCOM +The Ghostscript command line used to convert PostScript to PDF files. + +.IP GSCOMSTR +The string displayed when +Ghostscript is used to convert +a PostScript file to a PDF file. +If this is not set, then \fB$GSCOM\fP (the command line) is displayed. + +.IP GSFLAGS +General options passed to the Ghostscript program +when converting PostScript to PDF files. + +.IP HOST_ARCH +Sets the host architecture for Visual Studio compiler. If not set, +default to the detected host architecture: note that this may depend +on the python you are using. +This variable must be passed as an argument to the Environment() +constructor; setting it later has no effect. + +Valid values are the same as for \fB$TARGET_ARCH\fP. + +This is currently only used on Windows, but in the future it will be +used on other OSes as well. + +.IP HOST_OS + The name of the host operating system used to create the Environment. + If a platform is specified when creating the Environment, then + that Platform's logic will handle setting this value. + This value is immutable, and should not be changed by the user after + the Environment is initialized. + Currently only set for Win32. + +.IP IDLSUFFIXES +The list of suffixes of files that will be scanned +for IDL implicit dependencies +(#include or import lines). +The default list is: + +.ES +[".idl", ".IDL"] +.EE + +.IP IMPLICIT_COMMAND_DEPENDENCIES +Controls whether or not SCons will +add implicit dependencies for the commands +executed to build targets. + +By default, SCons will add +to each target +an implicit dependency on the command +represented by the first argument on any +command line it executes. +The specific file for the dependency is +found by searching the +.I PATH +variable in the +.I ENV +environment used to execute the command. + +If the construction variable +.B $IMPLICIT_COMMAND_DEPENDENCIES +is set to a false value +.RB ( None , +.BR False , +.BR 0 , +etc.), +then the implicit dependency will +not be added to the targets +built with that construction environment. + +.ES +env = Environment(IMPLICIT_COMMAND_DEPENDENCIES = 0) +.EE + +.IP INCPREFIX +The prefix used to specify an include directory on the C compiler command +line. +This will be appended to the beginning of each directory +in the \fB$CPPPATH\fP and \fB$FORTRANPATH\fP construction variables +when the \fB$_CPPINCFLAGS\fP and \fB$_FORTRANINCFLAGS\fP +variables are automatically generated. + +.IP INCSUFFIX +The suffix used to specify an include directory on the C compiler command +line. +This will be appended to the end of each directory +in the \fB$CPPPATH\fP and \fB$FORTRANPATH\fP construction variables +when the \fB$_CPPINCFLAGS\fP and \fB$_FORTRANINCFLAGS\fP +variables are automatically generated. + +.IP INSTALL +A function to be called to install a file into a +destination file name. +The default function copies the file into the destination +(and sets the destination file's mode and permission bits +to match the source file's). +The function takes the following arguments: + +.ES +def install(dest, source, env): +.EE +.IP +.I dest +is the path name of the destination file. +.I source +is the path name of the source file. +.I env +is the construction environment +(a dictionary of construction values) +in force for this file installation. + +.IP INSTALLSTR +The string displayed when a file is +installed into a destination file name. +The default is: +.ES +Install file: "$SOURCE" as "$TARGET" +.EE + +.IP INTEL_C_COMPILER_VERSION +Set by the "intelc" Tool +to the major version number of the Intel C compiler +selected for use. + +.IP JAR +The Java archive tool. + +.IP JARCHDIR +The directory to which the Java archive tool should change +(using the +.B \-C +option). + +.IP JARCOM +The command line used to call the Java archive tool. + +.IP JARCOMSTR +The string displayed when the Java archive tool +is called +If this is not set, then \fB$JARCOM\fP (the command line) is displayed. + +.ES +env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") +.EE + +.IP JARFLAGS +General options passed to the Java archive tool. +By default this is set to +.B cf +to create the necessary +.B jar +file. + +.IP JARSUFFIX +The suffix for Java archives: +.B .jar +by default. + +.IP JAVABOOTCLASSPATH +Specifies the list of directories that +will be added to the +&javac; command line +via the \fB\-bootclasspath\fP option. +The individual directory names will be +separated by the operating system's path separate character +(\fB:\fP on UNIX/Linux/POSIX, +\fB;\fP on Windows). + +.IP JAVAC +The Java compiler. + +.IP JAVACCOM +The command line used to compile a directory tree containing +Java source files to +corresponding Java class files. +Any options specified in the \fB$JAVACFLAGS\fP construction variable +are included on this command line. + +.IP JAVACCOMSTR +The string displayed when compiling +a directory tree of Java source files to +corresponding Java class files. +If this is not set, then \fB$JAVACCOM\fP (the command line) is displayed. + +.ES +env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") +.EE + +.IP JAVACFLAGS +General options that are passed to the Java compiler. + +.IP JAVACLASSDIR +The directory in which Java class files may be found. +This is stripped from the beginning of any Java .class +file names supplied to the +.B JavaH +builder. + +.IP JAVACLASSPATH +Specifies the list of directories that +will be searched for Java +\fB.class\fP file. +The directories in this list will be added to the +&javac; and &javah; command lines +via the \fB\-classpath\fP option. +The individual directory names will be +separated by the operating system's path separate character +(\fB:\fP on UNIX/Linux/POSIX, +\fB;\fP on Windows). + +Note that this currently just adds the specified +directory via the \fB\-classpath\fP option. +&SCons; does not currently search the +\fB$JAVACLASSPATH\fP directories for dependency +\fB.class\fP files. + +.IP JAVACLASSSUFFIX +The suffix for Java class files; +.B .class +by default. + +.IP JAVAH +The Java generator for C header and stub files. + +.IP JAVAHCOM +The command line used to generate C header and stub files +from Java classes. +Any options specified in the \fB$JAVAHFLAGS\fP construction variable +are included on this command line. + +.IP JAVAHCOMSTR +The string displayed when C header and stub files +are generated from Java classes. +If this is not set, then \fB$JAVAHCOM\fP (the command line) is displayed. + +.ES +env = Environment(JAVAHCOMSTR = "Generating header/stub file(s) $TARGETS from $SOURCES") +.EE + +.IP JAVAHFLAGS +General options passed to the C header and stub file generator +for Java classes. + +.IP JAVASOURCEPATH +Specifies the list of directories that +will be searched for input +\fB.java\fP file. +The directories in this list will be added to the +&javac; command line +via the \fB\-sourcepath\fP option. +The individual directory names will be +separated by the operating system's path separate character +(\fB:\fP on UNIX/Linux/POSIX, +\fB;\fP on Windows). + +Note that this currently just adds the specified +directory via the \fB\-sourcepath\fP option. +&SCons; does not currently search the +\fB$JAVASOURCEPATH\fP directories for dependency +\fB.java\fP files. + +.IP JAVASUFFIX +The suffix for Java files; +.B .java +by default. + +.IP JAVAVERSION +Specifies the Java version being used by the \fBJava\fP() builder. +This is \fInot\fP currently used to select one +version of the Java compiler vs. another. +Instead, you should set this to specify the version of Java +supported by your &javac; compiler. +The default is \fB1.4\fP. + +This is sometimes necessary because +Java 1.5 changed the file names that are created +for nested anonymous inner classes, +which can cause a mismatch with the files +that &SCons; expects will be generated by the &javac; compiler. +Setting \fB$JAVAVERSION\fP to \fB1.5\fP +(or \fB1.6\fP, as appropriate) +can make &SCons; realize that a Java 1.5 or 1.6 +build is actually up to date. + +.IP LATEX +The LaTeX structured formatter and typesetter. + +.IP LATEXCOM +The command line used to call the LaTeX structured formatter and typesetter. + +.IP LATEXCOMSTR +The string displayed when calling +the LaTeX structured formatter and typesetter. +If this is not set, then \fB$LATEXCOM\fP (the command line) is displayed. + +.ES +env = Environment(LATEXCOMSTR = "Building $TARGET from LaTeX input $SOURCES") +.EE + +.IP LATEXFLAGS +General options passed to the LaTeX structured formatter and typesetter. + +.IP LATEXRETRIES +The maximum number of times that LaTeX +will be re-run if the +.B .log +generated by the \fB$LATEXCOM\fP command +indicates that there are undefined references. +The default is to try to resolve undefined references +by re-running LaTeX up to three times. + +.IP LATEXSUFFIXES +The list of suffixes of files that will be scanned +for LaTeX implicit dependencies +(\fB\\include\fP or \fB\\import\fP files). +The default list is: + +.ES +[".tex", ".ltx", ".latex"] +.EE + +.IP LDMODULE +The linker for building loadable modules. +By default, this is the same as \fB$SHLINK\fP. + +.IP LDMODULECOM +The command line for building loadable modules. +On Mac OS X, this uses the \fB$LDMODULE\fP, +\fB$LDMODULEFLAGS\fP and +\fB$FRAMEWORKSFLAGS\fP variables. +On other systems, this is the same as \fB$SHLINK\fP. + +.IP LDMODULECOMSTR +The string displayed when building loadable modules. +If this is not set, then \fB$LDMODULECOM\fP (the command line) is displayed. + +.IP LDMODULEFLAGS +General user options passed to the linker for building loadable modules. + +.IP LDMODULEPREFIX +The prefix used for loadable module file names. +On Mac OS X, this is null; +on other systems, this is +the same as \fB$SHLIBPREFIX\fP. + +.IP LDMODULESUFFIX +The suffix used for loadable module file names. +On Mac OS X, this is null; +on other systems, this is +the same as $SHLIBSUFFIX. + +.IP LEX +The lexical analyzer generator. + +.IP LEXCOM +The command line used to call the lexical analyzer generator +to generate a source file. + +.IP LEXCOMSTR +The string displayed when generating a source file +using the lexical analyzer generator. +If this is not set, then \fB$LEXCOM\fP (the command line) is displayed. + +.ES +env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES") +.EE + +.IP LEXFLAGS +General options passed to the lexical analyzer generator. + +.IP _LIBDIRFLAGS +An automatically-generated construction variable +containing the linker command-line options +for specifying directories to be searched for library. +The value of \fB$_LIBDIRFLAGS\fP is created +by appending \fB$LIBDIRPREFIX\fP and \fB$LIBDIRSUFFIX\fP +to the beginning and end +of each directory in \fB$LIBPATH\fP. + +.IP LIBDIRPREFIX +The prefix used to specify a library directory on the linker command line. +This will be appended to the beginning of each directory +in the \fB$LIBPATH\fP construction variable +when the \fB$_LIBDIRFLAGS\fP variable is automatically generated. + +.IP LIBDIRSUFFIX +The suffix used to specify a library directory on the linker command line. +This will be appended to the end of each directory +in the \fB$LIBPATH\fP construction variable +when the \fB$_LIBDIRFLAGS\fP variable is automatically generated. + +.IP LIBEMITTER +TODO + +.IP _LIBFLAGS +An automatically-generated construction variable +containing the linker command-line options +for specifying libraries to be linked with the resulting target. +The value of \fB$_LIBFLAGS\fP is created +by appending \fB$LIBLINKPREFIX\fP and \fB$LIBLINKSUFFIX\fP +to the beginning and end +of each filename in \fB$LIBS\fP. + +.IP LIBLINKPREFIX +The prefix used to specify a library to link on the linker command line. +This will be appended to the beginning of each library +in the \fB$LIBS\fP construction variable +when the \fB$_LIBFLAGS\fP variable is automatically generated. + +.IP LIBLINKSUFFIX +The suffix used to specify a library to link on the linker command line. +This will be appended to the end of each library +in the \fB$LIBS\fP construction variable +when the \fB$_LIBFLAGS\fP variable is automatically generated. + +.IP LIBPATH +The list of directories that will be searched for libraries. +The implicit dependency scanner will search these +directories for include files. Don't explicitly put include directory +arguments in \fB$LINKFLAGS\fP or \fB$SHLINKFLAGS\fP +because the result will be non-portable +and the directories will not be searched by the dependency scanner. Note: +directory names in LIBPATH will be looked-up relative to the SConscript +directory when they are used in a command. To force +.B scons +to look-up a directory relative to the root of the source tree use #: + +.ES +env = Environment(LIBPATH='#/libs') +.EE +.IP +The directory look-up can also be forced using the +.BR Dir () +function: + +.ES +libs = Dir('libs') +env = Environment(LIBPATH=libs) +.EE +.IP +The directory list will be added to command lines +through the automatically-generated +.B $_LIBDIRFLAGS +construction variable, +which is constructed by +appending the values of the +.BR $LIBDIRPREFIX and $LIBDIRSUFFIX +construction variables +to the beginning and end +of each directory in \fB$LIBPATH\fP. +Any command lines you define that need +the LIBPATH directory list should +include \fB$_LIBDIRFLAGS\fP: + +.ES +env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE") +.EE + +.IP LIBPREFIX +The prefix used for (static) library file names. +A default value is set for each platform +(posix, win32, os2, etc.), +but the value is overridden by individual tools +(ar, mslib, sgiar, sunar, tlib, etc.) +to reflect the names of the libraries they create. + +.IP LIBPREFIXES +A list of all legal prefixes for library file names. +When searching for library dependencies, +SCons will look for files with these prefixes, +the base library name, +and suffixes in the \fB$LIBSUFFIXES\fP list. + +.IP LIBS +A list of one or more libraries +that will be linked with +any executable programs +created by this environment. + +The library list will be added to command lines +through the automatically-generated +.B $_LIBFLAGS +construction variable, +which is constructed by +appending the values of the +.BR $LIBLINKPREFIX and $LIBLINKSUFFIX +construction variables +to the beginning and end +of each filename in \fB$LIBS\fP. +Any command lines you define that need +the LIBS library list should +include \fB$_LIBFLAGS\fP: + +.ES +env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE") +.EE +.IP +If you add a +File +object to the +.B $LIBS +list, the name of that file will be added to +.BR $_LIBFLAGS , +and thus the link line, as is, without +.B $LIBLINKPREFIX +or +.BR $LIBLINKSUFFIX . +For example: + +.ES +env.Append(LIBS=File('/tmp/mylib.so')) +.EE +.IP +In all cases, scons will add dependencies from the executable program to +all the libraries in this list. + +.IP LIBSUFFIX +The suffix used for (static) library file names. +A default value is set for each platform +(posix, win32, os2, etc.), +but the value is overridden by individual tools +(ar, mslib, sgiar, sunar, tlib, etc.) +to reflect the names of the libraries they create. + +.IP LIBSUFFIXES +A list of all legal suffixes for library file names. +When searching for library dependencies, +SCons will look for files with prefixes, in the \fB$LIBPREFIXES\fP list, +the base library name, +and these suffixes. + +.IP LICENSE +The abbreviated name of the license under which +this project is released (gpl, lpgl, bsd etc.). +See http://www.opensource.org/licenses/alphabetical +for a list of license names. + +.IP LINESEPARATOR +The separator used by the \fBSubstfile\fP() and \fBTextfile\fP() builders. +This value is used between sources when constructing the target. +It defaults to the current system line separator. + +.IP LINK +The linker. + +.IP LINKCOM +The command line used to link object files into an executable. + +.IP LINKCOMSTR +The string displayed when object files +are linked into an executable. +If this is not set, then \fB$LINKCOM\fP (the command line) is displayed. + +.ES +env = Environment(LINKCOMSTR = "Linking $TARGET") +.EE + +.IP LINKFLAGS +General user options passed to the linker. +Note that this variable should +.I not +contain +.B \-l +(or similar) options for linking with the libraries listed in \fB$LIBS\fP, +nor +.B \-L +(or similar) library search path options +that scons generates automatically from \fB$LIBPATH\fP. +See +.B $_LIBFLAGS +above, +for the variable that expands to library-link options, +and +.B $_LIBDIRFLAGS +above, +for the variable that expands to library search path options. + +.IP M4 +The M4 macro preprocessor. + +.IP M4COM +The command line used to pass files through the M4 macro preprocessor. + +.IP M4COMSTR +The string displayed when +a file is passed through the M4 macro preprocessor. +If this is not set, then \fB$M4COM\fP (the command line) is displayed. + +.IP M4FLAGS +General options passed to the M4 macro preprocessor. + +.IP MAKEINDEX +The makeindex generator for the TeX formatter and typesetter and the +LaTeX structured formatter and typesetter. + +.IP MAKEINDEXCOM +The command line used to call the makeindex generator for the +TeX formatter and typesetter and the LaTeX structured formatter and +typesetter. + +.IP MAKEINDEXCOMSTR +The string displayed when calling the makeindex generator for the +TeX formatter and typesetter +and the LaTeX structured formatter and typesetter. +If this is not set, then \fB$MAKEINDEXCOM\fP (the command line) is displayed. + +.IP MAKEINDEXFLAGS +General options passed to the makeindex generator for the TeX formatter +and typesetter and the LaTeX structured formatter and typesetter. + +.IP MAXLINELENGTH +The maximum number of characters allowed on an external command line. +On Win32 systems, +link lines longer than this many characters +are linked via a temporary file name. + +.IP MIDL +The Microsoft IDL compiler. + +.IP MIDLCOM +The command line used to pass files to the Microsoft IDL compiler. + +.IP MIDLCOMSTR +The string displayed when +the Microsoft IDL copmiler is called. +If this is not set, then \fB$MIDLCOM\fP (the command line) is displayed. + +.IP MIDLFLAGS +General options passed to the Microsoft IDL compiler. + +.IP MSSDK_DIR +The directory containing the Microsoft SDK +(either Platform SDK or Windows SDK) +to be used for compilation. + +.IP MSSDK_VERSION +The version string of the Microsoft SDK +(either Platform SDK or Windows SDK) +to be used for compilation. +Supported versions include +.BR 6.1 , +.BR 6.0A , +.BR 6.0 , +.B 2003R2 +and +.BR 2003R1 . + +.IP MSVC_BATCH +When set to any true value, +specifies that SCons should batch +compilation of object files +when calling the Microsoft Visual C/C++ compiler. +All compilations of source files from the same source directory +that generate target files in a same output directory +and were configured in SCons using the same construction environment +will be built in a single call to the compiler. +Only source files that have changed since their +object files were built will be passed to each compiler invocation +(via the \fB$CHANGED_SOURCES\fP construction variable). +Any compilations where the object (target) file base name +(minus the \fB.obj\fP) +does not match the source file base name +will be compiled separately. + +.IP MSVC_USE_SCRIPT +Use a batch script to set up Microsoft Visual Studio compiler + +.BR $MSVC_USE_SCRIPT\fP overrides \fB$MSVC_VERSION\fP and \fB$TARGET_ARCH . +If set to the name of a Visual Studio .bat file (e.g. vcvars.bat), +SCons will run that bat file and extract the relevant variables from +the result (typically %INCLUDE%, %LIB%, and %PATH%). Setting +MSVC_USE_SCRIPT to None bypasses the Visual Studio autodetection +entirely; use this if you are running SCons in a Visual Studio cmd +window and importing the shell's environment variables. + +.IP MSVC_VERSION +Sets the preferred version of Microsoft Visual C/C++ to use. + +If \fB$MSVC_VERSION\fP is not set, SCons will (by default) select the +latest version of Visual C/C++ installed on your system. If the +specified version isn't installed, tool initialization will fail. +This variable must be passed as an argument to the Environment() +constructor; setting it later has no effect. Set it to an unexpected +value (e.g. "XXX") to see the valid values on your system. + +.IP MSVS +When the Microsoft Visual Studio tools are initialized, they set up +this dictionary with the following keys: + +.BR VERSION : +the version of MSVS being used (can be set via +.BR $MSVS_VERSION ) + +.BR VERSIONS : +the available versions of MSVS installed + +.BR VCINSTALLDIR : +installed directory of Visual C++ + +.BR VSINSTALLDIR : +installed directory of Visual Studio + +.BR FRAMEWORKDIR : +installed directory of the .NET framework + +.BR FRAMEWORKVERSIONS : +list of installed versions of the .NET framework, sorted latest to oldest. + +.BR FRAMEWORKVERSION : +latest installed version of the .NET framework + +.BR FRAMEWORKSDKDIR : +installed location of the .NET SDK. + +.BR PLATFORMSDKDIR : +installed location of the Platform SDK. + +.BR PLATFORMSDK_MODULES : +dictionary of installed Platform SDK modules, +where the dictionary keys are keywords for the various modules, and +the values are 2-tuples where the first is the release date, and the +second is the version number. + +If a value isn't set, it wasn't available in the registry. + +.IP MSVS_ARCH +Sets the architecture for which the generated project(s) should build. + +The default value is \fBx86\fP. +\fBamd64\fP is also supported +by &SCons; for some Visual Studio versions. +Trying to set \fB$MSVS_ARCH\fP to an architecture that's not +supported for a given Visual Studio version +will generate an error. + +.IP MSVS_PROJECT_GUID +The string +placed in a generated Microsoft Visual Studio project file +as the value of the +.B ProjectGUID +attribute. +There is no default value. If not defined, a new GUID is generated. + +.IP MSVS_SCC_AUX_PATH +The path name +placed in a generated Microsoft Visual Studio project file +as the value of the +.B SccAuxPath +attribute +if the +.B MSVS_SCC_PROVIDER +construction variable is also set. +There is no default value. + +.IP MSVS_SCC_CONNECTION_ROOT +The root path of projects in your SCC workspace, i.e the path under which +all project and solution files will be generated. It is used as a +reference path from which the relative paths of the generated +Microsoft Visual Studio project and solution files are computed. +The relative project file path is placed as the value of the +.B SccLocalPath +attribute +of the project file +and as the values of the +.B SccProjectFilePathRelativizedFromConnection[i] +(where [i] ranges from 0 to the number of projects in the solution) +attributes of the +.B GlobalSection(SourceCodeControl) +section of the Microsoft Visual Studio solution file. +Similarly the relative solution file path is placed as the values of the +.B SccLocalPath[i] +(where [i] ranges from 0 to the number of projects in the solution) +attributes of the +.B GlobalSection(SourceCodeControl) +section of the Microsoft Visual Studio solution file. +This is used only +if the +.B MSVS_SCC_PROVIDER +construction variable is also set. +The default value is the current working directory. + +.IP MSVS_SCC_PROJECT_NAME +The project name +placed in a generated Microsoft Visual Studio project file +as the value of the +.B SccProjectName +attribute +if the +.B MSVS_SCC_PROVIDER +construction variable is also set. +In this case the string is also placed in the +.B SccProjectName0 +attribute of the +.B GlobalSection(SourceCodeControl) +section of the Microsoft Visual Studio solution file. +There is no default value. + +.IP MSVS_SCC_PROVIDER +The string +placed in a generated Microsoft Visual Studio project file +as the value of the +.B SccProvider +attribute. +The string is also placed in the +.B SccProvider0 +attribute of the +.B GlobalSection(SourceCodeControl) +section of the Microsoft Visual Studio solution file. +There is no default value. + +.IP MSVS_VERSION +Sets the preferred version of Microsoft Visual Studio to use. + +If \fB$MSVS_VERSION\fP is not set, +&SCons; will (by default) select the latest version +of Visual Studio installed on your system. +So, if you have version 6 and version 7 (MSVS .NET) installed, +it will prefer version 7. +You can override this by +specifying the +.B MSVS_VERSION +variable in the Environment initialization, setting it to the +appropriate version ('6.0' or '7.0', for example). +If the specified version isn't installed, +tool initialization will fail. + +This is obsolete: use \fB$MSVC_VERSION\fP instead. If \fB$MSVS_VERSION\fP is set and +.BR $MSVC_VERSION\fP is not, \fB$MSVC_VERSION\fP will be set automatically to \fB$MSVS_VERSION . +If both are set to different values, scons will raise an error. + +.IP MSVSBUILDCOM +The build command line placed in +a generated Microsoft Visual Studio project file. +The default is to have Visual Studio invoke SCons with any specified +build targets. + +.IP MSVSCLEANCOM +The clean command line placed in +a generated Microsoft Visual Studio project file. +The default is to have Visual Studio invoke SCons with the -c option +to remove any specified targets. + +.IP MSVSENCODING +The encoding string placed in +a generated Microsoft Visual Studio project file. +The default is encoding +.BR Windows-1252 . + +.IP MSVSPROJECTCOM +The action used to generate Microsoft Visual Studio project files. + +.IP MSVSPROJECTSUFFIX +The suffix used for Microsoft Visual Studio project (DSP) files. +The default value is +.B .vcproj +when using Visual Studio version 7.x (.NET) +or later version, +and +.B .dsp +when using earlier versions of Visual Studio. + +.IP MSVSREBUILDCOM +The rebuild command line placed in +a generated Microsoft Visual Studio project file. +The default is to have Visual Studio invoke SCons with any specified +rebuild targets. + +.IP MSVSSCONS +The SCons used in generated Microsoft Visual Studio project files. +The default is the version of SCons being +used to generate the project file. + +.IP MSVSSCONSCOM +The default SCons command used in generated Microsoft Visual Studio +project files. + +.IP MSVSSCONSCRIPT +The sconscript file +(that is, +.B SConstruct +or +.B SConscript +file) +that will be invoked by Visual Studio +project files +(through the +.B $MSVSSCONSCOM +variable). +The default is the same sconscript file +that contains the call to +.BR MSVSProject () +to build the project file. + +.IP MSVSSCONSFLAGS +The SCons flags used in generated Microsoft Visual Studio +project files. + +.IP MSVSSOLUTIONCOM +The action used to generate Microsoft Visual Studio solution files. + +.IP MSVSSOLUTIONSUFFIX +The suffix used for Microsoft Visual Studio solution (DSW) files. +The default value is +.B .sln +when using Visual Studio version 7.x (.NET), +and +.B .dsw +when using earlier versions of Visual Studio. + +.IP MT +The program used on Windows systems to embed manifests into DLLs and EXEs. +See also \fB$WINDOWS_EMBED_MANIFEST\fP. + +.IP MTEXECOM +The Windows command line used to embed manifests into executables. +See also \fB$MTSHLIBCOM\fP. + +.IP MTFLAGS +Flags passed to the \fB$MT\fP manifest embedding program (Windows only). + +.IP MTSHLIBCOM +The Windows command line used to embed manifests into shared libraries (DLLs). +See also \fB$MTEXECOM\fP. + +.IP MWCW_VERSION +The version number of the MetroWerks CodeWarrior C compiler +to be used. + +.IP MWCW_VERSIONS +A list of installed versions of the MetroWerks CodeWarrior C compiler +on this system. + +.IP NAME +Specfies the name of the project to package. + +.IP no_import_lib +When set to non-zero, +suppresses creation of a corresponding Windows static import lib by the +.B SharedLibrary +builder when used with +MinGW, Microsoft Visual Studio or Metrowerks. +This also suppresses creation +of an export (.exp) file +when using Microsoft Visual Studio. + +.IP OBJPREFIX +The prefix used for (static) object file names. + +.IP OBJSUFFIX +The suffix used for (static) object file names. + +.IP P4 +The Perforce executable. + +.IP P4COM +The command line used to +fetch source files from Perforce. + +.IP P4COMSTR +The string displayed when +fetching a source file from Perforce. +If this is not set, then \fB$P4COM\fP (the command line) is displayed. + +.IP P4FLAGS +General options that are passed to Perforce. + +.IP PACKAGEROOT +Specifies the directory where all files in resulting archive will be +placed if applicable. The default value is "$NAME-$VERSION". + +.IP PACKAGETYPE +Selects the package type to build. Currently these are available: + + * msi - Microsoft Installer + * rpm - Redhat Package Manger + * ipkg - Itsy Package Management System + * tarbz2 - compressed tar + * targz - compressed tar + * zip - zip file + * src_tarbz2 - compressed tar source + * src_targz - compressed tar source + * src_zip - zip file source + +This may be overridden with the "package_type" command line option. + +.IP PACKAGEVERSION +The version of the package (not the underlying project). +This is currently only used by the rpm packager +and should reflect changes in the packaging, +not the underlying project code itself. + +.IP PCH +The Microsoft Visual C++ precompiled header that will be used when compiling +object files. This variable is ignored by tools other than Microsoft Visual C++. +When this variable is +defined SCons will add options to the compiler command line to +cause it to use the precompiled header, and will also set up the +dependencies for the PCH file. +Example: + +.ES +env['PCH'] = 'StdAfx.pch' +.EE + +.IP PCHCOM +The command line used by the +.BR PCH () +builder to generated a precompiled header. + +.IP PCHCOMSTR +The string displayed when generating a precompiled header. +If this is not set, then \fB$PCHCOM\fP (the command line) is displayed. + +.IP PCHPDBFLAGS +A construction variable that, when expanded, +adds the \fB/yD\fP flag to the command line +only if the \fB$PDB\fP construction variable is set. + +.IP PCHSTOP +This variable specifies how much of a source file is precompiled. This +variable is ignored by tools other than Microsoft Visual C++, or when +the PCH variable is not being used. When this variable is define it +must be a string that is the name of the header that +is included at the end of the precompiled portion of the source files, or +the empty string if the "#pragma hrdstop" construct is being used: + +.ES +env['PCHSTOP'] = 'StdAfx.h' +.EE + +.IP PDB +The Microsoft Visual C++ PDB file that will store debugging information for +object files, shared libraries, and programs. This variable is ignored by +tools other than Microsoft Visual C++. +When this variable is +defined SCons will add options to the compiler and linker command line to +cause them to generate external debugging information, and will also set up the +dependencies for the PDB file. +Example: + +.ES +env['PDB'] = 'hello.pdb' +.EE +.IP +The Visual C++ compiler switch that SCons uses by default +to generate PDB information is \fB/Z7\fP. +This works correctly with parallel (\fB\-j\fP) builds +because it embeds the debug information in the intermediate object files, +as opposed to sharing a single PDB file between multiple object files. +This is also the only way to get debug information +embedded into a static library. +Using the \fB/Zi\fP instead may yield improved +link-time performance, +although parallel builds will no longer work. +You can generate PDB files with the \fB/Zi\fP +switch by overriding the default \fB$CCPDBFLAGS\fP variable; +see the entry for that variable for specific examples. + +.IP PDFCOM +A deprecated synonym for \fB$DVIPDFCOM\fP. + +.IP PDFLATEX +The &pdflatex; utility. + +.IP PDFLATEXCOM +The command line used to call the &pdflatex; utility. + +.IP PDFLATEXCOMSTR +The string displayed when calling the &pdflatex; utility. +If this is not set, then \fB$PDFLATEXCOM\fP (the command line) is displayed. + +.ES +env = Environment(PDFLATEX;COMSTR = "Building $TARGET from LaTeX input $SOURCES") +.EE + +.IP PDFLATEXFLAGS +General options passed to the &pdflatex; utility. + +.IP PDFPREFIX +The prefix used for PDF file names. + +.IP PDFSUFFIX +The suffix used for PDF file names. + +.IP PDFTEX +The &pdftex; utility. + +.IP PDFTEXCOM +The command line used to call the &pdftex; utility. + +.IP PDFTEXCOMSTR +The string displayed when calling the &pdftex; utility. +If this is not set, then \fB$PDFTEXCOM\fP (the command line) is displayed. + +.ES +env = Environment(PDFTEXCOMSTR = "Building $TARGET from TeX input $SOURCES") +.EE + +.IP PDFTEXFLAGS +General options passed to the &pdftex; utility. + +.IP PKGCHK +On Solaris systems, +the package-checking program that will +be used (along with \fB$PKGINFO\fP) +to look for installed versions of +the Sun PRO C++ compiler. +The default is +.BR /usr/sbin/pgkchk . + +.IP PKGINFO +On Solaris systems, +the package information program that will +be used (along with \fB$PKGCHK\fP) +to look for installed versions of +the Sun PRO C++ compiler. +The default is +.BR pkginfo . + +.IP PLATFORM +The name of the platform used to create the Environment. If no platform is +specified when the Environment is created, +.B scons +autodetects the platform. + +.ES +env = Environment(tools = []) +if env['PLATFORM'] == 'cygwin': + Tool('mingw')(env) +else: + Tool('msvc')(env) +.EE + +.IP PRINT_CMD_LINE_FUNC +A Python function used to print the command lines as they are executed +(assuming command printing is not disabled by the +.B \-q +or +.B \-s +options or their equivalents). +The function should take four arguments: +.IR s , +the command being executed (a string), +.IR target , +the target being built (file node, list, or string name(s)), +.IR source , +the source(s) used (file node, list, or string name(s)), and +.IR env , +the environment being used. + +The function must do the printing itself. The default implementation, +used if this variable is not set or is None, is: +.ES +def print_cmd_line(s, target, source, env): + sys.stdout.write(s + "\\n") +.EE +.IP +Here's an example of a more interesting function: + +.ES +def print_cmd_line(s, target, source, env): + sys.stdout.write("Building %s -> %s...\\n" % + (' and '.join([str(x) for x in source]), + ' and '.join([str(x) for x in target]))) +env=Environment(PRINT_CMD_LINE_FUNC=print_cmd_line) +env.Program('foo', 'foo.c') +.EE +.IP +This just prints "Building \fItargetname\fP from \fIsourcename\fP..." instead +of the actual commands. +Such a function could also log the actual commands to a log file, +for example. + +.IP PROGEMITTER +TODO + +.IP PROGPREFIX +The prefix used for executable file names. + +.IP PROGSUFFIX +The suffix used for executable file names. + +.IP PSCOM +The command line used to convert TeX DVI files into a PostScript file. + +.IP PSCOMSTR +The string displayed when a TeX DVI file +is converted into a PostScript file. +If this is not set, then \fB$PSCOM\fP (the command line) is displayed. + +.IP PSPREFIX +The prefix used for PostScript file names. + +.IP PSSUFFIX +The prefix used for PostScript file names. + +.IP QT_AUTOSCAN +Turn off scanning for mocable files. Use the Moc Builder to explicitly +specify files to run moc on. + +.IP QT_BINPATH +The path where the qt binaries are installed. +The default value is '\fB$QTDIR\fP/bin'. + +.IP QT_CPPPATH +The path where the qt header files are installed. +The default value is '\fB$QTDIR\fP/include'. +Note: If you set this variable to None, +the tool won't change the \fB$CPPPATH\fP +construction variable. + +.IP QT_DEBUG +Prints lots of debugging information while scanning for moc files. + +.IP QT_LIB +Default value is 'qt'. You may want to set this to 'qt-mt'. Note: If you set +this variable to None, the tool won't change the \fB$LIBS\fP variable. + +.IP QT_LIBPATH +The path where the qt libraries are installed. +The default value is '\fB$QTDIR\fP/lib'. +Note: If you set this variable to None, +the tool won't change the \fB$LIBPATH\fP +construction variable. + +.IP QT_MOC +Default value is '\fB$QT_BINPATH\fP/moc'. + +.IP QT_MOCCXXPREFIX +Default value is ''. Prefix for moc output files, when source is a cxx file. + +.IP QT_MOCCXXSUFFIX +Default value is '.moc'. Suffix for moc output files, when source is a cxx +file. + +.IP QT_MOCFROMCXXCOM +Command to generate a moc file from a cpp file. + +.IP QT_MOCFROMCXXCOMSTR +The string displayed when generating a moc file from a cpp file. +If this is not set, then \fB$QT_MOCFROMCXXCOM\fP (the command line) is displayed. + +.IP QT_MOCFROMCXXFLAGS +Default value is '-i'. These flags are passed to moc, when moccing a +C++ file. + +.IP QT_MOCFROMHCOM +Command to generate a moc file from a header. + +.IP QT_MOCFROMHCOMSTR +The string displayed when generating a moc file from a cpp file. +If this is not set, then \fB$QT_MOCFROMHCOM\fP (the command line) is displayed. + +.IP QT_MOCFROMHFLAGS +Default value is ''. These flags are passed to moc, when moccing a header +file. + +.IP QT_MOCHPREFIX +Default value is 'moc_'. Prefix for moc output files, when source is a header. + +.IP QT_MOCHSUFFIX +Default value is '\fB$CXXFILESUFFIX\fP'. Suffix for moc output files, when source is +a header. + +.IP QT_UIC +Default value is '\fB$QT_BINPATH\fP/uic'. + +.IP QT_UICCOM +Command to generate header files from .ui files. + +.IP QT_UICCOMSTR +The string displayed when generating header files from .ui files. +If this is not set, then \fB$QT_UICCOM\fP (the command line) is displayed. + +.IP QT_UICDECLFLAGS +Default value is ''. These flags are passed to uic, when creating a a h +file from a .ui file. + +.IP QT_UICDECLPREFIX +Default value is ''. Prefix for uic generated header files. + +.IP QT_UICDECLSUFFIX +Default value is '.h'. Suffix for uic generated header files. + +.IP QT_UICIMPLFLAGS +Default value is ''. These flags are passed to uic, when creating a cxx +file from a .ui file. + +.IP QT_UICIMPLPREFIX +Default value is 'uic_'. Prefix for uic generated implementation files. + +.IP QT_UICIMPLSUFFIX +Default value is '\fB$CXXFILESUFFIX\fP'. Suffix for uic generated implementation +files. + +.IP QT_UISUFFIX +Default value is '.ui'. Suffix of designer input files. + +.IP QTDIR +The qt tool tries to take this from os.environ. +It also initializes all QT_* +construction variables listed below. +(Note that all paths are constructed +with python's os.path.join() method, +but are listed here with the '/' separator +for easier reading.) +In addition, the construction environment +variables \fB$CPPPATH\fP, +\fB$LIBPATH\fP and +\fB$LIBS\fP may be modified +and the variables +.BR $PROGEMITTER\fP, \fB$SHLIBEMITTER and $LIBEMITTER +are modified. Because the build-performance is affected when using this tool, +you have to explicitly specify it at Environment creation: + +.ES +Environment(tools=['default','qt']) +.EE +.IP +The qt tool supports the following operations: + +.I "Automatic moc file generation from header files." +You do not have to specify moc files explicitly, the tool does it for you. +However, there are a few preconditions to do so: Your header file must have +the same filebase as your implementation file and must stay in the same +directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. You +can turn off automatic moc file generation by setting QT_AUTOSCAN to 0. +See also the corresponding +.BR Moc() () +builder method. + +.I "Automatic moc file generation from cxx files." +As stated in the qt documentation, include the moc file at the end of +the cxx file. Note that you have to include the file, which is generated +by the transformation ${QT_MOCCXXPREFIX}${QT_MOCCXXSUFFIX}, by default +.moc. A warning is generated after building the moc file, if you +do not include the correct file. If you are using VariantDir, you may +need to specify duplicate=1. You can turn off automatic moc file generation +by setting QT_AUTOSCAN to 0. See also the corresponding +.BR Moc () +builder method. + +.I "Automatic handling of .ui files." +The implementation files generated from .ui files are handled much the same +as yacc or lex files. Each .ui file given as a source of Program, Library or +SharedLibrary will generate three files, the declaration file, the +implementation file and a moc file. Because there are also generated headers, +you may need to specify duplicate=1 in calls to VariantDir. +See also the corresponding +.BR Uic () +builder method. + +.IP RANLIB +The archive indexer. + +.IP RANLIBCOM +The command line used to index a static library archive. + +.IP RANLIBCOMSTR +The string displayed when a static library archive is indexed. +If this is not set, then \fB$RANLIBCOM\fP (the command line) is displayed. + +.ES +env = Environment(RANLIBCOMSTR = "Indexing $TARGET") +.EE + +.IP RANLIBFLAGS +General options passed to the archive indexer. + +.IP RC +The resource compiler used to build +a Microsoft Visual C++ resource file. + +.IP RCCOM +The command line used to build +a Microsoft Visual C++ resource file. + +.IP RCCOMSTR +The string displayed when invoking the resource compiler +to build a Microsoft Visual C++ resource file. +If this is not set, then \fB$RCCOM\fP (the command line) is displayed. + +.IP RCFLAGS +The flags passed to the resource compiler by the RES builder. + +.IP RCINCFLAGS +An automatically-generated construction variable +containing the command-line options +for specifying directories to be searched +by the resource compiler. +The value of \fB$RCINCFLAGS\fP is created +by appending \fB$RCINCPREFIX\fP and \fB$RCINCSUFFIX\fP +to the beginning and end +of each directory in \fB$CPPPATH\fP. + +.IP RCINCPREFIX +The prefix (flag) used to specify an include directory +on the resource compiler command line. +This will be appended to the beginning of each directory +in the \fB$CPPPATH\fP construction variable +when the \fB$RCINCFLAGS\fP variable is expanded. + +.IP RCINCSUFFIX +The suffix used to specify an include directory +on the resource compiler command line. +This will be appended to the end of each directory +in the \fB$CPPPATH\fP construction variable +when the \fB$RCINCFLAGS\fP variable is expanded. + +.IP RCS +The RCS executable. +Note that this variable is not actually used +for the command to fetch source files from RCS; +see the +.B $RCS_CO +construction variable, below. + +.IP RCS_CO +The RCS "checkout" executable, +used to fetch source files from RCS. + +.IP RCS_COCOM +The command line used to +fetch (checkout) source files from RCS. + +.IP RCS_COCOMSTR +The string displayed when fetching +a source file from RCS. +If this is not set, then \fB$RCS_COCOM\fP +(the command line) is displayed. + +.IP RCS_COFLAGS +Options that are passed to the \fB$RCS_CO\fP command. + +.IP RDirs +A function that converts a string into a list of Dir instances by +searching the repositories. + +.IP REGSVR +The program used on Windows systems +to register a newly-built DLL library +whenever the \fBSharedLibrary\fP() builder +is passed a keyword argument of \fBregister=1\fP. + +.IP REGSVRCOM +The command line used on Windows systems +to register a newly-built DLL library +whenever the \fBSharedLibrary\fP() builder +is passed a keyword argument of \fBregister=1\fP. + +.IP REGSVRCOMSTR +The string displayed when registering a newly-built DLL file. +If this is not set, then \fB$REGSVRCOM\fP (the command line) is displayed. + +.IP REGSVRFLAGS +Flags passed to the DLL registration program +on Windows systems when a newly-built DLL library is registered. +By default, +this includes the \fB/s\fP +that prevents dialog boxes from popping up +and requiring user attention. + +.IP RMIC +The Java RMI stub compiler. + +.IP RMICCOM +The command line used to compile stub +and skeleton class files +from Java classes that contain RMI implementations. +Any options specified in the \fB$RMICFLAGS\fP construction variable +are included on this command line. + +.IP RMICCOMSTR +The string displayed when compiling +stub and skeleton class files +from Java classes that contain RMI implementations. +If this is not set, then \fB$RMICCOM\fP (the command line) is displayed. + +.ES +env = Environment(RMICCOMSTR = "Generating stub/skeleton class files $TARGETS from $SOURCES") +.EE + +.IP RMICFLAGS +General options passed to the Java RMI stub compiler. + +.IP _RPATH +An automatically-generated construction variable +containing the rpath flags to be used when linking +a program with shared libraries. +The value of \fB$_RPATH\fP is created +by appending \fB$RPATHPREFIX\fP and \fB$RPATHSUFFIX\fP +to the beginning and end +of each directory in \fB$RPATH\fP. + +.IP RPATH +A list of paths to search for shared libraries when running programs. +Currently only used in the GNU (gnulink), +IRIX (sgilink) and Sun (sunlink) linkers. +Ignored on platforms and toolchains that don't support it. +Note that the paths added to RPATH +are not transformed by +.B scons +in any way: if you want an absolute +path, you must make it absolute yourself. + +.IP RPATHPREFIX +The prefix used to specify a directory to be searched for +shared libraries when running programs. +This will be appended to the beginning of each directory +in the \fB$RPATH\fP construction variable +when the \fB$_RPATH\fP variable is automatically generated. + +.IP RPATHSUFFIX +The suffix used to specify a directory to be searched for +shared libraries when running programs. +This will be appended to the end of each directory +in the \fB$RPATH\fP construction variable +when the \fB$_RPATH\fP variable is automatically generated. + +.IP RPCGEN +The RPC protocol compiler. + +.IP RPCGENCLIENTFLAGS +Options passed to the RPC protocol compiler +when generating client side stubs. +These are in addition to any flags specified in the +.B $RPCGENFLAGS +construction variable. + +.IP RPCGENFLAGS +General options passed to the RPC protocol compiler. + +.IP RPCGENHEADERFLAGS +Options passed to the RPC protocol compiler +when generating a header file. +These are in addition to any flags specified in the +.B $RPCGENFLAGS +construction variable. + +.IP RPCGENSERVICEFLAGS +Options passed to the RPC protocol compiler +when generating server side stubs. +These are in addition to any flags specified in the +.B $RPCGENFLAGS +construction variable. + +.IP RPCGENXDRFLAGS +Options passed to the RPC protocol compiler +when generating XDR routines. +These are in addition to any flags specified in the +.B $RPCGENFLAGS +construction variable. + +.IP SCANNERS +A list of the available implicit dependency scanners. +New file scanners may be added by +appending to this list, +although the more flexible approach +is to associate scanners +with a specific Builder. +See the sections "Builder Objects" +and "Scanner Objects," +below, for more information. + +.IP SCCS +The SCCS executable. + +.IP SCCSCOM +The command line used to +fetch source files from SCCS. + +.IP SCCSCOMSTR +The string displayed when fetching +a source file from a CVS repository. +If this is not set, then \fB$SCCSCOM\fP +(the command line) is displayed. + +.IP SCCSFLAGS +General options that are passed to SCCS. + +.IP SCCSGETFLAGS +Options that are passed specifically to the SCCS "get" subcommand. +This can be set, for example, to +.B \-e +to check out editable files from SCCS. + +.IP SCONS_HOME +The (optional) path to the SCons library directory, +initialized from the external environment. +If set, this is used to construct a shorter and more +efficient search path in the +.B $MSVSSCONS +command line executed +from Microsoft Visual Studio project files. + +.IP SHCC +The C compiler used for generating shared-library objects. + +.IP SHCCCOM +The command line used to compile a C source file +to a shared-library object file. +Any options specified in the \fB$SHCFLAGS\fP, +\fB$SHCCFLAGS\fP and +\fB$CPPFLAGS\fP construction variables +are included on this command line. + +.IP SHCCCOMSTR +The string displayed when a C source file +is compiled to a shared object file. +If this is not set, then \fB$SHCCCOM\fP (the command line) is displayed. + +.ES +env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET") +.EE + +.IP SHCCFLAGS +Options that are passed to the C and C++ compilers +to generate shared-library objects. + +.IP SHCFLAGS +Options that are passed to the C compiler (only; not C++) +to generate shared-library objects. + +.IP SHCXX +The C++ compiler used for generating shared-library objects. + +.IP SHCXXCOM +The command line used to compile a C++ source file +to a shared-library object file. +Any options specified in the \fB$SHCXXFLAGS\fP and +\fB$CPPFLAGS\fP construction variables +are included on this command line. + +.IP SHCXXCOMSTR +The string displayed when a C++ source file +is compiled to a shared object file. +If this is not set, then \fB$SHCXXCOM\fP (the command line) is displayed. + +.ES +env = Environment(SHCXXCOMSTR = "Compiling shared object $TARGET") +.EE + +.IP SHCXXFLAGS +Options that are passed to the C++ compiler +to generate shared-library objects. + +.IP SHELL +A string naming the shell program that will be passed to the +.B $SPAWN +function. +See the +.B $SPAWN +construction variable for more information. + +.IP SHF77 +The Fortran 77 compiler used for generating shared-library objects. +You should normally set the \fB$SHFORTRAN\fP variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set \fB$SHF77\fP if you need to use a specific compiler +or compiler version for Fortran 77 files. + +.IP SHF77COM +The command line used to compile a Fortran 77 source file +to a shared-library object file. +You only need to set \fB$SHF77COM\fP if you need to use a specific +command line for Fortran 77 files. +You should normally set the \fB$SHFORTRANCOM\fP variable, +which specifies the default command line +for all Fortran versions. + +.IP SHF77COMSTR +The string displayed when a Fortran 77 source file +is compiled to a shared-library object file. +If this is not set, then \fB$SHF77COM\fP or \fB$SHFORTRANCOM\fP +(the command line) is displayed. + +.IP SHF77FLAGS +Options that are passed to the Fortran 77 compiler +to generated shared-library objects. +You only need to set \fB$SHF77FLAGS\fP if you need to define specific +user options for Fortran 77 files. +You should normally set the \fB$SHFORTRANFLAGS\fP variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. + +.IP SHF77PPCOM +The command line used to compile a Fortran 77 source file to a +shared-library object file +after first running the file through the C preprocessor. +Any options specified in the \fB$SHF77FLAGS\fP and \fB$CPPFLAGS\fP construction variables +are included on this command line. +You only need to set \fB$SHF77PPCOM\fP if you need to use a specific +C-preprocessor command line for Fortran 77 files. +You should normally set the \fB$SHFORTRANPPCOM\fP variable, +which specifies the default C-preprocessor command line +for all Fortran versions. + +.IP SHF77PPCOMSTR +The string displayed when a Fortran 77 source file +is compiled to a shared-library object file +after first running the file through the C preprocessor. +If this is not set, then \fB$SHF77PPCOM\fP or \fB$SHFORTRANPPCOM\fP +(the command line) is displayed. + +.IP SHF90 +The Fortran 90 compiler used for generating shared-library objects. +You should normally set the \fB$SHFORTRAN\fP variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set \fB$SHF90\fP if you need to use a specific compiler +or compiler version for Fortran 90 files. + +.IP SHF90COM +The command line used to compile a Fortran 90 source file +to a shared-library object file. +You only need to set \fB$SHF90COM\fP if you need to use a specific +command line for Fortran 90 files. +You should normally set the \fB$SHFORTRANCOM\fP variable, +which specifies the default command line +for all Fortran versions. + +.IP SHF90COMSTR +The string displayed when a Fortran 90 source file +is compiled to a shared-library object file. +If this is not set, then \fB$SHF90COM\fP or \fB$SHFORTRANCOM\fP +(the command line) is displayed. + +.IP SHF90FLAGS +Options that are passed to the Fortran 90 compiler +to generated shared-library objects. +You only need to set \fB$SHF90FLAGS\fP if you need to define specific +user options for Fortran 90 files. +You should normally set the \fB$SHFORTRANFLAGS\fP variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. + +.IP SHF90PPCOM +The command line used to compile a Fortran 90 source file to a +shared-library object file +after first running the file through the C preprocessor. +Any options specified in the \fB$SHF90FLAGS\fP and \fB$CPPFLAGS\fP construction variables +are included on this command line. +You only need to set \fB$SHF90PPCOM\fP if you need to use a specific +C-preprocessor command line for Fortran 90 files. +You should normally set the \fB$SHFORTRANPPCOM\fP variable, +which specifies the default C-preprocessor command line +for all Fortran versions. + +.IP SHF90PPCOMSTR +The string displayed when a Fortran 90 source file +is compiled to a shared-library object file +after first running the file through the C preprocessor. +If this is not set, then \fB$SHF90PPCOM\fP or \fB$SHFORTRANPPCOM\fP +(the command line) is displayed. + +.IP SHF95 +The Fortran 95 compiler used for generating shared-library objects. +You should normally set the \fB$SHFORTRAN\fP variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set \fB$SHF95\fP if you need to use a specific compiler +or compiler version for Fortran 95 files. + +.IP SHF95COM +The command line used to compile a Fortran 95 source file +to a shared-library object file. +You only need to set \fB$SHF95COM\fP if you need to use a specific +command line for Fortran 95 files. +You should normally set the \fB$SHFORTRANCOM\fP variable, +which specifies the default command line +for all Fortran versions. + +.IP SHF95COMSTR +The string displayed when a Fortran 95 source file +is compiled to a shared-library object file. +If this is not set, then \fB$SHF95COM\fP or \fB$SHFORTRANCOM\fP +(the command line) is displayed. + +.IP SHF95FLAGS +Options that are passed to the Fortran 95 compiler +to generated shared-library objects. +You only need to set \fB$SHF95FLAGS\fP if you need to define specific +user options for Fortran 95 files. +You should normally set the \fB$SHFORTRANFLAGS\fP variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. + +.IP SHF95PPCOM +The command line used to compile a Fortran 95 source file to a +shared-library object file +after first running the file through the C preprocessor. +Any options specified in the \fB$SHF95FLAGS\fP and \fB$CPPFLAGS\fP construction variables +are included on this command line. +You only need to set \fB$SHF95PPCOM\fP if you need to use a specific +C-preprocessor command line for Fortran 95 files. +You should normally set the \fB$SHFORTRANPPCOM\fP variable, +which specifies the default C-preprocessor command line +for all Fortran versions. + +.IP SHF95PPCOMSTR +The string displayed when a Fortran 95 source file +is compiled to a shared-library object file +after first running the file through the C preprocessor. +If this is not set, then \fB$SHF95PPCOM\fP or \fB$SHFORTRANPPCOM\fP +(the command line) is displayed. + +.IP SHFORTRAN +The default Fortran compiler used for generating shared-library objects. + +.IP SHFORTRANCOM +The command line used to compile a Fortran source file +to a shared-library object file. + +.IP SHFORTRANCOMSTR +The string displayed when a Fortran source file +is compiled to a shared-library object file. +If this is not set, then \fB$SHFORTRANCOM\fP +(the command line) is displayed. + +.IP SHFORTRANFLAGS +Options that are passed to the Fortran compiler +to generate shared-library objects. + +.IP SHFORTRANPPCOM +The command line used to compile a Fortran source file to a +shared-library object file +after first running the file through the C preprocessor. +Any options specified +in the \fB$SHFORTRANFLAGS\fP and +\fB$CPPFLAGS\fP construction variables +are included on this command line. + +.IP SHFORTRANPPCOMSTR +The string displayed when a Fortran source file +is compiled to a shared-library object file +after first running the file through the C preprocessor. +If this is not set, then \fB$SHFORTRANPPCOM\fP +(the command line) is displayed. + +.IP SHLIBEMITTER +TODO + +.IP SHLIBPREFIX +The prefix used for shared library file names. + +.IP SHLIBSUFFIX +The suffix used for shared library file names. + +.IP SHLINK +The linker for programs that use shared libraries. + +.IP SHLINKCOM +The command line used to link programs using shared libraries. + +.IP SHLINKCOMSTR +The string displayed when programs using shared libraries are linked. +If this is not set, then \fB$SHLINKCOM\fP (the command line) is displayed. + +.ES +env = Environment(SHLINKCOMSTR = "Linking shared $TARGET") +.EE + +.IP SHLINKFLAGS +General user options passed to the linker for programs using shared libraries. +Note that this variable should +.I not +contain +.B \-l +(or similar) options for linking with the libraries listed in \fB$LIBS\fP, +nor +.B \-L +(or similar) include search path options +that scons generates automatically from \fB$LIBPATH\fP. +See +.B $_LIBFLAGS +above, +for the variable that expands to library-link options, +and +.B $_LIBDIRFLAGS +above, +for the variable that expands to library search path options. + +.IP SHOBJPREFIX +The prefix used for shared object file names. + +.IP SHOBJSUFFIX +The suffix used for shared object file names. + +.IP SOURCE +A reserved variable name +that may not be set or used in a construction environment. +(See "Variable Substitution," below.) + +.IP SOURCE_URL +The URL +(web address) +of the location from which the project was retrieved. +This is used to fill in the +.B Source: +field in the controlling information for Ipkg and RPM packages. + +.IP SOURCES +A reserved variable name +that may not be set or used in a construction environment. +(See "Variable Substitution," below.) + +.IP SPAWN +A command interpreter function that will be called to execute command line +strings. The function must expect the following arguments: + +.ES +def spawn(shell, escape, cmd, args, env): +.EE +.IP +.I sh +is a string naming the shell program to use. +.I escape +is a function that can be called to escape shell special characters in +the command line. +.I cmd +is the path to the command to be executed. +.I args +is the arguments to the command. +.I env +is a dictionary of the environment variables +in which the command should be executed. + +.IP SUBST_DICT +The dictionary used by the \fBSubstfile\fP() or \fBTextfile\fP() builders +for substitution values. +It can be anything acceptable to the dict() constructor, +so in addition to a dictionary, +lists of tuples are also acceptable. + +.IP SUBSTFILEPREFIX +The prefix used for \fBSubstfile\fP() file names, +the null string by default. + +.IP SUBSTFILESUFFIX +The suffix used for \fBSubstfile\fP() file names, +the null string by default. + +.IP SUMMARY +A short summary of what the project is about. +This is used to fill in the +.B Summary: +field in the controlling information for Ipkg and RPM packages, +and as the +.B Description: +field in MSI packages. + +.IP SWIG +The scripting language wrapper and interface generator. + +.IP SWIGCFILESUFFIX +The suffix that will be used for intermediate C +source files generated by +the scripting language wrapper and interface generator. +The default value is +.BR _wrap\fP\fB$CFILESUFFIX . +By default, this value is used whenever the +.B \-c++ +option is +.I not +specified as part of the +.B $SWIGFLAGS +construction variable. + +.IP SWIGCOM +The command line used to call +the scripting language wrapper and interface generator. + +.IP SWIGCOMSTR +The string displayed when calling +the scripting language wrapper and interface generator. +If this is not set, then \fB$SWIGCOM\fP (the command line) is displayed. + +.IP SWIGCXXFILESUFFIX +The suffix that will be used for intermediate C++ +source files generated by +the scripting language wrapper and interface generator. +The default value is +.BR _wrap\fP\fB$CFILESUFFIX . +By default, this value is used whenever the +.B \-c++ +option is specified as part of the +.B $SWIGFLAGS +construction variable. + +.IP SWIGDIRECTORSUFFIX +The suffix that will be used for intermediate C++ header +files generated by the scripting language wrapper and interface generator. +These are only generated for C++ code when the SWIG 'directors' feature is +turned on. +The default value is +.BR _wrap.h . + +.IP SWIGFLAGS +General options passed to +the scripting language wrapper and interface generator. +This is where you should set +.BR \-python , +.BR \-perl5 , +.BR \-tcl , +or whatever other options you want to specify to SWIG. +If you set the +.B \-c++ +option in this variable, +.B scons +will, by default, +generate a C++ intermediate source file +with the extension that is specified as the +.B $CXXFILESUFFIX +variable. + +.IP _SWIGINCFLAGS +An automatically-generated construction variable +containing the SWIG command-line options +for specifying directories to be searched for included files. +The value of \fB$_SWIGINCFLAGS\fP is created +by appending \fB$SWIGINCPREFIX\fP and \fB$SWIGINCSUFFIX\fP +to the beginning and end +of each directory in \fB$SWIGPATH\fP. + +.IP SWIGINCPREFIX +The prefix used to specify an include directory on the SWIG command line. +This will be appended to the beginning of each directory +in the \fB$SWIGPATH\fP construction variable +when the \fB$_SWIGINCFLAGS\fP variable is automatically generated. + +.IP SWIGINCSUFFIX +The suffix used to specify an include directory on the SWIG command line. +This will be appended to the end of each directory +in the \fB$SWIGPATH\fP construction variable +when the \fB$_SWIGINCFLAGS\fP variable is automatically generated. + +.IP SWIGOUTDIR +Specifies the output directory in which +the scripting language wrapper and interface generator +should place generated language-specific files. +This will be used by SCons to identify +the files that will be generated by the &swig; call, +and translated into the +\fBswig -outdir\fP option on the command line. + +.IP SWIGPATH +The list of directories that the scripting language wrapper +and interface generate will search for included files. +The SWIG implicit dependency scanner will search these +directories for include files. +The default is to use the same path +specified as \fB$CPPPATH\fP. + +Don't explicitly put include directory +arguments in SWIGFLAGS; +the result will be non-portable +and the directories will not be searched by the dependency scanner. +Note: directory names in SWIGPATH will be looked-up relative to the SConscript +directory when they are used in a command. +To force +.B scons +to look-up a directory relative to the root of the source tree use #: + +.ES +env = Environment(SWIGPATH='#/include') +.EE +.IP +The directory look-up can also be forced using the +.BR Dir () +function: + +.ES +include = Dir('include') +env = Environment(SWIGPATH=include) +.EE +.IP +The directory list will be added to command lines +through the automatically-generated +.B $_SWIGINCFLAGS +construction variable, +which is constructed by +appending the values of the +.BR $SWIGINCPREFIX and $SWIGINCSUFFIX +construction variables +to the beginning and end +of each directory in \fB$SWIGPATH\fP. +Any command lines you define that need +the SWIGPATH directory list should +include \fB$_SWIGINCFLAGS\fP: + +.ES +env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SORUCES") +.EE + +.IP SWIGVERSION +The version number of the SWIG tool. + +.IP TAR +The tar archiver. + +.IP TARCOM +The command line used to call the tar archiver. + +.IP TARCOMSTR +The string displayed when archiving files +using the tar archiver. +If this is not set, then \fB$TARCOM\fP (the command line) is displayed. + +.ES +env = Environment(TARCOMSTR = "Archiving $TARGET") +.EE + +.IP TARFLAGS +General options passed to the tar archiver. + +.IP TARGET +A reserved variable name +that may not be set or used in a construction environment. +(See "Variable Substitution," below.) + +.IP TARGET_ARCH +Sets the target architecture for Visual Studio compiler (i.e. the arch +of the binaries generated by the compiler). If not set, default to +\fB$HOST_ARCH\fP, or, if that is unset, to the architecture of the +running machine's OS (note that the python build or architecture has no +effect). +This variable must be passed as an argument to the Environment() +constructor; setting it later has no effect. +This is currently only used on Windows, but in the future it will be +used on other OSes as well. + +Valid values for Windows are +.BR x86 , +.B i386 +(for 32 bits); +.BR amd64 , +.BR emt64 , +.B x86_64 +(for 64 bits); +and \fBia64\fP (Itanium). +For example, if you want to compile 64-bit binaries, you would set +\fBTARGET_ARCH='x86_64'\fP in your SCons environment. + +.IP TARGET_OS + The name of the target operating system for the compiled objects + created by this Environment. + This defaults to the value of HOST_OS, and the user can override it. + Currently only set for Win32. + +.IP TARGETS +A reserved variable name +that may not be set or used in a construction environment. +(See "Variable Substitution," below.) + +.IP TARSUFFIX +The suffix used for tar file names. + +.IP TEMPFILEPREFIX +The prefix for a temporary file used +to execute lines longer than $MAXLINELENGTH. +The default is '@'. +This may be set for toolchains that use other values, +such as '-@' for the diab compiler +or '-via' for ARM toolchain. + +.IP TEX +The TeX formatter and typesetter. + +.IP TEXCOM +The command line used to call the TeX formatter and typesetter. + +.IP TEXCOMSTR +The string displayed when calling +the TeX formatter and typesetter. +If this is not set, then \fB$TEXCOM\fP (the command line) is displayed. + +.ES +env = Environment(TEXCOMSTR = "Building $TARGET from TeX input $SOURCES") +.EE + +.IP TEXFLAGS +General options passed to the TeX formatter and typesetter. + +.IP TEXINPUTS +List of directories that the LaTeX program will search +for include directories. +The LaTeX implicit dependency scanner will search these +directories for \\include and \\import files. + +.IP TEXTFILEPREFIX +The prefix used for \fBTextfile\fP() file names, +the null string by default. + +.IP TEXTFILESUFFIX +The suffix used for \fBTextfile\fP() file names; +\fB.txt\fP by default. + +.IP TOOLS +A list of the names of the Tool specifications +that are part of this construction environment. + +.IP UNCHANGED_SOURCES +A reserved variable name +that may not be set or used in a construction environment. +(See "Variable Substitution," below.) + +.IP UNCHANGED_TARGETS +A reserved variable name +that may not be set or used in a construction environment. +(See "Variable Substitution," below.) + +.IP VENDOR +The person or organization who supply the packaged software. +This is used to fill in the +.B Vendor: +field in the controlling information for RPM packages, +and the +.B Manufacturer: +field in the controlling information for MSI packages. + +.IP VERSION +The version of the project, specified as a string. + +.IP WIN32_INSERT_DEF +A deprecated synonym for \fB$WINDOWS_INSERT_DEF\fP. + +.IP WIN32DEFPREFIX +A deprecated synonym for \fB$WINDOWSDEFPREFIX\fP. + +.IP WIN32DEFSUFFIX +A deprecated synonym for \fB$WINDOWSDEFSUFFIX\fP. + +.IP WIN32EXPPREFIX +A deprecated synonym for \fB$WINDOWSEXPSUFFIX\fP. + +.IP WIN32EXPSUFFIX +A deprecated synonym for \fB$WINDOWSEXPSUFFIX\fP. + +.IP WINDOWS_EMBED_MANIFEST +Set this variable to True or 1 to embed the compiler-generated manifest +(normally \fB${TARGET}.manifest\fP) +into all Windows exes and DLLs built with this environment, +as a resource during their link step. +This is done using \fB$MT\fP and \fB$MTEXECOM\fP and \fB$MTSHLIBCOM\fP. + +.IP WINDOWS_INSERT_DEF +When this is set to true, +a library build of a Windows shared library +.RB ( .dll file) +will also build a corresponding \fB.def\fP file +at the same time, +if a \fB.def\fP file +is not already listed as a build target. +The default is 0 (do not build a \fB.def\fP file). + +.IP WINDOWS_INSERT_MANIFEST +When this is set to true, +.B scons +will be aware of the +.B .manifest +files generated by Microsoft Visua C/C++ 8. + +.IP WINDOWSDEFPREFIX +The prefix used for Windows \fB.def\fP file names. + +.IP WINDOWSDEFSUFFIX +The suffix used for Windows \fB.def\fP file names. + +.IP WINDOWSEXPPREFIX +The prefix used for Windows \fB.exp\fP file names. + +.IP WINDOWSEXPSUFFIX +The suffix used for Windows \fB.exp\fP file names. + +.IP WINDOWSPROGMANIFESTPREFIX +The prefix used for executable program \fB.manifest\fP files +generated by Microsoft Visual C/C++. + +.IP WINDOWSPROGMANIFESTSUFFIX +The suffix used for executable program \fB.manifest\fP files +generated by Microsoft Visual C/C++. + +.IP WINDOWSSHLIBMANIFESTPREFIX +The prefix used for shared library \fB.manifest\fP files +generated by Microsoft Visual C/C++. + +.IP WINDOWSSHLIBMANIFESTSUFFIX +The suffix used for shared library \fB.manifest\fP files +generated by Microsoft Visual C/C++. + +.IP X_IPK_DEPENDS +This is used to fill in the +.B Depends: +field in the controlling information for Ipkg packages. + +.IP X_IPK_DESCRIPTION +This is used to fill in the +.B Description: +field in the controlling information for Ipkg packages. +The default value is +.B "$SUMMARY\\n$DESCRIPTION" +.IP X_IPK_MAINTAINER +This is used to fill in the +.B Maintainer: +field in the controlling information for Ipkg packages. + +.IP X_IPK_PRIORITY +This is used to fill in the +.B Priority: +field in the controlling information for Ipkg packages. + +.IP X_IPK_SECTION +This is used to fill in the +.B Section: +field in the controlling information for Ipkg packages. + +.IP X_MSI_LANGUAGE +This is used to fill in the +.B Language: +attribute in the controlling information for MSI packages. + +.IP X_MSI_LICENSE_TEXT +The text of the software license in RTF format. +Carriage return characters will be +replaced with the RTF equivalent \\\\par. + +.IP X_MSI_UPGRADE_CODE +TODO + +.IP X_RPM_AUTOREQPROV +This is used to fill in the +.B AutoReqProv: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_BUILD +internal, but overridable + +.IP X_RPM_BUILDREQUIRES +This is used to fill in the +.B BuildRequires: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_BUILDROOT +internal, but overridable + +.IP X_RPM_CLEAN +internal, but overridable + +.IP X_RPM_CONFLICTS +This is used to fill in the +.B Conflicts: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_DEFATTR +This value is used as the default attributes +for the files in the RPM package. +The default value is +.BR (-,root,root) . + +.IP X_RPM_DISTRIBUTION +This is used to fill in the +.B Distribution: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_EPOCH +This is used to fill in the +.B Epoch: +field in the controlling information for RPM packages. + +.IP X_RPM_EXCLUDEARCH +This is used to fill in the +.B ExcludeArch: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_EXLUSIVEARCH +This is used to fill in the +.B ExclusiveArch: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_GROUP +This is used to fill in the +.B Group: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_GROUP_lang +This is used to fill in the +.B Group(lang): +field in the RPM +\fB.spec\fP file. +Note that +.I lang +is not literal +and should be replaced by +the appropriate language code. + +.IP X_RPM_ICON +This is used to fill in the +.B Icon: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_INSTALL +internal, but overridable + +.IP X_RPM_PACKAGER +This is used to fill in the +.B Packager: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_POSTINSTALL +This is used to fill in the +.B %post: +section in the RPM +\fB.spec\fP file. + +.IP X_RPM_POSTUNINSTALL +This is used to fill in the +.B %postun: +section in the RPM +\fB.spec\fP file. + +.IP X_RPM_PREFIX +This is used to fill in the +.B Prefix: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_PREINSTALL +This is used to fill in the +.B %pre: +section in the RPM +\fB.spec\fP file. + +.IP X_RPM_PREP +internal, but overridable + +.IP X_RPM_PREUNINSTALL +This is used to fill in the +.B %preun: +section in the RPM +\fB.spec\fP file. + +.IP X_RPM_PROVIDES +This is used to fill in the +.B Provides: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_REQUIRES +This is used to fill in the +.B Requires: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_SERIAL +This is used to fill in the +.B Serial: +field in the RPM +\fB.spec\fP file. + +.IP X_RPM_URL +This is used to fill in the +.B Url: +field in the RPM +\fB.spec\fP file. + +.IP YACC +The parser generator. + +.IP YACCCOM +The command line used to call the parser generator +to generate a source file. + +.IP YACCCOMSTR +The string displayed when generating a source file +using the parser generator. +If this is not set, then \fB$YACCCOM\fP (the command line) is displayed. + +.ES +env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES") +.EE + +.IP YACCFLAGS +General options passed to the parser generator. +If \fB$YACCFLAGS\fP contains a \fB\-d\fP option, +SCons assumes that the call will also create a .h file +(if the yacc source file ends in a .y suffix) +or a .hpp file +(if the yacc source file ends in a .yy suffix) + +.IP YACCHFILESUFFIX +The suffix of the C +header file generated by the parser generator +when the +.B \-d +option is used. +Note that setting this variable does not cause +the parser generator to generate a header +file with the specified suffix, +it exists to allow you to specify +what suffix the parser generator will use of its own accord. +The default value is +.BR .h . + +.IP YACCHXXFILESUFFIX +The suffix of the C++ +header file generated by the parser generator +when the +.B \-d +option is used. +Note that setting this variable does not cause +the parser generator to generate a header +file with the specified suffix, +it exists to allow you to specify +what suffix the parser generator will use of its own accord. +The default value is +.BR .hpp , +except on Mac OS X, +where the default is +.BR ${TARGET.suffix}.h . +because the default &bison; parser generator just +appends \fB.h\fP +to the name of the generated C++ file. + +.IP YACCVCGFILESUFFIX +The suffix of the file +containing the VCG grammar automaton definition +when the +.B \-\-graph= +option is used. +Note that setting this variable does not cause +the parser generator to generate a VCG +file with the specified suffix, +it exists to allow you to specify +what suffix the parser generator will use of its own accord. +The default value is +.BR .vcg . + +.IP ZIP +The zip compression and file packaging utility. + +.IP ZIPCOM +The command line used to call the zip utility, +or the internal Python function used to create a +zip archive. + +.IP ZIPCOMPRESSION +The +.I compression +flag +from the Python +.B zipfile +module used by the internal Python function +to control whether the zip archive +is compressed or not. +The default value is +.BR zipfile.ZIP_DEFLATED , +which creates a compressed zip archive. +This value has no effect if the +.B zipfile +module is unavailable. + +.IP ZIPCOMSTR +The string displayed when archiving files +using the zip utility. +If this is not set, then \fB$ZIPCOM\fP +(the command line or internal Python function) is displayed. + +.ES +env = Environment(ZIPCOMSTR = "Zipping $TARGET") +.EE + +.IP ZIPFLAGS +General options passed to the zip utility. + +.IP ZIPSUFFIX +The suffix used for zip file names. +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +'\" END GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS +'\" +'\" The descriptions above of the various SCons construction variables +'\" are generated from the .xml files that live next to the various +'\" Python modules in the build enginer library. If you're reading +'\" this [gnt]roff file with an eye towards patching this man page, +'\" you can still submit a diff against this text, but it will have to +'\" be translated to a diff against the underlying .xml file before the +'\" patch is actually accepted. If you do that yourself, it will make +'\" it easier to integrate the patch. +'\" +'\" END GENERATED CONSTRUCTION VARIABLE DESCRIPTIONS +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.LP +Construction variables can be retrieved and set using the +.B Dictionary +method of the construction environment: + +.ES +dict = env.Dictionary() +dict["CC"] = "cc" +.EE + +or using the [] operator: + +.ES +env["CC"] = "cc" +.EE + +Construction variables can also be passed to the construction environment +constructor: + +.ES +env = Environment(CC="cc") +.EE + +or when copying a construction environment using the +.B Clone +method: + +.ES +env2 = env.Clone(CC="cl.exe") +.EE + +.SS Configure Contexts + +.B scons +supports +.I configure contexts, +an integrated mechanism similar to the +various AC_CHECK macros in GNU autoconf +for testing for the existence of C header +files, libraries, etc. +In contrast to autoconf, +.B scons +does not maintain an explicit cache of the tested values, +but uses its normal dependency tracking to keep the checked values +up to date. However, users may override this behaviour with the +.B --config +command line option. + +The following methods can be used to perform checks: + +.TP +.RI Configure( env ", [" custom_tests ", " conf_dir ", " log_file ", " config_h ", " clean ", " help]) +.TP +.RI env.Configure([ custom_tests ", " conf_dir ", " log_file ", " config_h ", " clean ", " help]) +This creates a configure context, which can be used to perform checks. +.I env +specifies the environment for building the tests. +This environment may be modified when performing checks. +.I custom_tests +is a dictionary containing custom tests. +See also the section about custom tests below. +By default, no custom tests are added to the configure context. +.I conf_dir +specifies a directory where the test cases are built. +Note that this directory is not used for building +normal targets. +The default value is the directory +#/.sconf_temp. +.I log_file +specifies a file which collects the output from commands +that are executed to check for the existence of header files, libraries, etc. +The default is the file #/config.log. +If you are using the +.BR VariantDir () +method, +you may want to specify a subdirectory under your variant directory. +.I config_h +specifies a C header file where the results of tests +will be written, e.g. #define HAVE_STDIO_H, #define HAVE_LIBM, etc. +The default is to not write a +.B config.h +file. +You can specify the same +.B config.h +file in multiple calls to Configure, +in which case +.B scons +will concatenate all results in the specified file. +Note that SCons +uses its normal dependency checking +to decide if it's necessary to rebuild +the specified +.I config_h +file. +This means that the file is not necessarily re-built each +time scons is run, +but is only rebuilt if its contents will have changed +and some target that depends on the +.I config_h +file is being built. + +The optional +.B clean +and +.B help +arguments can be used to suppress execution of the configuration +tests when the +.B -c/--clean +or +.B -H/-h/--help +options are used, respectively. +The default behavior is always to execute +configure context tests, +since the results of the tests may +affect the list of targets to be cleaned +or the help text. +If the configure tests do not affect these, +then you may add the +.B clean=False +or +.B help=False +arguments +(or both) +to avoid unnecessary test execution. + +.EE +A created +.B Configure +instance has the following associated methods: + +.TP +.RI SConf.Finish( context ) +.TP +.IR sconf .Finish() +This method should be called after configuration is done. +It returns the environment as modified +by the configuration checks performed. +After this method is called, no further checks can be performed +with this configuration context. +However, you can create a new +.RI Configure +context to perform additional checks. +Only one context should be active at a time. + +The following Checks are predefined. +(This list will likely grow larger as time +goes by and developers contribute new useful tests.) + +.TP +.RI SConf.CheckHeader( context ", " header ", [" include_quotes ", " language ]) +.TP +.IR sconf .CheckHeader( header ", [" include_quotes ", " language ]) +Checks if +.I header +is usable in the specified language. +.I header +may be a list, +in which case the last item in the list +is the header file to be checked, +and the previous list items are +header files whose +.B #include +lines should precede the +header line being checked for. +The optional argument +.I include_quotes +must be +a two character string, where the first character denotes the opening +quote and the second character denotes the closing quote. +By default, both characters are " (double quote). +The optional argument +.I language +should be either +.B C +or +.B C++ +and selects the compiler to be used for the check. +Returns 1 on success and 0 on failure. + +.TP +.RI SConf.CheckCHeader( context ", " header ", [" include_quotes ]) +.TP +.IR sconf .CheckCHeader( header ", [" include_quotes ]) +This is a wrapper around +.B SConf.CheckHeader +which checks if +.I header +is usable in the C language. +.I header +may be a list, +in which case the last item in the list +is the header file to be checked, +and the previous list items are +header files whose +.B #include +lines should precede the +header line being checked for. +The optional argument +.I include_quotes +must be +a two character string, where the first character denotes the opening +quote and the second character denotes the closing quote (both default +to \N'34'). +Returns 1 on success and 0 on failure. + +.TP +.RI SConf.CheckCXXHeader( context ", " header ", [" include_quotes ]) +.TP +.IR sconf .CheckCXXHeader( header ", [" include_quotes ]) +This is a wrapper around +.B SConf.CheckHeader +which checks if +.I header +is usable in the C++ language. +.I header +may be a list, +in which case the last item in the list +is the header file to be checked, +and the previous list items are +header files whose +.B #include +lines should precede the +header line being checked for. +The optional argument +.I include_quotes +must be +a two character string, where the first character denotes the opening +quote and the second character denotes the closing quote (both default +to \N'34'). +Returns 1 on success and 0 on failure. + +.TP +.RI SConf.CheckFunc( context, ", " function_name ", [" header ", " language ]) +.TP +.IR sconf .CheckFunc( function_name ", [" header ", " language ]) +Checks if the specified +C or C++ function is available. +.I function_name +is the name of the function to check for. +The optional +.I header +argument is a string +that will be +placed at the top +of the test file +that will be compiled +to check if the function exists; +the default is: +.ES +#ifdef __cplusplus +extern "C" +#endif +char function_name(); +.EE +The optional +.I language +argument should be +.B C +or +.B C++ +and selects the compiler to be used for the check; +the default is "C". + +.TP +.RI SConf.CheckLib( context ", [" library ", " symbol ", " header ", " language ", " autoadd=1 ]) +.TP +.IR sconf .CheckLib([ library ", " symbol ", " header ", " language ", " autoadd=1 ]) +Checks if +.I library +provides +.IR symbol . +If the value of +.I autoadd +is 1 and the library provides the specified +.IR symbol , +appends the library to the LIBS construction environment variable. +.I library +may also be None (the default), +in which case +.I symbol +is checked with the current LIBS variable, +or a list of library names, +in which case each library in the list +will be checked for +.IR symbol . +If +.I symbol +is not set or is +.BR None , +then +.BR SConf.CheckLib () +just checks if +you can link against the specified +.IR library . +The optional +.I language +argument should be +.B C +or +.B C++ +and selects the compiler to be used for the check; +the default is "C". +The default value for +.I autoadd +is 1. +This method returns 1 on success and 0 on error. + +.TP +.RI SConf.CheckLibWithHeader( context ", " library ", " header ", " language ", [" call ", " autoadd ]) +.TP +.IR sconf .CheckLibWithHeader( library ", " header ", " language ", [" call ", " autoadd ]) + +In contrast to the +.RI SConf.CheckLib +call, this call provides a more sophisticated way to check against libraries. +Again, +.I library +specifies the library or a list of libraries to check. +.I header +specifies a header to check for. +.I header +may be a list, +in which case the last item in the list +is the header file to be checked, +and the previous list items are +header files whose +.B #include +lines should precede the +header line being checked for. +.I language +may be one of 'C','c','CXX','cxx','C++' and 'c++'. +.I call +can be any valid expression (with a trailing ';'). +If +.I call +is not set, +the default simply checks that you +can link against the specified +.IR library . +.I autoadd +specifies whether to add the library to the environment (only if the check +succeeds). This method returns 1 on success and 0 on error. + +.TP +.RI SConf.CheckType( context ", " type_name ", [" includes ", " language ]) +.TP +.IR sconf .CheckType( type_name ", [" includes ", " language ]) +Checks for the existence of a type defined by +.BR typedef . +.I type_name +specifies the typedef name to check for. +.I includes +is a string containing one or more +.B #include +lines that will be inserted into the program +that will be run to test for the existence of the type. +The optional +.I language +argument should be +.B C +or +.B C++ +and selects the compiler to be used for the check; +the default is "C". +Example: +.ES +sconf.CheckType('foo_type', '#include "my_types.h"', 'C++') +.EE + +.TP +.RI Configure.CheckCC( self ) +Checks whether the C compiler (as defined by the CC construction variable) works +by trying to compile a small source file. + +By default, SCons only detects if there is a program with the correct name, not +if it is a functioning compiler. + +This uses the exact same command than the one used by the object builder for C +source file, so it can be used to detect if a particular compiler flag works or +not. + +.TP +.RI Configure.CheckCXX( self ) +Checks whether the C++ compiler (as defined by the CXX construction variable) +works by trying to compile a small source file. By default, SCons only detects +if there is a program with the correct name, not if it is a functioning compiler. + +This uses the exact same command than the one used by the object builder for +CXX source files, so it can be used to detect if a particular compiler flag +works or not. + +.TP +.RI Configure.CheckSHCC( self ) +Checks whether the C compiler (as defined by the SHCC construction variable) works +by trying to compile a small source file. By default, SCons only detects if +there is a program with the correct name, not if it is a functioning compiler. + +This uses the exact same command than the one used by the object builder for C +source file, so it can be used to detect if a particular compiler flag works or +not. This does not check whether the object code can be used to build a shared +library, only that the compilation (not link) succeeds. + +.TP +.RI Configure.CheckSHCXX( self ) +Checks whether the C++ compiler (as defined by the SHCXX construction variable) +works by trying to compile a small source file. By default, SCons only detects +if there is a program with the correct name, not if it is a functioning compiler. + +This uses the exact same command than the one used by the object builder for +CXX source files, so it can be used to detect if a particular compiler flag +works or not. This does not check whether the object code can be used to build +a shared library, only that the compilation (not link) succeeds. + +.EE +Example of a typical Configure usage: + +.ES +env = Environment() +conf = Configure( env ) +if not conf.CheckCHeader( 'math.h' ): + print 'We really need math.h!' + Exit(1) +if conf.CheckLibWithHeader( 'qt', 'qapp.h', 'c++', + 'QApplication qapp(0,0);' ): + # do stuff for qt - usage, e.g. + conf.env.Append( CPPFLAGS = '-DWITH_QT' ) +env = conf.Finish() +.EE + +.TP +.RI SConf.CheckTypeSize( context ", " type_name ", [" header ", " language ", " expect ]) +.TP +.IR sconf .CheckTypeSize( type_name ", [" header ", " language ", " expect ]) +Checks for the size of a type defined by +.BR typedef . +.I type_name +specifies the typedef name to check for. +The optional +.I header +argument is a string +that will be +placed at the top +of the test file +that will be compiled +to check if the function exists; +the default is empty. +The optional +.I language +argument should be +.B C +or +.B C++ +and selects the compiler to be used for the check; +the default is "C". +The optional +.I expect +argument should be an integer. +If this argument is used, +the function will only check whether the type +given in type_name has the expected size (in bytes). +For example, +.B "CheckTypeSize('short', expect = 2)" +will return success only if short is two bytes. + +.ES +.EE + +.TP +.RI SConf.CheckDeclaration( context ", " symbol ", [" includes ", " language ]) +.TP +.IR sconf .CheckDeclaration( symbol ", [" includes ", " language ]) +Checks if the specified +.I symbol +is declared. +.I includes +is a string containing one or more +.B #include +lines that will be inserted into the program +that will be run to test for the existence of the type. +The optional +.I language +argument should be +.B C +or +.B C++ +and selects the compiler to be used for the check; +the default is "C". + +.TP +.RI SConf.Define( context ", " symbol ", [" value ", " comment ]) +.TP +.IR sconf .Define( symbol ", [" value ", " comment ]) +This function does not check for anything, but defines a +preprocessor symbol that will be added to the configuration header file. +It is the equivalent of AC_DEFINE, +and defines the symbol +.I name +with the optional +.B value +and the optional comment +.BR comment . + +.IP +Examples: + +.ES +env = Environment() +conf = Configure( env ) + +# Puts the following line in the config header file: +# #define A_SYMBOL +conf.Define('A_SYMBOL') + +# Puts the following line in the config header file: +# #define A_SYMBOL 1 +conf.Define('A_SYMBOL', 1) +.EE + +.IP +Be careful about quoting string values, though: + +.ES +env = Environment() +conf = Configure( env ) + +# Puts the following line in the config header file: +# #define A_SYMBOL YA +conf.Define('A_SYMBOL', "YA") + +# Puts the following line in the config header file: +# #define A_SYMBOL "YA" +conf.Define('A_SYMBOL', '"YA"') +.EE + +.IP +For comment: + +.ES +env = Environment() +conf = Configure( env ) + +# Puts the following lines in the config header file: +# /* Set to 1 if you have a symbol */ +# #define A_SYMBOL 1 +conf.Define('A_SYMBOL', 1, 'Set to 1 if you have a symbol') +.EE + +.EE +You can define your own custom checks. +in addition to the predefined checks. +These are passed in a dictionary to the Configure function. +This dictionary maps the names of the checks +to user defined Python callables +(either Python functions or class instances implementing the +.I __call__ +method). +The first argument of the call is always a +.I CheckContext +instance followed by the arguments, +which must be supplied by the user of the check. +These CheckContext instances define the following methods: + +.TP +.RI CheckContext.Message( self ", " text ) + +Usually called before the check is started. +.I text +will be displayed to the user, e.g. 'Checking for library X...' + +.TP +.RI CheckContext.Result( self, ", " res ) + +Usually called after the check is done. +.I res +can be either an integer or a string. In the former case, 'yes' (res != 0) +or 'no' (res == 0) is displayed to the user, in the latter case the +given string is displayed. + +.TP +.RI CheckContext.TryCompile( self ", " text ", " extension ) +Checks if a file with the specified +.I extension +(e.g. '.c') containing +.I text +can be compiled using the environment's +.B Object +builder. Returns 1 on success and 0 on failure. + +.TP +.RI CheckContext.TryLink( self ", " text ", " extension ) +Checks, if a file with the specified +.I extension +(e.g. '.c') containing +.I text +can be compiled using the environment's +.B Program +builder. Returns 1 on success and 0 on failure. + +.TP +.RI CheckContext.TryRun( self ", " text ", " extension ) +Checks, if a file with the specified +.I extension +(e.g. '.c') containing +.I text +can be compiled using the environment's +.B Program +builder. On success, the program is run. If the program +executes successfully +(that is, its return status is 0), +a tuple +.I (1, outputStr) +is returned, where +.I outputStr +is the standard output of the +program. +If the program fails execution +(its return status is non-zero), +then (0, '') is returned. + +.TP +.RI CheckContext.TryAction( self ", " action ", [" text ", " extension ]) +Checks if the specified +.I action +with an optional source file (contents +.I text +, extension +.I extension += '' +) can be executed. +.I action +may be anything which can be converted to a +.B scons +.RI Action. +On success, +.I (1, outputStr) +is returned, where +.I outputStr +is the content of the target file. +On failure +.I (0, '') +is returned. + +.TP +.RI CheckContext.TryBuild( self ", " builder ", [" text ", " extension ]) +Low level implementation for testing specific builds; +the methods above are based on this method. +Given the Builder instance +.I builder +and the optional +.I text +of a source file with optional +.IR extension , +this method returns 1 on success and 0 on failure. In addition, +.I self.lastTarget +is set to the build target node, if the build was successful. + +.EE +Example for implementing and using custom tests: + +.ES +def CheckQt(context, qtdir): + context.Message( 'Checking for qt ...' ) + lastLIBS = context.env['LIBS'] + lastLIBPATH = context.env['LIBPATH'] + lastCPPPATH= context.env['CPPPATH'] + context.env.Append(LIBS = 'qt', LIBPATH = qtdir + '/lib', CPPPATH = qtdir + '/include' ) + ret = context.TryLink(""" +#include +int main(int argc, char **argv) { + QApplication qapp(argc, argv); + return 0; +} +""") + if not ret: + context.env.Replace(LIBS = lastLIBS, LIBPATH=lastLIBPATH, CPPPATH=lastCPPPATH) + context.Result( ret ) + return ret + +env = Environment() +conf = Configure( env, custom_tests = { 'CheckQt' : CheckQt } ) +if not conf.CheckQt('/usr/lib/qt'): + print 'We really need qt!' + Exit(1) +env = conf.Finish() +.EE + +.SS Command-Line Construction Variables + +Often when building software, +some variables must be specified at build time. +For example, libraries needed for the build may be in non-standard +locations, or site-specific compiler options may need to be passed to the +compiler. +.B scons +provides a +.B Variables +object to support overriding construction variables +on the command line: +.ES +$ scons VARIABLE=foo +.EE +The variable values can also be specified in a text-based SConscript file. +To create a Variables object, call the Variables() function: + +.TP +.RI Variables([ files "], [" args ]) +This creates a Variables object that will read construction variables from +the file or list of filenames specified in +.IR files . +If no files are specified, +or the +.I files +argument is +.BR None , +then no files will be read. +The optional argument +.I args +is a dictionary of +values that will override anything read from the specified files; +it is primarily intended to be passed the +.B ARGUMENTS +dictionary that holds variables +specified on the command line. +Example: + +.ES +vars = Variables('custom.py') +vars = Variables('overrides.py', ARGUMENTS) +vars = Variables(None, {FOO:'expansion', BAR:7}) +.EE + +Variables objects have the following methods: + +.TP +.RI Add( key ", [" help ", " default ", " validator ", " converter ]) +This adds a customizable construction variable to the Variables object. +.I key +is the name of the variable. +.I help +is the help text for the variable. +.I default +is the default value of the variable; +if the default value is +.B None +and there is no explicit value specified, +the construction variable will +.I not +be added to the construction environment. +.I validator +is called to validate the value of the variable, and should take three +arguments: key, value, and environment. +The recommended way to handle an invalid value is +to raise an exception (see example below). +.I converter +is called to convert the value before putting it in the environment, and +should take either a value, or the value and environment, as parameters. +The +.I converter +must return a value, +which will be converted into a string +before being validated by the +.I validator +(if any) +and then added to the environment. + +Examples: + +.ES +vars.Add('CC', 'The C compiler') + +def validate_color(key, val, env): + if not val in ['red', 'blue', 'yellow']: + raise Exception("Invalid color value '%s'" % val) +vars.Add('COLOR', validator=valid_color) +.EE + +.TP +.RI AddVariables( list ) +A wrapper script that adds +multiple customizable construction variables +to a Variables object. +.I list +is a list of tuple or list objects +that contain the arguments +for an individual call to the +.B Add +method. + +.ES +opt.AddVariables( + ('debug', '', 0), + ('CC', 'The C compiler'), + ('VALIDATE', 'An option for testing validation', + 'notset', validator, None), + ) +.EE + +.TP +.RI Update( env ", [" args ]) +This updates a construction environment +.I env +with the customized construction variables. +Any specified variables that are +.I not +configured for the Variables object +will be saved and may be +retrieved with the +.BR UnknownVariables () +method, below. + +Normally this method is not called directly, +but is called indirectly by passing the Variables object to +the Environment() function: + +.ES +env = Environment(variables=vars) +.EE + +.IP +The text file(s) that were specified +when the Variables object was created +are executed as Python scripts, +and the values of (global) Python variables set in the file +are added to the construction environment. + +Example: + +.ES +CC = 'my_cc' +.EE + +.TP +.RI UnknownVariables( ) +Returns a dictionary containing any +variables that were specified +either in the files or the dictionary +with which the Variables object was initialized, +but for which the Variables object was +not configured. + +.ES +env = Environment(variables=vars) +for key, value in vars.UnknownVariables(): + print "unknown variable: %s=%s" % (key, value) +.EE + +.TP +.RI Save( filename ", " env ) +This saves the currently set variables into a script file named +.I filename +that can be used on the next invocation to automatically load the current +settings. This method combined with the Variables method can be used to +support caching of variables between runs. + +.ES +env = Environment() +vars = Variables(['variables.cache', 'custom.py']) +vars.Add(...) +vars.Update(env) +vars.Save('variables.cache', env) +.EE + +.TP +.RI GenerateHelpText( env ", [" sort ]) +This generates help text documenting the customizable construction +variables suitable to passing in to the Help() function. +.I env +is the construction environment that will be used to get the actual values +of customizable variables. Calling with +an optional +.I sort +function +will cause the output to be sorted +by the specified argument. +The specific +.I sort +function +should take two arguments +and return +-1, 0 or 1 +(like the standard Python +.I cmp +function). + +.ES +Help(vars.GenerateHelpText(env)) +Help(vars.GenerateHelpText(env, sort=cmp)) +.EE + +.TP +.RI FormatVariableHelpText( env ", " opt ", " help ", " default ", " actual ) +This method returns a formatted string +containing the printable help text +for one option. +It is normally not called directly, +but is called by the +.IR GenerateHelpText () +method to create the returned help text. +It may be overridden with your own +function that takes the arguments specified above +and returns a string of help text formatted to your liking. +Note that the +.IR GenerateHelpText () +will not put any blank lines or extra +characters in between the entries, +so you must add those characters to the returned +string if you want the entries separated. + +.ES +def my_format(env, opt, help, default, actual): + fmt = "\n%s: default=%s actual=%s (%s)\n" + return fmt % (opt, default. actual, help) +vars.FormatVariableHelpText = my_format +.EE + +To make it more convenient to work with customizable Variables, +.B scons +provides a number of functions +that make it easy to set up +various types of Variables: + +.TP +.RI BoolVariable( key ", " help ", " default ) +Return a tuple of arguments +to set up a Boolean option. +The option will use +the specified name +.IR key , +have a default value of +.IR default , +and display the specified +.I help +text. +The option will interpret the values +.BR y , +.BR yes , +.BR t , +.BR true , +.BR 1 , +.B on +and +.B all +as true, +and the values +.BR n , +.BR no , +.BR f , +.BR false , +.BR 0 , +.B off +and +.B none +as false. + +.TP +.RI EnumVariable( key ", " help ", " default ", " allowed_values ", [" map ", " ignorecase ]) +Return a tuple of arguments +to set up an option +whose value may be one +of a specified list of legal enumerated values. +The option will use +the specified name +.IR key , +have a default value of +.IR default , +and display the specified +.I help +text. +The option will only support those +values in the +.I allowed_values +list. +The optional +.I map +argument is a dictionary +that can be used to convert +input values into specific legal values +in the +.I allowed_values +list. +If the value of +.I ignore_case +is +.B 0 +(the default), +then the values are case-sensitive. +If the value of +.I ignore_case +is +.BR 1 , +then values will be matched +case-insensitive. +If the value of +.I ignore_case +is +.BR 1 , +then values will be matched +case-insensitive, +and all input values will be +converted to lower case. + +.TP +.RI ListVariable( key ", " help ", " default ", " names ", [", map ]) +Return a tuple of arguments +to set up an option +whose value may be one or more +of a specified list of legal enumerated values. +The option will use +the specified name +.IR key , +have a default value of +.IR default , +and display the specified +.I help +text. +The option will only support the values +.BR all , +.BR none , +or the values in the +.I names +list. +More than one value may be specified, +with all values separated by commas. +The default may be a string of +comma-separated default values, +or a list of the default values. +The optional +.I map +argument is a dictionary +that can be used to convert +input values into specific legal values +in the +.I names +list. + +.TP +.RI PackageVariable( key ", " help ", " default ) +Return a tuple of arguments +to set up an option +whose value is a path name +of a package that may be +enabled, disabled or +given an explicit path name. +The option will use +the specified name +.IR key , +have a default value of +.IR default , +and display the specified +.I help +text. +The option will support the values +.BR yes , +.BR true , +.BR on , +.BR enable +or +.BR search , +in which case the specified +.I default +will be used, +or the option may be set to an +arbitrary string +(typically the path name to a package +that is being enabled). +The option will also support the values +.BR no , +.BR false , +.BR off +or +.BR disable +to disable use of the specified option. + +.TP +.RI PathVariable( key ", " help ", " default ", [" validator ]) +Return a tuple of arguments +to set up an option +whose value is expected to be a path name. +The option will use +the specified name +.IR key , +have a default value of +.IR default , +and display the specified +.I help +text. +An additional +.I validator +may be specified +that will be called to +verify that the specified path +is acceptable. +SCons supplies the +following ready-made validators: +.BR PathVariable.PathExists +(the default), +which verifies that the specified path exists; +.BR PathVariable.PathIsFile , +which verifies that the specified path is an existing file; +.BR PathVariable.PathIsDir , +which verifies that the specified path is an existing directory; +.BR PathVariable.PathIsDirCreate , +which verifies that the specified path is a directory +and will create the specified directory if the path does not exist; +and +.BR PathVariable.PathAccept , +which simply accepts the specific path name argument without validation, +and which is suitable if you want your users +to be able to specify a directory path that will be +created as part of the build process, for example. +You may supply your own +.I validator +function, +which must take three arguments +.RI ( key , +the name of the variable to be set; +.IR val , +the specified value being checked; +and +.IR env , +the construction environment) +and should raise an exception +if the specified value is not acceptable. + +.RE +These functions make it +convenient to create a number +of variables with consistent behavior +in a single call to the +.B AddVariables +method: + +.ES +vars.AddVariables( + BoolVariable('warnings', 'compilation with -Wall and similiar', 1), + EnumVariable('debug', 'debug output and symbols', 'no' + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=0), # case sensitive + ListVariable('shared', + 'libraries to build as shared libraries', + 'all', + names = list_of_libs), + PackageVariable('x11', + 'use X11 installed here (yes = search some places)', + 'yes'), + PathVariable('qtdir', 'where the root of Qt is installed', qtdir), + PathVariable('foopath', 'where the foo library is installed', foopath, + PathVariable.PathIsDir), + +) +.EE + +.SS File and Directory Nodes + +The +.IR File () +and +.IR Dir () +functions return +.I File +and +.I Dir +Nodes, respectively. +python objects, respectively. +Those objects have several user-visible attributes +and methods that are often useful: + +.IP path +The build path +of the given +file or directory. +This path is relative to the top-level directory +(where the +.B SConstruct +file is found). +The build path is the same as the source path if +.I variant_dir +is not being used. + +.IP abspath +The absolute build path of the given file or directory. + +.IP srcnode() +The +.IR srcnode () +method +returns another +.I File +or +.I Dir +object representing the +.I source +path of the given +.I File +or +.IR Dir . +The + +.ES +# Get the current build dir's path, relative to top. +Dir('.').path +# Current dir's absolute path +Dir('.').abspath +# Next line is always '.', because it is the top dir's path relative to itself. +Dir('#.').path +File('foo.c').srcnode().path # source path of the given source file. + +# Builders also return File objects: +foo = env.Program('foo.c') +print "foo will be built in %s"%foo.path +.EE + +A +.I Dir +Node or +.I File +Node can also be used to create +file and subdirectory Nodes relative to the generating Node. +A +.I Dir +Node will place the new Nodes within the directory it represents. +A +.I File +node will place the new Nodes within its parent directory +(that is, "beside" the file in question). +If +.I d +is a +.I Dir +(directory) Node and +.I f +is a +.I File +(file) Node, +then these methods are available: + +.TP +.IR d .Dir( name ) +Returns a directory Node for a subdirectory of +.I d +named +.IR name . + +.TP +.IR d .File( name ) +Returns a file Node for a file within +.I d +named +.IR name . + +.TP +.IR d .Entry( name ) +Returns an unresolved Node within +.I d +named +.IR name . + +.TP +.IR f .Dir( name ) +Returns a directory named +.I name +within the parent directory of +.IR f . + +.TP +.IR f .File( name ) +Returns a file named +.I name +within the parent directory of +.IR f . + +.TP +.IR f .Entry( name ) +Returns an unresolved Node named +.I name +within the parent directory of +.IR f . + +.RE +For example: + +.ES +# Get a Node for a file within a directory +incl = Dir('include') +f = incl.File('header.h') + +# Get a Node for a subdirectory within a directory +dist = Dir('project-3.2.1) +src = dist.Dir('src') + +# Get a Node for a file in the same directory +cfile = File('sample.c') +hfile = cfile.File('sample.h') + +# Combined example +docs = Dir('docs') +html = docs.Dir('html') +index = html.File('index.html') +css = index.File('app.css') +.EE + +.SH EXTENDING SCONS +.SS Builder Objects +.B scons +can be extended to build different types of targets +by adding new Builder objects +to a construction environment. +.IR "In general" , +you should only need to add a new Builder object +when you want to build a new type of file or other external target. +If you just want to invoke a different compiler or other tool +to build a Program, Object, Library, or any other +type of output file for which +.B scons +already has an existing Builder, +it is generally much easier to +use those existing Builders +in a construction environment +that sets the appropriate construction variables +(CC, LINK, etc.). + +Builder objects are created +using the +.B Builder +function. +The +.B Builder +function accepts the following arguments: + +.IP action +The command line string used to build the target from the source. +.B action +can also be: +a list of strings representing the command +to be executed and its arguments +(suitable for enclosing white space in an argument), +a dictionary +mapping source file name suffixes to +any combination of command line strings +(if the builder should accept multiple source file extensions), +a Python function; +an Action object +(see the next section); +or a list of any of the above. + +An action function +takes three arguments: +.I source +- a list of source nodes, +.I target +- a list of target nodes, +.I env +- the construction environment. + +.IP prefix +The prefix that will be prepended to the target file name. +This may be specified as a: + +.RS 10 +.HP 6 +* +.IR string , + +.HP 6 +* +.I callable object +- a function or other callable that takes +two arguments (a construction environment and a list of sources) +and returns a prefix, + +.HP 6 +* +.I dictionary +- specifies a mapping from a specific source suffix (of the first +source specified) to a corresponding target prefix. Both the source +suffix and target prefix specifications may use environment variable +substitution, and the target prefix (the 'value' entries in the +dictionary) may also be a callable object. The default target prefix +may be indicated by a dictionary entry with a key value of None. +.RE +.P + +.ES +b = Builder("build_it < $SOURCE > $TARGET", + prefix = "file-") + +def gen_prefix(env, sources): + return "file-" + env['PLATFORM'] + '-' +b = Builder("build_it < $SOURCE > $TARGET", + prefix = gen_prefix) + +b = Builder("build_it < $SOURCE > $TARGET", + suffix = { None: "file-", + "$SRC_SFX_A": gen_prefix }) +.EE + +.IP suffix +The suffix that will be appended to the target file name. +This may be specified in the same manner as the prefix above. +If the suffix is a string, then +.B scons +will append a '.' to the beginning of the suffix if it's not already +there. The string returned by callable object (or obtained from the +dictionary) is untouched and must append its own '.' to the beginning +if one is desired. + +.ES +b = Builder("build_it < $SOURCE > $TARGET" + suffix = "-file") + +def gen_suffix(env, sources): + return "." + env['PLATFORM'] + "-file" +b = Builder("build_it < $SOURCE > $TARGET", + suffix = gen_suffix) + +b = Builder("build_it < $SOURCE > $TARGET", + suffix = { None: ".sfx1", + "$SRC_SFX_A": gen_suffix }) +.EE + +.IP ensure_suffix +When set to any true value, causes +.B scons +to add the target suffix specified by the +.I suffix +keyword to any target strings +that have a different suffix. +(The default behavior is to leave untouched +any target file name that looks like it already has any suffix.) + +.ES +b1 = Builder("build_it < $SOURCE > $TARGET" + suffix = ".out") +b2 = Builder("build_it < $SOURCE > $TARGET" + suffix = ".out", + ensure_suffix) +env = Environment() +env['BUILDERS']['B1'] = b1 +env['BUILDERS']['B2'] = b2 + +# Builds "foo.txt" because ensure_suffix is not set. +env.B1('foo.txt', 'foo.in') + +# Builds "bar.txt.out" because ensure_suffix is set. +env.B2('bar.txt', 'bar.in') +.EE + +.IP src_suffix +The expected source file name suffix. This may be a string or a list +of strings. + +.IP target_scanner +A Scanner object that +will be invoked to find +implicit dependencies for this target file. +This keyword argument should be used +for Scanner objects that find +implicit dependencies +based only on the target file +and the construction environment, +.I not +for implicit dependencies based on source files. +(See the section "Scanner Objects" below, +for information about creating Scanner objects.) + +.IP source_scanner +A Scanner object that +will be invoked to +find implicit dependencies in +any source files +used to build this target file. +This is where you would +specify a scanner to +find things like +.B #include +lines in source files. +The pre-built +.B DirScanner +Scanner object may be used to +indicate that this Builder +should scan directory trees +for on-disk changes to files +that +.B scons +does not know about from other Builder or function calls. +(See the section "Scanner Objects" below, +for information about creating your own Scanner objects.) + +.IP target_factory +A factory function that the Builder will use +to turn any targets specified as strings into SCons Nodes. +By default, +SCons assumes that all targets are files. +Other useful target_factory +values include +.BR Dir , +for when a Builder creates a directory target, +and +.BR Entry , +for when a Builder can create either a file +or directory target. + +Example: + +.ES +MakeDirectoryBuilder = Builder(action=my_mkdir, target_factory=Dir) +env = Environment() +env.Append(BUILDERS = {'MakeDirectory':MakeDirectoryBuilder}) +env.MakeDirectory('new_directory', []) +.EE + +.IP +Note that the call to the MakeDirectory Builder +needs to specify an empty source list +to make the string represent the builder's target; +without that, it would assume the argument is the source, +and would try to deduce the target name from it, +which in the absence of an automatically-added prefix or suffix +would lead to a matching target and source name +and a circular dependency. + +.IP source_factory +A factory function that the Builder will use +to turn any sources specified as strings into SCons Nodes. +By default, +SCons assumes that all source are files. +Other useful source_factory +values include +.BR Dir , +for when a Builder uses a directory as a source, +and +.BR Entry , +for when a Builder can use files +or directories (or both) as sources. + +Example: + +.ES +CollectBuilder = Builder(action=my_mkdir, source_factory=Entry) +env = Environment() +env.Append(BUILDERS = {'Collect':CollectBuilder}) +env.Collect('archive', ['directory_name', 'file_name']) +.EE + +.IP emitter +A function or list of functions to manipulate the target and source +lists before dependencies are established +and the target(s) are actually built. +.B emitter +can also be a string containing a construction variable to expand +to an emitter function or list of functions, +or a dictionary mapping source file suffixes +to emitter functions. +(Only the suffix of the first source file +is used to select the actual emitter function +from an emitter dictionary.) + +An emitter function +takes three arguments: +.I source +- a list of source nodes, +.I target +- a list of target nodes, +.I env +- the construction environment. +An emitter must return a tuple containing two lists, +the list of targets to be built by this builder, +and the list of sources for this builder. + +Example: + +.ES +def e(target, source, env): + return (target + ['foo.foo'], source + ['foo.src']) + +# Simple association of an emitter function with a Builder. +b = Builder("my_build < $TARGET > $SOURCE", + emitter = e) + +def e2(target, source, env): + return (target + ['bar.foo'], source + ['bar.src']) + +# Simple association of a list of emitter functions with a Builder. +b = Builder("my_build < $TARGET > $SOURCE", + emitter = [e, e2]) + +# Calling an emitter function through a construction variable. +env = Environment(MY_EMITTER = e) +b = Builder("my_build < $TARGET > $SOURCE", + emitter = '$MY_EMITTER') + +# Calling a list of emitter functions through a construction variable. +env = Environment(EMITTER_LIST = [e, e2]) +b = Builder("my_build < $TARGET > $SOURCE", + emitter = '$EMITTER_LIST') + +# Associating multiple emitters with different file +# suffixes using a dictionary. +def e_suf1(target, source, env): + return (target + ['another_target_file'], source) +def e_suf2(target, source, env): + return (target, source + ['another_source_file']) +b = Builder("my_build < $TARGET > $SOURCE", + emitter = {'.suf1' : e_suf1, + '.suf2' : e_suf2}) +.EE + +.IP multi +Specifies whether this builder is allowed to be called multiple times for +the same target file(s). The default is 0, which means the builder +can not be called multiple times for the same target file(s). Calling a +builder multiple times for the same target simply adds additional source +files to the target; it is not allowed to change the environment associated +with the target, specify addition environment overrides, or associate a different +builder with the target. + +.IP env +A construction environment that can be used +to fetch source code using this Builder. +(Note that this environment is +.I not +used for normal builds of normal target files, +which use the environment that was +used to call the Builder for the target file.) + +.IP generator +A function that returns a list of actions that will be executed to build +the target(s) from the source(s). +The returned action(s) may be +an Action object, or anything that +can be converted into an Action object +(see the next section). + +The generator function +takes four arguments: +.I source +- a list of source nodes, +.I target +- a list of target nodes, +.I env +- the construction environment, +.I for_signature +- a Boolean value that specifies +whether the generator is being called +for generating a build signature +(as opposed to actually executing the command). +Example: + +.ES +def g(source, target, env, for_signature): + return [["gcc", "-c", "-o"] + target + source] + +b = Builder(generator=g) +.EE + +.IP +The +.I generator +and +.I action +arguments must not both be used for the same Builder. + +.IP src_builder +Specifies a builder to use when a source file name suffix does not match +any of the suffixes of the builder. Using this argument produces a +multi-stage builder. + +.IP single_source +Specifies that this builder expects exactly one source file per call. Giving +more than one source file without target files results in implicitely calling +the builder multiple times (once for each source given). Giving multiple +source files together with target files results in a UserError exception. + +.RE +.IP +The +.I generator +and +.I action +arguments must not both be used for the same Builder. + +.IP source_ext_match +When the specified +.I action +argument is a dictionary, +the default behavior when a builder is passed +multiple source files is to make sure that the +extensions of all the source files match. +If it is legal for this builder to be +called with a list of source files with different extensions, +this check can be suppressed by setting +.B source_ext_match +to +.B None +or some other non-true value. +When +.B source_ext_match +is disable, +.B scons +will use the suffix of the first specified +source file to select the appropriate action from the +.I action +dictionary. + +In the following example, +the setting of +.B source_ext_match +prevents +.B scons +from exiting with an error +due to the mismatched suffixes of +.B foo.in +and +.BR foo.extra . + +.ES +b = Builder(action={'.in' : 'build $SOURCES > $TARGET'}, + source_ext_match = None) + +env = Environment(BUILDERS = {'MyBuild':b}) +env.MyBuild('foo.out', ['foo.in', 'foo.extra']) +.EE + +.IP env +A construction environment that can be used +to fetch source code using this Builder. +(Note that this environment is +.I not +used for normal builds of normal target files, +which use the environment that was +used to call the Builder for the target file.) + +.ES +b = Builder(action="build < $SOURCE > $TARGET") +env = Environment(BUILDERS = {'MyBuild' : b}) +env.MyBuild('foo.out', 'foo.in', my_arg = 'xyzzy') +.EE + +.IP chdir +A directory from which scons +will execute the +action(s) specified +for this Builder. +If the +.B chdir +argument is +a string or a directory Node, +scons will change to the specified directory. +If the +.B chdir +is not a string or Node +and is non-zero, +then scons will change to the +target file's directory. + +Note that scons will +.I not +automatically modify +its expansion of +construction variables like +.B $TARGET +and +.B $SOURCE +when using the chdir +keyword argument--that is, +the expanded file names +will still be relative to +the top-level SConstruct directory, +and consequently incorrect +relative to the chdir directory. +Builders created using chdir keyword argument, +will need to use construction variable +expansions like +.B ${TARGET.file} +and +.B ${SOURCE.file} +to use just the filename portion of the +targets and source. + +.ES +b = Builder(action="build < ${SOURCE.file} > ${TARGET.file}", + chdir=1) +env = Environment(BUILDERS = {'MyBuild' : b}) +env.MyBuild('sub/dir/foo.out', 'sub/dir/foo.in') +.EE + +.B WARNING: +Python only keeps one current directory +location for all of the threads. +This means that use of the +.B chdir +argument +will +.I not +work with the SCons +.B -j +option, +because individual worker threads spawned +by SCons interfere with each other +when they start changing directory. + +.RE +Any additional keyword arguments supplied +when a Builder object is created +(that is, when the Builder() function is called) +will be set in the executing construction +environment when the Builder object is called. +The canonical example here would be +to set a construction variable to +the repository of a source code system. + +Any additional keyword arguments supplied +when a Builder +.I object +is called +will only be associated with the target +created by that particular Builder call +(and any other files built as a +result of the call). + +These extra keyword arguments are passed to the +following functions: +command generator functions, +function Actions, +and emitter functions. + +.SS Action Objects + +The +.BR Builder () +function will turn its +.B action +keyword argument into an appropriate +internal Action object. +You can also explicity create Action objects +using the +.BR Action () +global function, +which can then be passed to the +.BR Builder () +function. +This can be used to configure +an Action object more flexibly, +or it may simply be more efficient +than letting each separate Builder object +create a separate Action +when multiple +Builder objects need to do the same thing. + +The +.BR Action () +global function +returns an appropriate object for the action +represented by the type of the first argument: + +.IP Action +If the first argument is already an Action object, +the object is simply returned. + +.IP String +If the first argument is a string, +a command-line Action is returned. +Note that the command-line string +may be preceded by an +.B @ +(at-sign) +to suppress printing of the specified command line, +or by a +.B \- +(hyphen) +to ignore the exit status from the specified command: + +.ES +Action('$CC -c -o $TARGET $SOURCES') + +# Doesn't print the line being executed. +Action('@build $TARGET $SOURCES') + +# Ignores return value +Action('-build $TARGET $SOURCES') +.EE +.\" XXX From Gary Ruben, 23 April 2002: +.\" What would be useful is a discussion of how you execute command +.\" shell commands ie. what is the process used to spawn the shell, pass +.\" environment variables to it etc., whether there is one shell per +.\" environment or one per command etc. It might help to look at the Gnu +.\" make documentation to see what they think is important to discuss about +.\" a build system. I'm sure you can do a better job of organising the +.\" documentation than they have :-) + +.IP List +If the first argument is a list, +then a list of Action objects is returned. +An Action object is created as necessary +for each element in the list. +If an element +.I within +the list is itself a list, +the internal list is the +command and arguments to be executed via +the command line. +This allows white space to be enclosed +in an argument by defining +a command in a list within a list: + +.ES +Action([['cc', '-c', '-DWHITE SPACE', '-o', '$TARGET', '$SOURCES']]) +.EE + +.IP Function +If the first argument is a Python function, +a function Action is returned. +The Python function must take three keyword arguments, +.B target +(a Node object representing the target file), +.B source +(a Node object representing the source file) +and +.B env +(the construction environment +used for building the target file). +The +.B target +and +.B source +arguments may be lists of Node objects if there is +more than one target file or source file. +The actual target and source file name(s) may +be retrieved from their Node objects +via the built-in Python str() function: + +.ES +target_file_name = str(target) +source_file_names = map(lambda x: str(x), source) +.EE +.IP +The function should return +.B 0 +or +.B None +to indicate a successful build of the target file(s). +The function may raise an exception +or return a non-zero exit status +to indicate an unsuccessful build. + +.ES +def build_it(target = None, source = None, env = None): + # build the target from the source + return 0 + +a = Action(build_it) +.EE + +If the action argument is not one of the above, +None is returned. +.PP + +The second argument is optional and is used to define the output +which is printed when the Action is actually performed. +In the absence of this parameter, +or if it's an empty string, +a default output depending on the type of the action is used. +For example, a command-line action will print the executed command. +The argument must be either a Python function or a string. + +In the first case, +it's a function that returns a string to be printed +to describe the action being executed. +The function may also be specified by the +.IR strfunction = +keyword argument. +Like a function to build a file, +this function must take three keyword arguments: +.B target +(a Node object representing the target file), +.B source +(a Node object representing the source file) +and +.BR env +(a construction environment). +The +.B target +and +.B source +arguments may be lists of Node objects if there is +more than one target file or source file. + +In the second case, you provide the string itself. +The string may also be specified by the +.IR cmdstr = +keyword argument. +The string typically contains variables, notably +$TARGET(S) and $SOURCE(S), or consists of just a single +variable, which is optionally defined somewhere else. +SCons itself heavily uses the latter variant. + +Examples: + +.ES +def build_it(target, source, env): + # build the target from the source + return 0 + +def string_it(target, source, env): + return "building '%s' from '%s'" % (target[0], source[0]) + +# Use a positional argument. +f = Action(build_it, string_it) +s = Action(build_it, "building '$TARGET' from '$SOURCE'") + +# Alternatively, use a keyword argument. +f = Action(build_it, strfunction=string_it) +s = Action(build_it, cmdstr="building '$TARGET' from '$SOURCE'") + +# You can provide a configurable variable. +l = Action(build_it, '$STRINGIT') +.EE + +The third and succeeding arguments, if present, +may either be a construction variable or a list of construction variables +whose values will be included in the signature of the Action +when deciding whether a target should be rebuilt because the action changed. +The variables may also be specified by a +.IR varlist = +keyword parameter; +if both are present, they are combined. +This is necessary whenever you want a target to be rebuilt +when a specific construction variable changes. +This is not often needed for a string action, +as the expanded variables will normally be part of the command line, +but may be needed if a Python function action uses +the value of a construction variable when generating the command line. + +.ES +def build_it(target, source, env): + # build the target from the 'XXX' construction variable + open(target[0], 'w').write(env['XXX']) + return 0 + +# Use positional arguments. +a = Action(build_it, '$STRINGIT', ['XXX']) + +# Alternatively, use a keyword argument. +a = Action(build_it, varlist=['XXX']) +.EE + +The +.BR Action () +global function +can be passed the following +optional keyword arguments +to modify the Action object's behavior: + +.IP +.B chdir +The +.B chdir +keyword argument specifies that +scons will execute the action +after changing to the specified directory. +If the +.B chdir +argument is +a string or a directory Node, +scons will change to the specified directory. +If the +.B chdir +argument +is not a string or Node +and is non-zero, +then scons will change to the +target file's directory. + +Note that scons will +.I not +automatically modify +its expansion of +construction variables like +.B $TARGET +and +.B $SOURCE +when using the chdir +keyword argument--that is, +the expanded file names +will still be relative to +the top-level SConstruct directory, +and consequently incorrect +relative to the chdir directory. +Builders created using chdir keyword argument, +will need to use construction variable +expansions like +.B ${TARGET.file} +and +.B ${SOURCE.file} +to use just the filename portion of the +targets and source. + +.ES +a = Action("build < ${SOURCE.file} > ${TARGET.file}", + chdir=1) +.EE + +.IP +.B exitstatfunc +The +.BR Action () +global function +also takes an +.B exitstatfunc +keyword argument +which specifies a function +that is passed the exit status +(or return value) +from the specified action +and can return an arbitrary +or modified value. +This can be used, for example, +to specify that an Action object's +return value should be ignored +under special conditions +and SCons should, therefore, +consider that the action always suceeds: + +.ES +def always_succeed(s): + # Always return 0, which indicates success. + return 0 +a = Action("build < ${SOURCE.file} > ${TARGET.file}", + exitstatfunc=always_succeed) +.EE + +.IP +.B batch_key +The +.B batch_key +keyword argument can be used +to specify that the Action can create multiple target files +by processing multiple independent source files simultaneously. +(The canonical example is "batch compilation" +of multiple object files +by passing multiple source files +to a single invocation of a compiler +such as Microsoft's Visual C / C++ compiler.) +If the +.B batch_key +argument is any non-False, non-callable Python value, +the configured Action object will cause +.B scons +to collect all targets built with the Action object +and configured with the same construction environment +into single invocations of the Action object's +command line or function. +Command lines will typically want to use the +.BR CHANGED_SOURCES +construction variable +(and possibly +.BR CHANGED_TARGETS +as well) +to only pass to the command line those sources that +have actually changed since their targets were built. + +Example: + +.ES +a = Action('build $CHANGED_SOURCES', batch_key=True) +.EE + +The +.B batch_key +argument may also be +a callable function +that returns a key that +will be used to identify different +"batches" of target files to be collected +for batch building. +A +.B batch_key +function must take the following arguments: + +.IP action +The action object. + +.IP env +The construction environment +configured for the target. + +.IP target +The list of targets for a particular configured action. + +.IP source +The list of source for a particular configured action. + +The returned key should typically +be a tuple of values derived from the arguments, +using any appropriate logic to decide +how multiple invocations should be batched. +For example, a +.B batch_key +function may decide to return +the value of a specific construction +variable from the +.B env +argument +which will cause +.B scons +to batch-build targets +with matching values of that variable, +or perhaps return the +.BR id () +of the entire construction environment, +in which case +.B scons +will batch-build +all targets configured with the same construction environment. +Returning +.B None +indicates that +the particular target should +.I not +be part of any batched build, +but instead will be built +by a separate invocation of action's +command or function. +Example: + +.ES +def batch_key(action, env, target, source): + tdir = target[0].dir + if tdir.name == 'special': + # Don't batch-build any target + # in the special/ subdirectory. + return None + return (id(action), id(env), tdir) +a = Action('build $CHANGED_SOURCES', batch_key=batch_key) +.EE + +.SS Miscellaneous Action Functions + +.B scons +supplies a number of functions +that arrange for various common +file and directory manipulations +to be performed. +These are similar in concept to "tasks" in the +Ant build tool, +although the implementation is slightly different. +These functions do not actually +perform the specified action +at the time the function is called, +but instead return an Action object +that can be executed at the +appropriate time. +(In Object-Oriented terminology, +these are actually +Action +.I Factory +functions +that return Action objects.) + +In practice, +there are two natural ways +that these +Action Functions +are intended to be used. + +First, +if you need +to perform the action +at the time the SConscript +file is being read, +you can use the +.B Execute +global function to do so: +.ES +Execute(Touch('file')) +.EE + +Second, +you can use these functions +to supply Actions in a list +for use by the +.B Command +method. +This can allow you to +perform more complicated +sequences of file manipulation +without relying +on platform-specific +external commands: +that +.ES +env = Environment(TMPBUILD = '/tmp/builddir') +env.Command('foo.out', 'foo.in', + [Mkdir('$TMPBUILD'), + Copy('$TMPBUILD', '${SOURCE.dir}'), + "cd $TMPBUILD && make", + Delete('$TMPBUILD')]) +.EE + +.TP +.RI Chmod( dest ", " mode ) +Returns an Action object that +changes the permissions on the specified +.I dest +file or directory to the specified +.IR mode . +Examples: + +.ES +Execute(Chmod('file', 0755)) + +env.Command('foo.out', 'foo.in', + [Copy('$TARGET', '$SOURCE'), + Chmod('$TARGET', 0755)]) +.EE + +.TP +.RI Copy( dest ", " src ) +Returns an Action object +that will copy the +.I src +source file or directory to the +.I dest +destination file or directory. +Examples: + +.ES +Execute(Copy('foo.output', 'foo.input')) + +env.Command('bar.out', 'bar.in', + Copy('$TARGET', '$SOURCE')) +.EE + +.TP +.RI Delete( entry ", [" must_exist ]) +Returns an Action that +deletes the specified +.IR entry , +which may be a file or a directory tree. +If a directory is specified, +the entire directory tree +will be removed. +If the +.I must_exist +flag is set, +then a Python error will be thrown +if the specified entry does not exist; +the default is +.BR must_exist=0 , +that is, the Action will silently do nothing +if the entry does not exist. +Examples: + +.ES +Execute(Delete('/tmp/buildroot')) + +env.Command('foo.out', 'foo.in', + [Delete('${TARGET.dir}'), + MyBuildAction]) + +Execute(Delete('file_that_must_exist', must_exist=1)) +.EE + +.TP +.RI Mkdir( dir ) +Returns an Action +that creates the specified +directory +.I dir . +Examples: + +.ES +Execute(Mkdir('/tmp/outputdir')) + +env.Command('foo.out', 'foo.in', + [Mkdir('/tmp/builddir'), + Copy('/tmp/builddir/foo.in', '$SOURCE'), + "cd /tmp/builddir && make", + Copy('$TARGET', '/tmp/builddir/foo.out')]) +.EE + +.TP +.RI Move( dest ", " src ) +Returns an Action +that moves the specified +.I src +file or directory to +the specified +.I dest +file or directory. +Examples: + +.ES +Execute(Move('file.destination', 'file.source')) + +env.Command('output_file', 'input_file', + [MyBuildAction, + Move('$TARGET', 'file_created_by_MyBuildAction')]) +.EE + +.TP +.RI Touch( file ) +Returns an Action +that updates the modification time +on the specified +.IR file . +Examples: + +.ES +Execute(Touch('file_to_be_touched')) + +env.Command('marker', 'input_file', + [MyBuildAction, + Touch('$TARGET')]) +.EE + +.SS Variable Substitution + +Before executing a command, +.B scons +performs construction variable interpolation on the strings that make up +the command line of builders. +Variables are introduced by a +.B $ +prefix. +Besides construction variables, scons provides the following +variables for each command execution: + +.IP CHANGED_SOURCES +The file names of all sources of the build command +that have changed since the target was last built. + +.IP CHANGED_TARGETS +The file names of all targets that would be built +from sources that have changed since the target was last built. + +.IP SOURCE +The file name of the source of the build command, +or the file name of the first source +if multiple sources are being built. + +.IP SOURCES +The file names of the sources of the build command. + +.IP TARGET +The file name of the target being built, +or the file name of the first target +if multiple targets are being built. + +.IP TARGETS +The file names of all targets being built. + +.IP UNCHANGED_SOURCES +The file names of all sources of the build command +that have +.I not +changed since the target was last built. + +.IP UNCHANGED_TARGETS +The file names of all targets that would be built +from sources that have +.I not +changed since the target was last built. + +(Note that the above variables are reserved +and may not be set in a construction environment.) + +.LP +For example, given the construction variable CC='cc', targets=['foo'], and +sources=['foo.c', 'bar.c']: + +.ES +action='$CC -c -o $TARGET $SOURCES' +.EE + +would produce the command line: + +.ES +cc -c -o foo foo.c bar.c +.EE + +Variable names may be surrounded by curly braces ({}) +to separate the name from the trailing characters. +Within the curly braces, a variable name may have +a Python slice subscript appended to select one +or more items from a list. +In the previous example, the string: + +.ES +${SOURCES[1]} +.EE + +would produce: + +.ES +bar.c +.EE + +Additionally, a variable name may +have the following special +modifiers appended within the enclosing curly braces +to modify the interpolated string: + +.IP base +The base path of the file name, +including the directory path +but excluding any suffix. + +.IP dir +The name of the directory in which the file exists. + +.IP file +The file name, +minus any directory portion. + +.IP filebase +Just the basename of the file, +minus any suffix +and minus the directory. + +.IP suffix +Just the file suffix. + +.IP abspath +The absolute path name of the file. + +.IP posix +The POSIX form of the path, +with directories separated by +.B / +(forward slashes) +not backslashes. +This is sometimes necessary on Windows systems +when a path references a file on other (POSIX) systems. + +.IP srcpath +The directory and file name to the source file linked to this file through +.BR VariantDir (). +If this file isn't linked, +it just returns the directory and filename unchanged. + +.IP srcdir +The directory containing the source file linked to this file through +.BR VariantDir (). +If this file isn't linked, +it just returns the directory part of the filename. + +.IP rsrcpath +The directory and file name to the source file linked to this file through +.BR VariantDir (). +If the file does not exist locally but exists in a Repository, +the path in the Repository is returned. +If this file isn't linked, it just returns the +directory and filename unchanged. + +.IP rsrcdir +The Repository directory containing the source file linked to this file through +.BR VariantDir (). +If this file isn't linked, +it just returns the directory part of the filename. + +.LP +For example, the specified target will +expand as follows for the corresponding modifiers: + +.ES +$TARGET => sub/dir/file.x +${TARGET.base} => sub/dir/file +${TARGET.dir} => sub/dir +${TARGET.file} => file.x +${TARGET.filebase} => file +${TARGET.suffix} => .x +${TARGET.abspath} => /top/dir/sub/dir/file.x + +SConscript('src/SConscript', variant_dir='sub/dir') +$SOURCE => sub/dir/file.x +${SOURCE.srcpath} => src/file.x +${SOURCE.srcdir} => src + +Repository('/usr/repository') +$SOURCE => sub/dir/file.x +${SOURCE.rsrcpath} => /usr/repository/src/file.x +${SOURCE.rsrcdir} => /usr/repository/src +.EE + +Note that curly braces braces may also be used +to enclose arbitrary Python code to be evaluated. +(In fact, this is how the above modifiers are substituted, +they are simply attributes of the Python objects +that represent TARGET, SOURCES, etc.) +See the section "Python Code Substitution" below, +for more thorough examples of +how this can be used. + +Lastly, a variable name +may be a callable Python function +associated with a +construction variable in the environment. +The function should +take four arguments: +.I target +- a list of target nodes, +.I source +- a list of source nodes, +.I env +- the construction environment, +.I for_signature +- a Boolean value that specifies +whether the function is being called +for generating a build signature. +SCons will insert whatever +the called function returns +into the expanded string: + +.ES +def foo(target, source, env, for_signature): + return "bar" + +# Will expand $BAR to "bar baz" +env=Environment(FOO=foo, BAR="$FOO baz") +.EE + +You can use this feature to pass arguments to a +Python function by creating a callable class +that stores one or more arguments in an object, +and then uses them when the +.B __call__() +method is called. +Note that in this case, +the entire variable expansion must +be enclosed by curly braces +so that the arguments will +be associated with the +instantiation of the class: + +.ES +class foo(object): + def __init__(self, arg): + self.arg = arg + + def __call__(self, target, source, env, for_signature): + return self.arg + " bar" + +# Will expand $BAR to "my argument bar baz" +env=Environment(FOO=foo, BAR="${FOO('my argument')} baz") +.EE + +.LP +The special pseudo-variables +.B "$(" +and +.B "$)" +may be used to surround parts of a command line +that may change +.I without +causing a rebuild--that is, +which are not included in the signature +of target files built with this command. +All text between +.B "$(" +and +.B "$)" +will be removed from the command line +before it is added to file signatures, +and the +.B "$(" +and +.B "$)" +will be removed before the command is executed. +For example, the command line: + +.ES +echo Last build occurred $( $TODAY $). > $TARGET +.EE + +.LP +would execute the command: + +.ES +echo Last build occurred $TODAY. > $TARGET +.EE + +.LP +but the command signature added to any target files would be: + +.ES +echo Last build occurred . > $TARGET +.EE + +.SS Python Code Substitution + +Any python code within +.BR "${" - "}" +pairs gets evaluated by python 'eval', with the python globals set to +the current environment's set of construction variables. +So in the following case: +.ES +env['COND'] = 0 +env.Command('foo.out', 'foo.in', + '''echo ${COND==1 and 'FOO' or 'BAR'} > $TARGET''') +.EE +the command executed will be either +.ES +echo FOO > foo.out +.EE +or +.ES +echo BAR > foo.out +.EE +according to the current value of env['COND'] when the command is +executed. The evaluation occurs when the target is being +built, not when the SConscript is being read. So if env['COND'] is changed +later in the SConscript, the final value will be used. + +Here's a more interesting example. Note that all of COND, FOO, and +BAR are environment variables, and their values are substituted into +the final command. FOO is a list, so its elements are interpolated +separated by spaces. + +.ES +env=Environment() +env['COND'] = 0 +env['FOO'] = ['foo1', 'foo2'] +env['BAR'] = 'barbar' +env.Command('foo.out', 'foo.in', + 'echo ${COND==1 and FOO or BAR} > $TARGET') + +# Will execute this: +# echo foo1 foo2 > foo.out +.EE + +SCons uses the following rules when converting construction variables into +command lines: + +.IP String +When the value is a string it is interpreted as a space delimited list of +command line arguments. + +.IP List +When the value is a list it is interpreted as a list of command line +arguments. Each element of the list is converted to a string. + +.IP Other +Anything that is not a list or string is converted to a string and +interpreted as a single command line argument. + +.IP Newline +Newline characters (\\n) delimit lines. The newline parsing is done after +all other parsing, so it is not possible for arguments (e.g. file names) to +contain embedded newline characters. This limitation will likely go away in +a future version of SCons. + +.SS Scanner Objects + +You can use the +.B Scanner +function to define +objects to scan +new file types for implicit dependencies. +The +.B Scanner +function accepts the following arguments: + +.IP function +This can be either: +1) a Python function that will process +the Node (file) +and return a list of File Nodes +representing the implicit +dependencies (file names) found in the contents; +or: +2) a dictionary that maps keys +(typically the file suffix, but see below for more discussion) +to other Scanners that should be called. + +If the argument is actually a Python function, +the function must take three or four arguments: + + def scanner_function(node, env, path): + + def scanner_function(node, env, path, arg=None): + +The +.B node +argument is the internal +SCons node representing the file. +Use +.B str(node) +to fetch the name of the file, and +.B node.get_contents() +to fetch contents of the file. +Note that the file is +.I not +guaranteed to exist before the scanner is called, +so the scanner function should check that +if there's any chance that the scanned file +might not exist +(for example, if it's built from other files). + +The +.B env +argument is the construction environment for the scan. +Fetch values from it using the +.B env.Dictionary() +method. + +The +.B path +argument is a tuple (or list) +of directories that can be searched +for files. +This will usually be the tuple returned by the +.B path_function +argument (see below). + +The +.B arg +argument is the argument supplied +when the scanner was created, if any. + +.IP name +The name of the Scanner. +This is mainly used +to identify the Scanner internally. + +.IP argument +An optional argument that, if specified, +will be passed to the scanner function +(described above) +and the path function +(specified below). + +.IP skeys +An optional list that can be used to +determine which scanner should be used for +a given Node. +In the usual case of scanning for file names, +this argument will be a list of suffixes +for the different file types that this +Scanner knows how to scan. +If the argument is a string, +then it will be expanded +into a list by the current environment. + +.IP path_function +A Python function that takes four or five arguments: +a construction environment, +a Node for the directory containing +the SConscript file in which +the first target was defined, +a list of target nodes, +a list of source nodes, +and an optional argument supplied +when the scanner was created. +The +.B path_function +returns a tuple of directories +that can be searched for files to be returned +by this Scanner object. +(Note that the +.BR FindPathDirs () +function can be used to return a ready-made +.B path_function +for a given construction variable name, +instead of having to write your own function from scratch.) + +.IP node_class +The class of Node that should be returned +by this Scanner object. +Any strings or other objects returned +by the scanner function +that are not of this class +will be run through the +.B node_factory +function. + +.IP node_factory +A Python function that will take a string +or other object +and turn it into the appropriate class of Node +to be returned by this Scanner object. + +.IP scan_check +An optional Python function that takes two arguments, +a Node (file) and a construction environment, +and returns whether the +Node should, in fact, +be scanned for dependencies. +This check can be used to eliminate unnecessary +calls to the scanner function when, +for example, the underlying file +represented by a Node does not yet exist. + +.IP recursive +An optional flag that +specifies whether this scanner should be re-invoked +on the dependency files returned by the scanner. +When this flag is not set, +the Node subsystem will +only invoke the scanner on the file being scanned, +and not (for example) also on the files +specified by the #include lines +in the file being scanned. +.I recursive +may be a callable function, +in which case it will be called with a list of +Nodes found and +should return a list of Nodes +that should be scanned recursively; +this can be used to select a specific subset of +Nodes for additional scanning. + +.RE +Note that +.B scons +has a global +.B SourceFileScanner +object that is used by +the +.BR Object (), +.BR SharedObject (), +and +.BR StaticObject () +builders to decide +which scanner should be used +for different file extensions. +You can using the +.BR SourceFileScanner.add_scanner () +method to add your own Scanner object +to the +.B scons +infrastructure +that builds target programs or +libraries from a list of +source files of different types: + +.ES +def xyz_scan(node, env, path): + contents = node.get_text_contents() + # Scan the contents and return the included files. + +XYZScanner = Scanner(xyz_scan) + +SourceFileScanner.add_scanner('.xyz', XYZScanner) + +env.Program('my_prog', ['file1.c', 'file2.f', 'file3.xyz']) +.EE + +.SH SYSTEM-SPECIFIC BEHAVIOR +SCons and its configuration files are very portable, +due largely to its implementation in Python. +There are, however, a few portability +issues waiting to trap the unwary. +.SS .C file suffix +SCons handles the upper-case +.B .C +file suffix differently, +depending on the capabilities of +the underlying system. +On a case-sensitive system +such as Linux or UNIX, +SCons treats a file with a +.B .C +suffix as a C++ source file. +On a case-insensitive system +such as Windows, +SCons treats a file with a +.B .C +suffix as a C source file. +.SS .F file suffix +SCons handles the upper-case +.B .F +file suffix differently, +depending on the capabilities of +the underlying system. +On a case-sensitive system +such as Linux or UNIX, +SCons treats a file with a +.B .F +suffix as a Fortran source file +that is to be first run through +the standard C preprocessor. +On a case-insensitive system +such as Windows, +SCons treats a file with a +.B .F +suffix as a Fortran source file that should +.I not +be run through the C preprocessor. +.SS Windows: Cygwin Tools and Cygwin Python vs. Windows Pythons +Cygwin supplies a set of tools and utilities +that let users work on a +Windows system using a more POSIX-like environment. +The Cygwin tools, including Cygwin Python, +do this, in part, +by sharing an ability to interpret UNIX-like path names. +For example, the Cygwin tools +will internally translate a Cygwin path name +like /cygdrive/c/mydir +to an equivalent Windows pathname +of C:/mydir (equivalent to C:\\mydir). + +Versions of Python +that are built for native Windows execution, +such as the python.org and ActiveState versions, +do not have the Cygwin path name semantics. +This means that using a native Windows version of Python +to build compiled programs using Cygwin tools +(such as gcc, bison, and flex) +may yield unpredictable results. +"Mixing and matching" in this way +can be made to work, +but it requires careful attention to the use of path names +in your SConscript files. + +In practice, users can sidestep +the issue by adopting the following rules: +When using gcc, +use the Cygwin-supplied Python interpreter +to run SCons; +when using Microsoft Visual C/C++ +(or some other Windows compiler) +use the python.org or ActiveState version of Python +to run SCons. +.SS Windows: scons.bat file +On Windows systems, +SCons is executed via a wrapper +.B scons.bat +file. +This has (at least) two ramifications: + +First, Windows command-line users +that want to use variable assignment +on the command line +may have to put double quotes +around the assignments: + +.ES +scons "FOO=BAR" "BAZ=BLEH" +.EE + +Second, the Cygwin shell does not +recognize this file as being the same +as an +.B scons +command issued at the command-line prompt. +You can work around this either by +executing +.B scons.bat +from the Cygwin command line, +or by creating a wrapper shell +script named +.B scons . + +.SS MinGW + +The MinGW bin directory must be in your PATH environment variable or the +PATH variable under the ENV construction variable for SCons +to detect and use the MinGW tools. When running under the native Windows +Python interpreter, SCons will prefer the MinGW tools over the Cygwin +tools, if they are both installed, regardless of the order of the bin +directories in the PATH variable. If you have both MSVC and MinGW +installed and you want to use MinGW instead of MSVC, +then you must explictly tell SCons to use MinGW by passing + +.ES +tools=['mingw'] +.EE + +to the Environment() function, because SCons will prefer the MSVC tools +over the MinGW tools. + +.SH EXAMPLES + +To help you get started using SCons, +this section contains a brief overview of some common tasks. + +.SS Basic Compilation From a Single Source File + +.ES +env = Environment() +env.Program(target = 'foo', source = 'foo.c') +.EE + +Note: Build the file by specifying +the target as an argument +("scons foo" or "scons foo.exe"). +or by specifying a dot ("scons ."). + +.SS Basic Compilation From Multiple Source Files + +.ES +env = Environment() +env.Program(target = 'foo', source = Split('f1.c f2.c f3.c')) +.EE + +.SS Setting a Compilation Flag + +.ES +env = Environment(CCFLAGS = '-g') +env.Program(target = 'foo', source = 'foo.c') +.EE + +.SS Search The Local Directory For .h Files + +Note: You do +.I not +need to set CCFLAGS to specify -I options by hand. +SCons will construct the right -I options from CPPPATH. + +.ES +env = Environment(CPPPATH = ['.']) +env.Program(target = 'foo', source = 'foo.c') +.EE + +.SS Search Multiple Directories For .h Files + +.ES +env = Environment(CPPPATH = ['include1', 'include2']) +env.Program(target = 'foo', source = 'foo.c') +.EE + +.SS Building a Static Library + +.ES +env = Environment() +env.StaticLibrary(target = 'foo', source = Split('l1.c l2.c')) +env.StaticLibrary(target = 'bar', source = ['l3.c', 'l4.c']) +.EE + +.SS Building a Shared Library + +.ES +env = Environment() +env.SharedLibrary(target = 'foo', source = ['l5.c', 'l6.c']) +env.SharedLibrary(target = 'bar', source = Split('l7.c l8.c')) +.EE + +.SS Linking a Local Library Into a Program + +.ES +env = Environment(LIBS = 'mylib', LIBPATH = ['.']) +env.Library(target = 'mylib', source = Split('l1.c l2.c')) +env.Program(target = 'prog', source = ['p1.c', 'p2.c']) +.EE + +.SS Defining Your Own Builder Object + +Notice that when you invoke the Builder, +you can leave off the target file suffix, +and SCons will add it automatically. + +.ES +bld = Builder(action = 'pdftex < $SOURCES > $TARGET' + suffix = '.pdf', + src_suffix = '.tex') +env = Environment(BUILDERS = {'PDFBuilder' : bld}) +env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex') + +# The following creates "bar.pdf" from "bar.tex" +env.PDFBuilder(target = 'bar', source = 'bar') +.EE + +Note also that the above initialization +overwrites the default Builder objects, +so the Environment created above +can not be used call Builders like env.Program(), +env.Object(), env.StaticLibrary(), etc. + +.SS Adding Your Own Builder Object to an Environment + +.ES +bld = Builder(action = 'pdftex < $SOURCES > $TARGET' + suffix = '.pdf', + src_suffix = '.tex') +env = Environment() +env.Append(BUILDERS = {'PDFBuilder' : bld}) +env.PDFBuilder(target = 'foo.pdf', source = 'foo.tex') +env.Program(target = 'bar', source = 'bar.c') +.EE + +You also can use other Pythonic techniques to add +to the BUILDERS construction variable, such as: + +.ES +env = Environment() +env['BUILDERS]['PDFBuilder'] = bld +.EE + +.SS Defining Your Own Scanner Object + +The following example shows an extremely simple scanner (the +.BR kfile_scan () +function) +that doesn't use a search path at all +and simply returns the +file names present on any +.B include +lines in the scanned file. +This would implicitly assume that all included +files live in the top-level directory: + +.ES +import re + +'\" Note: the \\ in the following are for the benefit of nroff/troff, +'\" not inappropriate doubled escape characters within the r'' raw string. +include_re = re.compile(r'^include\\s+(\\S+)$', re.M) + +def kfile_scan(node, env, path, arg): + contents = node.get_text_contents() + includes = include_re.findall(contents) + return env.File(includes) + +kscan = Scanner(name = 'kfile', + function = kfile_scan, + argument = None, + skeys = ['.k']) +scanners = Environment().Dictionary('SCANNERS') +env = Environment(SCANNERS = scanners + [kscan]) + +env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET') + +bar_in = File('bar.in') +env.Command('bar', bar_in, 'kprocess $SOURCES > $TARGET') +bar_in.target_scanner = kscan +.EE + +It is important to note that you +have to return a list of File nodes from the scan function, simple +strings for the file names won't do. As in the examples we are showing here, +you can use the +.BR File() +function of your current Environment in order to create nodes on the fly from +a sequence of file names with relative paths. + +Here is a similar but more complete example that searches +a path of directories +(specified as the +.B MYPATH +construction variable) +for files that actually exist: + +.ES +import re +import os +include_re = re.compile(r'^include\\s+(\\S+)$', re.M) + +def my_scan(node, env, path, arg): + contents = node.get_text_contents() + includes = include_re.findall(contents) + if includes == []: + return [] + results = [] + for inc in includes: + for dir in path: + file = str(dir) + os.sep + inc + if os.path.exists(file): + results.append(file) + break + return env.File(results) + +scanner = Scanner(name = 'myscanner', + function = my_scan, + argument = None, + skeys = ['.x'], + path_function = FindPathDirs('MYPATH') + ) +scanners = Environment().Dictionary('SCANNERS') +env = Environment(SCANNERS = scanners + [scanner], + MYPATH = ['incs']) + +env.Command('foo', 'foo.x', 'xprocess < $SOURCES > $TARGET') +.EE + +The +.BR FindPathDirs () +function used in the previous example returns a function +(actually a callable Python object) +that will return a list of directories +specified in the +.B $MYPATH +construction variable. It lets SCons detect the file +.B incs/foo.inc +, even if +.B foo.x +contains the line +.B include foo.inc +only. +If you need to customize how the search path is derived, +you would provide your own +.B path_function +argument when creating the Scanner object, +as follows: + +.ES +# MYPATH is a list of directories to search for files in +def pf(env, dir, target, source, arg): + top_dir = Dir('#').abspath + results = [] + if 'MYPATH' in env: + for p in env['MYPATH']: + results.append(top_dir + os.sep + p) + return results + +scanner = Scanner(name = 'myscanner', + function = my_scan, + argument = None, + skeys = ['.x'], + path_function = pf + ) +.EE + +.SS Creating a Hierarchical Build + +Notice that the file names specified in a subdirectory's +SConscript +file are relative to that subdirectory. + +.ES +SConstruct: + + env = Environment() + env.Program(target = 'foo', source = 'foo.c') + + SConscript('sub/SConscript') + +sub/SConscript: + + env = Environment() + # Builds sub/foo from sub/foo.c + env.Program(target = 'foo', source = 'foo.c') + + SConscript('dir/SConscript') + +sub/dir/SConscript: + + env = Environment() + # Builds sub/dir/foo from sub/dir/foo.c + env.Program(target = 'foo', source = 'foo.c') +.EE + +.SS Sharing Variables Between SConscript Files + +You must explicitly Export() and Import() variables that +you want to share between SConscript files. + +.ES +SConstruct: + + env = Environment() + env.Program(target = 'foo', source = 'foo.c') + + Export("env") + SConscript('subdirectory/SConscript') + +subdirectory/SConscript: + + Import("env") + env.Program(target = 'foo', source = 'foo.c') +.EE + +.SS Building Multiple Variants From the Same Source + +Use the variant_dir keyword argument to +the SConscript function to establish +one or more separate variant build directory trees +for a given source directory: + +.ES +SConstruct: + + cppdefines = ['FOO'] + Export("cppdefines") + SConscript('src/SConscript', variant_dir='foo') + + cppdefines = ['BAR'] + Export("cppdefines") + SConscript('src/SConscript', variant_dir='bar') + +src/SConscript: + + Import("cppdefines") + env = Environment(CPPDEFINES = cppdefines) + env.Program(target = 'src', source = 'src.c') +.EE + +Note the use of the Export() method +to set the "cppdefines" variable to a different +value each time we call the SConscript function. + +.SS Hierarchical Build of Two Libraries Linked With a Program + +.ES +SConstruct: + + env = Environment(LIBPATH = ['#libA', '#libB']) + Export('env') + SConscript('libA/SConscript') + SConscript('libB/SConscript') + SConscript('Main/SConscript') + +libA/SConscript: + + Import('env') + env.Library('a', Split('a1.c a2.c a3.c')) + +libB/SConscript: + + Import('env') + env.Library('b', Split('b1.c b2.c b3.c')) + +Main/SConscript: + + Import('env') + e = env.Copy(LIBS = ['a', 'b']) + e.Program('foo', Split('m1.c m2.c m3.c')) +.EE + +The '#' in the LIBPATH directories specify that they're relative to the +top-level directory, so they don't turn into "Main/libA" when they're +used in Main/SConscript. + +Specifying only 'a' and 'b' for the library names +allows SCons to append the appropriate library +prefix and suffix for the current platform +(for example, 'liba.a' on POSIX systems, +\&'a.lib' on Windows). + +.SS Customizing construction variables from the command line. + +The following would allow the C compiler to be specified on the command +line or in the file custom.py. + +.ES +vars = Variables('custom.py') +vars.Add('CC', 'The C compiler.') +env = Environment(variables=vars) +Help(vars.GenerateHelpText(env)) +.EE + +The user could specify the C compiler on the command line: + +.ES +scons "CC=my_cc" +.EE + +or in the custom.py file: + +.ES +CC = 'my_cc' +.EE + +or get documentation on the options: + +.ES +$ scons -h + +CC: The C compiler. + default: None + actual: cc + +.EE + +.SS Using Microsoft Visual C++ precompiled headers + +Since windows.h includes everything and the kitchen sink, it can take quite +some time to compile it over and over again for a bunch of object files, so +Microsoft provides a mechanism to compile a set of headers once and then +include the previously compiled headers in any object file. This +technology is called precompiled headers. The general recipe is to create a +file named "StdAfx.cpp" that includes a single header named "StdAfx.h", and +then include every header you want to precompile in "StdAfx.h", and finally +include "StdAfx.h" as the first header in all the source files you are +compiling to object files. For example: + +StdAfx.h: +.ES +#include +#include +.EE + +StdAfx.cpp: +.ES +#include +.EE + +Foo.cpp: +.ES +#include + +/* do some stuff */ +.EE + +Bar.cpp: +.ES +#include + +/* do some other stuff */ +.EE + +SConstruct: +.ES +env=Environment() +env['PCHSTOP'] = 'StdAfx.h' +env['PCH'] = env.PCH('StdAfx.cpp')[0] +env.Program('MyApp', ['Foo.cpp', 'Bar.cpp']) +.EE + +For more information see the document for the PCH builder, and the PCH and +PCHSTOP construction variables. To learn about the details of precompiled +headers consult the MSDN documention for /Yc, /Yu, and /Yp. + +.SS Using Microsoft Visual C++ external debugging information + +Since including debugging information in programs and shared libraries can +cause their size to increase significantly, Microsoft provides a mechanism +for including the debugging information in an external file called a PDB +file. SCons supports PDB files through the PDB construction +variable. + +SConstruct: +.ES +env=Environment() +env['PDB'] = 'MyApp.pdb' +env.Program('MyApp', ['Foo.cpp', 'Bar.cpp']) +.EE + +For more information see the document for the PDB construction variable. + +.SH ENVIRONMENT + +.IP SCONS_LIB_DIR +Specifies the directory that contains the SCons Python module directory +(e.g. /home/aroach/scons-src-0.01/src/engine). + +.IP SCONSFLAGS +A string of options that will be used by scons in addition to those passed +on the command line. + +.SH "SEE ALSO" +.B scons +User Manual, +.B scons +Design Document, +.B scons +source code. + +.SH AUTHORS +Steven Knight +.br +Anthony Roach + diff --git a/sconsign.1 b/sconsign.1 new file mode 100644 index 0000000..2cd2fca --- /dev/null +++ b/sconsign.1 @@ -0,0 +1,208 @@ +.\" Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining +.\" a copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, sublicense, and/or sell copies of the Software, and to +.\" permit persons to whom the Software is furnished to do so, subject to +.\" the following conditions: +.\" +.\" The above copyright notice and this permission notice shall be included +.\" in all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +.\" KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +.\" WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +.\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +.\" +.\" doc/man/sconsign.1 5357 2011/09/09 21:31:03 bdeegan +.\" +.\" ES - Example Start - indents and turns off line fill +.de ES +.RS +.nf +.. +.\" EE - Example End - ends indent and turns line fill back on +.de EE +.RE +.fi +.. +.TH SCONSIGN 1 "September 2011" +.SH NAME +sconsign \- print SCons .sconsign file information +.SH SYNOPSIS +.B sconsign +[ +.IR options ... +] +.IR file +[ ... ] +.SH DESCRIPTION + +The +.B sconsign +command +displays the contents of one or more +.B .sconsign +files specified by the user. + +By default, +.B sconsign +dumps the entire contents of the +specified file(s). +Each entry is printed in the following format: + + file: signature timestamp length + implicit_dependency_1: signature timestamp length + implicit_dependency_2: signature timestamp length + action_signature [action string] + +.B None +is printed +in place of any missing timestamp, bsig, or csig +values for +any entry +or any of its dependencies. +If the entry has no implicit dependencies, +or no build action, +the lines are simply omitted. + +By default, +.B sconsign +assumes that any +.I file +arguments that end with a +.B .dbm +suffix contains +signature entries for +more than one directory +(that is, +was specified by the +.B SConsignFile () +function). +Any +.I file +argument that does not end in +.B .dbm +is assumed to be a traditional +.B .sconsign +file containing the signature entries +for a single directory. +An explicit format +may be specified using the +.B -f +or +.B --file= +options. + +.SH OPTIONS + +Various options control what information is printed +and the format: + +.TP +-a, --act, --action +Prints the build action information +for all entries or the specified entries. + +.TP +-c, --csig +Prints the content signature (csig) information +for all entries or the specified entries. + +.TP +-d DIRECTORY, --dir=DIRECTORY +When the signatures are being +read from a +.B .dbm +file, or the +.B -f dbm +or +.B --format=dbm +options are used, +prints information about +only the signatures +for entries in the specified +.IR DIRECTORY . + +.TP +-e ENTRY, --entry=ENTRY +Prints information about only the specified +.IR ENTRY . +Multiple -e options may be used, +in which case information about each +.I ENTRY +is printed in the order in which the +options are specified on the command line. + +.TP +-f FORMAT, --format=FORMAT +The file(s) to be printed +are in the specified +.IR FORMAT . +Legal values are +.B dbm +(the DBM format used +when the +.BR SConsignFile () +function is used) +or +.B sconsign +(the default format +used for an individual +.B .sconsign +file in each directory). + +.TP +-h, --help +Prints a help message and exits. + +.TP +-i, --implicit +Prints the list of cached implicit dependencies +for all entries or the the specified entries. + +.TP +--raw +Prints a pretty-printed representation +of the raw Python dictionary that holds +build information about individual entry +(both the entry itself or its implicit dependencies). +An entry's build action is still printed in its usual format. + +.TP +-r, --readable +Prints timestamps in a human-readable string, +enclosed in single quotes. + +.TP +-t, --timestamp +Prints the timestamp information +for all entries or the specified entries. + +.TP +-v, --verbose +Prints labels identifying each field being printed. + +.SH ENVIRONMENT + +.IP SCONS_LIB_DIR +Specifies the directory that contains the SCons Python module directory +(e.g. /home/aroach/scons-src-0.01/src/engine). +on the command line. + +.SH "SEE ALSO" +.BR scons , +.B scons +User Manual, +.B scons +Design Document, +.B scons +source code. + +.SH AUTHORS +Steven Knight diff --git a/script/scons b/script/scons new file mode 100644 index 0000000..0e5b8ec --- /dev/null +++ b/script/scons @@ -0,0 +1,196 @@ +#! /usr/bin/env python +# +# SCons - a Software Constructor +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/script/scons.py 5357 2011/09/09 21:31:03 bdeegan" + +__version__ = "2.1.0" + +__build__ = "r5357[MODIFIED]" + +__buildsys__ = "ubuntu" + +__date__ = "2011/09/09 21:31:03" + +__developer__ = "bdeegan" + +import os +import sys + +############################################################################## +# BEGIN STANDARD SCons SCRIPT HEADER +# +# This is the cut-and-paste logic so that a self-contained script can +# interoperate correctly with different SCons versions and installation +# locations for the engine. If you modify anything in this section, you +# should also change other scripts that use this same header. +############################################################################## + +# Strip the script directory from sys.path() so on case-insensitive +# (WIN32) systems Python doesn't think that the "scons" script is the +# "SCons" package. Replace it with our own library directories +# (version-specific first, in case they installed by hand there, +# followed by generic) so we pick up the right version of the build +# engine modules if they're in either directory. + + +# Check to see if the python version is > 3.0 which is currently unsupported +# If so exit with error message +try: + if sys.version_info >= (3,0,0): + msg = "scons: *** SCons version %s does not run under Python version %s.\n\ +Python 3.0 and later are not yet supported.\n" + sys.stderr.write(msg % (__version__, sys.version.split()[0])) + sys.exit(1) +except AttributeError: + # Pre-1.6 Python has no sys.version_info + # No need to check version as we then know the version is < 3.0.0 and supported + pass + +script_dir = sys.path[0] + +if script_dir in sys.path: + sys.path.remove(script_dir) + +libs = [] + +if "SCONS_LIB_DIR" in os.environ: + libs.append(os.environ["SCONS_LIB_DIR"]) + +local_version = 'scons-local-' + __version__ +local = 'scons-local' +if script_dir: + local_version = os.path.join(script_dir, local_version) + local = os.path.join(script_dir, local) +libs.append(os.path.abspath(local_version)) +libs.append(os.path.abspath(local)) + +scons_version = 'scons-%s' % __version__ + +# preferred order of scons lookup paths +prefs = [] + +try: + import pkg_resources +except ImportError: + pass +else: + # when running from an egg add the egg's directory + try: + d = pkg_resources.get_distribution('scons') + except pkg_resources.DistributionNotFound: + pass + else: + prefs.append(d.location) + +if sys.platform == 'win32': + # sys.prefix is (likely) C:\Python*; + # check only C:\Python*. + prefs.append(sys.prefix) + prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) +else: + # On other (POSIX) platforms, things are more complicated due to + # the variety of path names and library locations. Try to be smart + # about it. + if script_dir == 'bin': + # script_dir is `pwd`/bin; + # check `pwd`/lib/scons*. + prefs.append(os.getcwd()) + else: + if script_dir == '.' or script_dir == '': + script_dir = os.getcwd() + head, tail = os.path.split(script_dir) + if tail == "bin": + # script_dir is /foo/bin; + # check /foo/lib/scons*. + prefs.append(head) + + head, tail = os.path.split(sys.prefix) + if tail == "usr": + # sys.prefix is /foo/usr; + # check /foo/usr/lib/scons* first, + # then /foo/usr/local/lib/scons*. + prefs.append(sys.prefix) + prefs.append(os.path.join(sys.prefix, "local")) + elif tail == "local": + h, t = os.path.split(head) + if t == "usr": + # sys.prefix is /foo/usr/local; + # check /foo/usr/local/lib/scons* first, + # then /foo/usr/lib/scons*. + prefs.append(sys.prefix) + prefs.append(head) + else: + # sys.prefix is /foo/local; + # check only /foo/local/lib/scons*. + prefs.append(sys.prefix) + else: + # sys.prefix is /foo (ends in neither /usr or /local); + # check only /foo/lib/scons*. + prefs.append(sys.prefix) + + temp = [os.path.join(x, 'lib') for x in prefs] + temp.extend([os.path.join(x, + 'lib', + 'python' + sys.version[:3], + 'site-packages') for x in prefs]) + prefs = temp + + # Add the parent directory of the current python's library to the + # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, + # not /usr/lib. + try: + libpath = os.__file__ + except AttributeError: + pass + else: + # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*. + libpath, tail = os.path.split(libpath) + # Split /usr/libfoo/python* to /usr/libfoo + libpath, tail = os.path.split(libpath) + # Check /usr/libfoo/scons*. + prefs.append(libpath) + +# Look first for 'scons-__version__' in all of our preference libs, +# then for 'scons'. +libs.extend([os.path.join(x, scons_version) for x in prefs]) +libs.extend([os.path.join(x, 'scons') for x in prefs]) + +sys.path = libs + sys.path + +############################################################################## +# END STANDARD SCons SCRIPT HEADER +############################################################################## + +if __name__ == "__main__": + import SCons.Script + # this does all the work, and calls sys.exit + # with the proper exit status when done. + SCons.Script.main() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/script/scons-time b/script/scons-time new file mode 100644 index 0000000..ace2d85 --- /dev/null +++ b/script/scons-time @@ -0,0 +1,1544 @@ +#!/usr/bin/env python +# +# scons-time - run SCons timings and collect statistics +# +# A script for running a configuration through SCons with a standard +# set of invocations to collect timing and memory statistics and to +# capture the results in a consistent set of output files for display +# and analysis. +# + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import division +from __future__ import nested_scopes + +__revision__ = "src/script/scons-time.py 5357 2011/09/09 21:31:03 bdeegan" + +import getopt +import glob +import os +import re +import shutil +import sys +import tempfile +import time + +try: + sorted +except NameError: + # Pre-2.4 Python has no sorted() function. + # + # The pre-2.4 Python list.sort() method does not support + # list.sort(key=) nor list.sort(reverse=) keyword arguments, so + # we must implement the functionality of those keyword arguments + # by hand instead of passing them to list.sort(). + def sorted(iterable, cmp=None, key=None, reverse=False): + if key is not None: + result = [(key(x), x) for x in iterable] + else: + result = iterable[:] + if cmp is None: + # Pre-2.3 Python does not support list.sort(None). + result.sort() + else: + result.sort(cmp) + if key is not None: + result = [t1 for t0,t1 in result] + if reverse: + result.reverse() + return result + +if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: + # We can't apply the 'callable' fixer until the floor is 2.6, but the + # '-3' option to Python 2.6 and 2.7 generates almost ten thousand + # warnings. This hack allows us to run regression tests with the '-3' + # option by replacing the callable() built-in function with a hack + # that performs the same function but doesn't generate the warning. + # Note that this hack is ONLY intended to be used for regression + # testing, and should NEVER be used for real runs. + from types import ClassType + def callable(obj): + if hasattr(obj, '__call__'): return True + if isinstance(obj, (ClassType, type)): return True + return False + +def make_temp_file(**kw): + try: + result = tempfile.mktemp(**kw) + try: + result = os.path.realpath(result) + except AttributeError: + # Python 2.1 has no os.path.realpath() method. + pass + except TypeError: + try: + save_template = tempfile.template + prefix = kw['prefix'] + del kw['prefix'] + tempfile.template = prefix + result = tempfile.mktemp(**kw) + finally: + tempfile.template = save_template + return result + +def HACK_for_exec(cmd, *args): + ''' + For some reason, Python won't allow an exec() within a function + that also declares an internal function (including lambda functions). + This function is a hack that calls exec() in a function with no + internal functions. + ''' + if not args: exec(cmd) + elif len(args) == 1: exec cmd in args[0] + else: exec cmd in args[0], args[1] + +class Plotter(object): + def increment_size(self, largest): + """ + Return the size of each horizontal increment line for a specified + maximum value. This returns a value that will provide somewhere + between 5 and 9 horizontal lines on the graph, on some set of + boundaries that are multiples of 10/100/1000/etc. + """ + i = largest // 5 + if not i: + return largest + multiplier = 1 + while i >= 10: + i = i // 10 + multiplier = multiplier * 10 + return i * multiplier + + def max_graph_value(self, largest): + # Round up to next integer. + largest = int(largest) + 1 + increment = self.increment_size(largest) + return ((largest + increment - 1) // increment) * increment + +class Line(object): + def __init__(self, points, type, title, label, comment, fmt="%s %s"): + self.points = points + self.type = type + self.title = title + self.label = label + self.comment = comment + self.fmt = fmt + + def print_label(self, inx, x, y): + if self.label: + print 'set label %s "%s" at %s,%s right' % (inx, self.label, x, y) + + def plot_string(self): + if self.title: + title_string = 'title "%s"' % self.title + else: + title_string = 'notitle' + return "'-' %s with lines lt %s" % (title_string, self.type) + + def print_points(self, fmt=None): + if fmt is None: + fmt = self.fmt + if self.comment: + print '# %s' % self.comment + for x, y in self.points: + # If y is None, it usually represents some kind of break + # in the line's index number. We might want to represent + # this some way rather than just drawing the line straight + # between the two points on either side. + if not y is None: + print fmt % (x, y) + print 'e' + + def get_x_values(self): + return [ p[0] for p in self.points ] + + def get_y_values(self): + return [ p[1] for p in self.points ] + +class Gnuplotter(Plotter): + + def __init__(self, title, key_location): + self.lines = [] + self.title = title + self.key_location = key_location + + def line(self, points, type, title=None, label=None, comment=None, fmt='%s %s'): + if points: + line = Line(points, type, title, label, comment, fmt) + self.lines.append(line) + + def plot_string(self, line): + return line.plot_string() + + def vertical_bar(self, x, type, label, comment): + if self.get_min_x() <= x and x <= self.get_max_x(): + points = [(x, 0), (x, self.max_graph_value(self.get_max_y()))] + self.line(points, type, label, comment) + + def get_all_x_values(self): + result = [] + for line in self.lines: + result.extend(line.get_x_values()) + return [r for r in result if not r is None] + + def get_all_y_values(self): + result = [] + for line in self.lines: + result.extend(line.get_y_values()) + return [r for r in result if not r is None] + + def get_min_x(self): + try: + return self.min_x + except AttributeError: + try: + self.min_x = min(self.get_all_x_values()) + except ValueError: + self.min_x = 0 + return self.min_x + + def get_max_x(self): + try: + return self.max_x + except AttributeError: + try: + self.max_x = max(self.get_all_x_values()) + except ValueError: + self.max_x = 0 + return self.max_x + + def get_min_y(self): + try: + return self.min_y + except AttributeError: + try: + self.min_y = min(self.get_all_y_values()) + except ValueError: + self.min_y = 0 + return self.min_y + + def get_max_y(self): + try: + return self.max_y + except AttributeError: + try: + self.max_y = max(self.get_all_y_values()) + except ValueError: + self.max_y = 0 + return self.max_y + + def draw(self): + + if not self.lines: + return + + if self.title: + print 'set title "%s"' % self.title + print 'set key %s' % self.key_location + + min_y = self.get_min_y() + max_y = self.max_graph_value(self.get_max_y()) + incr = (max_y - min_y) / 10.0 + start = min_y + (max_y / 2.0) + (2.0 * incr) + position = [ start - (i * incr) for i in range(5) ] + + inx = 1 + for line in self.lines: + line.print_label(inx, line.points[0][0]-1, + position[(inx-1) % len(position)]) + inx += 1 + + plot_strings = [ self.plot_string(l) for l in self.lines ] + print 'plot ' + ', \\\n '.join(plot_strings) + + for line in self.lines: + line.print_points() + + + +def untar(fname): + import tarfile + tar = tarfile.open(name=fname, mode='r') + for tarinfo in tar: + tar.extract(tarinfo) + tar.close() + +def unzip(fname): + import zipfile + zf = zipfile.ZipFile(fname, 'r') + for name in zf.namelist(): + dir = os.path.dirname(name) + try: + os.makedirs(dir) + except: + pass + open(name, 'w').write(zf.read(name)) + +def read_tree(dir): + for dirpath, dirnames, filenames in os.walk(dir): + for fn in filenames: + fn = os.path.join(dirpath, fn) + if os.path.isfile(fn): + open(fn, 'rb').read() + +def redirect_to_file(command, log): + return '%s > %s 2>&1' % (command, log) + +def tee_to_file(command, log): + return '%s 2>&1 | tee %s' % (command, log) + + + +class SConsTimer(object): + """ + Usage: scons-time SUBCOMMAND [ARGUMENTS] + Type "scons-time help SUBCOMMAND" for help on a specific subcommand. + + Available subcommands: + func Extract test-run data for a function + help Provides help + mem Extract --debug=memory data from test runs + obj Extract --debug=count data from test runs + time Extract --debug=time data from test runs + run Runs a test configuration + """ + + name = 'scons-time' + name_spaces = ' '*len(name) + + def makedict(**kw): + return kw + + default_settings = makedict( + aegis = 'aegis', + aegis_project = None, + chdir = None, + config_file = None, + initial_commands = [], + key_location = 'bottom left', + orig_cwd = os.getcwd(), + outdir = None, + prefix = '', + python = '"%s"' % sys.executable, + redirect = redirect_to_file, + scons = None, + scons_flags = '--debug=count --debug=memory --debug=time --debug=memoizer', + scons_lib_dir = None, + scons_wrapper = None, + startup_targets = '--help', + subdir = None, + subversion_url = None, + svn = 'svn', + svn_co_flag = '-q', + tar = 'tar', + targets = '', + targets0 = None, + targets1 = None, + targets2 = None, + title = None, + unzip = 'unzip', + verbose = False, + vertical_bars = [], + + unpack_map = { + '.tar.gz' : (untar, '%(tar)s xzf %%s'), + '.tgz' : (untar, '%(tar)s xzf %%s'), + '.tar' : (untar, '%(tar)s xf %%s'), + '.zip' : (unzip, '%(unzip)s %%s'), + }, + ) + + run_titles = [ + 'Startup', + 'Full build', + 'Up-to-date build', + ] + + run_commands = [ + '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targets0)s', + '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s', + '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)s', + ] + + stages = [ + 'pre-read', + 'post-read', + 'pre-build', + 'post-build', + ] + + stage_strings = { + 'pre-read' : 'Memory before reading SConscript files:', + 'post-read' : 'Memory after reading SConscript files:', + 'pre-build' : 'Memory before building targets:', + 'post-build' : 'Memory after building targets:', + } + + memory_string_all = 'Memory ' + + default_stage = stages[-1] + + time_strings = { + 'total' : 'Total build time', + 'SConscripts' : 'Total SConscript file execution time', + 'SCons' : 'Total SCons execution time', + 'commands' : 'Total command execution time', + } + + time_string_all = 'Total .* time' + + # + + def __init__(self): + self.__dict__.update(self.default_settings) + + # Functions for displaying and executing commands. + + def subst(self, x, dictionary): + try: + return x % dictionary + except TypeError: + # x isn't a string (it's probably a Python function), + # so just return it. + return x + + def subst_variables(self, command, dictionary): + """ + Substitutes (via the format operator) the values in the specified + dictionary into the specified command. + + The command can be an (action, string) tuple. In all cases, we + perform substitution on strings and don't worry if something isn't + a string. (It's probably a Python function to be executed.) + """ + try: + command + '' + except TypeError: + action = command[0] + string = command[1] + args = command[2:] + else: + action = command + string = action + args = (()) + action = self.subst(action, dictionary) + string = self.subst(string, dictionary) + return (action, string, args) + + def _do_not_display(self, msg, *args): + pass + + def display(self, msg, *args): + """ + Displays the specified message. + + Each message is prepended with a standard prefix of our name + plus the time. + """ + if callable(msg): + msg = msg(*args) + else: + msg = msg % args + if msg is None: + return + fmt = '%s[%s]: %s\n' + sys.stdout.write(fmt % (self.name, time.strftime('%H:%M:%S'), msg)) + + def _do_not_execute(self, action, *args): + pass + + def execute(self, action, *args): + """ + Executes the specified action. + + The action is called if it's a callable Python function, and + otherwise passed to os.system(). + """ + if callable(action): + action(*args) + else: + os.system(action % args) + + def run_command_list(self, commands, dict): + """ + Executes a list of commands, substituting values from the + specified dictionary. + """ + commands = [ self.subst_variables(c, dict) for c in commands ] + for action, string, args in commands: + self.display(string, *args) + sys.stdout.flush() + status = self.execute(action, *args) + if status: + sys.exit(status) + + def log_display(self, command, log): + command = self.subst(command, self.__dict__) + if log: + command = self.redirect(command, log) + return command + + def log_execute(self, command, log): + command = self.subst(command, self.__dict__) + output = os.popen(command).read() + if self.verbose: + sys.stdout.write(output) + open(log, 'wb').write(output) + + # + + def archive_splitext(self, path): + """ + Splits an archive name into a filename base and extension. + + This is like os.path.splitext() (which it calls) except that it + also looks for '.tar.gz' and treats it as an atomic extensions. + """ + if path.endswith('.tar.gz'): + return path[:-7], path[-7:] + else: + return os.path.splitext(path) + + def args_to_files(self, args, tail=None): + """ + Takes a list of arguments, expands any glob patterns, and + returns the last "tail" files from the list. + """ + files = [] + for a in args: + files.extend(sorted(glob.glob(a))) + + if tail: + files = files[-tail:] + + return files + + def ascii_table(self, files, columns, + line_function, file_function=lambda x: x, + *args, **kw): + + header_fmt = ' '.join(['%12s'] * len(columns)) + line_fmt = header_fmt + ' %s' + + print header_fmt % columns + + for file in files: + t = line_function(file, *args, **kw) + if t is None: + t = [] + diff = len(columns) - len(t) + if diff > 0: + t += [''] * diff + t.append(file_function(file)) + print line_fmt % tuple(t) + + def collect_results(self, files, function, *args, **kw): + results = {} + + for file in files: + base = os.path.splitext(file)[0] + run, index = base.split('-')[-2:] + + run = int(run) + index = int(index) + + value = function(file, *args, **kw) + + try: + r = results[index] + except KeyError: + r = [] + results[index] = r + r.append((run, value)) + + return results + + def doc_to_help(self, obj): + """ + Translates an object's __doc__ string into help text. + + This strips a consistent number of spaces from each line in the + help text, essentially "outdenting" the text to the left-most + column. + """ + doc = obj.__doc__ + if doc is None: + return '' + return self.outdent(doc) + + def find_next_run_number(self, dir, prefix): + """ + Returns the next run number in a directory for the specified prefix. + + Examines the contents the specified directory for files with the + specified prefix, extracts the run numbers from each file name, + and returns the next run number after the largest it finds. + """ + x = re.compile(re.escape(prefix) + '-([0-9]+).*') + matches = [x.match(e) for e in os.listdir(dir)] + matches = [_f for _f in matches if _f] + if not matches: + return 0 + run_numbers = [int(m.group(1)) for m in matches] + return int(max(run_numbers)) + 1 + + def gnuplot_results(self, results, fmt='%s %.3f'): + """ + Prints out a set of results in Gnuplot format. + """ + gp = Gnuplotter(self.title, self.key_location) + + for i in sorted(results.keys()): + try: + t = self.run_titles[i] + except IndexError: + t = '??? %s ???' % i + results[i].sort() + gp.line(results[i], i+1, t, None, t, fmt=fmt) + + for bar_tuple in self.vertical_bars: + try: + x, type, label, comment = bar_tuple + except ValueError: + x, type, label = bar_tuple + comment = label + gp.vertical_bar(x, type, label, comment) + + gp.draw() + + def logfile_name(self, invocation): + """ + Returns the absolute path of a log file for the specificed + invocation number. + """ + name = self.prefix_run + '-%d.log' % invocation + return os.path.join(self.outdir, name) + + def outdent(self, s): + """ + Strip as many spaces from each line as are found at the beginning + of the first line in the list. + """ + lines = s.split('\n') + if lines[0] == '': + lines = lines[1:] + spaces = re.match(' *', lines[0]).group(0) + def strip_initial_spaces(l, s=spaces): + if l.startswith(spaces): + l = l[len(spaces):] + return l + return '\n'.join([ strip_initial_spaces(l) for l in lines ]) + '\n' + + def profile_name(self, invocation): + """ + Returns the absolute path of a profile file for the specified + invocation number. + """ + name = self.prefix_run + '-%d.prof' % invocation + return os.path.join(self.outdir, name) + + def set_env(self, key, value): + os.environ[key] = value + + # + + def get_debug_times(self, file, time_string=None): + """ + Fetch times from the --debug=time strings in the specified file. + """ + if time_string is None: + search_string = self.time_string_all + else: + search_string = time_string + contents = open(file).read() + if not contents: + sys.stderr.write('file %s has no contents!\n' % repr(file)) + return None + result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:] + result = [ float(r) for r in result ] + if not time_string is None: + try: + result = result[0] + except IndexError: + sys.stderr.write('file %s has no results!\n' % repr(file)) + return None + return result + + def get_function_profile(self, file, function): + """ + Returns the file, line number, function name, and cumulative time. + """ + try: + import pstats + except ImportError, e: + sys.stderr.write('%s: func: %s\n' % (self.name, e)) + sys.stderr.write('%s This version of Python is missing the profiler.\n' % self.name_spaces) + sys.stderr.write('%s Cannot use the "func" subcommand.\n' % self.name_spaces) + sys.exit(1) + statistics = pstats.Stats(file).stats + matches = [ e for e in statistics.items() if e[0][2] == function ] + r = matches[0] + return r[0][0], r[0][1], r[0][2], r[1][3] + + def get_function_time(self, file, function): + """ + Returns just the cumulative time for the specified function. + """ + return self.get_function_profile(file, function)[3] + + def get_memory(self, file, memory_string=None): + """ + Returns a list of integers of the amount of memory used. The + default behavior is to return all the stages. + """ + if memory_string is None: + search_string = self.memory_string_all + else: + search_string = memory_string + lines = open(file).readlines() + lines = [ l for l in lines if l.startswith(search_string) ][-4:] + result = [ int(l.split()[-1]) for l in lines[-4:] ] + if len(result) == 1: + result = result[0] + return result + + def get_object_counts(self, file, object_name, index=None): + """ + Returns the counts of the specified object_name. + """ + object_string = ' ' + object_name + '\n' + lines = open(file).readlines() + line = [ l for l in lines if l.endswith(object_string) ][0] + result = [ int(field) for field in line.split()[:4] ] + if index is not None: + result = result[index] + return result + + # + + command_alias = {} + + def execute_subcommand(self, argv): + """ + Executes the do_*() function for the specified subcommand (argv[0]). + """ + if not argv: + return + cmdName = self.command_alias.get(argv[0], argv[0]) + try: + func = getattr(self, 'do_' + cmdName) + except AttributeError: + return self.default(argv) + try: + return func(argv) + except TypeError, e: + sys.stderr.write("%s %s: %s\n" % (self.name, cmdName, e)) + import traceback + traceback.print_exc(file=sys.stderr) + sys.stderr.write("Try '%s help %s'\n" % (self.name, cmdName)) + + def default(self, argv): + """ + The default behavior for an unknown subcommand. Prints an + error message and exits. + """ + sys.stderr.write('%s: Unknown subcommand "%s".\n' % (self.name, argv[0])) + sys.stderr.write('Type "%s help" for usage.\n' % self.name) + sys.exit(1) + + # + + def do_help(self, argv): + """ + """ + if argv[1:]: + for arg in argv[1:]: + try: + func = getattr(self, 'do_' + arg) + except AttributeError: + sys.stderr.write('%s: No help for "%s"\n' % (self.name, arg)) + else: + try: + help = getattr(self, 'help_' + arg) + except AttributeError: + sys.stdout.write(self.doc_to_help(func)) + sys.stdout.flush() + else: + help() + else: + doc = self.doc_to_help(self.__class__) + if doc: + sys.stdout.write(doc) + sys.stdout.flush() + return None + + # + + def help_func(self): + help = """\ + Usage: scons-time func [OPTIONS] FILE [...] + + -C DIR, --chdir=DIR Change to DIR before looking for files + -f FILE, --file=FILE Read configuration from specified FILE + --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT + --func=NAME, --function=NAME Report time for function NAME + -h, --help Print this help and exit + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + -t NUMBER, --tail=NUMBER Only report the last NUMBER files + --title=TITLE Specify the output plot TITLE + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_func(self, argv): + """ + """ + format = 'ascii' + function_name = '_main' + tail = None + + short_opts = '?C:f:hp:t:' + + long_opts = [ + 'chdir=', + 'file=', + 'fmt=', + 'format=', + 'func=', + 'function=', + 'help', + 'prefix=', + 'tail=', + 'title=', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('-C', '--chdir'): + self.chdir = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('--fmt', '--format'): + format = a + elif o in ('--func', '--function'): + function_name = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'func']) + sys.exit(0) + elif o in ('--max',): + max_time = int(a) + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('-t', '--tail'): + tail = int(a) + elif o in ('--title',): + self.title = a + + if self.config_file: + exec open(self.config_file, 'rU').read() in self.__dict__ + + if self.chdir: + os.chdir(self.chdir) + + if not args: + + pattern = '%s*.prof' % self.prefix + args = self.args_to_files([pattern], tail) + + if not args: + if self.chdir: + directory = self.chdir + else: + directory = os.getcwd() + + sys.stderr.write('%s: func: No arguments specified.\n' % self.name) + sys.stderr.write('%s No %s*.prof files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) + sys.stderr.write('%s Type "%s help func" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + else: + + args = self.args_to_files(args, tail) + + cwd_ = os.getcwd() + os.sep + + if format == 'ascii': + + for file in args: + try: + f, line, func, time = \ + self.get_function_profile(file, function_name) + except ValueError, e: + sys.stderr.write("%s: func: %s: %s\n" % + (self.name, file, e)) + else: + if f.startswith(cwd_): + f = f[len(cwd_):] + print "%.3f %s:%d(%s)" % (time, f, line, func) + + elif format == 'gnuplot': + + results = self.collect_results(args, self.get_function_time, + function_name) + + self.gnuplot_results(results) + + else: + + sys.stderr.write('%s: func: Unknown format "%s".\n' % (self.name, format)) + sys.exit(1) + + # + + def help_mem(self): + help = """\ + Usage: scons-time mem [OPTIONS] FILE [...] + + -C DIR, --chdir=DIR Change to DIR before looking for files + -f FILE, --file=FILE Read configuration from specified FILE + --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT + -h, --help Print this help and exit + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + --stage=STAGE Plot memory at the specified stage: + pre-read, post-read, pre-build, + post-build (default: post-build) + -t NUMBER, --tail=NUMBER Only report the last NUMBER files + --title=TITLE Specify the output plot TITLE + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_mem(self, argv): + + format = 'ascii' + logfile_path = lambda x: x + stage = self.default_stage + tail = None + + short_opts = '?C:f:hp:t:' + + long_opts = [ + 'chdir=', + 'file=', + 'fmt=', + 'format=', + 'help', + 'prefix=', + 'stage=', + 'tail=', + 'title=', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('-C', '--chdir'): + self.chdir = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('--fmt', '--format'): + format = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'mem']) + sys.exit(0) + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('--stage',): + if not a in self.stages: + sys.stderr.write('%s: mem: Unrecognized stage "%s".\n' % (self.name, a)) + sys.exit(1) + stage = a + elif o in ('-t', '--tail'): + tail = int(a) + elif o in ('--title',): + self.title = a + + if self.config_file: + HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) + + if self.chdir: + os.chdir(self.chdir) + logfile_path = lambda x: os.path.join(self.chdir, x) + + if not args: + + pattern = '%s*.log' % self.prefix + args = self.args_to_files([pattern], tail) + + if not args: + if self.chdir: + directory = self.chdir + else: + directory = os.getcwd() + + sys.stderr.write('%s: mem: No arguments specified.\n' % self.name) + sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) + sys.stderr.write('%s Type "%s help mem" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + else: + + args = self.args_to_files(args, tail) + + cwd_ = os.getcwd() + os.sep + + if format == 'ascii': + + self.ascii_table(args, tuple(self.stages), self.get_memory, logfile_path) + + elif format == 'gnuplot': + + results = self.collect_results(args, self.get_memory, + self.stage_strings[stage]) + + self.gnuplot_results(results) + + else: + + sys.stderr.write('%s: mem: Unknown format "%s".\n' % (self.name, format)) + sys.exit(1) + + return 0 + + # + + def help_obj(self): + help = """\ + Usage: scons-time obj [OPTIONS] OBJECT FILE [...] + + -C DIR, --chdir=DIR Change to DIR before looking for files + -f FILE, --file=FILE Read configuration from specified FILE + --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT + -h, --help Print this help and exit + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + --stage=STAGE Plot memory at the specified stage: + pre-read, post-read, pre-build, + post-build (default: post-build) + -t NUMBER, --tail=NUMBER Only report the last NUMBER files + --title=TITLE Specify the output plot TITLE + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_obj(self, argv): + + format = 'ascii' + logfile_path = lambda x: x + stage = self.default_stage + tail = None + + short_opts = '?C:f:hp:t:' + + long_opts = [ + 'chdir=', + 'file=', + 'fmt=', + 'format=', + 'help', + 'prefix=', + 'stage=', + 'tail=', + 'title=', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('-C', '--chdir'): + self.chdir = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('--fmt', '--format'): + format = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'obj']) + sys.exit(0) + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('--stage',): + if not a in self.stages: + sys.stderr.write('%s: obj: Unrecognized stage "%s".\n' % (self.name, a)) + sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + stage = a + elif o in ('-t', '--tail'): + tail = int(a) + elif o in ('--title',): + self.title = a + + if not args: + sys.stderr.write('%s: obj: Must specify an object name.\n' % self.name) + sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + object_name = args.pop(0) + + if self.config_file: + HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) + + if self.chdir: + os.chdir(self.chdir) + logfile_path = lambda x: os.path.join(self.chdir, x) + + if not args: + + pattern = '%s*.log' % self.prefix + args = self.args_to_files([pattern], tail) + + if not args: + if self.chdir: + directory = self.chdir + else: + directory = os.getcwd() + + sys.stderr.write('%s: obj: No arguments specified.\n' % self.name) + sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) + sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + else: + + args = self.args_to_files(args, tail) + + cwd_ = os.getcwd() + os.sep + + if format == 'ascii': + + self.ascii_table(args, tuple(self.stages), self.get_object_counts, logfile_path, object_name) + + elif format == 'gnuplot': + + stage_index = 0 + for s in self.stages: + if stage == s: + break + stage_index = stage_index + 1 + + results = self.collect_results(args, self.get_object_counts, + object_name, stage_index) + + self.gnuplot_results(results) + + else: + + sys.stderr.write('%s: obj: Unknown format "%s".\n' % (self.name, format)) + sys.exit(1) + + return 0 + + # + + def help_run(self): + help = """\ + Usage: scons-time run [OPTIONS] [FILE ...] + + --aegis=PROJECT Use SCons from the Aegis PROJECT + --chdir=DIR Name of unpacked directory for chdir + -f FILE, --file=FILE Read configuration from specified FILE + -h, --help Print this help and exit + -n, --no-exec No execute, just print command lines + --number=NUMBER Put output in files for run NUMBER + --outdir=OUTDIR Put output files in OUTDIR + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + --python=PYTHON Time using the specified PYTHON + -q, --quiet Don't print command lines + --scons=SCONS Time using the specified SCONS + --svn=URL, --subversion=URL Use SCons from Subversion URL + -v, --verbose Display output of commands + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_run(self, argv): + """ + """ + run_number_list = [None] + + short_opts = '?f:hnp:qs:v' + + long_opts = [ + 'aegis=', + 'file=', + 'help', + 'no-exec', + 'number=', + 'outdir=', + 'prefix=', + 'python=', + 'quiet', + 'scons=', + 'svn=', + 'subdir=', + 'subversion=', + 'verbose', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('--aegis',): + self.aegis_project = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'run']) + sys.exit(0) + elif o in ('-n', '--no-exec'): + self.execute = self._do_not_execute + elif o in ('--number',): + run_number_list = self.split_run_numbers(a) + elif o in ('--outdir',): + self.outdir = a + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('--python',): + self.python = a + elif o in ('-q', '--quiet'): + self.display = self._do_not_display + elif o in ('-s', '--subdir'): + self.subdir = a + elif o in ('--scons',): + self.scons = a + elif o in ('--svn', '--subversion'): + self.subversion_url = a + elif o in ('-v', '--verbose'): + self.redirect = tee_to_file + self.verbose = True + self.svn_co_flag = '' + + if not args and not self.config_file: + sys.stderr.write('%s: run: No arguments or -f config file specified.\n' % self.name) + sys.stderr.write('%s Type "%s help run" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + if self.config_file: + exec open(self.config_file, 'rU').read() in self.__dict__ + + if args: + self.archive_list = args + + archive_file_name = os.path.split(self.archive_list[0])[1] + + if not self.subdir: + self.subdir = self.archive_splitext(archive_file_name)[0] + + if not self.prefix: + self.prefix = self.archive_splitext(archive_file_name)[0] + + prepare = None + if self.subversion_url: + prepare = self.prep_subversion_run + elif self.aegis_project: + prepare = self.prep_aegis_run + + for run_number in run_number_list: + self.individual_run(run_number, self.archive_list, prepare) + + def split_run_numbers(self, s): + result = [] + for n in s.split(','): + try: + x, y = n.split('-') + except ValueError: + result.append(int(n)) + else: + result.extend(list(range(int(x), int(y)+1))) + return result + + def scons_path(self, dir): + return os.path.join(dir, 'src', 'script', 'scons.py') + + def scons_lib_dir_path(self, dir): + return os.path.join(dir, 'src', 'engine') + + def prep_aegis_run(self, commands, removals): + self.aegis_tmpdir = make_temp_file(prefix = self.name + '-aegis-') + removals.append((shutil.rmtree, 'rm -rf %%s', self.aegis_tmpdir)) + + self.aegis_parent_project = os.path.splitext(self.aegis_project)[0] + self.scons = self.scons_path(self.aegis_tmpdir) + self.scons_lib_dir = self.scons_lib_dir_path(self.aegis_tmpdir) + + commands.extend([ + 'mkdir %(aegis_tmpdir)s', + (lambda: os.chdir(self.aegis_tmpdir), 'cd %(aegis_tmpdir)s'), + '%(aegis)s -cp -ind -p %(aegis_parent_project)s .', + '%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .', + ]) + + def prep_subversion_run(self, commands, removals): + self.svn_tmpdir = make_temp_file(prefix = self.name + '-svn-') + removals.append((shutil.rmtree, 'rm -rf %%s', self.svn_tmpdir)) + + self.scons = self.scons_path(self.svn_tmpdir) + self.scons_lib_dir = self.scons_lib_dir_path(self.svn_tmpdir) + + commands.extend([ + 'mkdir %(svn_tmpdir)s', + '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(svn_tmpdir)s', + ]) + + def individual_run(self, run_number, archive_list, prepare=None): + """ + Performs an individual run of the default SCons invocations. + """ + + commands = [] + removals = [] + + if prepare: + prepare(commands, removals) + + save_scons = self.scons + save_scons_wrapper = self.scons_wrapper + save_scons_lib_dir = self.scons_lib_dir + + if self.outdir is None: + self.outdir = self.orig_cwd + elif not os.path.isabs(self.outdir): + self.outdir = os.path.join(self.orig_cwd, self.outdir) + + if self.scons is None: + self.scons = self.scons_path(self.orig_cwd) + + if self.scons_lib_dir is None: + self.scons_lib_dir = self.scons_lib_dir_path(self.orig_cwd) + + if self.scons_wrapper is None: + self.scons_wrapper = self.scons + + if not run_number: + run_number = self.find_next_run_number(self.outdir, self.prefix) + + self.run_number = str(run_number) + + self.prefix_run = self.prefix + '-%03d' % run_number + + if self.targets0 is None: + self.targets0 = self.startup_targets + if self.targets1 is None: + self.targets1 = self.targets + if self.targets2 is None: + self.targets2 = self.targets + + self.tmpdir = make_temp_file(prefix = self.name + '-') + + commands.extend([ + 'mkdir %(tmpdir)s', + + (os.chdir, 'cd %%s', self.tmpdir), + ]) + + for archive in archive_list: + if not os.path.isabs(archive): + archive = os.path.join(self.orig_cwd, archive) + if os.path.isdir(archive): + dest = os.path.split(archive)[1] + commands.append((shutil.copytree, 'cp -r %%s %%s', archive, dest)) + else: + suffix = self.archive_splitext(archive)[1] + unpack_command = self.unpack_map.get(suffix) + if not unpack_command: + dest = os.path.split(archive)[1] + commands.append((shutil.copyfile, 'cp %%s %%s', archive, dest)) + else: + commands.append(unpack_command + (archive,)) + + commands.extend([ + (os.chdir, 'cd %%s', self.subdir), + ]) + + commands.extend(self.initial_commands) + + commands.extend([ + (lambda: read_tree('.'), + 'find * -type f | xargs cat > /dev/null'), + + (self.set_env, 'export %%s=%%s', + 'SCONS_LIB_DIR', self.scons_lib_dir), + + '%(python)s %(scons_wrapper)s --version', + ]) + + index = 0 + for run_command in self.run_commands: + setattr(self, 'prof%d' % index, self.profile_name(index)) + c = ( + self.log_execute, + self.log_display, + run_command, + self.logfile_name(index), + ) + commands.append(c) + index = index + 1 + + commands.extend([ + (os.chdir, 'cd %%s', self.orig_cwd), + ]) + + if not os.environ.get('PRESERVE'): + commands.extend(removals) + + commands.append((shutil.rmtree, 'rm -rf %%s', self.tmpdir)) + + self.run_command_list(commands, self.__dict__) + + self.scons = save_scons + self.scons_lib_dir = save_scons_lib_dir + self.scons_wrapper = save_scons_wrapper + + # + + def help_time(self): + help = """\ + Usage: scons-time time [OPTIONS] FILE [...] + + -C DIR, --chdir=DIR Change to DIR before looking for files + -f FILE, --file=FILE Read configuration from specified FILE + --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT + -h, --help Print this help and exit + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + -t NUMBER, --tail=NUMBER Only report the last NUMBER files + --which=TIMER Plot timings for TIMER: total, + SConscripts, SCons, commands. + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_time(self, argv): + + format = 'ascii' + logfile_path = lambda x: x + tail = None + which = 'total' + + short_opts = '?C:f:hp:t:' + + long_opts = [ + 'chdir=', + 'file=', + 'fmt=', + 'format=', + 'help', + 'prefix=', + 'tail=', + 'title=', + 'which=', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('-C', '--chdir'): + self.chdir = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('--fmt', '--format'): + format = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'time']) + sys.exit(0) + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('-t', '--tail'): + tail = int(a) + elif o in ('--title',): + self.title = a + elif o in ('--which',): + if not a in self.time_strings.keys(): + sys.stderr.write('%s: time: Unrecognized timer "%s".\n' % (self.name, a)) + sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + which = a + + if self.config_file: + HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) + + if self.chdir: + os.chdir(self.chdir) + logfile_path = lambda x: os.path.join(self.chdir, x) + + if not args: + + pattern = '%s*.log' % self.prefix + args = self.args_to_files([pattern], tail) + + if not args: + if self.chdir: + directory = self.chdir + else: + directory = os.getcwd() + + sys.stderr.write('%s: time: No arguments specified.\n' % self.name) + sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) + sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + else: + + args = self.args_to_files(args, tail) + + cwd_ = os.getcwd() + os.sep + + if format == 'ascii': + + columns = ("Total", "SConscripts", "SCons", "commands") + self.ascii_table(args, columns, self.get_debug_times, logfile_path) + + elif format == 'gnuplot': + + results = self.collect_results(args, self.get_debug_times, + self.time_strings[which]) + + self.gnuplot_results(results, fmt='%s %.6f') + + else: + + sys.stderr.write('%s: time: Unknown format "%s".\n' % (self.name, format)) + sys.exit(1) + +if __name__ == '__main__': + opts, args = getopt.getopt(sys.argv[1:], 'h?V', ['help', 'version']) + + ST = SConsTimer() + + for o, a in opts: + if o in ('-?', '-h', '--help'): + ST.do_help(['help']) + sys.exit(0) + elif o in ('-V', '--version'): + sys.stdout.write('scons-time version\n') + sys.exit(0) + + if not args: + sys.stderr.write('Type "%s help" for usage.\n' % ST.name) + sys.exit(1) + + ST.execute_subcommand(args) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/script/scons.bat b/script/scons.bat new file mode 100644 index 0000000..1648cb3 --- /dev/null +++ b/script/scons.bat @@ -0,0 +1,34 @@ +@REM Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +@REM src/script/scons.bat 5357 2011/09/09 21:31:03 bdeegan +@echo off +set SCONS_ERRORLEVEL= +if "%OS%" == "Windows_NT" goto WinNT + +@REM for 9x/Me you better not have more than 9 args +python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-2.1.0'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-2.1.0'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %1 %2 %3 %4 %5 %6 %7 %8 %9 +@REM no way to set exit status of this script for 9x/Me +goto endscons + +@REM Credit where credit is due: we return the exit code despite our +@REM use of setlocal+endlocal using a technique from Bear's Journal: +@REM http://code-bear.com/bearlog/2007/06/01/getting-the-exit-code-from-a-batch-file-that-is-run-from-a-python-program/ + +:WinNT +setlocal +@REM ensure the script will be executed with the Python it was installed for +set path=%~dp0;%~dp0..;%path% +@REM try the script named as the .bat file in current dir, then in Scripts subdir +set scriptname=%~dp0%~n0.py +if not exist %scriptname% set scriptname=%~dp0Scripts\%~n0.py +python "%scriptname%" %* +endlocal & set SCONS_ERRORLEVEL=%ERRORLEVEL% + +if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto returncode +if errorlevel 9009 echo you do not have python in your PATH +goto endscons + +:returncode +exit /B %SCONS_ERRORLEVEL% + +:endscons +call :returncode %SCONS_ERRORLEVEL% diff --git a/script/sconsign b/script/sconsign new file mode 100644 index 0000000..4debf04 --- /dev/null +++ b/script/sconsign @@ -0,0 +1,514 @@ +#! /usr/bin/env python +# +# SCons - a Software Constructor +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "src/script/sconsign.py 5357 2011/09/09 21:31:03 bdeegan" + +__version__ = "2.1.0" + +__build__ = "r5357[MODIFIED]" + +__buildsys__ = "ubuntu" + +__date__ = "2011/09/09 21:31:03" + +__developer__ = "bdeegan" + +import os +import sys + +############################################################################## +# BEGIN STANDARD SCons SCRIPT HEADER +# +# This is the cut-and-paste logic so that a self-contained script can +# interoperate correctly with different SCons versions and installation +# locations for the engine. If you modify anything in this section, you +# should also change other scripts that use this same header. +############################################################################## + +# Strip the script directory from sys.path() so on case-insensitive +# (WIN32) systems Python doesn't think that the "scons" script is the +# "SCons" package. Replace it with our own library directories +# (version-specific first, in case they installed by hand there, +# followed by generic) so we pick up the right version of the build +# engine modules if they're in either directory. + +script_dir = sys.path[0] + +if script_dir in sys.path: + sys.path.remove(script_dir) + +libs = [] + +if "SCONS_LIB_DIR" in os.environ: + libs.append(os.environ["SCONS_LIB_DIR"]) + +local_version = 'scons-local-' + __version__ +local = 'scons-local' +if script_dir: + local_version = os.path.join(script_dir, local_version) + local = os.path.join(script_dir, local) +libs.append(os.path.abspath(local_version)) +libs.append(os.path.abspath(local)) + +scons_version = 'scons-%s' % __version__ + +# preferred order of scons lookup paths +prefs = [] + +try: + import pkg_resources +except ImportError: + pass +else: + # when running from an egg add the egg's directory + try: + d = pkg_resources.get_distribution('scons') + except pkg_resources.DistributionNotFound: + pass + else: + prefs.append(d.location) + +if sys.platform == 'win32': + # sys.prefix is (likely) C:\Python*; + # check only C:\Python*. + prefs.append(sys.prefix) + prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) +else: + # On other (POSIX) platforms, things are more complicated due to + # the variety of path names and library locations. Try to be smart + # about it. + if script_dir == 'bin': + # script_dir is `pwd`/bin; + # check `pwd`/lib/scons*. + prefs.append(os.getcwd()) + else: + if script_dir == '.' or script_dir == '': + script_dir = os.getcwd() + head, tail = os.path.split(script_dir) + if tail == "bin": + # script_dir is /foo/bin; + # check /foo/lib/scons*. + prefs.append(head) + + head, tail = os.path.split(sys.prefix) + if tail == "usr": + # sys.prefix is /foo/usr; + # check /foo/usr/lib/scons* first, + # then /foo/usr/local/lib/scons*. + prefs.append(sys.prefix) + prefs.append(os.path.join(sys.prefix, "local")) + elif tail == "local": + h, t = os.path.split(head) + if t == "usr": + # sys.prefix is /foo/usr/local; + # check /foo/usr/local/lib/scons* first, + # then /foo/usr/lib/scons*. + prefs.append(sys.prefix) + prefs.append(head) + else: + # sys.prefix is /foo/local; + # check only /foo/local/lib/scons*. + prefs.append(sys.prefix) + else: + # sys.prefix is /foo (ends in neither /usr or /local); + # check only /foo/lib/scons*. + prefs.append(sys.prefix) + + temp = [os.path.join(x, 'lib') for x in prefs] + temp.extend([os.path.join(x, + 'lib', + 'python' + sys.version[:3], + 'site-packages') for x in prefs]) + prefs = temp + + # Add the parent directory of the current python's library to the + # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, + # not /usr/lib. + try: + libpath = os.__file__ + except AttributeError: + pass + else: + # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*. + libpath, tail = os.path.split(libpath) + # Split /usr/libfoo/python* to /usr/libfoo + libpath, tail = os.path.split(libpath) + # Check /usr/libfoo/scons*. + prefs.append(libpath) + +# Look first for 'scons-__version__' in all of our preference libs, +# then for 'scons'. +libs.extend([os.path.join(x, scons_version) for x in prefs]) +libs.extend([os.path.join(x, 'scons') for x in prefs]) + +sys.path = libs + sys.path + +############################################################################## +# END STANDARD SCons SCRIPT HEADER +############################################################################## + +import SCons.compat # so pickle will import cPickle instead + +import whichdb +import time +import pickle +import imp + +import SCons.SConsign + +def my_whichdb(filename): + if filename[-7:] == ".dblite": + return "SCons.dblite" + try: + f = open(filename + ".dblite", "rb") + f.close() + return "SCons.dblite" + except IOError: + pass + return _orig_whichdb(filename) + +_orig_whichdb = whichdb.whichdb +whichdb.whichdb = my_whichdb + +def my_import(mname): + if '.' in mname: + i = mname.rfind('.') + parent = my_import(mname[:i]) + fp, pathname, description = imp.find_module(mname[i+1:], + parent.__path__) + else: + fp, pathname, description = imp.find_module(mname) + return imp.load_module(mname, fp, pathname, description) + +class Flagger(object): + default_value = 1 + def __setitem__(self, item, value): + self.__dict__[item] = value + self.default_value = 0 + def __getitem__(self, item): + return self.__dict__.get(item, self.default_value) + +Do_Call = None +Print_Directories = [] +Print_Entries = [] +Print_Flags = Flagger() +Verbose = 0 +Readable = 0 + +def default_mapper(entry, name): + try: + val = eval("entry."+name) + except: + val = None + return str(val) + +def map_action(entry, name): + try: + bact = entry.bact + bactsig = entry.bactsig + except AttributeError: + return None + return '%s [%s]' % (bactsig, bact) + +def map_timestamp(entry, name): + try: + timestamp = entry.timestamp + except AttributeError: + timestamp = None + if Readable and timestamp: + return "'" + time.ctime(timestamp) + "'" + else: + return str(timestamp) + +def map_bkids(entry, name): + try: + bkids = entry.bsources + entry.bdepends + entry.bimplicit + bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs + except AttributeError: + return None + result = [] + for i in range(len(bkids)): + result.append(nodeinfo_string(bkids[i], bkidsigs[i], " ")) + if result == []: + return None + return "\n ".join(result) + +map_field = { + 'action' : map_action, + 'timestamp' : map_timestamp, + 'bkids' : map_bkids, +} + +map_name = { + 'implicit' : 'bkids', +} + +def field(name, entry, verbose=Verbose): + if not Print_Flags[name]: + return None + fieldname = map_name.get(name, name) + mapper = map_field.get(fieldname, default_mapper) + val = mapper(entry, name) + if verbose: + val = name + ": " + val + return val + +def nodeinfo_raw(name, ninfo, prefix=""): + # This just formats the dictionary, which we would normally use str() + # to do, except that we want the keys sorted for deterministic output. + d = ninfo.__dict__ + try: + keys = ninfo.field_list + ['_version_id'] + except AttributeError: + keys = sorted(d.keys()) + l = [] + for k in keys: + l.append('%s: %s' % (repr(k), repr(d.get(k)))) + if '\n' in name: + name = repr(name) + return name + ': {' + ', '.join(l) + '}' + +def nodeinfo_cooked(name, ninfo, prefix=""): + try: + field_list = ninfo.field_list + except AttributeError: + field_list = [] + if '\n' in name: + name = repr(name) + outlist = [name+':'] + [_f for _f in [field(x, ninfo, Verbose) for x in field_list] if _f] + if Verbose: + sep = '\n ' + prefix + else: + sep = ' ' + return sep.join(outlist) + +nodeinfo_string = nodeinfo_cooked + +def printfield(name, entry, prefix=""): + outlist = field("implicit", entry, 0) + if outlist: + if Verbose: + print " implicit:" + print " " + outlist + outact = field("action", entry, 0) + if outact: + if Verbose: + print " action: " + outact + else: + print " " + outact + +def printentries(entries, location): + if Print_Entries: + for name in Print_Entries: + try: + entry = entries[name] + except KeyError: + sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, location)) + else: + try: + ninfo = entry.ninfo + except AttributeError: + print name + ":" + else: + print nodeinfo_string(name, entry.ninfo) + printfield(name, entry.binfo) + else: + for name in sorted(entries.keys()): + entry = entries[name] + try: + ninfo = entry.ninfo + except AttributeError: + print name + ":" + else: + print nodeinfo_string(name, entry.ninfo) + printfield(name, entry.binfo) + +class Do_SConsignDB(object): + def __init__(self, dbm_name, dbm): + self.dbm_name = dbm_name + self.dbm = dbm + + def __call__(self, fname): + # The *dbm modules stick their own file suffixes on the names + # that are passed in. This is causes us to jump through some + # hoops here to be able to allow the user + try: + # Try opening the specified file name. Example: + # SPECIFIED OPENED BY self.dbm.open() + # --------- ------------------------- + # .sconsign => .sconsign.dblite + # .sconsign.dblite => .sconsign.dblite.dblite + db = self.dbm.open(fname, "r") + except (IOError, OSError), e: + print_e = e + try: + # That didn't work, so try opening the base name, + # so that if the actually passed in 'sconsign.dblite' + # (for example), the dbm module will put the suffix back + # on for us and open it anyway. + db = self.dbm.open(os.path.splitext(fname)[0], "r") + except (IOError, OSError): + # That didn't work either. See if the file name + # they specified just exists (independent of the dbm + # suffix-mangling). + try: + open(fname, "r") + except (IOError, OSError), e: + # Nope, that file doesn't even exist, so report that + # fact back. + print_e = e + sys.stderr.write("sconsign: %s\n" % (print_e)) + return + except KeyboardInterrupt: + raise + except pickle.UnpicklingError: + sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n" % (self.dbm_name, fname)) + return + except Exception, e: + sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e)) + return + + if Print_Directories: + for dir in Print_Directories: + try: + val = db[dir] + except KeyError: + sys.stderr.write("sconsign: no dir `%s' in `%s'\n" % (dir, args[0])) + else: + self.printentries(dir, val) + else: + for dir in sorted(db.keys()): + self.printentries(dir, db[dir]) + + def printentries(self, dir, val): + print '=== ' + dir + ':' + printentries(pickle.loads(val), dir) + +def Do_SConsignDir(name): + try: + fp = open(name, 'rb') + except (IOError, OSError), e: + sys.stderr.write("sconsign: %s\n" % (e)) + return + try: + sconsign = SCons.SConsign.Dir(fp) + except KeyboardInterrupt: + raise + except pickle.UnpicklingError: + sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % (name)) + return + except Exception, e: + sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e)) + return + printentries(sconsign.entries, args[0]) + +############################################################################## + +import getopt + +helpstr = """\ +Usage: sconsign [OPTIONS] FILE [...] +Options: + -a, --act, --action Print build action information. + -c, --csig Print content signature information. + -d DIR, --dir=DIR Print only info about DIR. + -e ENTRY, --entry=ENTRY Print only info about ENTRY. + -f FORMAT, --format=FORMAT FILE is in the specified FORMAT. + -h, --help Print this message and exit. + -i, --implicit Print implicit dependency information. + -r, --readable Print timestamps in human-readable form. + --raw Print raw Python object representations. + -s, --size Print file sizes. + -t, --timestamp Print timestamp information. + -v, --verbose Verbose, describe each field. +""" + +opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv", + ['act', 'action', + 'csig', 'dir=', 'entry=', + 'format=', 'help', 'implicit', + 'raw', 'readable', + 'size', 'timestamp', 'verbose']) + + +for o, a in opts: + if o in ('-a', '--act', '--action'): + Print_Flags['action'] = 1 + elif o in ('-c', '--csig'): + Print_Flags['csig'] = 1 + elif o in ('-d', '--dir'): + Print_Directories.append(a) + elif o in ('-e', '--entry'): + Print_Entries.append(a) + elif o in ('-f', '--format'): + Module_Map = {'dblite' : 'SCons.dblite', + 'sconsign' : None} + dbm_name = Module_Map.get(a, a) + if dbm_name: + try: + dbm = my_import(dbm_name) + except: + sys.stderr.write("sconsign: illegal file format `%s'\n" % a) + print helpstr + sys.exit(2) + Do_Call = Do_SConsignDB(a, dbm) + else: + Do_Call = Do_SConsignDir + elif o in ('-h', '--help'): + print helpstr + sys.exit(0) + elif o in ('-i', '--implicit'): + Print_Flags['implicit'] = 1 + elif o in ('--raw',): + nodeinfo_string = nodeinfo_raw + elif o in ('-r', '--readable'): + Readable = 1 + elif o in ('-s', '--size'): + Print_Flags['size'] = 1 + elif o in ('-t', '--timestamp'): + Print_Flags['timestamp'] = 1 + elif o in ('-v', '--verbose'): + Verbose = 1 + +if Do_Call: + for a in args: + Do_Call(a) +else: + for a in args: + dbm_name = whichdb.whichdb(a) + if dbm_name: + Map_Module = {'SCons.dblite' : 'dblite'} + dbm = my_import(dbm_name) + Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a) + else: + Do_SConsignDir(a) + +sys.exit(0) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..f04ca1b --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[bdist_rpm] +group = Development/Tools + +[bdist_wininst] +title = SCons - a software construction tool diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..2d04341 --- /dev/null +++ b/setup.py @@ -0,0 +1,423 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +""" +NOTE: Installed SCons is not importable like usual Python packages. It is + executed explicitly with command line scripts. This allows multiple + SCons versions to coexist within single Python installation, which + is critical for enterprise build cases. Explicit invokation is + necessary to avoid confusion over which version of SCons is active. + + By default SCons is installed into versioned directory, e.g. + site-packages/scons-2.1.0.alpha.20101125 and much of the stuff + below is dedicated to make it happen on various platforms. +""" + +__revision__ = "src/setup.py 5357 2011/09/09 21:31:03 bdeegan" + +import os +import stat +import sys + +Version = "2.1.0" + +man_pages = [ + 'scons.1', + 'sconsign.1', + 'scons-time.1', +] + +# change to setup.py directory if it was executed from other dir +(head, tail) = os.path.split(sys.argv[0]) +if head: + os.chdir(head) + sys.argv[0] = tail + + +# flag if setup.py is run on win32 or _for_ win32 platform, +# (when building windows installer on linux, for example) +is_win32 = 0 +if not sys.platform == 'win32': + try: + if sys.argv[1] == 'bdist_wininst': + is_win32 = 1 + except IndexError: + pass +else: + is_win32 = 1 + + +import distutils +import distutils.core +import distutils.command.install +import distutils.command.install_data +import distutils.command.install_lib +import distutils.command.install_scripts +import distutils.command.build_scripts + +_install = distutils.command.install.install +_install_data = distutils.command.install_data.install_data +_install_lib = distutils.command.install_lib.install_lib +_install_scripts = distutils.command.install_scripts.install_scripts +_build_scripts = distutils.command.build_scripts.build_scripts + +class _options(object): + pass + +Options = _options() + +Installed = [] + +def set_explicitly(name, args): + """ + Return if the installation directory was set explicitly by the + user on the command line. This is complicated by the fact that + "install --install-lib=/foo" gets turned into "install_lib + --install-dir=/foo" internally. + """ + if args[0] == "install_" + name: + s = "--install-dir=" + else: + # The command is something else (usually "install") + s = "--install-%s=" % name + set = 0 + length = len(s) + for a in args[1:]: + if a[:length] == s: + set = 1 + break + return set + +class install(_install): + user_options = _install.user_options + [ + ('no-scons-script', None, + "don't install 'scons', only install 'scons-%s'" % Version), + ('no-version-script', None, + "don't install 'scons-%s', only install 'scons'" % Version), + ('install-bat', None, + "install 'scons.bat' script"), + ('no-install-bat', None, + "do not install 'scons.bat' script"), + ('install-man', None, + "install SCons man pages"), + ('no-install-man', None, + "do not install SCons man pages"), + ('standard-lib', None, + "install SCons library in standard Python location"), + ('standalone-lib', None, + "install SCons library in separate standalone directory"), + ('version-lib', None, + "install SCons library in version-numbered directory"), + ] + boolean_options = _install.boolean_options + [ + 'no-scons-script', + 'no-version-script', + 'install-bat', + 'no-install-bat', + 'install-man', + 'no-install-man', + 'standard-lib', + 'standalone-lib', + 'version-lib' + ] + + if hasattr(os, 'link'): + user_options.append( + ('hardlink-scons', None, + "hard link 'scons' to the version-numbered script, don't make a separate 'scons' copy"), + ) + boolean_options.append('hardlink-script') + + if hasattr(os, 'symlink'): + user_options.append( + ('symlink-scons', None, + "make 'scons' a symbolic link to the version-numbered script, don't make a separate 'scons' copy"), + ) + boolean_options.append('symlink-script') + + def initialize_options(self): + _install.initialize_options(self) + self.no_scons_script = 0 + self.no_version_script = 0 + self.install_bat = 0 + self.no_install_bat = not is_win32 + self.install_man = 0 + self.no_install_man = is_win32 + self.standard_lib = 0 + self.standalone_lib = 0 + self.version_lib = 0 + self.hardlink_scons = 0 + self.symlink_scons = 0 + # Don't warn about having to put the library directory in the + # search path. + self.warn_dir = 0 + + def finalize_options(self): + _install.finalize_options(self) + if self.install_bat: + Options.install_bat = 1 + else: + Options.install_bat = not self.no_install_bat + if self.install_man: + Options.install_man = 1 + else: + Options.install_man = not self.no_install_man + Options.standard_lib = self.standard_lib + Options.standalone_lib = self.standalone_lib + Options.version_lib = self.version_lib + Options.install_scons_script = not self.no_scons_script + Options.install_version_script = not self.no_version_script + Options.hardlink_scons = self.hardlink_scons + Options.symlink_scons = self.symlink_scons + +def get_scons_prefix(libdir, is_win32): + """ + Return the right prefix for SCons library installation. Find + this by starting with the library installation directory + (.../site-packages, most likely) and crawling back up until we reach + a directory name beginning with "python" (or "Python"). + """ + drive, head = os.path.splitdrive(libdir) + while head: + if head == os.sep: + break + head, tail = os.path.split(head) + if tail.lower()[:6] == "python": + # Found the Python library directory... + if is_win32: + # ...on Win32 systems, "scons" goes in the directory: + # C:\PythonXX => C:\PythonXX\scons + return os.path.join(drive + head, tail) + else: + # ...on other systems, "scons" goes above the directory: + # /usr/lib/pythonX.X => /usr/lib/scons + return os.path.join(drive + head) + return libdir + +def force_to_usr_local(self): + """ + A hack to decide if we need to "force" the installation directories + to be under /usr/local. This is because Mac Os X Tiger and + Leopard, by default, put the libraries and scripts in their own + directories under /Library or /System/Library. + """ + return (sys.platform[:6] == 'darwin' and + (self.install_dir[:9] == '/Library/' or + self.install_dir[:16] == '/System/Library/')) + +class install_lib(_install_lib): + def finalize_options(self): + _install_lib.finalize_options(self) + if force_to_usr_local(self): + self.install_dir = '/usr/local/lib' + args = self.distribution.script_args + if not set_explicitly("lib", args): + # They didn't explicitly specify the installation + # directory for libraries... + is_win32 = sys.platform == "win32" or args[0] == 'bdist_wininst' + prefix = get_scons_prefix(self.install_dir, is_win32) + if Options.standalone_lib: + # ...but they asked for a standalone directory. + self.install_dir = os.path.join(prefix, "scons") + elif Options.version_lib or not Options.standard_lib: + # ...they asked for a version-specific directory, + # or they get it by default. + self.install_dir = os.path.join(prefix, "scons-%s" % Version) + + msg = "Installed SCons library modules into %s" % self.install_dir + Installed.append(msg) + +class install_scripts(_install_scripts): + def finalize_options(self): + _install_scripts.finalize_options(self) + if force_to_usr_local(self): + self.install_dir = '/usr/local/bin' + self.build_dir = os.path.join('build', 'scripts') + msg = "Installed SCons scripts into %s" % self.install_dir + Installed.append(msg) + + def do_nothing(self, *args, **kw): + pass + + def hardlink_scons(self, src, dst, ver): + try: os.unlink(dst) + except OSError: pass + os.link(ver, dst) + + def symlink_scons(self, src, dst, ver): + try: os.unlink(dst) + except OSError: pass + os.symlink(os.path.split(ver)[1], dst) + + def copy_scons(self, src, dst, *args): + try: os.unlink(dst) + except OSError: pass + self.copy_file(src, dst) + self.outfiles.append(dst) + + def run(self): + # --- distutils copy/paste --- + if not self.skip_build: + self.run_command('build_scripts') + # --- /distutils copy/paste --- + + # Custom SCons installation stuff. + if Options.hardlink_scons: + create_basename_script = self.hardlink_scons + elif Options.symlink_scons: + create_basename_script = self.symlink_scons + elif Options.install_scons_script: + create_basename_script = self.copy_scons + else: + create_basename_script = self.do_nothing + + if Options.install_version_script: + create_version_script = self.copy_scons + else: + create_version_script = self.do_nothing + + inputs = self.get_inputs() + bat_scripts = [x for x in inputs if x[-4:] == '.bat'] + non_bat_scripts = [x for x in inputs if x[-4:] != '.bat'] + + self.outfiles = [] + self.mkpath(self.install_dir) + + for src in non_bat_scripts: + base = os.path.basename(src) + scons = os.path.join(self.install_dir, base) + scons_ver = scons + '-' + Version + if is_win32: + scons += '.py' + scons_ver += '.py' + create_version_script(src, scons_ver) + create_basename_script(src, scons, scons_ver) + + if Options.install_bat: + if is_win32: + bat_install_dir = get_scons_prefix(self.install_dir, is_win32) + else: + bat_install_dir = self.install_dir + for src in bat_scripts: + scons_bat = os.path.join(bat_install_dir, 'scons.bat') + scons_version_bat = os.path.join(bat_install_dir, + 'scons-' + Version + '.bat') + self.copy_scons(src, scons_bat) + self.copy_scons(src, scons_version_bat) + + # --- distutils copy/paste --- + if os.name == 'posix': + # Set the executable bits (owner, group, and world) on + # all the scripts we just installed. + for file in self.get_outputs(): + if self.dry_run: + # log.info("changing mode of %s", file) + pass + else: + mode = ((os.stat(file)[stat.ST_MODE]) | 0555) & 07777 + # log.info("changing mode of %s to %o", file, mode) + os.chmod(file, mode) + # --- /distutils copy/paste --- + +class build_scripts(_build_scripts): + def finalize_options(self): + _build_scripts.finalize_options(self) + self.build_dir = os.path.join('build', 'scripts') + +class install_data(_install_data): + def initialize_options(self): + _install_data.initialize_options(self) + def finalize_options(self): + _install_data.finalize_options(self) + if force_to_usr_local(self): + self.install_dir = '/usr/local' + if Options.install_man: + if is_win32: + dir = 'Doc' + else: + dir = os.path.join('man', 'man1') + self.data_files = [(dir, man_pages)] + man_dir = os.path.join(self.install_dir, dir) + msg = "Installed SCons man pages into %s" % man_dir + Installed.append(msg) + else: + self.data_files = [] + +description = "Open Source next-generation build tool." + +long_description = """Open Source next-generation build tool. +Improved, cross-platform substitute for the classic Make +utility. In short, SCons is an easier, more reliable +and faster way to build software.""" + +scripts = [ + 'script/scons', + 'script/sconsign', + 'script/scons-time', + + # We include scons.bat in the list of scripts, even on UNIX systems, + # because we provide an option to allow it be installed explicitly, + # for example if you're installing from UNIX on a share that's + # accessible to Windows and you want the scons.bat. + 'script/scons.bat', +] + +arguments = { + 'name' : "scons", + 'version' : Version, + 'description' : description, + 'long_description' : long_description, + 'author' : 'Steven Knight', + 'author_email' : 'knight@baldmt.com', + 'url' : "http://www.scons.org/", + 'packages' : ["SCons", + "SCons.compat", + "SCons.Node", + "SCons.Options", + "SCons.Platform", + "SCons.Scanner", + "SCons.Script", + "SCons.Tool", + "SCons.Tool.MSCommon", + "SCons.Tool.packaging", + "SCons.Variables", + ], + 'package_dir' : {'' : 'engine'}, + 'data_files' : [('man/man1', man_pages)], + 'scripts' : scripts, + 'cmdclass' : {'install' : install, + 'install_lib' : install_lib, + 'install_data' : install_data, + 'install_scripts' : install_scripts, + 'build_scripts' : build_scripts} +} + +distutils.core.setup(**arguments) + +if Installed: + print '\n'.join(Installed) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: