From 42c2902ea4fab7b0bb85e5b028f3ed0a2bb7bbcc Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Fri, 13 Apr 2007 23:36:39 +0000 Subject: Merged revisions 1826-1882 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1828 | stevenknight | 2007-02-12 13:29:17 -0600 (Mon, 12 Feb 2007) | 1 line 0.96.D588 - Speed up Builder suffix-matching. ........ r1829 | stevenknight | 2007-02-14 08:12:32 -0600 (Wed, 14 Feb 2007) | 1 line 0.96.D589 - The scons command, branch 0.96.94. ........ r1830 | stevenknight | 2007-02-14 09:49:44 -0600 (Wed, 14 Feb 2007) | 1 line 0.96.D590 - Fix the scons-doc .tar.gz file packaging. ........ r1835 | stevenknight | 2007-02-15 11:03:20 -0600 (Thu, 15 Feb 2007) | 1 line 0.96.D591 - Update the release HOWTO. ........ r1836 | stevenknight | 2007-02-15 13:39:24 -0600 (Thu, 15 Feb 2007) | 1 line 0.96.D592 - The scons command, branch 0.96.95. ........ r1837 | stevenknight | 2007-02-15 18:34:18 -0600 (Thu, 15 Feb 2007) | 1 line 0.96.D593 - Back out (comment out) Windows registry installer changes. ........ r1838 | stevenknight | 2007-02-16 10:37:28 -0600 (Fri, 16 Feb 2007) | 1 line 0.96.D594 - Update Debian packaging to remove hard-coded references to Python 2.2. (Jean-Baptiste Lab) ........ r1839 | stevenknight | 2007-02-20 09:34:23 -0600 (Tue, 20 Feb 2007) | 1 line 0.96.D595 - Documentation fixes. In the construction variable appendix, use cross-referenced links to entries. ........ r1840 | stevenknight | 2007-02-21 05:11:35 -0600 (Wed, 21 Feb 2007) | 1 line 0.96.D596 - Handle Java '.class' attributes after non-word tokens without assuming it introduces an inner class. ........ r1841 | stevenknight | 2007-02-21 22:33:28 -0600 (Wed, 21 Feb 2007) | 1 line 0.96.D597 - CPPDEFINES regression ........ r1842 | stevenknight | 2007-02-22 14:19:10 -0600 (Thu, 22 Feb 2007) | 1 line 0.96.D598 - Do not detect a Java anonymous class when the first non-skipped token after "new" is a closing brace. ........ r1843 | stevenknight | 2007-02-23 10:45:06 -0600 (Fri, 23 Feb 2007) | 1 line 0.96.D599 - Better [Errno 21] Is a directory error message. ........ r1844 | stevenknight | 2007-02-23 13:32:11 -0600 (Fri, 23 Feb 2007) | 1 line 0.96.D600 - Fix expansion of non-Node objects within a PathList (maximum recursion / unhashable type bug). ........ r1847 | stevenknight | 2007-03-02 00:12:27 -0600 (Fri, 02 Mar 2007) | 1 line 0.96.D601 - Generate SCons API documentation from the docstrings using epydoc. ........ r1848 | stevenknight | 2007-03-02 14:10:06 -0600 (Fri, 02 Mar 2007) | 1 line 0.96.D602 - Fix use of custom include and lib paths with Visual Studio 8. (Richard Viney) ........ r1849 | stevenknight | 2007-03-03 01:00:22 -0600 (Sat, 03 Mar 2007) | 1 line 0.96.D603 - Man page fix: ParseDepends(). User's Guide updates: NoCache(), Clean(), fix CPPDEFINES output, markers for to-be-documented features, white space clean-up. ........ r1850 | stevenknight | 2007-03-06 02:29:08 -0600 (Tue, 06 Mar 2007) | 1 line 0.96.D604 - Fix use of --debug=presub with the Actions for our out-of-the-box Builders. ........ r1851 | stevenknight | 2007-03-06 09:10:43 -0600 (Tue, 06 Mar 2007) | 1 line 0.96.D605 - User Guide updates: --random, AlwaysBuild(), --tree=, --debug=presub, --debug=stacktrace. ........ r1852 | stevenknight | 2007-03-06 15:38:06 -0600 (Tue, 06 Mar 2007) | 1 line 0.96.D606 - Have the Intel toolchain use the default smart linking logic. (Dmitry Grigorenko and Gary Oberbrunner) ........ r1853 | stevenknight | 2007-03-06 17:56:44 -0600 (Tue, 06 Mar 2007) | 1 line 0.96.D607 - Fix tests: ActionTests.py for presub change, command detection in test/Intel/icpc-link.py. ........ r1854 | stevenknight | 2007-03-08 09:35:25 -0600 (Thu, 08 Mar 2007) | 1 line 0.96.D608 - Better selection of .NET Framework SDK paths. (Richard Viney) ........ r1855 | stevenknight | 2007-03-08 10:34:37 -0600 (Thu, 08 Mar 2007) | 1 line 0.96.D609 - Don't re-run TeX if the triggering strings (\makeindex, \bibliography, \tableofcontents) are commented out. (Matthias Troffaes) ........ r1856 | stevenknight | 2007-03-09 16:18:36 -0600 (Fri, 09 Mar 2007) | 1 line 0.96.D610 - Teach the new PathList module to handle nested lists within CPPPATH and the like. ........ r1857 | stevenknight | 2007-03-10 23:30:29 -0600 (Sat, 10 Mar 2007) | 1 line 0.96.D611 - Qt builders_used failure. ........ r1858 | stevenknight | 2007-03-11 15:33:34 -0500 (Sun, 11 Mar 2007) | 1 line 0.96.D612 - Document limitations of --implicit-cache w.r.t. CPPPATH/LIBPATH/etc. ........ r1859 | stevenknight | 2007-03-11 21:11:26 -0500 (Sun, 11 Mar 2007) | 1 line 0.96.D613 - Document --debug=findlibs and --taskmastertrace in the User's Guide. ........ r1860 | stevenknight | 2007-03-12 13:28:42 -0500 (Mon, 12 Mar 2007) | 1 line 0.96.D614 - Remove deleted cons file from the User's Guide MANIFEST. Fix epydoc API build if the build directory is outside the current directory. ........ r1861 | stevenknight | 2007-03-13 13:03:56 -0500 (Tue, 13 Mar 2007) | 2 lines Ignore '*.pyc' files in the compat/ subdirectory. ........ r1862 | stevenknight | 2007-03-13 19:08:19 -0500 (Tue, 13 Mar 2007) | 1 line 0.96.D615 - Fix use of $VAR expansions within CPPPATH/LIBPATH values when the expansion is itself a Dir node concatenated with a string. ........ r1866 | stevenknight | 2007-03-16 01:46:10 -0500 (Fri, 16 Mar 2007) | 1 line 0.96.D616 - Back off to the 0.96.94 of Builder.py (with some performance improvements). ........ r1867 | stevenknight | 2007-03-16 11:20:39 -0500 (Fri, 16 Mar 2007) | 1 line 0.96.D617 - Fix an unnamed variable error if we can't map the Visual Studio version to a default framework version. ........ r1868 | stevenknight | 2007-03-16 12:08:18 -0500 (Fri, 16 Mar 2007) | 1 line 0.96.D618 - Quote the MSVS build target in command lines to handle spaces target name. (Jeff Mahovsky) ........ r1869 | stevenknight | 2007-03-16 13:30:06 -0500 (Fri, 16 Mar 2007) | 1 line 0.96.D619 - Portability fixes for tests run on Windows. ........ r1870 | stevenknight | 2007-03-20 00:18:04 -0500 (Tue, 20 Mar 2007) | 1 line 0.96.D620 - Windows portability fixes: test scripts and infrastructure, detect vcexpress.exe. ........ r1871 | garyo | 2007-03-21 18:32:54 -0500 (Wed, 21 Mar 2007) | 1 line Fix bug where site_scons dir was added to sys.path as relative, not absolute. Added test case. Bug reported by Timothy Woods; thanks for the test case! ........ r1872 | stevenknight | 2007-03-22 09:43:23 -0500 (Thu, 22 Mar 2007) | 1 line 0.96.D622 - Add mention of site_scons fix to src/CHANGES.txt. ........ r1873 | stevenknight | 2007-04-02 23:49:36 -0500 (Mon, 02 Apr 2007) | 1 line 0.96.D623 - Parallel build dependencies with multiple entries in children. (Adam Simpkins) ........ r1874 | stevenknight | 2007-04-04 07:45:05 -0500 (Wed, 04 Apr 2007) | 1 line 0.96.D624 - Make all necessary LaTeX auxiliary files Precious, so bibliography contents aren't affected by whether the auxiliary files exist or not. (Joel B. Mohler) ........ r1875 | stevenknight | 2007-04-04 13:15:39 -0500 (Wed, 04 Apr 2007) | 1 line 0.96.D625 - Fix --debug-time value when -j option is used. ........ r1876 | stevenknight | 2007-04-09 19:40:08 -0500 (Mon, 09 Apr 2007) | 1 line 0.96.D626 - Fix man page example of propagating external user environment. Eliminate cut-and-paste sentence in NoCache() description. (Helmut Grohne, Joe Bloggs) [Issue 1626] [Issue 1627] ........ r1877 | stevenknight | 2007-04-09 23:20:14 -0500 (Mon, 09 Apr 2007) | 1 line 0.96.D627 - Re-run latex after bibtex runs. (Rob Managan) ........ r1878 | stevenknight | 2007-04-11 23:38:17 -0500 (Wed, 11 Apr 2007) | 1 line 0.96.D628 - Fix typo in the User's Guide. [issue 1600] ........ r1879 | stevenknight | 2007-04-12 01:06:35 -0500 (Thu, 12 Apr 2007) | 1 line 0.96.D629 - Avoid name conflicts with compat/ modules (specifically _subprocess.py). ........ r1880 | stevenknight | 2007-04-12 01:33:42 -0500 (Thu, 12 Apr 2007) | 1 line 0.96.D630 - Portability fixes and other improvements in test scripts. ........ r1882 | stevenknight | 2007-04-13 16:42:02 -0500 (Fri, 13 Apr 2007) | 1 line 0.96.D631 - The scons command, branch 0.96.96. ........ --- HOWTO/release.txt | 9 +- QMTest/TestSCons.py | 34 +- README | 44 +- SConstruct | 6 +- bin/scons-proc.py | 2 +- config | 2 +- debian/changelog | 4 +- debian/control | 4 +- debian/dirs | 1 - debian/postinst | 6 +- debian/rules | 10 +- doc/SConscript | 56 +- doc/man/scons.1 | 137 ++- doc/scons.mod | 18 + doc/user/MANIFEST | 1 - doc/user/builders-built-in.in | 50 +- doc/user/builders-built-in.sgml | 50 +- doc/user/builders-writing.in | 8 +- doc/user/builders-writing.sgml | 8 +- doc/user/caching.in | 235 ++++- doc/user/caching.sgml | 231 ++++- doc/user/command-line.sgml | 20 +- doc/user/cons.in | 52 -- doc/user/cons.sgml | 52 -- doc/user/depends.in | 181 +++- doc/user/depends.sgml | 180 +++- doc/user/environments.in | 54 +- doc/user/environments.sgml | 54 +- doc/user/factories.in | 4 +- doc/user/factories.sgml | 4 +- doc/user/file-removal.in | 115 ++- doc/user/file-removal.sgml | 101 +- doc/user/hierarchy.in | 19 +- doc/user/hierarchy.sgml | 19 +- doc/user/main.in | 67 +- doc/user/main.sgml | 67 +- doc/user/nodes.in | 15 + doc/user/nodes.sgml | 15 + doc/user/preface.in | 4 +- doc/user/preface.sgml | 4 +- doc/user/repositories.in | 8 +- doc/user/repositories.sgml | 8 +- doc/user/sconf.in | 15 + doc/user/sconf.sgml | 15 + doc/user/separate.in | 6 +- doc/user/separate.sgml | 6 +- doc/user/tools.in | 2 +- doc/user/tools.sgml | 2 +- doc/user/troubleshoot.in | 571 +++++++++++- doc/user/troubleshoot.sgml | 835 ++++++++++++++++- rpm/scons.spec.in | 2 +- runtest.py | 1 + src/CHANGES.txt | 251 ++--- src/RELEASE.txt | 2 +- src/engine/MANIFEST.in | 8 +- src/engine/SCons/Action.py | 7 +- src/engine/SCons/ActionTests.py | 7 + src/engine/SCons/Builder.py | 8 +- src/engine/SCons/BuilderTests.py | 18 +- src/engine/SCons/Defaults.py | 89 +- src/engine/SCons/Memoize.py | 5 +- src/engine/SCons/Node/FS.py | 9 +- src/engine/SCons/PathList.py | 40 +- src/engine/SCons/PathListTests.py | 25 +- src/engine/SCons/Script/Main.py | 38 +- src/engine/SCons/Script/__init__.py | 1 - src/engine/SCons/Taskmaster.py | 12 +- src/engine/SCons/Tool/BitKeeper.xml | 4 +- src/engine/SCons/Tool/CVS.xml | 4 +- src/engine/SCons/Tool/JavaCommon.py | 34 +- src/engine/SCons/Tool/JavaCommonTests.py | 44 + src/engine/SCons/Tool/Perforce.xml | 2 +- src/engine/SCons/Tool/RCS.xml | 6 +- src/engine/SCons/Tool/SCCS.xml | 2 +- src/engine/SCons/Tool/__init__.xml | 34 +- src/engine/SCons/Tool/applelink.xml | 12 +- src/engine/SCons/Tool/ar.xml | 4 +- src/engine/SCons/Tool/as.xml | 9 +- src/engine/SCons/Tool/c++.xml | 14 +- src/engine/SCons/Tool/cc.xml | 43 +- src/engine/SCons/Tool/dvi.xml | 10 +- src/engine/SCons/Tool/dvipdf.xml | 4 +- src/engine/SCons/Tool/dvips.xml | 4 +- src/engine/SCons/Tool/f77.xml | 66 +- src/engine/SCons/Tool/f90.xml | 66 +- src/engine/SCons/Tool/f95.xml | 66 +- src/engine/SCons/Tool/fortran.xml | 54 +- src/engine/SCons/Tool/gs.xml | 2 +- src/engine/SCons/Tool/intelc.py | 4 +- src/engine/SCons/Tool/jar.xml | 4 +- src/engine/SCons/Tool/javac.xml | 4 +- src/engine/SCons/Tool/javah.xml | 6 +- src/engine/SCons/Tool/latex.xml | 4 +- src/engine/SCons/Tool/lex.xml | 2 +- src/engine/SCons/Tool/link.xml | 33 +- src/engine/SCons/Tool/m4.xml | 4 +- src/engine/SCons/Tool/mslink.xml | 14 +- src/engine/SCons/Tool/msvc.py | 7 +- src/engine/SCons/Tool/msvc.xml | 12 +- src/engine/SCons/Tool/msvs.py | 69 +- src/engine/SCons/Tool/msvs.xml | 24 +- src/engine/SCons/Tool/pdf.xml | 2 +- src/engine/SCons/Tool/qt.xml | 36 +- src/engine/SCons/Tool/rmic.xml | 6 +- src/engine/SCons/Tool/rpcgen.xml | 8 +- src/engine/SCons/Tool/swig.xml | 12 +- src/engine/SCons/Tool/tar.xml | 2 +- src/engine/SCons/Tool/tex.py | 62 +- src/engine/SCons/Tool/tex.xml | 6 +- src/engine/SCons/Tool/yacc.xml | 4 +- src/engine/SCons/Tool/zip.xml | 2 +- src/engine/SCons/Util.py | 6 + src/engine/SCons/compat/_UserString.py | 92 -- src/engine/SCons/compat/__init__.py | 16 +- src/engine/SCons/compat/_scons_UserString.py | 92 ++ src/engine/SCons/compat/_scons_sets.py | 577 ++++++++++++ src/engine/SCons/compat/_scons_sets15.py | 159 ++++ src/engine/SCons/compat/_scons_subprocess.py | 1280 ++++++++++++++++++++++++++ src/engine/SCons/compat/_sets.py | 577 ------------ src/engine/SCons/compat/_sets15.py | 159 ---- src/engine/SCons/compat/_subprocess.py | 1268 ------------------------- src/script/scons.bat | 2 +- src/setup.cfg | 2 +- src/setup.py | 16 +- src/test_strings.py | 46 +- test/CPPDEFINES.py | 12 +- test/CPPPATH.py | 325 ------- test/CPPPATH/CPPPATH.py | 304 ++++++ test/CPPPATH/Dir.py | 79 ++ test/CPPPATH/expand-object.py | 63 ++ test/CPPPATH/nested-lists.py | 77 ++ test/CPPPATH/null.py | 55 ++ test/CXX/CC-variables.py | 2 +- test/CacheDir/up-to-date-q.py | 2 +- test/Errors/preparation.py | 67 ++ test/Intel/icpc-link.py | 59 ++ test/Java/JAVAC.py | 223 +---- test/Java/live.py | 346 +++++++ test/MSVS/common-prefix.py | 6 +- test/MSVS/runfile.py | 6 +- test/MSVS/vs-6.0-files.py | 8 +- test/MSVS/vs-7.0-files.py | 6 +- test/MSVS/vs-7.1-files.py | 6 +- test/MSVS/vs-8.0-files.py | 6 +- test/Parallel/duplicate-children.py | 68 ++ test/QT/Tool.py | 148 +++ test/TEX/auxiliaries.py | 139 +++ test/TEX/bibtex-latex-rerun.py | 99 ++ test/TEX/multi-run.py | 25 +- test/gnutools.py | 63 +- test/import.py | 3 + test/option--debug.py | 240 ----- test/option/debug-pdb.py | 41 + test/option/debug-presub.py | 203 ++++ test/option/debug-time.py | 146 +-- test/site_scons/no-site-dir.py | 11 +- test/site_scons/sys-path.py | 65 ++ 157 files changed, 7833 insertions(+), 4107 deletions(-) delete mode 100644 doc/user/cons.in delete mode 100644 doc/user/cons.sgml delete mode 100644 src/engine/SCons/compat/_UserString.py create mode 100644 src/engine/SCons/compat/_scons_UserString.py create mode 100644 src/engine/SCons/compat/_scons_sets.py create mode 100644 src/engine/SCons/compat/_scons_sets15.py create mode 100644 src/engine/SCons/compat/_scons_subprocess.py delete mode 100644 src/engine/SCons/compat/_sets.py delete mode 100644 src/engine/SCons/compat/_sets15.py delete mode 100644 src/engine/SCons/compat/_subprocess.py delete mode 100644 test/CPPPATH.py create mode 100644 test/CPPPATH/CPPPATH.py create mode 100644 test/CPPPATH/Dir.py create mode 100644 test/CPPPATH/expand-object.py create mode 100644 test/CPPPATH/nested-lists.py create mode 100644 test/CPPPATH/null.py create mode 100644 test/Errors/preparation.py create mode 100644 test/Intel/icpc-link.py create mode 100644 test/Java/live.py create mode 100644 test/Parallel/duplicate-children.py create mode 100644 test/QT/Tool.py create mode 100644 test/TEX/auxiliaries.py create mode 100644 test/TEX/bibtex-latex-rerun.py delete mode 100644 test/option--debug.py create mode 100644 test/option/debug-pdb.py create mode 100644 test/option/debug-presub.py create mode 100644 test/site_scons/sys-path.py diff --git a/HOWTO/release.txt b/HOWTO/release.txt index 64c62af..a29b37b 100644 --- a/HOWTO/release.txt +++ b/HOWTO/release.txt @@ -353,10 +353,13 @@ Things to do to release a new X.Y version of SCons: download.php: new version number + includes/templates.php: + update $latestrelease + includes/versions.php: update $stablerelease and/or $latestrelease - add new version number do $docversions[], - SHIFT $docversions[] INDEX NUMBERS :-( + add new version number to $docversions[], + IMPORTANT: SHIFT $docversions[] INDEX NUMBERS :-( index.php: announcement on the home page remove out-of-date announcements @@ -386,7 +389,7 @@ Things to do to release a new X.Y version of SCons: [point your browser to http://www.scons.org/] - Update the tigris.org web site: + Update the project pages at tigris.org: svn co http://scons.tigris.org/svn/scons/trunk diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py index 79e96d7..1b499de 100644 --- a/QMTest/TestSCons.py +++ b/QMTest/TestSCons.py @@ -30,7 +30,7 @@ from TestCommon import __all__ # to what we expect. (If we derived the version number from the same # data driving the build we might miss errors if the logic breaks.) -SConsVersion = '0.96.94' +SConsVersion = '0.96.96' __all__.extend([ 'TestSCons', 'python', @@ -443,8 +443,8 @@ env.SharedLibrary( 'myqt', 'my_qobject.cpp' ) self.QT = self.workpath(dir) self.QT_LIB = 'myqt' - self.QT_MOC = '%s %s' % (python, self.workpath(dir, 'bin', 'mymoc.py')) - self.QT_UIC = '%s %s' % (python, self.workpath(dir, 'bin', 'myuic.py')) + self.QT_MOC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'mymoc.py')) + self.QT_UIC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'myuic.py')) self.QT_LIB_DIR = self.workpath(dir, 'lib') def Qt_create_SConstruct(self, place): @@ -550,15 +550,31 @@ print "self._msvs_versions =", str(env['MSVS']['VERSIONS']) """Returns a full path to the executable (MSDEV or devenv) for the specified version of Visual Studio. """ - sub_path = { - '6.0' : ['Common', 'MSDev98', 'Bin', 'MSDEV.COM'], - '7.0' : ['Common7', 'IDE', 'devenv.com'], - '7.1' : ['Common7', 'IDE', 'devenv.com'], - '8.0' : ['Common7', 'IDE', 'devenv.com'], + common_msdev98_bin_msdev_com = ['Common', 'MSDev98', 'Bin', 'MSDEV.COM'] + common7_ide_devenv_com = ['Common7', 'IDE', 'devenv.com'] + common7_ide_vcexpress_exe = ['Common7', 'IDE', 'VCExpress.exe'] + sub_paths = { + '6.0' : [ + common_msdev98_bin_msdev_com, + ], + '7.0' : [ + common7_ide_devenv_com, + ], + '7.1' : [ + common7_ide_devenv_com, + ], + '8.0' : [ + common7_ide_devenv_com, + common7_ide_vcexpress_exe, + ], } from SCons.Tool.msvs import get_msvs_install_dirs vs_path = get_msvs_install_dirs(version)['VSINSTALLDIR'] - return apply(os.path.join, [vs_path] + sub_path[version]) + for sp in sub_paths[version]: + p = apply(os.path.join, [vs_path] + sp) + if os.path.exists(p): + return p + return apply(os.path.join, [vs_path] + sub_paths[version][0]) # In some environments, $AR will generate a warning message to stderr # if the library doesn't previously exist and is being created. One diff --git a/README b/README index 8a5b562..699d1b5 100644 --- a/README +++ b/README @@ -83,12 +83,11 @@ In this case, your options are: -- (Optional.) Install from a pre-packaged SCons package that does not require distutils: - Red Hat Linux scons-0.96.94.noarch.rpm + Red Hat Linux scons-0.96.96.noarch.rpm - Debian GNU/Linux scons_0.96.94_all.deb - (or use apt-get) + Debian GNU/Linux use apt-get to get the official package - Windows scons-0.96.94.win32.exe + Windows scons-0.96.96.win32.exe -- (Recommended.) Download the latest distutils package from the following URL: @@ -160,7 +159,7 @@ And on Windows: By default, the above commands will do the following: - -- Install the version-numbered "scons-0.96.94" and "sconsign-0.96.94" + -- Install the version-numbered "scons-0.96.96" and "sconsign-0.96.96" 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 @@ -173,15 +172,15 @@ By default, the above commands will do the following: 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-0.96.94" and "sconsign-0.96.94" scripts + symbolic links to the "scons-0.96.96" and "sconsign-0.96.96" scripts by specifying the "--hardlink-scons" or "--symlink-scons" options on the command line. - -- Install "scons-0.96.94.bat" and "scons.bat" wrapper scripts in the + -- Install "scons-0.96.96.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-0.96.94.bat" + "--install-bat" option may be specified to have "scons-0.96.96.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 @@ -189,7 +188,7 @@ By default, the above commands will do the following: -- Install the SCons build engine (a Python module) in an appropriate version-numbered SCons library directory - (/usr/lib/scons-0.96.94 or C:\Python*\scons-0.96.94, for example). + (/usr/lib/scons-0.96.96 or C:\Python*\scons-0.96.96, for example). See below for more options related to installing the build engine library. @@ -444,7 +443,7 @@ development platform, Windows users can translate as appropriate)): $ python runtest.py -a - Be patient, there are more than 500 test scripts in the + Be patient, there are more than 700 test scripts in the whole suite. If any test scripts fail, they will be listed in a summary at @@ -526,18 +525,18 @@ On Windows: Depending on the utilities installed on your system, any or all of the following packages will be built: - build/dist/scons-0.96.94-1.noarch.rpm - build/dist/scons-0.96.94-1.src.rpm - build/dist/scons-0.96.94.linux-i686.tar.gz - build/dist/scons-0.96.94.tar.gz - build/dist/scons-0.96.94.win32.exe - build/dist/scons-0.96.94.zip - build/dist/scons-doc-0.96.94.tar.gz - build/dist/scons-local-0.96.94.tar.gz - build/dist/scons-local-0.96.94.zip - build/dist/scons-src-0.996.94tar.gz - build/dist/scons-src-0.96.94.zip - build/dist/scons_0.96.94-1_all.deb + build/dist/scons-0.96.96-1.noarch.rpm + build/dist/scons-0.96.96-1.src.rpm + build/dist/scons-0.96.96.linux-i686.tar.gz + build/dist/scons-0.96.96.tar.gz + build/dist/scons-0.96.96.win32.exe + build/dist/scons-0.96.96.zip + build/dist/scons-doc-0.96.96.tar.gz + build/dist/scons-local-0.96.96.tar.gz + build/dist/scons-local-0.96.96.zip + build/dist/scons-src-0.96.96.tar.gz + build/dist/scons-src-0.96.96.zip + build/dist/scons_0.96.96-1_all.deb The SConstruct file is supposed to be smart enough to avoid trying to build packages for which you don't have the proper utilities installed. @@ -809,6 +808,7 @@ With plenty of help from the SCons Development team: Chad Austin Charles Crain Steve Leblanc + Greg Noel Gary Oberbrunner Anthony Roach Greg Spencer diff --git a/SConstruct b/SConstruct index 76ec535..f0d8c1a 100644 --- a/SConstruct +++ b/SConstruct @@ -45,7 +45,7 @@ import sys import time project = 'scons' -default_version = '0.96.94' +default_version = '0.96.96' copyright = "Copyright (c) %s The SCons Foundation" % copyright_years SConsignFile() @@ -504,7 +504,7 @@ scons = { 'sconsign.1', 'scons-time.1', 'script/scons.bat', - 'script/scons-post-install.py', + #'script/scons-post-install.py', 'setup.cfg', 'setup.py', ], @@ -862,7 +862,7 @@ for p in [ scons ]: ]) old = os.path.join('lib', 'scons', '') - new = os.path.join('lib', 'python2.2', 'site-packages', '') + new = os.path.join('lib', 'python' + python_ver, 'site-packages', '') def xxx(s, old=old, new=new): if s[:len(old)] == old: s = new + s[len(old):] diff --git a/bin/scons-proc.py b/bin/scons-proc.py index bf345a7..d55016f 100644 --- a/bin/scons-proc.py +++ b/bin/scons-proc.py @@ -23,7 +23,7 @@ import SConsDoc base_sys_path = [os.getcwd() + '/build/test-tar-gz/lib/scons'] + sys.path helpstr = """\ -Usage: scons-varlist.py [--man|--sgml] \ +Usage: scons-proc.py [--man|--sgml] \ [-b file(s)] [-t file(s)] [-v file(s)] [infile ...] Options: -b file(s) dump builder information to the specified file(s) diff --git a/config b/config index 3af477c..b055bdb 100644 --- a/config +++ b/config @@ -50,7 +50,7 @@ * * Look in aesub(5) for more information about command substitutions. */ -build_command = "python2.4 ${Source bootstrap.py} -Y${SUBSTitute : \\ -Y $Search_Path} date='${DAte %Y/%m/%d %H:%M:%S}' developer=${DEVeloper} version=${VERsion} change=${Change}"; +build_command = "python2.1 ${Source bootstrap.py} -Y${SUBSTitute : \\ -Y $Search_Path} date='${DAte %Y/%m/%d %H:%M:%S}' developer=${DEVeloper} version=${VERsion} change=${Change}"; /* * SCons removes its targets before constructing them, which qualifies it diff --git a/debian/changelog b/debian/changelog index 84f2b2e..063cba4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -scons (0.96-94) unstable; urgency=low +scons (0.96-96) unstable; urgency=low * Pre-release of eighth beta release. - -- Steven Knight Sun, 07 Jan 2007 18:36:20 -0600 + -- Steven Knight Thu, 12 Apr 2007 12:36:25 -0500 scons (0.96-1) unstable; urgency=low diff --git a/debian/control b/debian/control index 7e0d2a5..ab360e6 100644 --- a/debian/control +++ b/debian/control @@ -2,12 +2,12 @@ Source: scons Section: devel Priority: optional Maintainer: Moshe Zadka -Build-Depends-Indep: debhelper (>> 2.0.0), python2.2-dev +Build-Depends-Indep: debhelper (>> 2.0.0), python-dev (>> 2.2) Standards-Version: 3.5.6 Package: scons Architecture: all -Depends: python2.2 +Depends: python (>> 2.2) Description: A replacement for Make SCons is an Open Source software construction tool--that is, a build tool; an improved substitute for the classic Make utility; a better diff --git a/debian/dirs b/debian/dirs index 47f84cf..93a451b 100644 --- a/debian/dirs +++ b/debian/dirs @@ -1,4 +1,3 @@ usr/bin -usr/lib/python2.2/site-packages usr/share/doc/scons usr/share/man/man1 diff --git a/debian/postinst b/debian/postinst index 6923e0f..ed8ed5c 100644 --- a/debian/postinst +++ b/debian/postinst @@ -7,11 +7,13 @@ if [ "$1" = "configure" ]; then fi NAME=scons +PYTHON_VERSION=`python -V 2>&1| cut -d" " -f2 | cut -d"." -f0-2` +PYTHON_EXE_NAME=`which python$PYTHON_VERSION` case "$1" in configure|abort-upgrade|abort-remove|abort-deconfigure) dpkg --listfiles $NAME | grep '\.py$' | \ - xargs -n 1 /usr/bin/python2.2 -c 'import py_compile,sys;py_compile.compile(sys.argv[1])' + xargs -n 1 $PYTHON_EXE_NAME -c 'import py_compile,sys;py_compile.compile(sys.argv[1])' dpkg --listfiles $NAME | grep '\.py$' | \ - xargs -n 1 /usr/bin/python2.2 -O -c 'import py_compile,sys;py_compile.compile(sys.argv[1])' + xargs -n 1 $PYTHON_EXE_NAME -O -c 'import py_compile,sys;py_compile.compile(sys.argv[1])' ;; esac diff --git a/debian/rules b/debian/rules index 4250539..1eaf4de 100644 --- a/debian/rules +++ b/debian/rules @@ -6,7 +6,9 @@ # Overridable variables added to support building test .deb files # as part of routine SCons builds. --SK BUILDDEB_OPTIONS= -PYTHON=/usr/bin/python2.2 +PYTHON_VERSION=`python -V 2>&1| cut -d" " -f2 | cut -d"." -f0-2` +PYTHON_PATH=/usr/bin/python +PYTHON=$(PYTHON_PATH)$(PYTHON_VERSION) ####### # Uncomment this to turn on verbose mode. @@ -51,9 +53,9 @@ install: build @# already exist on our non-Debian test build systems. Hence, @# we do a lot of mkdir -p and rm -f here... --SK @######## - mkdir -p debian/scons/usr/lib/python2.2/site-packages/ - rm -rf debian/scons/usr/lib/python2.2/site-packages/SCons - cp -r build/lib/SCons debian/scons/usr/lib/python2.2/site-packages/ + mkdir -p debian/scons/usr/lib/python$(PYTHON_VERSION)/site-packages/ + rm -rf debian/scons/usr/lib/python$(PYTHON_VERSION)/site-packages/SCons + cp -r build/lib/SCons debian/scons/usr/lib/python$(PYTHON_VERSION)/site-packages/ mkdir -p debian/scons/usr/bin/ rm -f debian/scons/usr/bin/scons diff --git a/doc/SConscript b/doc/SConscript index bf62024..7617974 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -49,6 +49,7 @@ doc_tar_gz = os.path.join(build_dir, # if lynx is available to do the dump. # fig2dev = whereis('fig2dev') +epydoc = whereis('epydoc') groff = whereis('groff') lynx = whereis('lynx') man2html = whereis('man2html') @@ -447,12 +448,63 @@ for man_1 in man_page_list: tar_deps.append(html) tar_list.append(html) +if epydoc: + # XXX Should be in common with reading the same thing in + # the SConstruct file. + e = os.path.join('#src', 'engine') + manifest_in = File(os.path.join(e, 'MANIFEST.in')).rstr() + sources = map(lambda x: x[:-1], open(manifest_in).readlines()) + sources = filter(lambda x: string.find(x, 'Optik') == -1, sources) + sources = filter(lambda x: string.find(x, 'Platform') == -1, sources) + sources = filter(lambda x: string.find(x, 'Tool') == -1, sources) + # XXX + sources = filter(lambda x: string.find(x, 'Options') == -1, sources) + + e = os.path.join(build, '..', 'scons', 'engine') + sources = map(lambda x, e=e: os.path.join(e, x), sources) + + epydoc_commands = [ + Delete('$OUTDIR'), + '$EPYDOC $EPYDOCFLAGS --output $OUTDIR --docformat=restructuredText --name SCons --url http://www.scons.org/ $SOURCES', + Touch('$TARGET'), + ] + + htmldir = os.path.join(build, 'HTML', 'scons-api') + env.Command('${OUTDIR}/index.html', sources, epydoc_commands, + EPYDOC=epydoc, EPYDOCFLAGS='--html', OUTDIR=htmldir) + tar_deps.append(htmldir) + tar_list.append(htmldir) + + # PDF and PostScript and TeX are built from the + # same invocation. + api_dir = os.path.join(build, 'scons-api') + api_pdf = os.path.join(api_dir, 'api.pdf') + api_ps = os.path.join(api_dir, 'api.ps') + api_tex = os.path.join(api_dir, 'api.tex') + api_targets = [api_pdf, api_ps, api_tex] + env.Command(api_targets, sources, epydoc_commands, + EPYDOC=epydoc, EPYDOCFLAGS='--pdf', OUTDIR=api_dir) + Local(api_targets) + + pdf_install = os.path.join(build, 'PDF', 'scons-api.pdf') + env.InstallAs(pdf_install, api_pdf) + tar_deps.append(pdf_install) + tar_list.append(pdf_install) + Local(pdf_install) + + ps_install = os.path.join(build, 'PS', 'scons-api.ps') + env.InstallAs(ps_install, api_ps) + tar_deps.append(ps_install) + tar_list.append(ps_install) + Local(ps_install) + # # Now actually create the tar file of the documentation, # for easy distribution to the web site. # if tar_deps: - tar_list = string.join(map(lambda x: x[11:], tar_list)) + tar_list = string.join(map(lambda x, b=build+'/': string.replace(x, b, ''), + tar_list)) env.Command(doc_tar_gz, tar_deps, - "tar cf${TAR_HFLAG} - -C build/doc %s | gzip > $TARGET" % tar_list) + "tar cf${TAR_HFLAG} - -C %s %s | gzip > $TARGET" % (build, tar_list)) Local(doc_tar_gz) diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 862cde5..73e3df9 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -184,7 +184,7 @@ complete external environment: .ES import os -env = Environment(ENV = os.environ['PATH']) +env = Environment(ENV = os.environ) .EE This comes at the expense of making your build @@ -681,10 +681,38 @@ and ultimately removed. .TP --debug=time -Prints various time profiling information: the time spent -executing each build command, the total build time, the total time spent -executing build commands, the total time spent executing SConstruct and -SConscript files, and the total time spent executing SCons itself. +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 @@ -735,6 +763,24 @@ 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 @@ -774,29 +820,35 @@ imported Python modules. If several options are used, the directories are searched in the order specified. -.TP -.RI --no-site-dir -Prevents the automatic addition of the standard -.I site_scons -dir to -.IR sys.path . -Also prevents loading the -.I site_scons/site_init.py -module if it exists, and prevents adding -.I site_scons/site_tools -to the toolpath. - .TP --implicit-cache -Cache implicit dependencies. This can cause +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 -to miss changes in the implicit dependencies in cases where a new implicit +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. CPPPATH) than a current implicit dependency with the same name. +(e.g. +.BR CPPPATH ", " LIBPATH ) +than a current implicit dependency with the same name. .TP --implicit-deps-changed -Force SCons to ignore the cached implicit dependencies. This causes the +Forces SCons to ignore the cached implicit dependencies. This causes the implicit dependencies to be rescanned and recached. This implies .BR --implicit-cache . @@ -835,24 +887,6 @@ targets specified on the command line will still be processed. .\" .I N .\" (a floating-point number). -.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 .\" --list-derived @@ -902,6 +936,18 @@ no matter how old the file is. 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 +dir to +.IR sys.path . +Also prevents loading the +.I site_scons/site_init.py +module if it exists, and prevents adding +.I site_scons/site_tools +to the toolpath. + .\" .TP .\" .RI -o " file" ", --old-file=" file ", --assume-old=" file .\" Do not rebuild @@ -3214,13 +3260,6 @@ be cached whenever the method has been activated. The specified targets may be a list or an individual target. -Multiple calls to -.BR NoCache () -are legal, -and prevent each specified target -from being removed by calls to the -.B -c -option. Multiple files should be specified either as separate arguments to the @@ -3324,9 +3363,9 @@ for a table of options and construction variables. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP -.RI ParseDepends( filename ", [" must_exist ]) +.RI ParseDepends( filename ", [" must_exist ", " only_one ]) .TP -.RI env.ParseDepends( filename ", [" must_exist " " only_one ]) +.RI env.ParseDepends( filename ", [" must_exist ", " only_one ]) Parses the contents of the specified .I filename as a list of dependencies in the style of @@ -3341,7 +3380,7 @@ if the specified .I filename does not exist. The optional -.I must_exit +.I must_exist argument may be set to a non-zero value to have scons diff --git a/doc/scons.mod b/doc/scons.mod index 975f445..aa1a0b9 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -79,10 +79,18 @@ --> +--config"> --debug=explain"> +--debug=findlibs"> +--debug=includes"> +--debug=presub"> +--debug=stacktrace"> --implicit-cache"> --implicit-deps-changed"> --implicit-deps-unchanged"> +--profile"> +--taskmastertrace"> +--tree"> -Q"> Add"> +AddPostAction"> +AddPreAction"> AddOptions"> Alias"> Aliases"> +AlwaysBuild"> Append"> +AppendENVPath"> +AppendUnique"> BoolOption"> Build"> CacheDir"> @@ -148,6 +161,7 @@ Execute"> Export"> File"> +FindFile"> Finish"> GenerateHelpText"> GetOption"> @@ -163,6 +177,7 @@ Module"> Move"> NoClean"> +NoCache"> Objects"> Options"> PackageOption"> @@ -175,6 +190,8 @@ PathOption.PathIsFile"> Precious"> Prepend"> +PrependENVPath"> +PrependUnique"> Replace"> Repository"> Return"> @@ -182,6 +199,7 @@ Salt"> SetBuildSignatureType"> SetContentSignatureType"> +SideEffect"> SourceSignature"> SourceSignatures"> Split"> diff --git a/doc/user/MANIFEST b/doc/user/MANIFEST index 04c293b..cc7dc1e 100644 --- a/doc/user/MANIFEST +++ b/doc/user/MANIFEST @@ -9,7 +9,6 @@ build-install.sgml caching.sgml command-line.sgml cons.pl -cons.sgml copyright.sgml depends.sgml ENV.sgml diff --git a/doc/user/builders-built-in.in b/doc/user/builders-built-in.in index 8b0f16f..c7516a6 100644 --- a/doc/user/builders-built-in.in +++ b/doc/user/builders-built-in.in @@ -214,7 +214,7 @@ &SCons; provides separate Builder objects to create static and shared object files. - The distinction becomes especially important when + The distinction becomes especially important when archiving object files into different types of libraries. @@ -568,7 +568,7 @@ - XXX + XXX PCH() @@ -579,7 +579,7 @@ - XXX + XXX RES() @@ -604,16 +604,16 @@ - XXX + XXX CFile() - XXX + XXX CFile() programlisting - XXX + XXX CFile() screen @@ -623,16 +623,16 @@ - XXX + XXX CXXFILE() - XXX + XXX CXXFILE() programlisting - XXX + XXX CXXFILE() screen @@ -654,16 +654,16 @@ - XXX + XXX DVI() para - XXX + XXX DVI() programlisting - XXX + XXX DVI() screen @@ -673,7 +673,7 @@ - XXX + XXX PDF() para @@ -684,16 +684,16 @@ - XXX + XXX PostScript() para - XXX + XXX PostScript() programlisting - XXX + XXX PostScript() screen @@ -896,7 +896,7 @@ - XXX + XXX Java() screen @@ -906,7 +906,7 @@ - The &Jar; builder object XXX + XXX The &Jar; builder object @@ -917,7 +917,7 @@ - XXX + XXX Jar() screen @@ -927,16 +927,16 @@ - XXX + XXX JavaH() para - XXX + XXX JavaH() programlisting - XXX + XXX JavaH() screen @@ -946,16 +946,16 @@ - XXX + XXX RMIC() para - XXX + XXX RMIC() programlisting - XXX + XXX RMIC() screen diff --git a/doc/user/builders-built-in.sgml b/doc/user/builders-built-in.sgml index 9c0a3b8..cf09fd5 100644 --- a/doc/user/builders-built-in.sgml +++ b/doc/user/builders-built-in.sgml @@ -212,7 +212,7 @@ &SCons; provides separate Builder objects to create static and shared object files. - The distinction becomes especially important when + The distinction becomes especially important when archiving object files into different types of libraries. @@ -566,7 +566,7 @@ - XXX + XXX PCH() @@ -577,7 +577,7 @@ - XXX + XXX RES() @@ -602,16 +602,16 @@ - XXX + XXX CFile() - XXX + XXX CFile() programlisting - XXX + XXX CFile() screen @@ -621,16 +621,16 @@ - XXX + XXX CXXFILE() - XXX + XXX CXXFILE() programlisting - XXX + XXX CXXFILE() screen @@ -652,16 +652,16 @@ - XXX + XXX DVI() para - XXX + XXX DVI() programlisting - XXX + XXX DVI() screen @@ -671,7 +671,7 @@ - XXX + XXX PDF() para @@ -682,16 +682,16 @@ - XXX + XXX PostScript() para - XXX + XXX PostScript() programlisting - XXX + XXX PostScript() screen @@ -870,7 +870,7 @@ - XXX + XXX Java() screen @@ -880,7 +880,7 @@ - The &Jar; builder object XXX + XXX The &Jar; builder object @@ -891,7 +891,7 @@ - XXX + XXX Jar() screen @@ -901,16 +901,16 @@ - XXX + XXX JavaH() para - XXX + XXX JavaH() programlisting - XXX + XXX JavaH() screen @@ -920,16 +920,16 @@ - XXX + XXX RMIC() para - XXX + XXX RMIC() programlisting - XXX + XXX RMIC() screen diff --git a/doc/user/builders-writing.in b/doc/user/builders-writing.in index ebfef5d..62717aa 100644 --- a/doc/user/builders-writing.in +++ b/doc/user/builders-writing.in @@ -594,7 +594,7 @@ This functionality could be invoked as in the following example: generator is being called to contribute to a build signature, as opposed to actually executing the command. - + @@ -720,7 +720,7 @@ This functionality could be invoked as in the following example: - bld = Builder(action = 'XXX', + bld = Builder(action = 'my_command', suffix = '.foo', src_suffix = '.input', emitter = 'MY_EMITTER') @@ -745,14 +745,14 @@ This functionality could be invoked as in the following example: - XXX + XXX para env = Environment() - #env.SourceCode('.', env.BitKeeper('XXX')) + #env.SourceCode('.', env.BitKeeper('my_command')) env.Program('hello.c') diff --git a/doc/user/builders-writing.sgml b/doc/user/builders-writing.sgml index 327650a..412d431 100644 --- a/doc/user/builders-writing.sgml +++ b/doc/user/builders-writing.sgml @@ -538,7 +538,7 @@ This functionality could be invoked as in the following example: generator is being called to contribute to a build signature, as opposed to actually executing the command. - + @@ -624,7 +624,7 @@ This functionality could be invoked as in the following example: - bld = Builder(action = 'XXX', + bld = Builder(action = 'my_command', suffix = '.foo', src_suffix = '.input', emitter = 'MY_EMITTER') @@ -649,14 +649,14 @@ This functionality could be invoked as in the following example: - XXX + XXX para env = Environment() - #env.SourceCode('.', env.BitKeeper('XXX')) + #env.SourceCode('.', env.BitKeeper('my_command')) env.Program('hello.c') diff --git a/doc/user/caching.in b/doc/user/caching.in index 0195c43..015407b 100644 --- a/doc/user/caching.in +++ b/doc/user/caching.in @@ -80,6 +80,12 @@ every time a file is built, it is stored in the shared cache directory along with its MD5 build signature. + + + Actually, the MD5 signature is used as the name of the file + in the shared cache directory in which the contents are stored. + + On subsequent builds, before an action is invoked to build a file, &SCons; will check the shared cache directory @@ -106,8 +112,8 @@ One potential drawback to using a shared cache - is that your build output can be inconsistent - from invocation to invocation, + is that the output printed by &SCons; + can be inconsistent from invocation to invocation, because any given file may be rebuilt one time and retrieved from the shared cache the next time. This can make analyzing build output more difficult, @@ -146,7 +152,74 @@
- Not Retrieving Files From a Shared Cache + Not Using the Shared Cache for Specific Files + + + + You may want to disable caching for certain + specific files in your configuration. + For example, if you only want to put + executable files in a central cache, + but not the intermediate object files, + you can use the &NoCache; + function to specify that the + object files should not be cached: + + + + + + env = Environment() + obj = env.Object('hello.c') + env.Program('hello.c') + CacheDir('cache') + NoCache('hello.o') + + + hello.c + + + + + + + + Then when you run &scons; after cleaning + the built targets, + it will recompile the object file locally + (since it doesn't exist in the shared cache directory), + but still realize that the shared cache directory + contains an up-to-date executable program + that can be retrieved instead of re-linking: + + + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + cc -o hello.o -c hello.c + Retrieved `hello' from cache + + +
+ +
+ Disabling the Shared Cache @@ -211,8 +284,8 @@ In this case, you can use the the --cache-force option to tell &SCons; to put all derived files in the cache, - even if the files had already been built - by a previous invocation: + even if the files already exist in your local tree + from having been built by a previous invocation: @@ -221,7 +294,6 @@ scons -Q -c scons -Q --cache-disable scons -Q --cache-force - scons -Q -c scons -Q @@ -231,7 +303,7 @@ demonstrates that the --cache-disable option avoids putting the built hello.o - and + and hello files in the cache, but after using the --cache-force option, the files have been put in the cache @@ -240,3 +312,152 @@
+ +
+ Minimizing Cache Contention: the <literal>--random</literal> Option + + + + If you allow multiple builds to update the + shared cache directory simultaneously, + two builds that occur at the same time + can sometimes start "racing" + with one another to build the same files + in the same order. + If, for example, + you are linking multiple files into an executable program: + + + + + + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + f1.c + f2.c + f3.c + f4.c + f5.c + f6.c + + + + + &SCons; will normally build the input object files + on which the program depends in their normal, sorted order: + + + + + scons -Q + + + + + But if two such builds take place simultaneously, + they may each look in the cache at nearly the same + time and both decide that f1.o + must be rebuilt and pushed into the shared cache directory, + then both decide that f2.o + must be rebuilt (and pushed into the shared cache directory), + then both decide that f3.o + must be rebuilt... + This won't cause any actual build problems--both + builds will succeed, + generate correct output files, + and populate the cache--but + it does represent wasted effort. + + + + + + To alleviate such contention for the cache, + you can use the --random command-line option + to tell &SCons; to build dependencies + in a random order: + + + + + + + % scons -Q --random + cc -o f3.o -c f3.c + cc -o f1.o -c f1.c + cc -o f5.o -c f5.c + cc -o f2.o -c f2.c + cc -o f4.o -c f4.c + cc -o prog f1.o f2.o f3.o f4.o f5.o + + + + + Multiple builds using the --random option + will usually build their dependencies in different, + random orders, + which minimizes the chances for a lot of + contention for same-named files + in the shared cache directory. + Multiple simultaneous builds might still race to try to build + the same target file on occasion, + but long sequences of inefficient contention + should be rare. + + + + + + Note, of course, + the --random option + will cause the output that &SCons; prints + to be inconsistent from invocation to invocation, + which may be an issue when + trying to compare output from different build runs. + + + +
+ + + + diff --git a/doc/user/caching.sgml b/doc/user/caching.sgml index d87e493..02c3597 100644 --- a/doc/user/caching.sgml +++ b/doc/user/caching.sgml @@ -68,6 +68,12 @@ every time a file is built, it is stored in the shared cache directory along with its MD5 build signature. + + + Actually, the MD5 signature is used as the name of the file + in the shared cache directory in which the contents are stored. + + On subsequent builds, before an action is invoked to build a file, &SCons; will check the shared cache directory @@ -100,8 +106,8 @@ One potential drawback to using a shared cache - is that your build output can be inconsistent - from invocation to invocation, + is that the output printed by &SCons; + can be inconsistent from invocation to invocation, because any given file may be rebuilt one time and retrieved from the shared cache the next time. This can make analyzing build output more difficult, @@ -146,7 +152,67 @@
- Not Retrieving Files From a Shared Cache + Not Using the Shared Cache for Specific Files + + + + You may want to disable caching for certain + specific files in your configuration. + For example, if you only want to put + executable files in a central cache, + but not the intermediate object files, + you can use the &NoCache; + function to specify that the + object files should not be cached: + + + + + env = Environment() + obj = env.Object('hello.c') + env.Program('hello.c') + CacheDir('cache') + NoCache('hello.o') + + + + + Then when you run &scons; after cleaning + the built targets, + it will recompile the object file locally + (since it doesn't exist in the shared cache directory), + but still realize that the shared cache directory + contains an up-to-date executable program + that can be retrieved instead of re-linking: + + + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + cc -o hello.o -c hello.c + Retrieved `hello' from cache + + +
+ +
+ Disabling the Shared Cache @@ -221,8 +287,8 @@ In this case, you can use the the --cache-force option to tell &SCons; to put all derived files in the cache, - even if the files had already been built - by a previous invocation: + even if the files already exist in your local tree + from having been built by a previous invocation: @@ -238,12 +304,8 @@ cc -o hello hello.o % scons -Q --cache-force scons: `.' is up to date. - % scons -Q -c - Removed hello.o - Removed hello % scons -Q - Retrieved `hello.o' from cache - Retrieved `hello' from cache + scons: `.' is up to date. @@ -252,7 +314,7 @@ demonstrates that the --cache-disable option avoids putting the built hello.o - and + and hello files in the cache, but after using the --cache-force option, the files have been put in the cache @@ -261,3 +323,150 @@
+ +
+ Minimizing Cache Contention: the <literal>--random</literal> Option + + + + If you allow multiple builds to update the + shared cache directory simultaneously, + two builds that occur at the same time + can sometimes start "racing" + with one another to build the same files + in the same order. + If, for example, + you are linking multiple files into an executable program: + + + + + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + + + + &SCons; will normally build the input object files + on which the program depends in their normal, sorted order: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + cc -o f4.o -c f4.c + cc -o f5.o -c f5.c + cc -o prog f1.o f2.o f3.o f4.o f5.o + + + + + But if two such builds take place simultaneously, + they may each look in the cache at nearly the same + time and both decide that f1.o + must be rebuilt and pushed into the shared cache directory, + then both decide that f2.o + must be rebuilt (and pushed into the shared cache directory), + then both decide that f3.o + must be rebuilt... + This won't cause any actual build problems--both + builds will succeed, + generate correct output files, + and populate the cache--but + it does represent wasted effort. + + + + + + To alleviate such contention for the cache, + you can use the --random command-line option + to tell &SCons; to build dependencies + in a random order: + + + + + + + % scons -Q --random + cc -o f3.o -c f3.c + cc -o f1.o -c f1.c + cc -o f5.o -c f5.c + cc -o f2.o -c f2.c + cc -o f4.o -c f4.c + cc -o prog f1.o f2.o f3.o f4.o f5.o + + + + + Multiple builds using the --random option + will usually build their dependencies in different, + random orders, + which minimizes the chances for a lot of + contention for same-named files + in the shared cache directory. + Multiple simultaneous builds might still race to try to build + the same target file on occasion, + but long sequences of inefficient contention + should be rare. + + + + + + Note, of course, + the --random option + will cause the output that &SCons; prints + to be inconsistent from invocation to invocation, + which may be an issue when + trying to compare output from different build runs. + + + +
+ + + + diff --git a/doc/user/command-line.sgml b/doc/user/command-line.sgml index 1953690..66de79c 100644 --- a/doc/user/command-line.sgml +++ b/doc/user/command-line.sgml @@ -808,8 +808,8 @@ % scons -Q - cc -o bar.o -c -D['RELEASE_BUILD=', 1] bar.c - cc -o foo.o -c -D['RELEASE_BUILD=', 1] foo.c + cc -o bar.o -c -DRELEASE_BUILD=1 bar.c + cc -o foo.o -c -DRELEASE_BUILD=1 foo.c cc -o foo foo.o bar.o @@ -832,8 +832,8 @@ % scons -Q - cc -o bar.o -c -D['RELEASE_BUILD=', 0] bar.c - cc -o foo.o -c -D['RELEASE_BUILD=', 0] foo.c + cc -o bar.o -c -DRELEASE_BUILD=0 bar.c + cc -o foo.o -c -DRELEASE_BUILD=0 foo.c cc -o foo foo.o bar.o @@ -898,12 +898,12 @@ % scons -Q RELEASE=yes foo.o - cc -o foo.o -c -D['RELEASE_BUILD=', True] foo.c + cc -o foo.o -c -DRELEASE_BUILD=True foo.c % scons -Q RELEASE=t foo.o - cc -o foo.o -c -D['RELEASE_BUILD=', True] foo.c + cc -o foo.o -c -DRELEASE_BUILD=True foo.c @@ -929,12 +929,12 @@ % scons -Q RELEASE=no foo.o - cc -o foo.o -c -D['RELEASE_BUILD=', False] foo.c + cc -o foo.o -c -DRELEASE_BUILD=False foo.c % scons -Q RELEASE=f foo.o - cc -o foo.o -c -D['RELEASE_BUILD=', False] foo.c + cc -o foo.o -c -DRELEASE_BUILD=False foo.c @@ -1430,9 +1430,9 @@ % scons -Q PACKAGE=/usr/local/location foo.o cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c % scons -Q PACKAGE=yes foo.o - cc -o foo.o -c -D['PACKAGE="', True, '"'] foo.c + cc -o foo.o -c -DPACKAGE="True" foo.c % scons -Q PACKAGE=no foo.o - cc -o foo.o -c -D['PACKAGE="', False, '"'] foo.c + cc -o foo.o -c -DPACKAGE="False" foo.c diff --git a/doc/user/cons.in b/doc/user/cons.in deleted file mode 100644 index 6967b42..0000000 --- a/doc/user/cons.in +++ /dev/null @@ -1,52 +0,0 @@ - - - - - XXX - - - -
- Differences Between &Cons; and &SCons; - - - - XXX - - - -
- -
- Advantages of &SCons; Over &Cons; - - - - XXX - - - -
diff --git a/doc/user/cons.sgml b/doc/user/cons.sgml deleted file mode 100644 index 6967b42..0000000 --- a/doc/user/cons.sgml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - XXX - - - -
- Differences Between &Cons; and &SCons; - - - - XXX - - - -
- -
- Advantages of &SCons; Over &Cons; - - - - XXX - - - -
diff --git a/doc/user/depends.in b/doc/user/depends.in index 0262924..e0d5e92 100644 --- a/doc/user/depends.in +++ b/doc/user/depends.in @@ -23,27 +23,6 @@ --> - - So far we've seen how &SCons; handles one-time builds. @@ -224,7 +203,7 @@ operating system on which the build is performed (as reported by C As you've just seen, - &SCons; uses signatures to decide whether a + &SCons; uses signatures to decide whether a target file is up to date or must be rebuilt. When a target file depends on another target file, &SCons; allows you to configure separately @@ -415,7 +394,7 @@ operating system on which the build is performed (as reported by C - The &cv-CPPPATH; value + The &cv-link-CPPPATH; value tells &SCons; to look in the current directory ('.') for any files included by C source files @@ -462,7 +441,7 @@ operating system on which the build is performed (as reported by C - Like the &cv-LIBPATH; variable, + Like the &cv-link-LIBPATH; variable, the &cv-CPPPATH; variable may be a list of directories, or a string separated by @@ -572,22 +551,46 @@ operating system on which the build is performed (as reported by C - + + + + + + When &implicit-cache; is used, &SCons; will ignore any changes that + may have been made to search paths + (like &cv-CPPPATH; or &cv-LIBPATH;,). + This can lead to &SCons; not rebuilding a file if a change to + &cv-CPPPATH; would normally cause a different, same-named file from + a different directory to be used. + + + + + + + + When &implicit-cache; is used, &SCons; will not detect if a + same-named file has been added to a directory that is earlier in + the search path than the directory in which the file was found + last time. + + + + +
The &implicit-deps-changed; Option @@ -633,7 +636,7 @@ operating system on which the build is performed (as reported by C + +
@@ -672,7 +686,7 @@ operating system on which the build is performed (as reported by C - Sometimes it makes sense + Sometimes it makes sense to not rebuild a program, even if a dependency file changes. In this case, @@ -687,7 +701,7 @@ operating system on which the build is performed (as reported by C - #include "hello.h" + #include "hello.h" int main() { printf("Hello, %s!\n", string); } @@ -785,14 +799,103 @@ operating system on which the build is performed (as reported by C - +
+ The &AlwaysBuild; Method + + + + How &SCons; handles dependencies can also be affected + by the &AlwaysBuild; method. + When a file is passed to the &AlwaysBuild; method, + like so: + + + + + + hello = Program('hello.c') + AlwaysBuild(hello) + + + int main() { printf("Hello, %s!\n", string); } + + + + + + Then the specified target file (&hello; in our example) + will always be considered out-of-date and + rebuilt whenever that target file is evaluated + while walking the dependency graph: + + + + + scons -Q + scons -Q + + + + + The &AlwaysBuild; function has a somewhat misleading name, + because it does not actually mean the target file will + be rebuilt every single time &SCons; is invoked. + Instead, it means that the target will, in fact, + be rebuilt whenever the target file is encountered + while evaluating the targets specified on + the command line (and their dependencies). + So specifying some other target on the command line, + a target that does not + itself depend on the &AlwaysBuild; target, + will still be rebuilt only if it's out-of-date + with respect to its dependencies: + + + + + scons -Q + scons -Q hello.o + + + + +
+ + - - So far we've seen how &SCons; handles one-time builds. @@ -230,7 +209,7 @@ operating system on which the build is performed (as reported by C As you've just seen, - &SCons; uses signatures to decide whether a + &SCons; uses signatures to decide whether a target file is up to date or must be rebuilt. When a target file depends on another target file, &SCons; allows you to configure separately @@ -422,7 +401,7 @@ operating system on which the build is performed (as reported by C - The &cv-CPPPATH; value + The &cv-link-CPPPATH; value tells &SCons; to look in the current directory ('.') for any files included by C source files @@ -475,7 +454,7 @@ operating system on which the build is performed (as reported by C - Like the &cv-LIBPATH; variable, + Like the &cv-link-LIBPATH; variable, the &cv-CPPPATH; variable may be a list of directories, or a string separated by @@ -587,22 +566,46 @@ operating system on which the build is performed (as reported by C - + + + + + + When &implicit-cache; is used, &SCons; will ignore any changes that + may have been made to search paths + (like &cv-CPPPATH; or &cv-LIBPATH;,). + This can lead to &SCons; not rebuilding a file if a change to + &cv-CPPPATH; would normally cause a different, same-named file from + a different directory to be used. + + + + + + + + When &implicit-cache; is used, &SCons; will not detect if a + same-named file has been added to a directory that is earlier in + the search path than the directory in which the file was found + last time. + + + + +
The &implicit-deps-changed; Option @@ -651,7 +654,7 @@ operating system on which the build is performed (as reported by C + +
@@ -693,7 +707,7 @@ operating system on which the build is performed (as reported by C - Sometimes it makes sense + Sometimes it makes sense to not rebuild a program, even if a dependency file changes. In this case, @@ -797,14 +811,104 @@ operating system on which the build is performed (as reported by C - +
+ The &AlwaysBuild; Method + + + + How &SCons; handles dependencies can also be affected + by the &AlwaysBuild; method. + When a file is passed to the &AlwaysBuild; method, + like so: + + + + + hello = Program('hello.c') + AlwaysBuild(hello) + + + + + Then the specified target file (&hello; in our example) + will always be considered out-of-date and + rebuilt whenever that target file is evaluated + while walking the dependency graph: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q + cc -o hello hello.o + + + + + The &AlwaysBuild; function has a somewhat misleading name, + because it does not actually mean the target file will + be rebuilt every single time &SCons; is invoked. + Instead, it means that the target will, in fact, + be rebuilt whenever the target file is encountered + while evaluating the targets specified on + the command line (and their dependencies). + So specifying some other target on the command line, + a target that does not + itself depend on the &AlwaysBuild; target, + will still be rebuilt only if it's out-of-date + with respect to its dependencies: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello.o + scons: `hello.o' is up to date. + + + + +
+ + +
Appending to the End of Values in a &ConsEnv; @@ -1050,6 +1061,12 @@ environment undisturbed. scons -Q + +
@@ -1057,7 +1074,7 @@ environment undisturbed. - You can append a value to the beginning + You can append a value to the beginning of an existing construction variable using the &Prepend; method: @@ -1110,6 +1127,29 @@ environment undisturbed. scons -Q + + +
+ + +
diff --git a/doc/user/environments.sgml b/doc/user/environments.sgml index 9404c23..67a5551 100644 --- a/doc/user/environments.sgml +++ b/doc/user/environments.sgml @@ -305,11 +305,7 @@ by the MD5 signature calculation on the actual file contents. =back -XXX - -DESCRIBE THE Literal() FUNCTION, TOO - -XXX +XXX DESCRIBE THE Literal() FUNCTION, TOO XXX =head2 Expanding construction variables in file names @@ -454,7 +450,7 @@ environment undisturbed. - + The construction environment in this example is still initialized with the same default construction variable values, @@ -988,6 +984,21 @@ environment undisturbed.
+ +
Appending to the End of Values in a &ConsEnv; @@ -1043,6 +1054,12 @@ environment undisturbed. scons: `.' is up to date. + +
@@ -1050,7 +1067,7 @@ environment undisturbed. - You can append a value to the beginning + You can append a value to the beginning of an existing construction variable using the &Prepend; method: @@ -1100,6 +1117,29 @@ environment undisturbed. scons: `.' is up to date. + + +
+ + + diff --git a/doc/user/factories.in b/doc/user/factories.in index 95145d8..6ef5249 100644 --- a/doc/user/factories.in +++ b/doc/user/factories.in @@ -61,7 +61,7 @@ Notice that the action returned by the &Copy; factory - will expand the &cv-TARGET; and &cv-SOURCE; strings + will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings at the time &file_out; is built, and that the order of the arguments is the same as that of a builder itself--that is, @@ -201,7 +201,7 @@ Of course, like all of these &Action; factories, the &Delete factory also expands - &cv-TARGET; and &cv-SOURCE; variables appropriately. + &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately. For example: diff --git a/doc/user/factories.sgml b/doc/user/factories.sgml index b145ff2..e0567f6 100644 --- a/doc/user/factories.sgml +++ b/doc/user/factories.sgml @@ -58,7 +58,7 @@ Notice that the action returned by the &Copy; factory - will expand the &cv-TARGET; and &cv-SOURCE; strings + will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings at the time &file_out; is built, and that the order of the arguments is the same as that of a builder itself--that is, @@ -180,7 +180,7 @@ Of course, like all of these &Action; factories, the &Delete; factory also expands - &cv-TARGET; and &cv-SOURCE; variables appropriately. + &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately. For example: diff --git a/doc/user/file-removal.in b/doc/user/file-removal.in index 1d259bf..adc5b5f 100644 --- a/doc/user/file-removal.in +++ b/doc/user/file-removal.in @@ -27,7 +27,7 @@ There are two occasions when &SCons; will, by default, remove target files. - The first is when &SCons; determines that + The first is when &SCons; determines that an target file needs to be rebuilt and removes the existing version of the target before executing @@ -44,7 +44,7 @@ Preventing target removal during build: the &Precious; Function - + By default, &SCons; removes targets before building them. Sometimes, however, this is not what you want. For example, you may want to update a library incrementally, @@ -53,9 +53,9 @@ In such cases, you can use the &Precious; method to prevent &SCons; from removing the target before it is built: - + - + env = Environment(RANLIBCOM='') @@ -72,34 +72,33 @@ int f3() { } - + - + Although the output doesn't look any different, &SCons; does not, in fact, delete the target library before rebuilding it: - + - + scons -Q - + - + &SCons; will, however, still delete files marked as &Precious; when the -c option is used. - + -
Preventing target removal during clean: the &NoClean; Function - + By default, &SCons; removes all built targets when invoked with the -c option to clean a source tree of built tragets. @@ -109,12 +108,12 @@ but leave the final targets (the libraries) untouched. - + In such cases, you can use the &NoClean; method to prevent &SCons; from removing a target during a clean: - + - + env = Environment(RANLIBCOM='') @@ -131,14 +130,14 @@ int f3() { } - + - + Notice that the libfoo.a is not listed as a removed file: - + - + scons -Q scons -c @@ -146,3 +145,79 @@
+
+ Removing additional files during clean: the &Clean; Function + + + + There may be additional files that you want removed + when the -c option is used, + but which &SCons; doesn't know about + because they're not normal target files. + For example, perhaps a command you invoke + creates a log file as + part of building the target file you want. + You would like the log file cleaned, + but you don't want to have to teach + SCons that the command + "builds" two files. + + + + + + You can use the &Clean; function to arrange for additional files + to be removed when the -c option is used. + Notice, however, that the &Clean; function takes two arguments, + and the second argument + is the name of the additional file you want cleaned + (foo.log in this example): + + + + + + t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE') + Clean(t, 'foo.log') + + + env = DefaultEnvironment() + import os + env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd() + SConscript('S') + + + foo.in + + + foo.log + + + cat $3 > $2 + + + + + + The first argument is the target with which you want + the cleaning of this additional file associated. + In the above example, + we've used the return value from the + &Command; function, + which represents the + foo.out + target. + Now whenever the + foo.out target is cleaned + by the -c option, + the foo.log file + will be removed as well: + + + + + scons -Q + scons -Q -c + + +
diff --git a/doc/user/file-removal.sgml b/doc/user/file-removal.sgml index 76a2e01..f64d394 100644 --- a/doc/user/file-removal.sgml +++ b/doc/user/file-removal.sgml @@ -27,7 +27,7 @@ There are two occasions when &SCons; will, by default, remove target files. - The first is when &SCons; determines that + The first is when &SCons; determines that an target file needs to be rebuilt and removes the existing version of the target before executing @@ -44,7 +44,7 @@ Preventing target removal during build: the &Precious; Function - + By default, &SCons; removes targets before building them. Sometimes, however, this is not what you want. For example, you may want to update a library incrementally, @@ -53,23 +53,23 @@ In such cases, you can use the &Precious; method to prevent &SCons; from removing the target before it is built: - + - + env = Environment(RANLIBCOM='') lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.Precious(lib) - + - + Although the output doesn't look any different, &SCons; does not, in fact, delete the target library before rebuilding it: - + - + % scons -Q cc -o f1.o -c f1.c @@ -77,22 +77,21 @@ cc -o f3.o -c f3.c ar rc libfoo.a f1.o f2.o f3.o - + - + &SCons; will, however, still delete files marked as &Precious; when the -c option is used. - + -
Preventing target removal during clean: the &NoClean; Function - + By default, &SCons; removes all built targets when invoked with the -c option to clean a source tree of built tragets. @@ -102,25 +101,25 @@ but leave the final targets (the libraries) untouched. - + In such cases, you can use the &NoClean; method to prevent &SCons; from removing a target during a clean: - + - + env = Environment(RANLIBCOM='') lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.NoClean(lib) - + - + Notice that the libfoo.a is not listed as a removed file: - + - + % scons -Q cc -o f1.o -c f1.c @@ -139,3 +138,65 @@
+
+ Removing additional files during clean: the &Clean; Function + + + + There may be additional files that you want removed + when the -c option is used, + but which &SCons; doesn't know about + because they're not normal target files. + For example, perhaps a command you invoke + creates a log file as + part of building the target file you want. + You would like the log file cleaned, + but you don't want to have to teach + SCons that the command + "builds" two files. + + + + + + You can use the &Clean; function to arrange for additional files + to be removed when the -c option is used. + Notice, however, that the &Clean; function takes two arguments, + and the second argument + is the name of the additional file you want cleaned + (foo.log in this example): + + + + + t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE') + Clean(t, 'foo.log') + + + + + The first argument is the target with which you want + the cleaning of this additional file associated. + In the above example, + we've used the return value from the + &Command; function, + which represents the + foo.out + target. + Now whenever the + foo.out target is cleaned + by the -c option, + the foo.log file + will be removed as well: + + + + + % scons -Q + build -o foo.out foo.in + % scons -Q -c + Removed foo.out + Removed foo.log + + +
diff --git a/doc/user/hierarchy.in b/doc/user/hierarchy.in index 2ec3fd3..d93e811 100644 --- a/doc/user/hierarchy.in +++ b/doc/user/hierarchy.in @@ -419,7 +419,7 @@ make no difference to the build. (Notice that the lib/foo1.o object file is built in the same directory as its source file. See , below, - for information about + for information about how to build the object file in a different subdirectory.)
@@ -471,7 +471,7 @@ make no difference to the build. notice that the /usr/joe/lib/foo1.o object file is built in the same directory as its source file. See , below, - for information about + for information about how to build the object file in a different subdirectory.)
@@ -773,3 +773,18 @@ make no difference to the build. + + diff --git a/doc/user/hierarchy.sgml b/doc/user/hierarchy.sgml index 0ce2430..713d605 100644 --- a/doc/user/hierarchy.sgml +++ b/doc/user/hierarchy.sgml @@ -393,7 +393,7 @@ make no difference to the build. (Notice that the lib/foo1.o object file is built in the same directory as its source file. See , below, - for information about + for information about how to build the object file in a different subdirectory.)
@@ -435,7 +435,7 @@ make no difference to the build. notice that the /usr/joe/lib/foo1.o object file is built in the same directory as its source file. See , below, - for information about + for information about how to build the object file in a different subdirectory.)
@@ -725,3 +725,18 @@ make no difference to the build. + + diff --git a/doc/user/main.in b/doc/user/main.in index be302d7..d864350 100644 --- a/doc/user/main.in +++ b/doc/user/main.in @@ -51,7 +51,6 @@ - @@ -90,6 +89,53 @@ ]> + + SCons User Guide &buildversion; @@ -231,6 +277,10 @@ - Construction Variables &variables; diff --git a/doc/user/main.sgml b/doc/user/main.sgml index be302d7..d864350 100644 --- a/doc/user/main.sgml +++ b/doc/user/main.sgml @@ -51,7 +51,6 @@ - @@ -90,6 +89,53 @@ ]> + + SCons User Guide &buildversion; @@ -231,6 +277,10 @@ - Construction Variables &variables; diff --git a/doc/user/nodes.in b/doc/user/nodes.in index 4b8aa0f..6d4b267 100644 --- a/doc/user/nodes.in +++ b/doc/user/nodes.in @@ -353,3 +353,18 @@ --> + + diff --git a/doc/user/nodes.sgml b/doc/user/nodes.sgml index c8756c5..4ada5b7 100644 --- a/doc/user/nodes.sgml +++ b/doc/user/nodes.sgml @@ -356,3 +356,18 @@ --> + + diff --git a/doc/user/preface.in b/doc/user/preface.in index 2914d40..fb888c4 100644 --- a/doc/user/preface.in +++ b/doc/user/preface.in @@ -198,7 +198,7 @@ - XXX + XXX history of SCons @@ -213,7 +213,7 @@ - XXX + XXX conventions used in this manual diff --git a/doc/user/preface.sgml b/doc/user/preface.sgml index ba5d1a3..694f41b 100644 --- a/doc/user/preface.sgml +++ b/doc/user/preface.sgml @@ -198,7 +198,7 @@ - XXX + XXX history of SCons @@ -213,7 +213,7 @@ - XXX + XXX conventions used in this manual diff --git a/doc/user/repositories.in b/doc/user/repositories.in index a667a91..9065593 100644 --- a/doc/user/repositories.in +++ b/doc/user/repositories.in @@ -114,7 +114,7 @@ found in the local build tree, &SCons; will search first for a /usr/repository1/hello.c file - and then for a /usr/repository1/hello.c file + and then for a /usr/repository2/hello.c file to use in its place.
@@ -195,7 +195,7 @@ a source file for #include file names and realize that targets built from that source file also depend on the #include file(s). - For each directory in the &cv-CPPPATH; list, + For each directory in the &cv-link-CPPPATH; list, &SCons; will actually search the corresponding directories in any repository trees and establish the correct dependencies on any @@ -390,8 +390,8 @@ coming into existence.) Some modern versions of C compilers do have an option to disable or control this behavior. - If so, add that option to &cv-CFLAGS; - (or &cv-CXXFLAGS; or both) in your construction environment(s). + If so, add that option to &cv-link-CFLAGS; + (or &cv-link-CXXFLAGS; or both) in your construction environment(s). Make sure the option is used for all construction environments that use C preprocessing! diff --git a/doc/user/repositories.sgml b/doc/user/repositories.sgml index c659aa2..f22611b 100644 --- a/doc/user/repositories.sgml +++ b/doc/user/repositories.sgml @@ -109,7 +109,7 @@ found in the local build tree, &SCons; will search first for a /usr/repository1/hello.c file - and then for a /usr/repository1/hello.c file + and then for a /usr/repository2/hello.c file to use in its place. @@ -178,7 +178,7 @@ a source file for #include file names and realize that targets built from that source file also depend on the #include file(s). - For each directory in the &cv-CPPPATH; list, + For each directory in the &cv-link-CPPPATH; list, &SCons; will actually search the corresponding directories in any repository trees and establish the correct dependencies on any @@ -360,8 +360,8 @@ coming into existence.) Some modern versions of C compilers do have an option to disable or control this behavior. - If so, add that option to &cv-CFLAGS; - (or &cv-CXXFLAGS; or both) in your construction environment(s). + If so, add that option to &cv-link-CFLAGS; + (or &cv-link-CXXFLAGS; or both) in your construction environment(s). Make sure the option is used for all construction environments that use C preprocessing! diff --git a/doc/user/sconf.in b/doc/user/sconf.in index 14c10b7..0165ddd 100644 --- a/doc/user/sconf.in +++ b/doc/user/sconf.in @@ -469,3 +469,18 @@ + + diff --git a/doc/user/sconf.sgml b/doc/user/sconf.sgml index 997c97d..df530fe 100644 --- a/doc/user/sconf.sgml +++ b/doc/user/sconf.sgml @@ -469,3 +469,18 @@ + + diff --git a/doc/user/separate.in b/doc/user/separate.in index 6d497a2..08bb986 100644 --- a/doc/user/separate.in +++ b/doc/user/separate.in @@ -268,7 +268,7 @@ program using the F path name. - +
Telling &SCons; to Not Duplicate Source Files in the Build Directory @@ -460,8 +460,8 @@ program using the F path name. Why You'd Want to Call &BuildDir; Instead of &SConscript; - - XXX + + XXX why call BuildDir() instead of SConscript(build_dir=) diff --git a/doc/user/separate.sgml b/doc/user/separate.sgml index 5f0341d..57acd48 100644 --- a/doc/user/separate.sgml +++ b/doc/user/separate.sgml @@ -263,7 +263,7 @@ program using the F path name. -
+
Telling &SCons; to Not Duplicate Source Files in the Build Directory @@ -451,8 +451,8 @@ program using the F path name. Why You'd Want to Call &BuildDir; Instead of &SConscript; - - XXX + + XXX why call BuildDir() instead of SConscript(build_dir=) diff --git a/doc/user/tools.in b/doc/user/tools.in index 8eaa35e..512bf97 100644 --- a/doc/user/tools.in +++ b/doc/user/tools.in @@ -26,7 +26,7 @@ This appendix contains descriptions of all of the -Tools that are +Tools modules that are available "out of the box" in this version of SCons. diff --git a/doc/user/tools.sgml b/doc/user/tools.sgml index 8eaa35e..512bf97 100644 --- a/doc/user/tools.sgml +++ b/doc/user/tools.sgml @@ -26,7 +26,7 @@ This appendix contains descriptions of all of the -Tools that are +Tools modules that are available "out of the box" in this version of SCons. diff --git a/doc/user/troubleshoot.in b/doc/user/troubleshoot.in index 206e50e..11f44dd 100644 --- a/doc/user/troubleshoot.in +++ b/doc/user/troubleshoot.in @@ -32,6 +32,28 @@ the tool is behaving a certain way, and how to get it to behave the way you want. &SCons; is no different. + This appendix contains a number of + different ways in which you can + get some additional insight into &SCons' behavior. + + + + + + Note that we're always interested in trying to + improve how you can troubleshoot configuration problems. + If you run into a problem that has + you scratching your head, + and which there just doesn't seem to be a good way to debug, + odds are pretty good that someone else will run into + the same problem, too. + If so, please let the SCons development team know + (preferably by filing a bug report + or feature request at our project pages at tigris.org) + so that we can use your feedback + to try to come up with a better way to help you, + and others, get the necessary insight into &SCons; behavior + to help identify and fix configuration issues. @@ -40,7 +62,7 @@ - Let's take a simple example of + Let's look at a simple example of a misconfigured build that causes a target to be rebuilt every time &SCons; is run: @@ -70,7 +92,7 @@ - Now if we run &SCons; multiple on this example, + Now if we run &SCons; multiple times on this example, we see that it re-runs the &cp; command every time: @@ -193,6 +215,17 @@ scons -Q --debug=explain + + + (Note that the &debug-explain; option will only tell you + why &SCons; decided to rebuild necessary targets. + It does not tell you what files it examined + when deciding not + to rebuild a target file, + which is often a more valuable question to answer.) + + +
@@ -223,7 +256,7 @@ - + env = Environment() print env.Dump() @@ -279,7 +312,7 @@ - + env = Environment() print env.Dump('ENV') @@ -306,3 +339,533 @@
+ +
+ + What Dependencies Does &SCons; Know About? the &tree; Option + + + + Sometimes the best way to try to figure out what + &SCons; is doing is simply to take a look at the + dependency graph that it constructs + based on your &SConscript; files. + The --tree option + will display all or part of the + &SCons; dependency graph in an + "ASCII art" graphical format + that shows the dependency hierarchy. + + + + + + For example, given the following input &SConstruct; file: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog', ['f1.c', 'f2.c', 'f3.c']) + + + #include "inc.h" + + + #include "inc.h" + + + #include "inc.h" + + + inc.h + + + + + + Running &SCons; with the --tree=all + option yields: + + + + + scons -Q --tree=all + + + + + The tree will also be printed when the + -n (no execute) option is used, + which allows you to examine the dependency graph + for a configuration without actually + rebuilding anything in the tree. + + + + + + The --tree option only prints + the dependency graph for the specified targets + (or the default target(s) if none are specified on the command line). + So if you specify a target like f2.o + on the command line, + the --tree option will only + print the dependency graph for that file: + + + + + scons -Q --tree=all f2.o + + + + + This is, of course, useful for + restricting the output from a very large + build configuration to just a + portion in which you're interested. + Multiple targets are fine, + in which case a tree will be printed + for each specified target: + + + + + scons -Q --tree=all f1.o f3.o + + + + + The status argument may be used + to tell &SCons; to print status information about + each file in the dependency graph: + + + + + scons -Q --tree=status + + + + + Note that --tree=all,status is equivalent; + the all + is assumed if only status is present. + As an alternative to all, + you can specify --tree=derived + to have &SCons; only print derived targets + in the tree output, + skipping source files + (like .c and .h files): + + + + + scons -Q --tree=derived + + + + + You can use the status + modifier with derived as well: + + + + + scons -Q --tree=derived,status + + + + + Note that the order of the --tree= + arguments doesn't matter; + --tree=status,derived is + completely equivalent. + + + + + + The default behavior of the --tree option + is to repeat all of the dependencies each time the library dependency + (or any other dependency file) is encountered in the tree. + If certain target files share other target files, + such as two programs that use the same library: + + + + + + env = Environment(CPPPATH = ['.'], + LIBS = ['foo'], + LIBPATH = ['.']) + env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Program('prog1.c') + env.Program('prog2.c') + + + #include "inc.h" + + + #include "inc.h" + + + #include "inc.h" + + + #include "inc.h" + + + #include "inc.h" + + + inc.h + + + + + + Then there can be a lot of repetition in the + --tree= output: + + + + + scons -Q --tree=all + + + + + In a large configuration with many internal libraries + and include files, + this can very quickly lead to huge output trees. + To help make this more manageable, + a prune modifier may + be added to the option list, + in which case &SCons; + will print the name of a target that has + already been visited during the tree-printing + in [square brackets] + as an indication that the dependencies + of the target file may be found + by looking farther up the tree: + + + + + scons -Q --tree=prune + + + + + Like the status keyword, + the prune argument by itself + is equivalent to --tree=all,prune. + + + +
+ +
+ + How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option + + + + Sometimes it's useful to look at the + pre-substitution string + that &SCons; uses to generate + the command lines it executes. + This can be done with the &debug-presub; option: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog', 'prog.c') + + + prog.c + + + + + + + % scons -Q --debug=presub + Building prog.o with action: + $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCOMCOM $SOURCES + cc -o prog.o -c -I. prog.c + Building prog with action: + $SMART_LINKCOM + cc -o prog prog.o + + +
+ +
+ + Where is &SCons; Searching for Libraries? the &debug-findlibs; Option + + + + To get some insight into what library names + &SCons; is searching for, + and in which directories it is searching, + Use the --debug=findlibs option. + Given the following input &SConstruct; file: + + + + + + env = Environment(LIBPATH = ['libs1', 'libs2']) + env.Program('prog.c', LIBS=['foo', 'bar']) + + + prog.c + + + libs1/libfoo.a + + + libs2/libbar.a + + + + + + And the libraries libfoo.a + and libbar.a + in libs1 and libs2, + respectively, + use of the --debug=findlibs option yields: + + + + + scons -Q --debug=findlibs + + +
+ + + +
+ + Where is &SCons; Blowing Up? the &debug-stacktrace; Option + + + + In general, &SCons; tries to keep its error + messages short and informative. + That means we usually try to avoid showing + the stack traces that are familiar + to experienced Python programmers, + since they usually contain much more + information than is useful to most people. + + + + + + For example, the following &SConstruct file: + + + + + + Program('prog.c') + + + + + + Generates the following error if the + prog.c file + does not exist: + + + + + scons -Q + + + + + In this case, + the error is pretty obvious. + But if it weren't, + and you wanted to try to get more information + about the error, + the &debug-stacktrace; option + would show you exactly where in the &SCons; source code + the problem occurs: + + + + + scons -Q --debug=stacktrace + + + + + Of course, if you do need to dive into the &SCons; source code, + we'd like to know if, or how, + the error messages or troubleshooting options + could have been improved to avoid that. + Not everyone has the necessary time or + Python skill to dive into the source code, + and we'd like to improve &SCons; + for those people as well... + + + +
+ +
+ + How is &SCons; Making Its Decisions? the &taskmastertrace; Option + + + + The internal &SCons; subsystem that handles walking + the dependency graph + and controls the decision-making about what to rebuild + is the Taskmaster. + &SCons; supports a --taskmastertrace + option that tells the Taskmaster to print + information about the children (dependencies) + of the various Nodes on its walk down the graph, + which specific dependent Nodes are being evaluated, + and in what order. + + + + + + The --taskmastertrace option + takes as an argument the name of a file in + which to put the trace output, + with - (a single hyphen) + indicating that the trace messages + should be printed to the standard output: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog.c') + + + #include "inc.h" + prog.c + + + #define STRING "one" + + + + + scons -Q --taskmastertrace=- prog + + + + + The --taskmastertrace option + doesn't provide information about the actual + calculations involved in deciding if a file is up-to-date, + but it does show all of the dependencies + it knows about for each Node, + and the order in which those dependencies are evaluated. + This can be useful as an alternate way to determine + whether or not your &SCons; configuration, + or the implicit dependency scan, + has actually identified all the correct dependencies + you want it to. + + + +
+ + + + diff --git a/doc/user/troubleshoot.sgml b/doc/user/troubleshoot.sgml index f019baa..3df9c67 100644 --- a/doc/user/troubleshoot.sgml +++ b/doc/user/troubleshoot.sgml @@ -32,6 +32,28 @@ the tool is behaving a certain way, and how to get it to behave the way you want. &SCons; is no different. + This appendix contains a number of + different ways in which you can + get some additional insight into &SCons;' behavior. + + + + + + Note that we're always interested in trying to + improve how you can troubleshoot configuration problems. + If you run into a problem that has + you scratching your head, + and which there just doesn't seem to be a good way to debug, + odds are pretty good that someone else will run into + the same problem, too. + If so, please let the SCons development team know + (preferably by filing a bug report + or feature request at our project pages at tigris.org) + so that we can use your feedback + to try to come up with a better way to help you, + and others, get the necessary insight into &SCons; behavior + to help identify and fix configuration issues. @@ -40,7 +62,7 @@ - Let's take a simple example of + Let's look at a simple example of a misconfigured build that causes a target to be rebuilt every time &SCons; is run: @@ -65,7 +87,7 @@ - Now if we run &SCons; multiple on this example, + Now if we run &SCons; multiple times on this example, we see that it re-runs the &cp; command every time: @@ -184,6 +206,17 @@ cc -o prog file1.o file2.o file3.o + + + (Note that the &debug-explain; option will only tell you + why &SCons; decided to rebuild necessary targets. + It does not tell you what files it examined + when deciding not + to rebuild a target file, + which is often a more valuable question to answer.) + + +
@@ -213,7 +246,10 @@ - + + env = Environment() + print env.Dump() + @@ -248,14 +284,14 @@ '.spp', '.SPP'], 'DSUFFIXES': ['.d'], - 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43bec>, - 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c0c>, + 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c3fdac>, + 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c3fdcc>, 'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'}, - 'ESCAPE': <function escape at 0xb7b66c34>, - 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c2c>, + 'ESCAPE': <function escape at 0xb7ba1f0c>, + 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c3fdec>, 'IDLSUFFIXES': ['.idl', '.IDL'], - 'INSTALL': <function installFunc at 0xb7c41f0c>, - 'INSTALLSTR': <function installStr at 0xb7c41f44>, + 'INSTALL': <function installFunc at 0xb7c4317c>, + 'INSTALLSTR': <function installStr at 0xb7c431b4>, 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'], 'LIBPREFIX': 'lib', 'LIBPREFIXES': '$LIBPREFIX', @@ -267,16 +303,16 @@ 'PLATFORM': 'posix', 'PROGPREFIX': '', 'PROGSUFFIX': '', - 'PSPAWN': <function piped_env_spawn at 0xb7b66fb4>, - 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c4c>, + 'PSPAWN': <function piped_env_spawn at 0xb7bb12cc>, + 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c3fe0c>, 'SCANNERS': [], 'SHELL': 'sh', 'SHLIBPREFIX': '$LIBPREFIX', 'SHLIBSUFFIX': '.so', 'SHOBJPREFIX': '$OBJPREFIX', 'SHOBJSUFFIX': '$OBJSUFFIX', - 'SPAWN': <function spawnvpe_spawn at 0xb7b66a74>, - 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7bd37ac>, + 'SPAWN': <function spawnvpe_spawn at 0xb7ba1d4c>, + 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7bce89c>, 'TEMPFILEPREFIX': '@', 'TOOLS': [], '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', @@ -284,10 +320,10 @@ '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', '__RPATH': '$_RPATH', - '_concat': <function _concat at 0xb7c41fb4>, - '_defines': <function _defines at 0xb7c47064>, - '_installStr': <function installStr at 0xb7c41f44>, - '_stripixes': <function _stripixes at 0xb7c4702c>} + '_concat': <function _concat at 0xb7c43224>, + '_defines': <function _defines at 0xb7c432cc>, + '_installStr': <function installStr at 0xb7c431b4>, + '_stripixes': <function _stripixes at 0xb7c43294>} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. @@ -304,9 +340,9 @@ C:\>scons scons: Reading SConscript files ... - { 'BUILDERS': {'Object': <SCons.Builder.CompositeBuilder instance at 0xb7b6024c>, 'SharedObject': <SCons.Builder.CompositeBuilder instance at 0xb7b603cc>, 'StaticObject': <SCons.Builder.CompositeBuilder instance at 0xb7b6024c>, 'PCH': <SCons.Builder.BuilderBase instance at 0xb7bd2eac>, 'RES': <SCons.Builder.BuilderBase instance at 0xb7b596ec>}, + { 'BUILDERS': {'Object': <SCons.Builder.CompositeBuilder instance at 0xb7b6354c>, 'SharedObject': <SCons.Builder.CompositeBuilder instance at 0xb7b636cc>, 'StaticObject': <SCons.Builder.CompositeBuilder instance at 0xb7b6354c>, 'PCH': <SCons.Builder.BuilderBase instance at 0xb7bd6e8c>, 'RES': <SCons.Builder.BuilderBase instance at 0xb7b5b9ec>}, 'CC': 'cl', - 'CCCOM': <SCons.Action.FunctionAction instance at 0xb7b6086c>, + 'CCCOM': <SCons.Action.FunctionAction instance at 0xb7b63b6c>, 'CCCOMFLAGS': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS', 'CCFLAGS': ['/nologo'], 'CCPCHFLAGS': ['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'], @@ -341,20 +377,20 @@ 'CXXFILESUFFIX': '.cc', 'CXXFLAGS': ['$CCFLAGS', '$(', '/TP', '$)'], 'DSUFFIXES': ['.d'], - 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58bec>, - 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c0c>, + 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c5adac>, + 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c5adcc>, 'ENV': { 'INCLUDE': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\include', 'LIB': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\lib', 'PATH': 'C:\\Program Files\\Microsoft Visual Studio\\Common\\tools\\WIN95;C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\bin;C:\\Program Files\\Microsoft Visual Studio\\Common\\tools;C:\\Program Files\\Microsoft Visual Studio/VC98\\bin', 'PATHEXT': '.COM;.EXE;.BAT;.CMD', 'SystemRoot': 'C:/WINDOWS'}, - 'ESCAPE': <function escape at 0xb7bc917c>, - 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c2c>, + 'ESCAPE': <function escape at 0xb7bcf454>, + 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c5adec>, 'IDLSUFFIXES': ['.idl', '.IDL'], 'INCPREFIX': '/I', 'INCSUFFIX': '', - 'INSTALL': <function installFunc at 0xb7c56f0c>, - 'INSTALLSTR': <function installStr at 0xb7c56f44>, + 'INSTALL': <function installFunc at 0xb7c5e17c>, + 'INSTALLSTR': <function installStr at 0xb7c5e1b4>, 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'], 'LIBPREFIX': '', 'LIBPREFIXES': ['$LIBPREFIX'], @@ -370,14 +406,14 @@ 'PLATFORM': 'win32', 'PROGPREFIX': '', 'PROGSUFFIX': '.exe', - 'PSPAWN': <function piped_spawn at 0xb7bc90d4>, + 'PSPAWN': <function piped_spawn at 0xb7bcf3ac>, 'RC': 'rc', 'RCCOM': '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES', 'RCFLAGS': [], - 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c4c>, + 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c5ae0c>, 'SCANNERS': [], 'SHCC': '$CC', - 'SHCCCOM': <SCons.Action.FunctionAction instance at 0xb7b608cc>, + 'SHCCCOM': <SCons.Action.FunctionAction instance at 0xb7b63bcc>, 'SHCCFLAGS': ['$CCFLAGS'], 'SHCFLAGS': ['$CFLAGS'], 'SHCXX': '$CXX', @@ -388,19 +424,19 @@ 'SHLIBSUFFIX': '.dll', 'SHOBJPREFIX': '$OBJPREFIX', 'SHOBJSUFFIX': '$OBJSUFFIX', - 'SPAWN': <function spawn at 0xb7bc9144>, + 'SPAWN': <function spawn at 0xb7bcf41c>, 'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1, - 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7be87ac>, + 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7be989c>, 'TEMPFILEPREFIX': '@', 'TOOLS': ['msvc'], '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', - '_concat': <function _concat at 0xb7c56fb4>, - '_defines': <function _defines at 0xb7c5c064>, - '_installStr': <function installStr at 0xb7c56f44>, - '_stripixes': <function _stripixes at 0xb7c5c02c>} + '_concat': <function _concat at 0xb7c5e224>, + '_defines': <function _defines at 0xb7c5e2cc>, + '_installStr': <function installStr at 0xb7c5e1b4>, + '_stripixes': <function _stripixes at 0xb7c5e294>} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. @@ -434,7 +470,10 @@ - + + env = Environment() + print env.Dump('ENV') + @@ -473,3 +512,729 @@
+ +
+ + What Dependencies Does &SCons; Know About? the &tree; Option + + + + Sometimes the best way to try to figure out what + &SCons; is doing is simply to take a look at the + dependency graph that it constructs + based on your &SConscript; files. + The --tree option + will display all or part of the + &SCons; dependency graph in an + "ASCII art" graphical format + that shows the dependency hierarchy. + + + + + + For example, given the following input &SConstruct; file: + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog', ['f1.c', 'f2.c', 'f3.c']) + + + + + Running &SCons; with the --tree=all + option yields: + + + + + % scons -Q --tree=all + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + +-. + +-- + +-SConstruct + +-f1.c + +-f1.o + | +-f1.c + | +-inc.h + +-f2.c + +-f2.o + | +-f2.c + | +-inc.h + +-f3.c + +-f3.o + | +-f3.c + | +-inc.h + +-inc.h + +-prog + +-f1.o + | +-f1.c + | +-inc.h + +-f2.o + | +-f2.c + | +-inc.h + +-f3.o + +-f3.c + +-inc.h + + + + + The tree will also be printed when the + -n (no execute) option is used, + which allows you to examine the dependency graph + for a configuration without actually + rebuilding anything in the tree. + + + + + + The --tree option only prints + the dependency graph for the specified targets + (or the default target(s) if none are specified on the command line). + So if you specify a target like f2.o + on the command line, + the --tree option will only + print the dependency graph for that file: + + + + + % scons -Q --tree=all f2.o + cc -o f2.o -c -I. f2.c + +-f2.o + +-f2.c + +-inc.h + + + + + This is, of course, useful for + restricting the output from a very large + build configuration to just a + portion in which you're interested. + Multiple targets are fine, + in which case a tree will be printed + for each specified target: + + + + + % scons -Q --tree=all f1.o f3.o + cc -o f1.o -c -I. f1.c + +-f1.o + +-f1.c + +-inc.h + cc -o f3.o -c -I. f3.c + +-f3.o + +-f3.c + +-inc.h + + + + + The status argument may be used + to tell &SCons; to print status information about + each file in the dependency graph: + + + + + % scons -Q --tree=status + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + E = exists + R = exists in repository only + b = implicit builder + B = explicit builder + S = side effect + P = precious + A = always build + C = current + N = no clean + H = no cache + + [E b ]+-. + [ ] +-- + [E ] +-SConstruct + [E ] +-f1.c + [E B C ] +-f1.o + [E ] | +-f1.c + [E ] | +-inc.h + [E ] +-f2.c + [E B C ] +-f2.o + [E ] | +-f2.c + [E ] | +-inc.h + [E ] +-f3.c + [E B C ] +-f3.o + [E ] | +-f3.c + [E ] | +-inc.h + [E ] +-inc.h + [E B C ] +-prog + [E B C ] +-f1.o + [E ] | +-f1.c + [E ] | +-inc.h + [E B C ] +-f2.o + [E ] | +-f2.c + [E ] | +-inc.h + [E B C ] +-f3.o + [E ] +-f3.c + [E ] +-inc.h + + + + + Note that --tree=all,status is equivalent; + the all + is assumed if only status is present. + As an alternative to all, + you can specify --tree=derived + to have &SCons; only print derived targets + in the tree output, + skipping source files + (like .c and .h files): + + + + + % scons -Q --tree=derived + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + +-. + + + + + You can use the status + modifier with derived as well: + + + + + % scons -Q --tree=derived,status + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + E = exists + R = exists in repository only + b = implicit builder + B = explicit builder + S = side effect + P = precious + A = always build + C = current + N = no clean + H = no cache + + [E b ]+-. + [E B C ] +-f1.o + [E B C ] +-f2.o + [E B C ] +-f3.o + [E B C ] +-prog + [E B C ] +-f1.o + [E B C ] +-f2.o + [E B C ] +-f3.o + + + + + Note that the order of the --tree= + arguments doesn't matter; + --tree=status,derived is + completely equivalent. + + + + + + The default behavior of the --tree option + is to repeat all of the dependencies each time the library dependency + (or any other dependency file) is encountered in the tree. + If certain target files share other target files, + such as two programs that use the same library: + + + + + env = Environment(CPPPATH = ['.'], + LIBS = ['foo'], + LIBPATH = ['.']) + env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Program('prog1.c') + env.Program('prog2.c') + + + + + Then there can be a lot of repetition in the + --tree= output: + + + + + % scons -Q --tree=all + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + ar rc libfoo.a f1.o f2.o f3.o + ranlib libfoo.a + cc -o prog1.o -c -I. prog1.c + cc -o prog1 prog1.o -L. -lfoo + cc -o prog2.o -c -I. prog2.c + cc -o prog2 prog2.o -L. -lfoo + +-. + +-- + +-SConstruct + +-f1.c + +-f1.o + | +-f1.c + | +-inc.h + +-f2.c + +-f2.o + | +-f2.c + | +-inc.h + +-f3.c + +-f3.o + | +-f3.c + | +-inc.h + +-inc.h + +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog1 + | +-prog1.o + | | +-prog1.c + | | +-inc.h + | +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog1.c + +-prog1.o + | +-prog1.c + | +-inc.h + +-prog2 + | +-prog2.o + | | +-prog2.c + | | +-inc.h + | +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog2.c + +-prog2.o + +-prog2.c + +-inc.h + + + + + In a large configuration with many internal libraries + and include files, + this can very quickly lead to huge output trees. + To help make this more manageable, + a prune modifier may + be added to the option list, + in which case &SCons; + will print the name of a target that has + already been visited during the tree-printing + in [square brackets] + as an indication that the dependencies + of the target file may be found + by looking farther up the tree: + + + + + % scons -Q --tree=prune + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + ar rc libfoo.a f1.o f2.o f3.o + ranlib libfoo.a + cc -o prog1.o -c -I. prog1.c + cc -o prog1 prog1.o -L. -lfoo + cc -o prog2.o -c -I. prog2.c + cc -o prog2 prog2.o -L. -lfoo + +-. + +-- + +-SConstruct + +-f1.c + +-f1.o + | +-[f1.c] + | +-inc.h + +-f2.c + +-f2.o + | +-[f2.c] + | +-[inc.h] + +-f3.c + +-f3.o + | +-[f3.c] + | +-[inc.h] + +-[inc.h] + +-libfoo.a + | +-[f1.o] + | +-[f2.o] + | +-[f3.o] + +-prog1 + | +-prog1.o + | | +-prog1.c + | | +-[inc.h] + | +-[libfoo.a] + +-[prog1.c] + +-[prog1.o] + +-prog2 + | +-prog2.o + | | +-prog2.c + | | +-[inc.h] + | +-[libfoo.a] + +-[prog2.c] + +-[prog2.o] + + + + + Like the status keyword, + the prune argument by itself + is equivalent to --tree=all,prune. + + + +
+ +
+ + How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option + + + + Sometimes it's useful to look at the + pre-substitution string + that &SCons; uses to generate + the command lines it executes. + This can be done with the &debug-presub; option: + + + + + + + + + % scons -Q --debug=presub + Building prog.o with action: + $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCOMCOM $SOURCES + cc -o prog.o -c -I. prog.c + Building prog with action: + $SMART_LINKCOM + cc -o prog prog.o + + +
+ +
+ + Where is &SCons; Searching for Libraries? the &debug-findlibs; Option + + + + To get some insight into what library names + &SCons; is searching for, + and in which directories it is searching, + Use the --debug=findlibs option. + Given the following input &SConstruct; file: + + + + + env = Environment(LIBPATH = ['libs1', 'libs2']) + env.Program('prog.c', LIBS=['foo', 'bar']) + + + + + And the libraries libfoo.a + and libbar.a + in libs1 and libs2, + respectively, + use of the --debug=findlibs option yields: + + + + + % scons -Q --debug=findlibs + findlibs: looking for 'libfoo.a' in 'libs1' ... + findlibs: ... FOUND 'libfoo.a' in 'libs1' + findlibs: looking for 'libfoo.so' in 'libs1' ... + findlibs: looking for 'libfoo.so' in 'libs2' ... + findlibs: looking for 'libbar.a' in 'libs1' ... + findlibs: looking for 'libbar.a' in 'libs2' ... + findlibs: ... FOUND 'libbar.a' in 'libs2' + findlibs: looking for 'libbar.so' in 'libs1' ... + findlibs: looking for 'libbar.so' in 'libs2' ... + cc -o prog.o -c prog.c + cc -o prog prog.o -Llibs1 -Llibs2 -lfoo -lbar + + +
+ + + +
+ + Where is &SCons; Blowing Up? the &debug-stacktrace; Option + + + + In general, &SCons; tries to keep its error + messages short and informative. + That means we usually try to avoid showing + the stack traces that are familiar + to experienced Python programmers, + since they usually contain much more + information than is useful to most people. + + + + + + For example, the following &SConstruct; file: + + + + + Program('prog.c') + + + + + Generates the following error if the + prog.c file + does not exist: + + + + + % scons -Q + scons: *** Source `prog.c' not found, needed by target `prog.o'. Stop. + + + + + In this case, + the error is pretty obvious. + But if it weren't, + and you wanted to try to get more information + about the error, + the &debug-stacktrace; option + would show you exactly where in the &SCons; source code + the problem occurs: + + + + + % scons -Q --debug=stacktrace + scons: *** Source `prog.c' not found, needed by target `prog.o'. Stop. + scons: internal stack trace: + File "/home/knight/SCons/scons.0.96.C763/bootstrap/src/engine/SCons/Job.py", line 111, in start + task.prepare() + File "/home/knight/SCons/scons.0.96.C763/bootstrap/src/engine/SCons/Taskmaster.py", line 166, in prepare + t.prepare() + File "/home/knight/SCons/scons.0.96.C763/bootstrap/src/engine/SCons/Node/FS.py", line 2137, in prepare + SCons.Node.Node.prepare(self) + File "/home/knight/SCons/scons.0.96.C763/bootstrap/src/engine/SCons/Node/__init__.py", line 806, in prepare + raise SCons.Errors.StopError, desc + + + + + Of course, if you do need to dive into the &SCons; source code, + we'd like to know if, or how, + the error messages or troubleshooting options + could have been improved to avoid that. + Not everyone has the necessary time or + Python skill to dive into the source code, + and we'd like to improve &SCons; + for those people as well... + + + +
+ +
+ + How is &SCons; Making Its Decisions? the &taskmastertrace; Option + + + + The internal &SCons; subsystem that handles walking + the dependency graph + and controls the decision-making about what to rebuild + is the Taskmaster. + &SCons; supports a --taskmastertrace + option that tells the Taskmaster to print + information about the children (dependencies) + of the various Nodes on its walk down the graph, + which specific dependent Nodes are being evaluated, + and in what order. + + + + + + The --taskmastertrace option + takes as an argument the name of a file in + which to put the trace output, + with - (a single hyphen) + indicating that the trace messages + should be printed to the standard output: + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog.c') + + + + % scons -Q --taskmastertrace=- prog + Taskmaster: 'prog': children: + ['prog.o'] + waiting on unstarted children: + ['prog.o'] + Taskmaster: 'prog.o': children: + ['inc.h', 'prog.c'] + evaluating prog.o + cc -o prog.o -c -I. prog.c + Taskmaster: 'prog': children: + ['prog.o'] + evaluating prog + cc -o prog prog.o + Taskmaster: 'prog': already handled (executed) + + + + + The --taskmastertrace option + doesn't provide information about the actual + calculations involved in deciding if a file is up-to-date, + but it does show all of the dependencies + it knows about for each Node, + and the order in which those dependencies are evaluated. + This can be useful as an alternate way to determine + whether or not your &SCons; configuration, + or the implicit dependency scan, + has actually identified all the correct dependencies + you want it to. + + + +
+ + + + diff --git a/rpm/scons.spec.in b/rpm/scons.spec.in index 3b49196..3b4865b 100644 --- a/rpm/scons.spec.in +++ b/rpm/scons.spec.in @@ -1,5 +1,5 @@ %define name scons -%define version 0.96.94 +%define version 0.96.96 %define release 1 Summary: an Open Source software construction tool diff --git a/runtest.py b/runtest.py index 0d9519e..ffaa68b 100644 --- a/runtest.py +++ b/runtest.py @@ -274,6 +274,7 @@ except NameError: qmtest = q else: sys.stderr.write('Warning: %s not found on $PATH, assuming --noqmtest option.\n' % q) + sys.stderr.flush() aegis = whereis('aegis') diff --git a/src/CHANGES.txt b/src/CHANGES.txt index b17ba4e..a52a6b7 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,7 +8,106 @@ -RELEASE 0.97 - XXX +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: @@ -253,156 +352,6 @@ RELEASE 0.96.93 - Mon, 06 Nov 2006 00:44:11 -0600 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: diff --git a/src/RELEASE.txt b/src/RELEASE.txt index e84a079..b7d701f 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -20,7 +20,7 @@ more effectively, please sign up for the scons-users mailing list at: -RELEASE 0.97 - XXX +RELEASE 0.96.96 - Thu, 12 Apr 2007 12:36:25 -0500 This is a pre-release for testing the eighth beta release of SCons. Please consult the CHANGES.txt file for a list of specific changes diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 2484877..3905a91 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -2,10 +2,10 @@ SCons/__init__.py SCons/Action.py SCons/Builder.py SCons/compat/__init__.py -SCons/compat/_sets.py -SCons/compat/_sets15.py -SCons/compat/_subprocess.py -SCons/compat/_UserString.py +SCons/compat/_scons_sets.py +SCons/compat/_scons_sets15.py +SCons/compat/_scons_subprocess.py +SCons/compat/_scons_UserString.py SCons/compat/builtins.py SCons/Conftest.py SCons/cpp.py diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index dd7009c..438588b 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -268,8 +268,6 @@ class _ActionAction(ActionBase): def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw): if not strfunction is _null: self.strfunction = strfunction - if presub is _null: - presub = print_actions_presub self.presub = presub self.chdir = chdir if not exitstatfunc: @@ -290,7 +288,10 @@ class _ActionAction(ActionBase): if not SCons.Util.is_List(source): source = [source] if exitstatfunc is _null: exitstatfunc = self.exitstatfunc - if presub is _null: presub = self.presub + if presub is _null: + presub = self.presub + if presub is _null: + presub = print_actions_presub if show is _null: show = print_actions if execute is _null: execute = execute_actions if chdir is _null: chdir = self.chdir diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 8339610..08373c3 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -484,6 +484,13 @@ class _ActionActionTestCase(unittest.TestCase): result = a("out", "in", env) assert result == 7, result s = sio.getvalue() + assert s == 'Building out with action:\n execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s + + sio = StringIO.StringIO() + sys.stdout = sio + result = a("out", "in", env, presub=0) + assert result == 7, result + s = sio.getvalue() assert s == 'execfunc(["out"], ["in"])\n', s sio = StringIO.StringIO() diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 51af90b..ecc93c0 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -815,9 +815,15 @@ class BuilderBase: (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): - suffixes.extend(builder.src_suffixes(env)) + for s in builder.src_suffixes(env): + if not sdict.has_key(s): + sdict[s] = 1 + suffixes.append(s) return suffixes class CompositeBuilder(SCons.Util.Proxy): diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 77ac9f4..bb5a69a 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -582,7 +582,9 @@ class BuilderTestCase(unittest.TestCase): "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path b2 = SCons.Builder.Builder(src_suffix = '.2', src_builder = b1) - assert b2.src_suffixes(env) == ['.2', '.c'], b2.src_suffixes(env) + r = b2.src_suffixes(env) + r.sort() + assert r == ['.2', '.c'], r b3 = SCons.Builder.Builder(action = {'.3a' : '', '.3b' : ''}) s = b3.src_suffixes(env) @@ -804,11 +806,6 @@ class BuilderTestCase(unittest.TestCase): s = map(str, tgt.sources[0].sources) assert s == ['aaa.bar'], s - builder3 = SCons.Builder.Builder(action = 'foo', - src_builder = 'xyzzy', - src_suffix = '.xyzzy') - assert builder3.get_src_builders(Environment()) == [] - builder4 = SCons.Builder.Builder(action='bld4', src_suffix='.i', suffix='_wrap.c') @@ -979,8 +976,6 @@ class BuilderTestCase(unittest.TestCase): assert r == '', r r = builder.src_suffixes(env) assert r == [], r - r = builder.targets('foo') - assert r == ['foo'], r # src_suffix can be a single string or a list of strings # src_suffixes() caches its return value, so we use a new @@ -1011,6 +1006,7 @@ class BuilderTestCase(unittest.TestCase): r = bld.get_src_suffix(env) assert r == '.bar', r r = bld.src_suffixes(env) + r.sort() assert r == ['.bar', '.foo'], r # adjust_suffix normalizes the suffix, adding a `.' if needed @@ -1131,6 +1127,7 @@ class BuilderTestCase(unittest.TestCase): r = builder.get_src_suffix(env) assert r == '.src_sfx1', r r = builder.src_suffixes(env) + r.sort() assert r == ['.src_sfx1', '.src_sfx2'], r @@ -1405,11 +1402,6 @@ class BuilderTestCase(unittest.TestCase): assert b5.get_name(None) == 'builder5', b5.get_name(None) assert b6.get_name(None) in b6_names, b6.get_name(None) - for B in b3.get_src_builders(env): - assert B.get_name(env) == 'bldr1' - for B in b3.get_src_builders(env2): - assert B.get_name(env2) == 'B1' - tgt = b4(env, target = 'moo', source='cow') assert tgt[0].builder.get_name(env) == 'bldr4' diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 11bace3..23d02e9 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -202,13 +202,12 @@ def installStr(dest, source, env): return 'Install %s: "%s" as "%s"' % (type, source, dest) 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 concatenating 'prefix' and 'suffix' 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.""" - + """ + 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 @@ -219,6 +218,16 @@ def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): if not l is 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 @@ -275,8 +284,68 @@ def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None): return result return c(prefix, list, suffix, env, f) -def _defines(prefix, defs, suffix, env, c=_concat): - """A wrapper around _concat that turns a list or string +# This is an alternate _stripixes() function that passes all of our tests +# (as of 21 February 2007), like the current version above. It's more +# straightforward because it does its manipulation directly, not using +# the funky f call-back function to _concat(). (In this respect it's +# like the updated _defines() function below.) +# +# The most convoluted thing is that it still uses a custom _concat() +# function if one was placed in the construction environment; there's +# a specific test for that functionality, but it might be worth getting +# rid of. +# +# Since this work was done while trying to get 0.97 out the door +# (just prior to 0.96.96), I decided to be cautious and leave the old +# function as is, to minimize the chance of other corner-case regressions. +# The updated version is captured here so we can uncomment it and start +# using it at a less sensitive time in the development cycle (or when +# it's clearly required to fix something). +# +#def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None): +# """ +# This is a wrapper around _concat()/_concat_ixes() that checks for the +# existence of prefixes or suffixes on list elements 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 list: +# return list +# +# 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 +# +# if SCons.Util.is_List(list): +# list = SCons.Util.flatten(list) +# +# lsp = len(stripprefix) +# lss = len(stripsuffix) +# stripped = [] +# for l in SCons.PathList.PathList(list).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) +# if l[:lsp] == stripprefix: +# l = l[lsp:] +# if l[-lss:] == stripsuffix: +# l = l[:-lss] +# stripped.append(l) +# +# return c(prefix, stripped, suffix, env) + +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. """ if SCons.Util.is_List(defs): @@ -304,7 +373,7 @@ def _defines(prefix, defs, suffix, env, c=_concat): l.append(str(k) + '=' + str(v)) else: l = [str(defs)] - return c(prefix, l, suffix, env) + return c(prefix, env.subst_path(l), suffix, env) class NullCmdGenerator: """This is a callable class that can be used in place of other diff --git a/src/engine/SCons/Memoize.py b/src/engine/SCons/Memoize.py index 6a46350..c2b4181 100644 --- a/src/engine/SCons/Memoize.py +++ b/src/engine/SCons/Memoize.py @@ -145,7 +145,10 @@ class Counter: fmt = " %7d hits %7d misses %s()" print fmt % (self.hit, self.miss, self.name) def __cmp__(self, other): - return cmp(self.name, other.name) + try: + return cmp(self.name, other.name) + except AttributeError: + return 0 class CountValue(Counter): """ diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 4d269d2..a16fee4 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -1929,7 +1929,14 @@ class File(Base): def get_contents(self): if not self.rexists(): return '' - return open(self.rfile().abspath, "rb").read() + fname = self.rfile().abspath + try: + r = open(fname, "rb").read() + except EnvironmentError, e: + if not e.filename: + e.filename = fname + raise + return r def get_timestamp(self): if self.rexists(): diff --git a/src/engine/SCons/PathList.py b/src/engine/SCons/PathList.py index 81b8135..943e9ad 100644 --- a/src/engine/SCons/PathList.py +++ b/src/engine/SCons/PathList.py @@ -36,6 +36,7 @@ import os import string import SCons.Memoize +import SCons.Node import SCons.Util # @@ -58,10 +59,13 @@ def node_conv(obj): try: get = obj.get except AttributeError: - pass + if isinstance(obj, SCons.Node.Node): + result = obj + else: + result = str(obj) else: - obj = get() - return obj + result = get() + return result class _PathList: """ @@ -95,9 +99,7 @@ class _PathList: """ if SCons.Util.is_String(pathlist): pathlist = string.split(pathlist, os.pathsep) - elif SCons.Util.is_List(pathlist) or SCons.Util.is_Tuple(pathlist): - pathlist = SCons.Util.flatten(pathlist) - else: + elif not SCons.Util.is_Sequence(pathlist): pathlist = [pathlist] pl = [] @@ -129,6 +131,11 @@ class _PathList: if type == TYPE_STRING_SUBST: value = env.subst(value, target=target, source=source, conv=node_conv) + if SCons.Util.is_Sequence(value): + # It came back as a string or tuple, which in this + # case usually means some variable expanded to an + # actually Dir node. Concatenate the values. + value = string.join(map(str, value), '') elif type == TYPE_OBJECT: value = node_conv(value) result.append(value) @@ -175,18 +182,19 @@ class PathListCache: """ Returns the key for memoization of PathLists. - Note that we want this to be quick, so we don't canonicalize - all forms of the same list. For example, 'x:y' and ['x', 'y'] - logically represent the same list, but we're not going to bother - massaging strings into canonical lists here. - - The reason - + 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_List(pathlist): - pathlist = tuple(pathlist) + 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): diff --git a/src/engine/SCons/PathListTests.py b/src/engine/SCons/PathListTests.py index d6fae0e..d0ba2a0 100644 --- a/src/engine/SCons/PathListTests.py +++ b/src/engine/SCons/PathListTests.py @@ -23,6 +23,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import sys import unittest import SCons.PathList @@ -48,20 +49,38 @@ class subst_pathTestCase(unittest.TestCase): self.env = FakeEnvironment(AAA = 'aaa') - def test_object(self): - """Test the subst_path() method on an object + def test_node(self): + """Test the subst_path() method on a Node """ + import SCons.Node + class A: pass + n = SCons.Node.Node() + + pl = SCons.PathList.PathList((n,)) + + result = pl.subst_path(self.env, 'y', 'z') + + assert result == (n,), result + + def test_object(self): + """Test the subst_path() method on a non-Node object + """ + + class A: + def __str__(self): + return '' + a = A() pl = SCons.PathList.PathList((a,)) result = pl.subst_path(self.env, 'y', 'z') - assert result == (a,), result + assert result == ('',), result def test_object_get(self): """Test the subst_path() method on an object with a get() method diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 6a0ad81..ca7f68c 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -74,6 +74,9 @@ import SCons.Warnings display = SCons.Util.display progress_display = SCons.Util.DisplayEngine() +first_command_start = None +last_command_end = None + # Task control. # class BuildTask(SCons.Taskmaster.Task): @@ -88,12 +91,17 @@ class BuildTask(SCons.Taskmaster.Task): if target.has_builder() and not hasattr(target.builder, 'status'): if print_time: start_time = time.time() + global first_command_start + if first_command_start is None: + first_command_start = start_time SCons.Taskmaster.Task.execute(self) if print_time: + global cumulative_command_time + global last_command_end finish_time = time.time() - global command_time - command_time = command_time+finish_time-start_time - print "Command execution time: %f seconds"%(finish_time-start_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)) break else: if self.top and target.has_builder(): @@ -320,10 +328,10 @@ print_stacktrace = 0 print_time = 0 ignore_errors = 0 sconscript_time = 0 -command_time = 0 +cumulative_command_time = 0 exit_status = 0 # exit status, assume success by default repositories = [] -num_jobs = 1 # this is modifed by SConscript.SetJobs() +num_jobs = None delayed_warnings = [] diskcheck_all = SCons.Node.FS.diskcheck_types() @@ -617,7 +625,7 @@ def _load_site_scons_dir(topdir, site_dir_name=None): site_init_filename = "site_init.py" site_init_modname = "site_init" site_tools_dirname = "site_tools" - sys.path = [site_dir] + 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): @@ -1354,9 +1362,10 @@ def _main(args, parser): tmtrace = None taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace) - nj = ssoptions.get('num_jobs') - jobs = SCons.Job.Jobs(nj, taskmaster) - if nj > 1 and jobs.num_jobs == 1: + global num_jobs + num_jobs = ssoptions.get('num_jobs') + jobs = SCons.Job.Jobs(num_jobs, taskmaster) + if num_jobs > 1 and jobs.num_jobs == 1: msg = "parallel builds are unsupported by this version of Python;\n" + \ "\tignoring -j or num_jobs option.\n" SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) @@ -1413,6 +1422,7 @@ def _exec_main(): def main(): global exit_status + global first_command_start try: _exec_main() @@ -1453,11 +1463,15 @@ def main(): SCons.Taskmaster.dump_stats() if print_time: - total_time = time.time()-SCons.Script.start_time - scons_time = total_time-sconscript_time-command_time + total_time = time.time() - SCons.Script.start_time + if num_jobs == 1: + ct = cumulative_command_time + 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"%command_time + print "Total command execution time: %f seconds"%ct sys.exit(exit_status) diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 35bbbf7..fde4997 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -122,7 +122,6 @@ ignore_errors = Main.ignore_errors #exit_status = Main.exit_status #profiling = Main.profiling repositories = Main.repositories -#num_jobs = Main.num_jobs # settable by SConscript.SetJobs() # import SConscript diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 7439168..4de35f0 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -50,6 +50,8 @@ interface and the SCons build engine. There are two key classes here: __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import SCons.compat + import string import sys import traceback @@ -272,7 +274,11 @@ class Task: """ self.out_of_date = [] for t in self.targets: - if t.disambiguate().current(): + try: + is_up_to_date = t.disambiguate().current() + except EnvironmentError, e: + raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) + if is_up_to_date: t.set_state(SCons.Node.up_to_date) else: self.out_of_date.append(t) @@ -572,7 +578,7 @@ class Taskmaster: # list will get cleared and we'll re-scan the newly-built # file(s) for updated implicit dependencies. map(lambda n, P=node: n.add_to_waiting_parents(P), not_started) - node.ref_count = len(not_started) + node.ref_count = len(set(not_started)) # Now we add these derived targets to the candidates # list so they can be examined and built. We have to @@ -607,7 +613,7 @@ class Taskmaster: # dependency list will get cleared and we'll re-scan the # newly-built file(s) for updated implicit dependencies. map(lambda n, P=node: n.add_to_waiting_parents(P), not_built) - node.ref_count = len(not_built) + node.ref_count = len(set(not_built)) if S: S.not_built = S.not_built + 1 if T: diff --git a/src/engine/SCons/Tool/BitKeeper.xml b/src/engine/SCons/Tool/BitKeeper.xml index 3fef72d..8ab7049 100644 --- a/src/engine/SCons/Tool/BitKeeper.xml +++ b/src/engine/SCons/Tool/BitKeeper.xml @@ -27,14 +27,14 @@ fetching source files using BitKeeper. The string displayed when fetching a source file using BitKeeper. -If this is not set, then &cv-BITKEEPERCOM; +If this is not set, then &cv-link-BITKEEPERCOM; (the command line) is displayed. -The command (&cv-BITKEEPER;) and subcommand +The command (&cv-link-BITKEEPER;) and subcommand for fetching source files using BitKeeper. diff --git a/src/engine/SCons/Tool/CVS.xml b/src/engine/SCons/Tool/CVS.xml index 9ad5fb5..fb666e8 100644 --- a/src/engine/SCons/Tool/CVS.xml +++ b/src/engine/SCons/Tool/CVS.xml @@ -33,7 +33,7 @@ fetch source files from a CVS repository. The string displayed when fetching a source file from a CVS repository. -If this is not set, then &cv-CVSCOM; +If this is not set, then &cv-link-CVSCOM; (the command line) is displayed. @@ -51,6 +51,6 @@ to specify from where the files must be fetched. The path to the CVS repository. This is referenced in the default -&cv-CVSFLAGS; value. +&cv-link-CVSFLAGS; value. diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 09ce1d1..177d579 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -51,8 +51,9 @@ if java_parsing: # any alphanumeric token (keyword, class name, specifier); # the multi-line comment begin and end tokens /* and */; # array declarations "[]"; - # semi-colons. - _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;]|' + + # semi-colons; + # periods. + _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.]|' + r'[A-Za-z_][\w\.]*|/\*|\*/|\[\])') class OuterState: @@ -101,20 +102,26 @@ if java_parsing: self.skipState = ret return ret + 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(string.join(self.listClasses, '$')) + self.listClasses.pop() + self.stackBrackets.pop() + def parseToken(self, token): if token[:2] == '//': return IgnoreState('\n', self) elif token == '/*': return IgnoreState('*/', self) elif token == '{': - self.brackets = self.brackets + 1 + self.openBracket() elif token == '}': - self.brackets = self.brackets - 1 - if len(self.stackBrackets) and \ - self.brackets == self.stackBrackets[-1]: - self.listOutputs.append(string.join(self.listClasses, '$')) - self.listClasses.pop() - self.stackBrackets.pop() + self.closeBracket() elif token in [ '"', "'" ]: return IgnoreState(token, self) elif token == "new": @@ -129,13 +136,17 @@ if java_parsing: 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""" clazz = self.listClasses[0] self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) - self.brackets = self.brackets + 1 self.nextAnon = self.nextAnon + 1 def setPackage(self, package): @@ -153,7 +164,10 @@ if java_parsing: if token == '\n': return self if token == '{': + self.outer_state.openBracket() self.outer_state.addAnonClass() + elif token == '}': + self.outer_state.closeBracket() elif token in ['"', "'"]: return IgnoreState(token, self) return self.outer_state diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py index 4a7f9cf..853afd4 100644 --- a/src/engine/SCons/Tool/JavaCommonTests.py +++ b/src/engine/SCons/Tool/JavaCommonTests.py @@ -286,6 +286,50 @@ public abstract class TestClass assert classes == ['TestClass$1', 'TestClass$2', 'TestClass'], classes + def test_closing_bracket(self): + """Test finding a closing bracket instead of an anonymous class""" + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\ +class TestSCons { + public static void main(String[] args) { + Foo[] fooArray = new Foo[] { new Foo() }; + } +} + +class Foo { } +""") + assert pkg_dir == None, pkg_dir + assert classes == ['TestSCons', 'Foo'], classes + + + def test_dot_class_attributes(self): + """Test handling ".class" attributes""" + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\ +public class Test extends Object +{ + static { + Class c = Object[].class; + Object[] s = new Object[] {}; + } +} +""") + assert classes == ['Test'], classes + + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\ +public class A { + public class B { + public void F(Object[] o) { + F(new Object[] {Object[].class}); + } + public void G(Object[] o) { + F(new Object[] {}); + } + } +} +""") + assert pkg_dir == None, pkg_dir + assert classes == ['A$B', 'A'], classes + + if __name__ == "__main__": suite = unittest.TestSuite() diff --git a/src/engine/SCons/Tool/Perforce.xml b/src/engine/SCons/Tool/Perforce.xml index 87565af..b8824ed 100644 --- a/src/engine/SCons/Tool/Perforce.xml +++ b/src/engine/SCons/Tool/Perforce.xml @@ -27,7 +27,7 @@ fetch source files from Perforce. The string displayed when fetching a source file from Perforce. -If this is not set, then &cv-P4COM; (the command line) is displayed. +If this is not set, then &cv-link-P4COM; (the command line) is displayed. diff --git a/src/engine/SCons/Tool/RCS.xml b/src/engine/SCons/Tool/RCS.xml index 5e3b83d..2c89a5b 100644 --- a/src/engine/SCons/Tool/RCS.xml +++ b/src/engine/SCons/Tool/RCS.xml @@ -16,7 +16,7 @@ The RCS executable. Note that this variable is not actually used for the command to fetch source files from RCS; see the -&cv-RCS_CO; +&cv-link-RCS_CO; construction variable, below. @@ -39,13 +39,13 @@ fetch (checkout) source files from RCS. The string displayed when fetching a source file from RCS. -If this is not set, then &cv-RCS_COCOM; +If this is not set, then &cv-link-RCS_COCOM; (the command line) is displayed. -Options that are passed to the &cv-RCS_CO; command. +Options that are passed to the &cv-link-RCS_CO; command. diff --git a/src/engine/SCons/Tool/SCCS.xml b/src/engine/SCons/Tool/SCCS.xml index 822547c..9f7539d 100644 --- a/src/engine/SCons/Tool/SCCS.xml +++ b/src/engine/SCons/Tool/SCCS.xml @@ -27,7 +27,7 @@ fetch source files from SCCS. The string displayed when fetching a source file from a CVS repository. -If this is not set, then &cv-SCCSCOM; +If this is not set, then &cv-link-SCCSCOM; (the command line) is displayed. diff --git a/src/engine/SCons/Tool/__init__.xml b/src/engine/SCons/Tool/__init__.xml index cc7e145..ff50450 100644 --- a/src/engine/SCons/Tool/__init__.xml +++ b/src/engine/SCons/Tool/__init__.xml @@ -8,7 +8,7 @@ See its __doc__ string for a discussion of the format. Builds a C source file given a lex (.l) or yacc (.y) input file. -The suffix specified by the &cv-CFILESUFFIX; construction variable +The suffix specified by the &cv-link-CFILESUFFIX; construction variable (.c by default) is automatically added to the target if it is not already present. @@ -28,7 +28,7 @@ env.CFile(target = 'bar', source = 'bar.y') Builds a C++ source file given a lex (.ll) or yacc (.yy) input file. -The suffix specified by the &cv-CXXFILESUFFIX; construction variable +The suffix specified by the &cv-link-CXXFILESUFFIX; construction variable (.cc by default) is automatically added to the target if it is not already present. @@ -82,9 +82,9 @@ 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 &cv-PROGPREFIX; construction variable; nothing by default) +(specified by the &cv-link-PROGPREFIX; construction variable; nothing by default) and suffix -(specified by the &cv-PROGSUFFIX; construction variable; +(specified by the &cv-link-PROGSUFFIX; construction variable; by default, .exe on Windows systems, nothing on POSIX systems) are automatically added to the target if not already present. @@ -109,11 +109,11 @@ 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 &cv-SHLIBPREFIX; construction variable; +(specified by the &cv-link-SHLIBPREFIX; construction variable; by default, lib on POSIX systems, nothing on Windows systems) and suffix -(specified by the &cv-SHLIBSUFFIX; construction variable; +(specified by the &cv-link-SHLIBSUFFIX; construction variable; by default, .dll on Windows systems, .so on POSIX systems) are automatically added to the target if not already present. @@ -146,12 +146,12 @@ On Windows systems, specifying will cause the .dll to be registered after it is built using REGSVR32. The command that is run -("regsvr32" by default) is determined by &cv-REGSVR; construction -variable, and the flags passed are determined by &cv-REGSVRFLAGS;. By -default, &cv-REGSVRFLAGS; includes the option, +("regsvr32" by default) is determined by &cv-link-REGSVR; construction +variable, and the flags passed are determined by &cv-link-REGSVRFLAGS;. By +default, &cv-link-REGSVRFLAGS; includes the option, to prevent dialogs from popping up and requiring user attention when it is run. If you change -&cv-REGSVRFLAGS;, be sure to include the option. +&cv-link-REGSVRFLAGS;, be sure to include the option. For example, @@ -186,10 +186,10 @@ 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 &cv-SHOBJPREFIX; construction variable; -by default, the same as &cv-OBJPREFIX;) +(specified by the &cv-link-SHOBJPREFIX; construction variable; +by default, the same as &cv-link-OBJPREFIX;) and suffix -(specified by the &cv-SHOBJSUFFIX; construction variable) +(specified by the &cv-link-SHOBJSUFFIX; construction variable) are automatically added to the target if not already present. Examples: @@ -218,11 +218,11 @@ 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 &cv-LIBPREFIX; construction variable; +(specified by the &cv-link-LIBPREFIX; construction variable; by default, lib on POSIX systems, nothing on Windows systems) and suffix -(specified by the &cv-LIBSUFFIX; construction variable; +(specified by the &cv-link-LIBSUFFIX; construction variable; by default, .lib on Windows systems, .a on POSIX systems) are automatically added to the target if not already present. @@ -279,9 +279,9 @@ Source files must have one of the following extensions: The target object file prefix -(specified by the &cv-OBJPREFIX; construction variable; nothing by default) +(specified by the &cv-link-OBJPREFIX; construction variable; nothing by default) and suffix -(specified by the &cv-OBJSUFFIX; construction variable; +(specified by the &cv-link-OBJSUFFIX; construction variable; .obj on Windows systems, .o on POSIX systems) are automatically added to the target if not already present. diff --git a/src/engine/SCons/Tool/applelink.xml b/src/engine/SCons/Tool/applelink.xml index b9ea63d..d5fadbb 100644 --- a/src/engine/SCons/Tool/applelink.xml +++ b/src/engine/SCons/Tool/applelink.xml @@ -11,8 +11,8 @@ general user-supplied frameworks options to be added at the end of a command line building a loadable module. (This has been largely superceded by -the &cv-FRAMEWORKPATH;, &cv-FRAMEWORKPATHPREFIX;, -&cv-FRAMEWORKPREFIX; and &cv-FRAMEWORKS; variables +the &cv-link-FRAMEWORKPATH;, &cv-link-FRAMEWORKPATHPREFIX;, +&cv-link-FRAMEWORKPREFIX; and &cv-link-FRAMEWORKS; variables described above.) @@ -35,7 +35,7 @@ For example: On Mac OS X with gcc, the prefix to be used for linking in frameworks -(see &cv-FRAMEWORKS;). +(see &cv-link-FRAMEWORKS;). The default value is . @@ -57,7 +57,7 @@ a list containing the paths to search for frameworks. Used by the compiler to find framework-style includes like #include <Fmwk/Header.h>. Used by the linker to find user-specified frameworks when linking (see -&cv-FRAMEWORKS;). +&cv-link-FRAMEWORKS;). For example: @@ -77,7 +77,7 @@ to the compiler and linker command lines. On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries. -(see &cv-FRAMEWORKPATH;). +(see &cv-link-FRAMEWORKPATH;). The default value is . @@ -87,6 +87,6 @@ The default value is On Mac OS X with gcc, an automatically-generated construction variable containing the linker command-line options corresponding to -&cv-FRAMEWORKPATH;. +&cv-link-FRAMEWORKPATH;. diff --git a/src/engine/SCons/Tool/ar.xml b/src/engine/SCons/Tool/ar.xml index 24f886b..b26f634 100644 --- a/src/engine/SCons/Tool/ar.xml +++ b/src/engine/SCons/Tool/ar.xml @@ -26,7 +26,7 @@ The command line used to generate a static library from object files. The string displayed when an object file is generated from an assembly-language source file. -If this is not set, then &cv-ARCOM; (the command line) is displayed. +If this is not set, then &cv-link-ARCOM; (the command line) is displayed. env = Environment(ARCOMSTR = "Archiving $TARGET") @@ -55,7 +55,7 @@ The command line used to index a static library archive. The string displayed when a static library archive is indexed. -If this is not set, then &cv-RANLIBCOM; (the command line) is displayed. +If this is not set, then &cv-link-RANLIBCOM; (the command line) is displayed. env = Environment(RANLIBCOMSTR = "Indexing $TARGET") diff --git a/src/engine/SCons/Tool/as.xml b/src/engine/SCons/Tool/as.xml index a2447bd..f61d26a 100644 --- a/src/engine/SCons/Tool/as.xml +++ b/src/engine/SCons/Tool/as.xml @@ -27,7 +27,7 @@ from an assembly-language source file. The string displayed when an object file is generated from an assembly-language source file. -If this is not set, then &cv-ASCOM; (the command line) is displayed. +If this is not set, then &cv-link-ASCOM; (the command line) is displayed. env = Environment(ASCOMSTR = "Assembling $TARGET") @@ -46,7 +46,8 @@ General options passed to the assembler. 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 &cv-ASFLAGS; and &cv-CPPFLAGS; construction variables +Any options specified +in the &cv-link-ASFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. @@ -56,7 +57,7 @@ are included on this command line. 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 &cv-ASPPCOM; (the command line) is displayed. +If this is not set, then &cv-link-ASPPCOM; (the command line) is displayed. env = Environment(ASPPCOMSTR = "Assembling $TARGET") @@ -69,6 +70,6 @@ env = Environment(ASPPCOMSTR = "Assembling $TARGET") 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 &cv-ASFLAGS;. +The default is to use the value of &cv-link-ASFLAGS;. diff --git a/src/engine/SCons/Tool/c++.xml b/src/engine/SCons/Tool/c++.xml index eb0a8fe..2dc7bb2 100644 --- a/src/engine/SCons/Tool/c++.xml +++ b/src/engine/SCons/Tool/c++.xml @@ -19,7 +19,8 @@ The C++ compiler. The command line used to compile a C++ source file to an object file. -Any options specified in the &cv-CXXFLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-CXXFLAGS; and +&cv-link-CPPFLAGS; construction variables are included on this command line. @@ -28,7 +29,7 @@ are included on this command line. The string displayed when a C++ source file is compiled to a (static) object file. -If this is not set, then &cv-CXXCOM; (the command line) is displayed. +If this is not set, then &cv-link-CXXCOM; (the command line) is displayed. env = Environment(CXXCOMSTR = "Compiling static object $TARGET") @@ -39,10 +40,10 @@ env = Environment(CXXCOMSTR = "Compiling static object $TARGET") General options that are passed to the C++ compiler. -By default, this includes the value of &cv-CCFLAGS;, +By default, this includes the value of &cv-link-CCFLAGS;, so that setting &cv-CCFLAGS; affects both C and C++ compilation. If you want to add C++-specific flags, -you must set or override the value of &cv-CXXFLAGS;. +you must set or override the value of &cv-link-CXXFLAGS;. @@ -56,7 +57,8 @@ The C++ compiler used for generating shared-library objects. The command line used to compile a C++ source file to a shared-library object file. -Any options specified in the &cv-SHCXXFLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-SHCXXFLAGS; and +&cv-link-CPPFLAGS; construction variables are included on this command line. @@ -65,7 +67,7 @@ are included on this command line. The string displayed when a C++ source file is compiled to a shared object file. -If this is not set, then &cv-SHCXXCOM; (the command line) is displayed. +If this is not set, then &cv-link-SHCXXCOM; (the command line) is displayed. env = Environment(SHCXXCOMSTR = "Compiling shared object $TARGET") diff --git a/src/engine/SCons/Tool/cc.xml b/src/engine/SCons/Tool/cc.xml index 0ebaf14..b7ff65a 100644 --- a/src/engine/SCons/Tool/cc.xml +++ b/src/engine/SCons/Tool/cc.xml @@ -6,7 +6,21 @@ See its __doc__ string for a discussion of the format. --> -XXX +&cv-link-CC; +&cv-link-CFLAGS; +&cv-link-CCCOM; +&cv-link-SHCC; +&cv-link-SHCFLAGS; +&cv-link-SHCCCOM; + +&cv-link-CPPDEFPREFIX; +&cv-link-CPPDEFSUFFIX; +&cv-link-INCPREFIX; +&cv-link-INCSUFFIX; +&cv-link-SHOBJSUFFIX; + + +&cv-link-CFILESUFFIX; @@ -19,8 +33,8 @@ The C compiler. The command line used to compile a C source file to a (static) object -file. Any options specified in the &cv-CFLAGS;, &cv-CCFLAGS; and -&cv-CPPFLAGS; construction variables are included on this command +file. Any options specified in the &cv-link-CFLAGS;, &cv-link-CCFLAGS; and +&cv-link-CPPFLAGS; construction variables are included on this command line. @@ -29,7 +43,7 @@ line. The string displayed when a C source file is compiled to a (static) object file. -If this is not set, then &cv-CCCOM; (the command line) is displayed. +If this is not set, then &cv-link-CCCOM; (the command line) is displayed. env = Environment(CCCOMSTR = "Compiling static object $TARGET") @@ -54,11 +68,16 @@ General options that are passed to the C compiler (C only; not C++). 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 &cv-CCCOM;, &cv-SHCCCOM;, &cv-CXXCOM; and &cv-SHCXXCOM; command lines, -but also the &cv-FORTRANPPCOM;, &cv-SHFORTRANPPCOM;, -&cv-F77PPCOM; and &cv-SHF77PPCOM; command lines +via the &cv-link-CCCOM;, +&cv-link-SHCCCOM;, +&cv-link-CXXCOM; and +&cv-link-SHCXXCOM; command lines, +but also the &cv-link-FORTRANPPCOM;, +&cv-link-SHFORTRANPPCOM;, +&cv-link-F77PPCOM; and +&cv-link-SHF77PPCOM; command lines used to compile a Fortran source file, -and the &cv-ASPPCOM; command line +and the &cv-link-ASPPCOM; command line used to assemble an assembly language source file, after first running each file through the C preprocessor. Note that this variable does @@ -66,7 +85,7 @@ Note that this variable does contain (or similar) include search path options -that scons generates automatically from &cv-CPPPATH;. +that scons generates automatically from &cv-link-CPPPATH;. See &cv-link-_CPPINCFLAGS;, below, for the variable that expands to those options. @@ -99,7 +118,9 @@ The C compiler used for generating shared-library objects. The command line used to compile a C source file to a shared-library object file. -Any options specified in the &cv-SHCFLAGS;, &cv-SHCCFLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-SHCFLAGS;, +&cv-link-SHCCFLAGS; and +&cv-link-CPPFLAGS; construction variables are included on this command line. @@ -108,7 +129,7 @@ are included on this command line. The string displayed when a C source file is compiled to a shared object file. -If this is not set, then &cv-SHCCCOM; (the command line) is displayed. +If this is not set, then &cv-link-SHCCCOM; (the command line) is displayed. env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET") diff --git a/src/engine/SCons/Tool/dvi.xml b/src/engine/SCons/Tool/dvi.xml index 9d3110e..26fdd43 100644 --- a/src/engine/SCons/Tool/dvi.xml +++ b/src/engine/SCons/Tool/dvi.xml @@ -17,24 +17,24 @@ if the string or \documentstyle is found, the file is assumed to be a LaTeX file and -the target is built by invoking the &cv-LATEXCOM; command line; -otherwise, the &cv-TEXCOM; command line is used. +the target is built by invoking the &cv-link-LATEXCOM; command line; +otherwise, the &cv-link-TEXCOM; command line is used. If the file is a LaTeX file, the &b-DVI; builder method will also examine the contents of the .aux -file and invoke the &cv-BIBTEX; command line +file and invoke the &cv-link-BIBTEX; command line if the string bibdata is found, -start &cv-MAKEINDEX; to generate an index if a +start &cv-link-MAKEINDEX; to generate an index if a .ind file is found and will examine the contents .log -file and re-run the &cv-LATEXCOM; command +file and re-run the &cv-link-LATEXCOM; command if the log file says it is necessary. The suffix .dvi diff --git a/src/engine/SCons/Tool/dvipdf.xml b/src/engine/SCons/Tool/dvipdf.xml index 4d82ffe..f0679b3 100644 --- a/src/engine/SCons/Tool/dvipdf.xml +++ b/src/engine/SCons/Tool/dvipdf.xml @@ -32,12 +32,12 @@ The command line used to convert TeX DVI files into a PDF file. The string displayed when a TeX DVI file is converted into a PDF file. -If this is not set, then &cv-DVIPDFCOM; (the command line) is displayed. +If this is not set, then &cv-link-DVIPDFCOM; (the command line) is displayed. -A deprecated synonym for &cv-DVIPDFCOM;. +A deprecated synonym for &cv-link-DVIPDFCOM;. diff --git a/src/engine/SCons/Tool/dvips.xml b/src/engine/SCons/Tool/dvips.xml index cb0d7d8..dbfcaf3 100644 --- a/src/engine/SCons/Tool/dvips.xml +++ b/src/engine/SCons/Tool/dvips.xml @@ -18,7 +18,7 @@ from a .dvi input file .ltx, or .latex input file). -The suffix specified by the &cv-PSSUFFIX; construction variable +The suffix specified by the &cv-link-PSSUFFIX; construction variable (.ps by default) is added automatically to the target if it is not already present. Example: @@ -54,7 +54,7 @@ The command line used to convert TeX DVI files into a PostScript file. The string displayed when a TeX DVI file is converted into a PostScript file. -If this is not set, then &cv-PSCOM; (the command line) is displayed. +If this is not set, then &cv-link-PSCOM; (the command line) is displayed. diff --git a/src/engine/SCons/Tool/f77.xml b/src/engine/SCons/Tool/f77.xml index c077175..a1d3c34 100644 --- a/src/engine/SCons/Tool/f77.xml +++ b/src/engine/SCons/Tool/f77.xml @@ -13,10 +13,10 @@ XXX The Fortran 77 compiler. -You should normally set the &cv-FORTRAN; variable, +You should normally set the &cv-link-FORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. -You only need to set &cv-F77; if you need to use a specific compiler +You only need to set &cv-link-F77; if you need to use a specific compiler or compiler version for Fortran 77 files. @@ -24,9 +24,9 @@ or compiler version for Fortran 77 files. The command line used to compile a Fortran 77 source file to an object file. -You only need to set &cv-F77COM; if you need to use a specific +You only need to set &cv-link-F77COM; if you need to use a specific command line for Fortran 77 files. -You should normally set the &cv-FORTRANCOM; variable, +You should normally set the &cv-link-FORTRANCOM; variable, which specifies the default command line for all Fortran versions. @@ -36,7 +36,7 @@ for all Fortran versions. The string displayed when a Fortran 77 source file is compiled to an object file. -If this is not set, then &cv-F77COM; or &cv-FORTRANCOM; +If this is not set, then &cv-link-F77COM; or &cv-link-FORTRANCOM; (the command line) is displayed. @@ -49,14 +49,14 @@ Note that this variable does contain (or similar) include search path options -that scons generates automatically from &cv-F77PATH;. +that scons generates automatically from &cv-link-F77PATH;. See -&cv-_F77INCFLAGS; +&cv-link-_F77INCFLAGS; below, for the variable that expands to those options. -You only need to set &cv-F77FLAGS; if you need to define specific +You only need to set &cv-link-F77FLAGS; if you need to define specific user options for Fortran 77 files. -You should normally set the &cv-FORTRANFLAGS; variable, +You should normally set the &cv-link-FORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -68,10 +68,10 @@ for all Fortran versions. 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 &cv-_F77INCFLAGS; is created -by appending &cv-INCPREFIX; and &cv-INCSUFFIX; +The value of &cv-link-_F77INCFLAGS; is created +by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end -of each directory in &cv-F77PATH;. +of each directory in &cv-link-F77PATH;. @@ -80,15 +80,15 @@ of each directory in &cv-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 &cv-F77FLAGS; because the result will be non-portable +arguments in &cv-link-F77FLAGS; because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: -directory names in &cv-F77PATH; will be looked-up relative to the SConscript +directory names in &cv-link-F77PATH; will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: -You only need to set &cv-F77PATH; if you need to define a specific +You only need to set &cv-link-F77PATH; if you need to define a specific include path for Fortran 77 files. -You should normally set the &cv-FORTRANPATH; variable, +You should normally set the &cv-link-FORTRANPATH; variable, which specifies the include path for the default Fortran compiler for all Fortran versions. @@ -108,17 +108,17 @@ env = Environment(F77PATH=include) The directory list will be added to command lines through the automatically-generated -&cv-_F77INCFLAGS; +&cv-link-_F77INCFLAGS; construction variable, which is constructed by appending the values of the -&cv-INCPREFIX; and &cv-INCSUFFIX; +&cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end -of each directory in &cv-F77PATH;. +of each directory in &cv-link-F77PATH;. Any command lines you define that need the F77PATH directory list should -include &cv-_F77INCFLAGS;: +include &cv-link-_F77INCFLAGS;: env = Environment(F77COM="my_compiler $_F77INCFLAGS -c -o $TARGET $SOURCE") @@ -130,11 +130,11 @@ env = Environment(F77COM="my_compiler $_F77INCFLAGS -c -o $TARGET $SOURCE") 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 &cv-F77FLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-F77FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. -You only need to set &cv-F77PPCOM; if you need to use a specific +You only need to set &cv-link-F77PPCOM; if you need to use a specific C-preprocessor command line for Fortran 77 files. -You should normally set the &cv-FORTRANPPCOM; variable, +You should normally set the &cv-link-FORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. @@ -143,10 +143,10 @@ for all Fortran versions. The Fortran 77 compiler used for generating shared-library objects. -You should normally set the &cv-SHFORTRAN; variable, +You should normally set the &cv-link-SHFORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. -You only need to set &cv-SHF77; if you need to use a specific compiler +You only need to set &cv-link-SHF77; if you need to use a specific compiler or compiler version for Fortran 77 files. @@ -155,9 +155,9 @@ or compiler version for Fortran 77 files. The command line used to compile a Fortran 77 source file to a shared-library object file. -You only need to set &cv-SHF77COM; if you need to use a specific +You only need to set &cv-link-SHF77COM; if you need to use a specific command line for Fortran 77 files. -You should normally set the &cv-SHFORTRANCOM; variable, +You should normally set the &cv-link-SHFORTRANCOM; variable, which specifies the default command line for all Fortran versions. @@ -167,7 +167,7 @@ for all Fortran versions. The string displayed when a Fortran 77 source file is compiled to a shared-library object file. -If this is not set, then &cv-SHF77COM; or &cv-SHFORTRANCOM; +If this is not set, then &cv-link-SHF77COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. @@ -176,9 +176,9 @@ If this is not set, then &cv-SHF77COM; or &cv-SHFORTRANCOM; Options that are passed to the Fortran 77 compiler to generated shared-library objects. -You only need to set &cv-SHF77FLAGS; if you need to define specific +You only need to set &cv-link-SHF77FLAGS; if you need to define specific user options for Fortran 77 files. -You should normally set the &cv-SHFORTRANFLAGS; variable, +You should normally set the &cv-link-SHFORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -190,11 +190,11 @@ for all Fortran versions. 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 &cv-SHF77FLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-SHF77FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. -You only need to set &cv-SHF77PPCOM; if you need to use a specific +You only need to set &cv-link-SHF77PPCOM; if you need to use a specific C-preprocessor command line for Fortran 77 files. -You should normally set the &cv-SHFORTRANPPCOM; variable, +You should normally set the &cv-link-SHFORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. diff --git a/src/engine/SCons/Tool/f90.xml b/src/engine/SCons/Tool/f90.xml index 5c252cc..9159a24 100644 --- a/src/engine/SCons/Tool/f90.xml +++ b/src/engine/SCons/Tool/f90.xml @@ -13,10 +13,10 @@ XXX The Fortran 90 compiler. -You should normally set the &cv-FORTRAN; variable, +You should normally set the &cv-link-FORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. -You only need to set &cv-F90; if you need to use a specific compiler +You only need to set &cv-link-F90; if you need to use a specific compiler or compiler version for Fortran 90 files. @@ -24,9 +24,9 @@ or compiler version for Fortran 90 files. The command line used to compile a Fortran 90 source file to an object file. -You only need to set &cv-F90COM; if you need to use a specific +You only need to set &cv-link-F90COM; if you need to use a specific command line for Fortran 90 files. -You should normally set the &cv-FORTRANCOM; variable, +You should normally set the &cv-link-FORTRANCOM; variable, which specifies the default command line for all Fortran versions. @@ -36,7 +36,7 @@ for all Fortran versions. The string displayed when a Fortran 90 source file is compiled to an object file. -If this is not set, then &cv-F90COM; or &cv-FORTRANCOM; +If this is not set, then &cv-link-F90COM; or &cv-link-FORTRANCOM; (the command line) is displayed. @@ -49,14 +49,14 @@ Note that this variable does contain (or similar) include search path options -that scons generates automatically from &cv-F90PATH;. +that scons generates automatically from &cv-link-F90PATH;. See -&cv-_F90INCFLAGS; +&cv-link-_F90INCFLAGS; below, for the variable that expands to those options. -You only need to set &cv-F90FLAGS; if you need to define specific +You only need to set &cv-link-F90FLAGS; if you need to define specific user options for Fortran 90 files. -You should normally set the &cv-FORTRANFLAGS; variable, +You should normally set the &cv-link-FORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -68,10 +68,10 @@ for all Fortran versions. 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 &cv-_F90INCFLAGS; is created -by appending &cv-INCPREFIX; and &cv-INCSUFFIX; +The value of &cv-link-_F90INCFLAGS; is created +by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end -of each directory in &cv-F90PATH;. +of each directory in &cv-link-F90PATH;. @@ -80,15 +80,15 @@ of each directory in &cv-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 &cv-F90FLAGS; because the result will be non-portable +arguments in &cv-link-F90FLAGS; because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: -directory names in &cv-F90PATH; will be looked-up relative to the SConscript +directory names in &cv-link-F90PATH; will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: -You only need to set &cv-F90PATH; if you need to define a specific +You only need to set &cv-link-F90PATH; if you need to define a specific include path for Fortran 90 files. -You should normally set the &cv-FORTRANPATH; variable, +You should normally set the &cv-link-FORTRANPATH; variable, which specifies the include path for the default Fortran compiler for all Fortran versions. @@ -108,17 +108,17 @@ env = Environment(F90PATH=include) The directory list will be added to command lines through the automatically-generated -&cv-_F90INCFLAGS; +&cv-link-_F90INCFLAGS; construction variable, which is constructed by appending the values of the -&cv-INCPREFIX; and &cv-INCSUFFIX; +&cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end -of each directory in &cv-F90PATH;. +of each directory in &cv-link-F90PATH;. Any command lines you define that need the F90PATH directory list should -include &cv-_F90INCFLAGS;: +include &cv-link-_F90INCFLAGS;: env = Environment(F90COM="my_compiler $_F90INCFLAGS -c -o $TARGET $SOURCE") @@ -130,11 +130,11 @@ env = Environment(F90COM="my_compiler $_F90INCFLAGS -c -o $TARGET $SOURCE") 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 &cv-F90FLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-F90FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. -You only need to set &cv-F90PPCOM; if you need to use a specific +You only need to set &cv-link-F90PPCOM; if you need to use a specific C-preprocessor command line for Fortran 90 files. -You should normally set the &cv-FORTRANPPCOM; variable, +You should normally set the &cv-link-FORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. @@ -143,10 +143,10 @@ for all Fortran versions. The Fortran 90 compiler used for generating shared-library objects. -You should normally set the &cv-SHFORTRAN; variable, +You should normally set the &cv-link-SHFORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. -You only need to set &cv-SHF90; if you need to use a specific compiler +You only need to set &cv-link-SHF90; if you need to use a specific compiler or compiler version for Fortran 90 files. @@ -155,9 +155,9 @@ or compiler version for Fortran 90 files. The command line used to compile a Fortran 90 source file to a shared-library object file. -You only need to set &cv-SHF90COM; if you need to use a specific +You only need to set &cv-link-SHF90COM; if you need to use a specific command line for Fortran 90 files. -You should normally set the &cv-SHFORTRANCOM; variable, +You should normally set the &cv-link-SHFORTRANCOM; variable, which specifies the default command line for all Fortran versions. @@ -167,7 +167,7 @@ for all Fortran versions. The string displayed when a Fortran 90 source file is compiled to a shared-library object file. -If this is not set, then &cv-SHF90COM; or &cv-SHFORTRANCOM; +If this is not set, then &cv-link-SHF90COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. @@ -176,9 +176,9 @@ If this is not set, then &cv-SHF90COM; or &cv-SHFORTRANCOM; Options that are passed to the Fortran 90 compiler to generated shared-library objects. -You only need to set &cv-SHF90FLAGS; if you need to define specific +You only need to set &cv-link-SHF90FLAGS; if you need to define specific user options for Fortran 90 files. -You should normally set the &cv-SHFORTRANFLAGS; variable, +You should normally set the &cv-link-SHFORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -190,11 +190,11 @@ for all Fortran versions. 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 &cv-SHF90FLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-SHF90FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. -You only need to set &cv-SHF90PPCOM; if you need to use a specific +You only need to set &cv-link-SHF90PPCOM; if you need to use a specific C-preprocessor command line for Fortran 90 files. -You should normally set the &cv-SHFORTRANPPCOM; variable, +You should normally set the &cv-link-SHFORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. diff --git a/src/engine/SCons/Tool/f95.xml b/src/engine/SCons/Tool/f95.xml index 86331a4..e3c026e 100644 --- a/src/engine/SCons/Tool/f95.xml +++ b/src/engine/SCons/Tool/f95.xml @@ -13,10 +13,10 @@ XXX The Fortran 95 compiler. -You should normally set the &cv-FORTRAN; variable, +You should normally set the &cv-link-FORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. -You only need to set &cv-F95; if you need to use a specific compiler +You only need to set &cv-link-F95; if you need to use a specific compiler or compiler version for Fortran 95 files. @@ -24,9 +24,9 @@ or compiler version for Fortran 95 files. The command line used to compile a Fortran 95 source file to an object file. -You only need to set &cv-F95COM; if you need to use a specific +You only need to set &cv-link-F95COM; if you need to use a specific command line for Fortran 95 files. -You should normally set the &cv-FORTRANCOM; variable, +You should normally set the &cv-link-FORTRANCOM; variable, which specifies the default command line for all Fortran versions. @@ -36,7 +36,7 @@ for all Fortran versions. The string displayed when a Fortran 95 source file is compiled to an object file. -If this is not set, then &cv-F95COM; or &cv-FORTRANCOM; +If this is not set, then &cv-link-F95COM; or &cv-link-FORTRANCOM; (the command line) is displayed. @@ -49,14 +49,14 @@ Note that this variable does contain (or similar) include search path options -that scons generates automatically from &cv-F95PATH;. +that scons generates automatically from &cv-link-F95PATH;. See -&cv-_F95INCFLAGS; +&cv-link-_F95INCFLAGS; below, for the variable that expands to those options. -You only need to set &cv-F95FLAGS; if you need to define specific +You only need to set &cv-link-F95FLAGS; if you need to define specific user options for Fortran 95 files. -You should normally set the &cv-FORTRANFLAGS; variable, +You should normally set the &cv-link-FORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -68,10 +68,10 @@ for all Fortran versions. 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 &cv-_F95INCFLAGS; is created -by appending &cv-INCPREFIX; and &cv-INCSUFFIX; +The value of &cv-link-_F95INCFLAGS; is created +by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end -of each directory in &cv-F95PATH;. +of each directory in &cv-link-F95PATH;. @@ -80,15 +80,15 @@ of each directory in &cv-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 &cv-F95FLAGS; because the result will be non-portable +arguments in &cv-link-F95FLAGS; because the result will be non-portable and the directories will not be searched by the dependency scanner. Note: -directory names in &cv-F95PATH; will be looked-up relative to the SConscript +directory names in &cv-link-F95PATH; will be looked-up relative to the SConscript directory when they are used in a command. To force &scons; to look-up a directory relative to the root of the source tree use #: -You only need to set &cv-F95PATH; if you need to define a specific +You only need to set &cv-link-F95PATH; if you need to define a specific include path for Fortran 95 files. -You should normally set the &cv-FORTRANPATH; variable, +You should normally set the &cv-link-FORTRANPATH; variable, which specifies the include path for the default Fortran compiler for all Fortran versions. @@ -108,17 +108,17 @@ env = Environment(F95PATH=include) The directory list will be added to command lines through the automatically-generated -&cv-_F95INCFLAGS; +&cv-link-_F95INCFLAGS; construction variable, which is constructed by appending the values of the -&cv-INCPREFIX; and &cv-INCSUFFIX; +&cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end -of each directory in &cv-F95PATH;. +of each directory in &cv-link-F95PATH;. Any command lines you define that need the F95PATH directory list should -include &cv-_F95INCFLAGS;: +include &cv-link-_F95INCFLAGS;: env = Environment(F95COM="my_compiler $_F95INCFLAGS -c -o $TARGET $SOURCE") @@ -130,11 +130,11 @@ env = Environment(F95COM="my_compiler $_F95INCFLAGS -c -o $TARGET $SOURCE") 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 &cv-F95FLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-F95FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. -You only need to set &cv-F95PPCOM; if you need to use a specific +You only need to set &cv-link-F95PPCOM; if you need to use a specific C-preprocessor command line for Fortran 95 files. -You should normally set the &cv-FORTRANPPCOM; variable, +You should normally set the &cv-link-FORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. @@ -143,10 +143,10 @@ for all Fortran versions. The Fortran 95 compiler used for generating shared-library objects. -You should normally set the &cv-SHFORTRAN; variable, +You should normally set the &cv-link-SHFORTRAN; variable, which specifies the default Fortran compiler for all Fortran versions. -You only need to set &cv-SHF95; if you need to use a specific compiler +You only need to set &cv-link-SHF95; if you need to use a specific compiler or compiler version for Fortran 95 files. @@ -155,9 +155,9 @@ or compiler version for Fortran 95 files. The command line used to compile a Fortran 95 source file to a shared-library object file. -You only need to set &cv-SHF95COM; if you need to use a specific +You only need to set &cv-link-SHF95COM; if you need to use a specific command line for Fortran 95 files. -You should normally set the &cv-SHFORTRANCOM; variable, +You should normally set the &cv-link-SHFORTRANCOM; variable, which specifies the default command line for all Fortran versions. @@ -167,7 +167,7 @@ for all Fortran versions. The string displayed when a Fortran 95 source file is compiled to a shared-library object file. -If this is not set, then &cv-SHF95COM; or &cv-SHFORTRANCOM; +If this is not set, then &cv-link-SHF95COM; or &cv-link-SHFORTRANCOM; (the command line) is displayed. @@ -176,9 +176,9 @@ If this is not set, then &cv-SHF95COM; or &cv-SHFORTRANCOM; Options that are passed to the Fortran 95 compiler to generated shared-library objects. -You only need to set &cv-SHF95FLAGS; if you need to define specific +You only need to set &cv-link-SHF95FLAGS; if you need to define specific user options for Fortran 95 files. -You should normally set the &cv-SHFORTRANFLAGS; variable, +You should normally set the &cv-link-SHFORTRANFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -190,11 +190,11 @@ for all Fortran versions. 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 &cv-SHF95FLAGS; and &cv-CPPFLAGS; construction variables +Any options specified in the &cv-link-SHF95FLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. -You only need to set &cv-SHF95PPCOM; if you need to use a specific +You only need to set &cv-link-SHF95PPCOM; if you need to use a specific C-preprocessor command line for Fortran 95 files. -You should normally set the &cv-SHFORTRANPPCOM; variable, +You should normally set the &cv-link-SHFORTRANPPCOM; variable, which specifies the default C-preprocessor command line for all Fortran versions. diff --git a/src/engine/SCons/Tool/fortran.xml b/src/engine/SCons/Tool/fortran.xml index c472343..c7000af 100644 --- a/src/engine/SCons/Tool/fortran.xml +++ b/src/engine/SCons/Tool/fortran.xml @@ -21,8 +21,11 @@ for all versions of Fortran. The command line used to compile a Fortran source file to an object file. By default, any options specified -in the &cv-FORTRANFLAGS;, &cv-CPPFLAGS;, &cv-_CPPDEFFLAGS;, -&cv-_FORTRANMODFLAG;, and &cv-_FORTRANINCFLAGS; construction variables +in the &cv-link-FORTRANFLAGS;, +&cv-link-CPPFLAGS;, +&cv-link-_CPPDEFFLAGS;, +&cv-link-_FORTRANMODFLAG;, and +&cv-link-_FORTRANINCFLAGS; construction variables are included on this command line. @@ -31,7 +34,7 @@ are included on this command line. The string displayed when a Fortran source file is compiled to an object file. -If this is not set, then &cv-FORTRANCOM; +If this is not set, then &cv-link-FORTRANCOM; (the command line) is displayed. @@ -44,9 +47,9 @@ Note that this variable does contain (or similar) include or module search path options -that scons generates automatically from &cv-FORTRANPATH;. +that scons generates automatically from &cv-link-FORTRANPATH;. See -&cv-_FORTRANINCFLAGS; and &cv-_FORTRANMODFLAG;, +&cv-link-_FORTRANINCFLAGS; and &cv-link-_FORTRANMODFLAG;, below, for the variables that expand those options. @@ -58,10 +61,10 @@ 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 &cv-_FORTRANINCFLAGS; is created -by prepending/appending &cv-INCPREFIX; and &cv-INCSUFFIX; +The value of &cv-link-_FORTRANINCFLAGS; is created +by prepending/appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; to the beginning and end -of each directory in &cv-FORTRANPATH;. +of each directory in &cv-link-FORTRANPATH;. @@ -79,8 +82,8 @@ for module files, as well. 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 &cv-FORTRANMODDIR; construction variables -when the &cv-_FORTRANMODFLAG; variables is automatically generated. +in the &cv-link-FORTRANMODDIR; construction variables +when the &cv-link-_FORTRANMODFLAG; variables is automatically generated. @@ -89,8 +92,8 @@ when the &cv-_FORTRANMODFLAG; variables is automatically generated. 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 &cv-FORTRANMODDIR; construction variables -when the &cv-_FORTRANMODFLAG; variables is automatically generated. +in the &cv-link-FORTRANMODDIR; construction variables +when the &cv-link-_FORTRANMODFLAG; variables is automatically generated. @@ -101,9 +104,10 @@ 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 &cv-_FORTRANMODFLAG; is created -by prepending/appending &cv-FORTRANMODDIRPREFIX; and &cv-FORTRANMODDIRSUFFIX; -to the beginning and end of the directory in &cv-FORTRANMODDIR;. +The value of &cv-link-_FORTRANMODFLAG; is created +by prepending/appending &cv-link-FORTRANMODDIRPREFIX; and +&cv-link-FORTRANMODDIRSUFFIX; +to the beginning and end of the directory in &cv-link-FORTRANMODDIR;. @@ -162,17 +166,17 @@ env = Environment(FORTRANPATH=include) The directory list will be added to command lines through the automatically-generated -&cv-_FORTRANINCFLAGS; +&cv-link-_FORTRANINCFLAGS; construction variable, which is constructed by appending the values of the -&cv-INCPREFIX; and &cv-INCSUFFIX; +&cv-link-INCPREFIX; and &cv-link-INCSUFFIX; construction variables to the beginning and end -of each directory in &cv-FORTRANPATH;. +of each directory in &cv-link-FORTRANPATH;. Any command lines you define that need the FORTRANPATH directory list should -include &cv-_FORTRANINCFLAGS;: +include &cv-link-_FORTRANINCFLAGS;: env = Environment(FORTRANCOM="my_compiler $_FORTRANINCFLAGS -c -o $TARGET $SOURCE") @@ -184,8 +188,11 @@ env = Environment(FORTRANCOM="my_compiler $_FORTRANINCFLAGS -c -o $TARGET $SOURC 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 &cv-FORTRANFLAGS;, &cv-CPPFLAGS;, -_CPPDEFFLAGS, &cv-_FORTRANMODFLAG;, and &cv-_FORTRANINCFLAGS; +By default, any options specified in the &cv-link-FORTRANFLAGS;, +&cv-link-CPPFLAGS;, +&cv-link-_CPPDEFFLAGS;, +&cv-link-_FORTRANMODFLAG;, and +&cv-link-_FORTRANINCFLAGS; construction variables are included on this command line. @@ -221,7 +228,7 @@ to a shared-library object file. The string displayed when a Fortran source file is compiled to a shared-library object file. -If this is not set, then &cv-SHFORTRANCOM; +If this is not set, then &cv-link-SHFORTRANCOM; (the command line) is displayed. @@ -239,7 +246,8 @@ 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 &cv-SHFORTRANFLAGS; and &cv-CPPFLAGS; construction variables +in the &cv-link-SHFORTRANFLAGS; and +&cv-link-CPPFLAGS; construction variables are included on this command line. diff --git a/src/engine/SCons/Tool/gs.xml b/src/engine/SCons/Tool/gs.xml index d4840d6..9a63e94 100644 --- a/src/engine/SCons/Tool/gs.xml +++ b/src/engine/SCons/Tool/gs.xml @@ -27,7 +27,7 @@ The Ghostscript command line used to convert PostScript to PDF files. The string displayed when Ghostscript is used to convert a PostScript file to a PDF file. -If this is not set, then &cv-GSCOM; (the command line) is displayed. +If this is not set, then &cv-link-GSCOM; (the command line) is displayed. diff --git a/src/engine/SCons/Tool/intelc.py b/src/engine/SCons/Tool/intelc.py index d95a4be..2d0a5a5 100644 --- a/src/engine/SCons/Tool/intelc.py +++ b/src/engine/SCons/Tool/intelc.py @@ -353,7 +353,9 @@ def generate(env, version=None, abi=None, topdir=None, verbose=0): else: env['CC'] = 'icc' env['CXX'] = 'icpc' - env['LINK'] = '$CC' + # 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 diff --git a/src/engine/SCons/Tool/jar.xml b/src/engine/SCons/Tool/jar.xml index 45d9ebc..488459b 100644 --- a/src/engine/SCons/Tool/jar.xml +++ b/src/engine/SCons/Tool/jar.xml @@ -14,7 +14,7 @@ XXX Builds a Java archive (.jar) file from a source tree of .class files. -If the &cv-JARCHDIR; value is set, the +If the &cv-link-JARCHDIR; value is set, the &jar; command will change to the specified directory using the @@ -59,7 +59,7 @@ The command line used to call the Java archive tool. The string displayed when the Java archive tool is called -If this is not set, then &cv-JARCOM; (the command line) is displayed. +If this is not set, then &cv-link-JARCOM; (the command line) is displayed. env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") diff --git a/src/engine/SCons/Tool/javac.xml b/src/engine/SCons/Tool/javac.xml index 86fd507..400a23b 100644 --- a/src/engine/SCons/Tool/javac.xml +++ b/src/engine/SCons/Tool/javac.xml @@ -63,7 +63,7 @@ The Java compiler. The command line used to compile a directory tree containing Java source files to corresponding Java class files. -Any options specified in the &cv-JAVACFLAGS; construction variable +Any options specified in the &cv-link-JAVACFLAGS; construction variable are included on this command line. @@ -73,7 +73,7 @@ are included on this command line. The string displayed when compiling a directory tree of Java source files to corresponding Java class files. -If this is not set, then &cv-JAVACCOM; (the command line) is displayed. +If this is not set, then &cv-link-JAVACCOM; (the command line) is displayed. env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") diff --git a/src/engine/SCons/Tool/javah.xml b/src/engine/SCons/Tool/javah.xml index 84d0068..75d0efb 100644 --- a/src/engine/SCons/Tool/javah.xml +++ b/src/engine/SCons/Tool/javah.xml @@ -24,7 +24,7 @@ or the objects returned from the builder method. If the construction variable -&cv-JAVACLASSDIR; +&cv-link-JAVACLASSDIR; is set, either in the environment or in the call to the &b-JavaH; @@ -62,7 +62,7 @@ The Java generator for C header and stub files. The command line used to generate C header and stub files from Java classes. -Any options specified in the &cv-JAVAHFLAGS; construction variable +Any options specified in the &cv-link-JAVAHFLAGS; construction variable are included on this command line. @@ -71,7 +71,7 @@ are included on this command line. The string displayed when C header and stub files are generated from Java classes. -If this is not set, then &cv-JAVAHCOM; (the command line) is displayed. +If this is not set, then &cv-link-JAVAHCOM; (the command line) is displayed. env = Environment(JAVAHCOMSTR = "Generating header/stub file(s) $TARGETS from $SOURCES") diff --git a/src/engine/SCons/Tool/latex.xml b/src/engine/SCons/Tool/latex.xml index 96c0f4a..35eea30 100644 --- a/src/engine/SCons/Tool/latex.xml +++ b/src/engine/SCons/Tool/latex.xml @@ -26,7 +26,7 @@ The command line used to call the LaTeX structured formatter and typesetter. The string displayed when calling the LaTeX structured formatter and typesetter. -If this is not set, then &cv-LATEXCOM; (the command line) is displayed. +If this is not set, then &cv-link-LATEXCOM; (the command line) is displayed. env = Environment(LATEXCOMSTR = "Building $TARGET from LaTeX input $SOURCES") @@ -45,7 +45,7 @@ General options passed to the LaTeX structured formatter and typesetter. The maximum number of times that LaTeX will be re-run if the .log -generated by the &cv-LATEXCOM; command +generated by the &cv-link-LATEXCOM; command indicates that there are undefined references. The default is to try to resolve undefined references by re-running LaTeX up to three times. diff --git a/src/engine/SCons/Tool/lex.xml b/src/engine/SCons/Tool/lex.xml index 51401dc..d67feb0 100644 --- a/src/engine/SCons/Tool/lex.xml +++ b/src/engine/SCons/Tool/lex.xml @@ -27,7 +27,7 @@ to generate a source file. The string displayed when generating a source file using the lexical analyzer generator. -If this is not set, then &cv-LEXCOM; (the command line) is displayed. +If this is not set, then &cv-link-LEXCOM; (the command line) is displayed. env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES") diff --git a/src/engine/SCons/Tool/link.xml b/src/engine/SCons/Tool/link.xml index 52694ad..0728dda 100644 --- a/src/engine/SCons/Tool/link.xml +++ b/src/engine/SCons/Tool/link.xml @@ -13,23 +13,24 @@ XXX The linker for building loadable modules. -By default, this is the same as &cv-SHLINK;. +By default, this is the same as &cv-link-SHLINK;. The command line for building loadable modules. -On Mac OS X, this uses the &cv-LDMODULE;, -&cv-LDMODULEFLAGS; and &cv-FRAMEWORKSFLAGS; variables. -On other systems, this is the same as &cv-SHLINK;. +On Mac OS X, this uses the &cv-link-LDMODULE;, +&cv-link-LDMODULEFLAGS; and +&cv-link-FRAMEWORKSFLAGS; variables. +On other systems, this is the same as &cv-link-SHLINK;. The string displayed when building loadable modules. -If this is not set, then &cv-LDMODULECOM; (the command line) is displayed. +If this is not set, then &cv-link-LDMODULECOM; (the command line) is displayed. @@ -44,7 +45,7 @@ General user options passed to the linker for building loadable modules. The prefix used for loadable module file names. On Mac OS X, this is null; on other systems, this is -the same as &cv-SHLIBPREFIX;. +the same as &cv-link-SHLIBPREFIX;. @@ -73,7 +74,7 @@ The command line used to link object files into an executable. The string displayed when object files are linked into an executable. -If this is not set, then &cv-LINKCOM; (the command line) is displayed. +If this is not set, then &cv-link-LINKCOM; (the command line) is displayed. env = Environment(LINKCOMSTR = "Linking $TARGET") @@ -88,17 +89,17 @@ Note that this variable should not contain -(or similar) options for linking with the libraries listed in &cv-LIBS;, +(or similar) options for linking with the libraries listed in &cv-link-LIBS;, nor (or similar) library search path options -that scons generates automatically from &cv-LIBPATH;. +that scons generates automatically from &cv-link-LIBPATH;. See -&cv-_LIBFLAGS; +&cv-link-_LIBFLAGS; above, for the variable that expands to library-link options, and -&cv-_LIBDIRFLAGS; +&cv-link-_LIBDIRFLAGS; above, for the variable that expands to library search path options. @@ -119,7 +120,7 @@ The command line used to link programs using shared libaries. The string displayed when programs using shared libraries are linked. -If this is not set, then &cv-SHLINKCOM; (the command line) is displayed. +If this is not set, then &cv-link-SHLINKCOM; (the command line) is displayed. env = Environment(SHLINKCOMSTR = "Linking shared $TARGET") @@ -134,17 +135,17 @@ Note that this variable should not contain -(or similar) options for linking with the libraries listed in &cv-LIBS;, +(or similar) options for linking with the libraries listed in &cv-link-LIBS;, nor (or similar) include search path options -that scons generates automatically from &cv-LIBPATH;. +that scons generates automatically from &cv-link-LIBPATH;. See -&cv-_LIBFLAGS; +&cv-link-_LIBFLAGS; above, for the variable that expands to library-link options, and -&cv-_LIBDIRFLAGS; +&cv-link-_LIBDIRFLAGS; above, for the variable that expands to library search path options. diff --git a/src/engine/SCons/Tool/m4.xml b/src/engine/SCons/Tool/m4.xml index 7a0f9c3..0ee421e 100644 --- a/src/engine/SCons/Tool/m4.xml +++ b/src/engine/SCons/Tool/m4.xml @@ -13,7 +13,7 @@ XXX Builds an output file from an M4 input file. -This uses a default &cv-M4FLAGS; value of +This uses a default &cv-link-M4FLAGS; value of , which considers all warnings to be fatal and stops on the first warning @@ -42,7 +42,7 @@ The command line used to pass files through the M4 macro preprocessor. The string displayed when a file is passed through the M4 macro preprocessor. -If this is not set, then &cv-M4COM; (the command line) is displayed. +If this is not set, then &cv-link-M4COM; (the command line) is displayed. diff --git a/src/engine/SCons/Tool/mslink.xml b/src/engine/SCons/Tool/mslink.xml index 6499cfe..160d415 100644 --- a/src/engine/SCons/Tool/mslink.xml +++ b/src/engine/SCons/Tool/mslink.xml @@ -49,7 +49,7 @@ Using the instead may yield improved link-time performance, although parallel builds will no longer work. You can generate PDB files with the -switch by overriding the default &cv-CCPDBFLAGS; variable; +switch by overriding the default &cv-link-CCPDBFLAGS; variable; see the entry for that variable for specific examples. @@ -75,7 +75,7 @@ is passed a keyword argument of register=1. The string displayed when registering a newly-built DLL file. -If this is not set, then &cv-REGSVRCOM; (the command line) is displayed. +If this is not set, then &cv-link-REGSVRCOM; (the command line) is displayed. @@ -92,31 +92,31 @@ and requiring user attention. -A deprecated synonym for &cv-WINDOWS_INSERT_DEF;. +A deprecated synonym for &cv-link-WINDOWS_INSERT_DEF;. -A deprecated synonym for &cv-WINDOWSDEFPREFIX;. +A deprecated synonym for &cv-link-WINDOWSDEFPREFIX;. -A deprecated synonym for &cv-WINDOWSDEFSUFFIX;. +A deprecated synonym for &cv-link-WINDOWSDEFSUFFIX;. -A deprecated synonym for &cv-WINDOWSEXPSUFFIX;. +A deprecated synonym for &cv-link-WINDOWSEXPSUFFIX;. -A deprecated synonym for &cv-WINDOWSEXPSUFFIX;. +A deprecated synonym for &cv-link-WINDOWSEXPSUFFIX;. diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index 5c9eaf0..c6fe461 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -138,12 +138,13 @@ def _parse_msvc8_overrides(version,platform,suite): if not SCons.Util.can_read_reg: raise SCons.Errors.InternalError, "No Windows registry module was found" - s = '' + # XXX This code assumes anything that isn't EXPRESS uses the default + # registry key string. Is this really true for all VS suites? if suite == 'EXPRESS': s = '\\VCExpress\\' + else: + s = '\\VisualStudio\\' - # ToDo: add registry key strings for the other versions of visual - # studio 2005. settings_path = "" try: (settings_path, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, diff --git a/src/engine/SCons/Tool/msvc.xml b/src/engine/SCons/Tool/msvc.xml index be155bd..0ea2602 100644 --- a/src/engine/SCons/Tool/msvc.xml +++ b/src/engine/SCons/Tool/msvc.xml @@ -53,7 +53,7 @@ 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 &cv-PCH; construction variable is set. +when the &cv-link-PCH; construction variable is set. @@ -64,7 +64,7 @@ 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 &cv-PDB; construction variable is set. +when the &cv-link-PDB; construction variable is set. The Visual C++ compiler option that SCons uses by default to generate PDB information is . @@ -78,7 +78,7 @@ link-time performance, although parallel builds will no longer work. You can generate PDB files with the -switch by overriding the default &cv-CCPDBFLAGS; variable as follows: +switch by overriding the default &cv-link-CCPDBFLAGS; variable as follows: import SCons.Util @@ -88,7 +88,7 @@ env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Zi /Fd%s" % File(PDB)) or "" An alternative would be to use the to put the debugging information in a separate .pdb file for each object file by overriding -the &cv-CCPDBFLAGS; variable as follows: +the &cv-link-CCPDBFLAGS; variable as follows: env['CCPDBFLAGS'] = '/Zi /Fd${TARGET}.pdb' @@ -123,7 +123,7 @@ builder to generated a precompiled header. The string displayed when generating a precompiled header. -If this is not set, then &cv-PCHCOM; (the command line) is displayed. +If this is not set, then &cv-link-PCHCOM; (the command line) is displayed. @@ -160,7 +160,7 @@ a Microsoft Visual C++ resource file. The string displayed when invoking the resource compiler to build a Microsoft Visual C++ resource file. -If this is not set, then &cv-RCCOM; (the command line) is displayed. +If this is not set, then &cv-link-RCCOM; (the command line) is displayed. diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index b84b277..db8e8fd 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -1385,10 +1385,7 @@ def get_msvs_install_dirs(version = None, vs8suite = None): # try and enumerate the installed versions of the .NET framework. contents = os.listdir(rv['FRAMEWORKDIR']) l = re.compile('v[0-9]+.*') - versions = [] - for entry in contents: - if l.match(entry): - versions.append(entry) + installed_framework_versions = filter(lambda e, l=l: l.match(e), contents) def versrt(a,b): # since version numbers aren't really floats... @@ -1403,23 +1400,51 @@ def get_msvs_install_dirs(version = None, vs8suite = None): c = int(bbl[2]) - int(aal[2]) return c - versions.sort(versrt) + installed_framework_versions.sort(versrt) - rv['FRAMEWORKVERSIONS'] = versions - # assume that the highest version is the latest version installed - rv['FRAMEWORKVERSION'] = versions[0] + rv['FRAMEWORKVERSIONS'] = installed_framework_versions - # .NET framework SDK install dir - try: - if rv.has_key('FRAMEWORKVERSION') and rv['FRAMEWORKVERSION'][:4] == 'v1.1': - key = r'Software\Microsoft\.NETFramework\sdkInstallRootv1.1' + # TODO: allow a specific framework version to be set + + # Choose a default framework version based on the Visual + # Studio version. + DefaultFrameworkVersionMap = { + '7.0' : 'v1.0', + '7.1' : 'v1.1', + '8.0' : 'v2.0', + # TODO: Does .NET 3.0 need to be worked into here somewhere? + } + try: + default_framework_version = DefaultFrameworkVersionMap[version[:3]] + except (KeyError, TypeError): + pass else: - key = r'Software\Microsoft\.NETFramework\sdkInstallRoot' + # Look for the first installed directory in FRAMEWORKDIR that + # begins with the framework version string that's appropriate + # for the Visual Studio version we're using. + for v in installed_framework_versions: + if v[:4] == default_framework_version: + rv['FRAMEWORKVERSION'] = v + break - (rv['FRAMEWORKSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,key) + # If the framework version couldn't be worked out by the previous + # code then fall back to using the latest version of the .NET + # framework + if not rv.has_key('FRAMEWORKVERSION'): + rv['FRAMEWORKVERSION'] = installed_framework_versions[0] - except SCons.Util.RegError: - pass + # .NET framework SDK install dir + if rv.has_key('FRAMEWORKVERSION'): + # The .NET SDK version used must match the .NET version used, + # so we deliberately don't fall back to other .NET framework SDK + # versions that might be present. + ver = rv['FRAMEWORKVERSION'][:4] + key = r'Software\Microsoft\.NETFramework\sdkInstallRoot' + ver + try: + (rv['FRAMEWORKSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, + key) + except SCons.Util.RegError: + pass # MS Platform SDK dir try: @@ -1704,9 +1729,9 @@ def generate(env): 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' + env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' + env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' + env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"' env['MSVSENCODING'] = 'Windows-1252' try: @@ -1746,7 +1771,11 @@ def exists(env): if env.has_key('MSVS_VERSION'): version_num, suite = msvs_parse_version(env['MSVS_VERSION']) if version_num >= 7.0: - return env.Detect('devenv') + # The executable is 'devenv' in Visual Studio Pro, + # Team System and others. Express Editions have different + # executable names. Right now we're only going to worry + # about Visual C++ 2005 Express Edition. + return env.Detect('devenv') or env.Detect('vcexpress') else: return env.Detect('msdev') else: diff --git a/src/engine/SCons/Tool/msvs.xml b/src/engine/SCons/Tool/msvs.xml index c16beaa..07fda0c 100644 --- a/src/engine/SCons/Tool/msvs.xml +++ b/src/engine/SCons/Tool/msvs.xml @@ -18,13 +18,13 @@ 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 -&cv-MSVS_VERSION; +&cv-link-MSVS_VERSION; in the Environment constructor). For Visual Studio 6, it will generate a .dsp file. -For Visual Studio 7 (.NET), it will generate a -.dsp +For Visual Studio 7 (.NET) and later versions, it will generate a +.vcproj file. By default, @@ -56,7 +56,7 @@ These are currently limited to and misc. These are pretty self-explanatory, but it should be noted that these -lists are added to the &cv-SOURCES; construction variable as strings, +lists are added to the &cv-link-SOURCES; 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. @@ -77,7 +77,7 @@ file. The correct suffix for the version of Visual Studio must be used, but the -&cv-MSVSPROJECTSUFFIX; +&cv-link-MSVSPROJECTSUFFIX; construction variable will be defined to the correct value (see example below). @@ -154,7 +154,7 @@ 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 -&cv-MSVS_VERSION; +&cv-link-MSVS_VERSION; in the construction environment). For Visual Studio 6, it will generate a .dsw @@ -169,7 +169,7 @@ The following values must be specified: 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 -&cv-MSVSSOLUTIONSUFFIX; +&cv-link-MSVSSOLUTIONSUFFIX; will be defined to the correct value (see example below). variant: @@ -184,12 +184,13 @@ A list of project file names, or Project nodes returned by calls to the &b-MSVSProject; Builder, to be placed into the solution file. -(NOTE: Currently only one project is supported per solution.) 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. +(NOTE: Currently only one project is supported per solution.) + Example Usage: @@ -479,7 +480,8 @@ The action used to generate Microsoft Visual Studio project files. The suffix used for Microsoft Visual Studio project (DSP) files. The default value is .vcproj -when using Visual Studio version 7.x (.NET), +when using Visual Studio version 7.x (.NET) +or later version, and .dsp when using earlier versions of Visual Studio. @@ -528,7 +530,7 @@ file) that will be invoked by Visual Studio project files (through the -&cv-MSVSSCONSCOM; +&cv-link-MSVSSCONSCOM; variable). The default is the same sconscript file that contains the call to @@ -561,7 +563,7 @@ 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 -&cv-MSVSSCONS; +&cv-link-MSVSSCONS; command line executed from Microsoft Visual Studio project files. diff --git a/src/engine/SCons/Tool/pdf.xml b/src/engine/SCons/Tool/pdf.xml index 3d64be2..1e447dd 100644 --- a/src/engine/SCons/Tool/pdf.xml +++ b/src/engine/SCons/Tool/pdf.xml @@ -12,7 +12,7 @@ from a .dvi input file .ltx, or .latex input file). -The suffix specified by the &cv-PDFSUFFIX; construction variable +The suffix specified by the &cv-link-PDFSUFFIX; construction variable (.pdf by default) is added automatically to the target if it is not already present. Example: diff --git a/src/engine/SCons/Tool/qt.xml b/src/engine/SCons/Tool/qt.xml index 6a4ac78..4b11cbc 100644 --- a/src/engine/SCons/Tool/qt.xml +++ b/src/engine/SCons/Tool/qt.xml @@ -14,7 +14,7 @@ XXX 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 &cv-QTDIR; variable for more information. +tool 'qt'. See the &cv-link-QTDIR; variable for more information. Example: @@ -35,7 +35,7 @@ files to the &b-Program;, 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 &cv-QTDIR; variable for more information. +See the &cv-link-QTDIR; variable for more information. Example: @@ -56,7 +56,9 @@ with python's os.path.join() method, but are listed here with the '/' separator for easier reading.) In addition, the construction environment -variables &cv-CPPPATH;, &cv-LIBPATH; and &cv-LIBS; may be modified +variables &cv-link-CPPPATH;, +&cv-link-LIBPATH; and +&cv-link-LIBS; may be modified and the variables PROGEMITTER, SHLIBEMITTER and LIBEMITTER are modified. Because the build-performance is affected when using this tool, @@ -110,15 +112,16 @@ specify files to run moc on. The path where the qt binaries are installed. -The default value is '&cv-QTDIR;/bin'. +The default value is '&cv-link-QTDIR;/bin'. The path where the qt header files are installed. -The default value is '&cv-QTDIR;/include'. -Note: If you set this variable to None, the tool won't change the &cv-CPPPATH; +The default value is '&cv-link-QTDIR;/include'. +Note: If you set this variable to None, +the tool won't change the &cv-link-CPPPATH; construction variable. @@ -132,22 +135,23 @@ Prints lots of debugging information while scanning for moc files. 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 &cv-LIBS; variable. +this variable to None, the tool won't change the &cv-link-LIBS; variable. The path where the qt libraries are installed. -The default value is '&cv-QTDIR;/lib'. -Note: If you set this variable to None, the tool won't change the &cv-LIBPATH; +The default value is '&cv-link-QTDIR;/lib'. +Note: If you set this variable to None, +the tool won't change the &cv-link-LIBPATH; construction variable. -Default value is '&cv-QT_BINPATH;/moc'. +Default value is '&cv-link-QT_BINPATH;/moc'. @@ -180,7 +184,7 @@ Command to generate a moc file from a cpp file. The string displayed when generating a moc file from a cpp file. -If this is not set, then &cv-QT_MOCFROMCXXCOM; (the command line) is displayed. +If this is not set, then &cv-link-QT_MOCFROMCXXCOM; (the command line) is displayed. @@ -193,7 +197,7 @@ Command to generate a moc file from a header. The string displayed when generating a moc file from a cpp file. -If this is not set, then &cv-QT_MOCFROMHCOM; (the command line) is displayed. +If this is not set, then &cv-link-QT_MOCFROMHCOM; (the command line) is displayed. @@ -212,14 +216,14 @@ Default value is 'moc_'. Prefix for moc output files, when source is a header. -Default value is '&cv-CXXFILESUFFIX;'. Suffix for moc output files, when source is +Default value is '&cv-link-CXXFILESUFFIX;'. Suffix for moc output files, when source is a header. -Default value is '&cv-QT_BINPATH;/uic'. +Default value is '&cv-link-QT_BINPATH;/uic'. @@ -232,7 +236,7 @@ Command to generate header files from .ui files. The string displayed when generating header files from .ui files. -If this is not set, then &cv-QT_UICCOM; (the command line) is displayed. +If this is not set, then &cv-link-QT_UICCOM; (the command line) is displayed. @@ -270,7 +274,7 @@ Default value is 'uic_'. Prefix for uic generated implementation files. -Default value is '&cv-CXXFILESUFFIX;'. Suffix for uic generated implementation +Default value is '&cv-link-CXXFILESUFFIX;'. Suffix for uic generated implementation files. diff --git a/src/engine/SCons/Tool/rmic.xml b/src/engine/SCons/Tool/rmic.xml index 1fe4bd8..63511e5 100644 --- a/src/engine/SCons/Tool/rmic.xml +++ b/src/engine/SCons/Tool/rmic.xml @@ -24,7 +24,7 @@ or the objects return from the builder method. If the construction variable -&cv-JAVACLASSDIR; +&cv-link-JAVACLASSDIR; is set, either in the environment or in the call to the &b-RMIC; @@ -59,7 +59,7 @@ The Java RMI stub compiler. The command line used to compile stub and skeleton class files from Java classes that contain RMI implementations. -Any options specified in the &cv-RMICFLAGS; construction variable +Any options specified in the &cv-link-RMICFLAGS; construction variable are included on this command line. @@ -69,7 +69,7 @@ are included on this command line. The string displayed when compiling stub and skeleton class files from Java classes that contain RMI implementations. -If this is not set, then &cv-RMICCOM; (the command line) is displayed. +If this is not set, then &cv-link-RMICCOM; (the command line) is displayed. env = Environment(RMICCOMSTR = "Generating stub/skeleton class files $TARGETS from $SOURCES") diff --git a/src/engine/SCons/Tool/rpcgen.xml b/src/engine/SCons/Tool/rpcgen.xml index 74fc3be..a3adb22 100644 --- a/src/engine/SCons/Tool/rpcgen.xml +++ b/src/engine/SCons/Tool/rpcgen.xml @@ -85,7 +85,7 @@ The RPC protocol compiler. Options passed to the RPC protocol compiler when generating client side stubs. These are in addition to any flags specified in the -&cv-RPCGENFLAGS; +&cv-link-RPCGENFLAGS; construction variable. @@ -101,7 +101,7 @@ General options passed to the RPC protocol compiler. Options passed to the RPC protocol compiler when generating a header file. These are in addition to any flags specified in the -&cv-RPCGENFLAGS; +&cv-link-RPCGENFLAGS; construction variable. @@ -111,7 +111,7 @@ construction variable. Options passed to the RPC protocol compiler when generating server side stubs. These are in addition to any flags specified in the -&cv-RPCGENFLAGS; +&cv-link-RPCGENFLAGS; construction variable. @@ -121,7 +121,7 @@ construction variable. Options passed to the RPC protocol compiler when generating XDR routines. These are in addition to any flags specified in the -&cv-RPCGENFLAGS; +&cv-link-RPCGENFLAGS; construction variable. diff --git a/src/engine/SCons/Tool/swig.xml b/src/engine/SCons/Tool/swig.xml index 0bdda96..05faff2 100644 --- a/src/engine/SCons/Tool/swig.xml +++ b/src/engine/SCons/Tool/swig.xml @@ -22,13 +22,13 @@ The suffix that will be used for intermediate C source files generated by the scripting language wrapper and interface generator. The default value is -_wrap&cv-CFILESUFFIX;. +_wrap&cv-link-CFILESUFFIX;. By default, this value is used whenever the option is not specified as part of the -&cv-SWIGFLAGS; +&cv-link-SWIGFLAGS; construction variable. @@ -44,7 +44,7 @@ the scripting language wrapper and interface generator. The string displayed when calling the scripting language wrapper and interface generator. -If this is not set, then &cv-SWIGCOM; (the command line) is displayed. +If this is not set, then &cv-link-SWIGCOM; (the command line) is displayed. @@ -54,11 +54,11 @@ The suffix that will be used for intermediate C++ source files generated by the scripting language wrapper and interface generator. The default value is -_wrap&cv-CFILESUFFIX;. +_wrap&cv-link-CFILESUFFIX;. By default, this value is used whenever the -c++ option is specified as part of the -&cv-SWIGFLAGS; +&cv-link-SWIGFLAGS; construction variable. @@ -79,7 +79,7 @@ option in this variable, will, by default, generate a C++ intermediate source file with the extension that is specified as the -&cv-CXXFILESUFFIX; +&cv-link-CXXFILESUFFIX; variable. diff --git a/src/engine/SCons/Tool/tar.xml b/src/engine/SCons/Tool/tar.xml index 88265dc..763df54 100644 --- a/src/engine/SCons/Tool/tar.xml +++ b/src/engine/SCons/Tool/tar.xml @@ -65,7 +65,7 @@ The command line used to call the tar archiver. The string displayed when archiving files using the tar archiver. -If this is not set, then &cv-TARCOM; (the command line) is displayed. +If this is not set, then &cv-link-TARCOM; (the command line) is displayed. env = Environment(TARCOMSTR = "Archiving $TARGET") diff --git a/src/engine/SCons/Tool/tex.py b/src/engine/SCons/Tool/tex.py index 1defd90..0e3d4b1 100644 --- a/src/engine/SCons/Tool/tex.py +++ b/src/engine/SCons/Tool/tex.py @@ -52,6 +52,10 @@ undefined_references_re = re.compile(undefined_references_str, re.MULTILINE) openout_aux_re = re.compile(r"\\openout.*`(.*\.aux)'") +makeindex_re = re.compile(r"^[^%]*\\makeindex", re.MULTILINE) +tableofcontents_re = re.compile(r"^[^%]*\\tableofcontents", re.MULTILINE) +bibliography_re = re.compile(r"^[^%]*\\bibliography", re.MULTILINE) + # An Action sufficient to build any generic tex file. TeXAction = None @@ -72,6 +76,24 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None basename = SCons.Util.splitext(str(source[0]))[0] basedir = os.path.split(str(source[0]))[0] + # Notice that all the filenames are not prefixed with the basedir. + # That's because the *COM variables have the cd command in the prolog. + + bblfilename = basename + '.bbl' + bblContents = "" + if os.path.exists(bblfilename): + bblContents = open(bblfilename, "rb").read() + + idxfilename = basename + '.idx' + idxContents = "" + if os.path.exists(idxfilename): + idxContents = open(idxfilename, "rb").read() + + tocfilename = basename + '.toc' + tocContents = "" + if os.path.exists(tocfilename): + tocContents = open(tocfilename, "rb").read() + # Run LaTeX once to generate a new aux file. XXXLaTeXAction(target, source, env) @@ -96,21 +118,26 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None BibTeXAction(bibfile, bibfile, env) break - # Now decide if makeindex will need to be run. - idxfilename = basename + '.idx' - if os.path.exists(idxfilename): + must_rerun_latex = 0 + # Now decide if latex will need to be run again due to table of contents. + if os.path.exists(tocfilename) and tocContents != open(tocfilename, "rb").read(): + must_rerun_latex = 1 + + # Now decide if latex will need to be run again due to bibliography. + if os.path.exists(bblfilename) and bblContents != open(bblfilename, "rb").read(): + must_rerun_latex = 1 + + # Now decide if latex will need to be run again due to index. + if os.path.exists(idxfilename) and idxContents != open(idxfilename, "rb").read(): + # We must run makeindex idxfile = env.fs.File(basename) - # TODO: if ( idxfile has changed) ... MakeIndexAction(idxfile, idxfile, env) - XXXLaTeXAction(target, source, env) + must_rerun_latex = 1 - # Now decide if latex will need to be run again due to table of contents. - tocfilename = basename + '.toc' - if os.path.exists(tocfilename): - # TODO: if ( tocfilename has changed) ... + if must_rerun_latex == 1: XXXLaTeXAction(target, source, env) - # Now decide if latex needs to be run yet again. + # Now decide if latex needs to be run yet again to resolve warnings. logfilename = basename + '.log' for _ in range(int(env.subst('$LATEXRETRIES'))): if not os.path.exists(logfilename): @@ -153,25 +180,30 @@ def tex_emitter(target, source, env): target.append(base + '.log') for f in source: content = f.get_contents() - if string.find(content, r'\tableofcontents') != -1: + if tableofcontents_re.search(content): target.append(base + '.toc') - if string.find(content, r'\makeindex') != -1: + env.Precious(base + '.toc') + if makeindex_re.search(content): target.append(base + '.ilg') target.append(base + '.ind') target.append(base + '.idx') - if string.find(content, r'\bibliography') != -1: + env.Precious(base + '.idx') + if bibliography_re.search(content): target.append(base + '.bbl') + env.Precious(base + '.bbl') target.append(base + '.blg') # read log file to get all .aux files logfilename = base + '.log' + dir, base_nodir = os.path.split(base) if os.path.exists(logfilename): content = open(logfilename, "rb").read() aux_files = openout_aux_re.findall(content) - aux_files = filter(lambda f, b=base+'.aux': f != b, aux_files) - dir = os.path.split(base)[0] + aux_files = filter(lambda f, b=base_nodir+'.aux': f != b, aux_files) aux_files = map(lambda f, d=dir: d+os.sep+f, aux_files) target.extend(aux_files) + for a in aux_files: + env.Precious( a ) return (target, source) diff --git a/src/engine/SCons/Tool/tex.xml b/src/engine/SCons/Tool/tex.xml index b30b36d..4cfc252 100644 --- a/src/engine/SCons/Tool/tex.xml +++ b/src/engine/SCons/Tool/tex.xml @@ -29,7 +29,7 @@ typesetter. The string displayed when generating a bibliography for TeX or LaTeX. -If this is not set, then &cv-BIBTEXCOM; (the command line) is displayed. +If this is not set, then &cv-link-BIBTEXCOM; (the command line) is displayed. env = Environment(BIBTEXCOMSTR = "Generating bibliography $TARGET") @@ -64,7 +64,7 @@ typesetter. 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 &cv-MAKEINDEXCOM; (the command line) is displayed. +If this is not set, then &cv-link-MAKEINDEXCOM; (the command line) is displayed. @@ -91,7 +91,7 @@ The command line used to call the TeX formatter and typesetter. The string displayed when calling the TeX formatter and typesetter. -If this is not set, then &cv-TEXCOM; (the command line) is displayed. +If this is not set, then &cv-link-TEXCOM; (the command line) is displayed. env = Environment(TEXCOMSTR = "Building $TARGET from TeX input $SOURCES") diff --git a/src/engine/SCons/Tool/yacc.xml b/src/engine/SCons/Tool/yacc.xml index 8a23d0b..a6ad9b4 100644 --- a/src/engine/SCons/Tool/yacc.xml +++ b/src/engine/SCons/Tool/yacc.xml @@ -27,7 +27,7 @@ to generate a source file. The string displayed when generating a source file using the parser generator. -If this is not set, then &cv-YACCCOM; (the command line) is displayed. +If this is not set, then &cv-link-YACCCOM; (the command line) is displayed. env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES") @@ -38,7 +38,7 @@ env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES") General options passed to the parser generator. -If &cv-YACCFLAGS; contains a option, +If &cv-link-YACCFLAGS; contains a 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 diff --git a/src/engine/SCons/Tool/zip.xml b/src/engine/SCons/Tool/zip.xml index 5e28bee..909a4c4 100644 --- a/src/engine/SCons/Tool/zip.xml +++ b/src/engine/SCons/Tool/zip.xml @@ -58,7 +58,7 @@ zip archive. The string displayed when archiving files using the zip utility. -If this is not set, then &cv-ZIPCOM; +If this is not set, then &cv-link-ZIPCOM; (the command line or internal Python function) is displayed. diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index eb38e44..f1e856b 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -367,6 +367,12 @@ def is_List(obj): return t is ListType \ or (t is InstanceType and isinstance(obj, UserList)) +def is_Sequence(obj): + t = type(obj) + return t is ListType \ + or t is TupleType \ + or (t is InstanceType and isinstance(obj, UserList)) + def is_Tuple(obj): t = type(obj) return t is TupleType diff --git a/src/engine/SCons/compat/_UserString.py b/src/engine/SCons/compat/_UserString.py deleted file mode 100644 index 4adffa2..0000000 --- a/src/engine/SCons/compat/_UserString.py +++ /dev/null @@ -1,92 +0,0 @@ -# -# __COPYRIGHT__ -# -# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -__doc__ = """ -A user-defined wrapper around string objects - -This class is "borrowed" from the Python 2.2 UserString and modified -slightly for use with SCons. It is *NOT* guaranteed to be fully compliant -with the standard UserString class from all later versions of Python. -In particular, it does not necessarily contain all of the methods found -in later versions. -""" - -import types - -StringType = types.StringType - -if hasattr(types, 'UnicodeType'): - UnicodeType = types.UnicodeType - def is_String(obj): - return type(obj) in (StringType, UnicodeType) -else: - def is_String(obj): - return type(obj) is StringType - -class UserString: - def __init__(self, seq): - if is_String(seq): - self.data = seq - elif isinstance(seq, UserString): - self.data = seq.data[:] - else: - self.data = str(seq) - def __str__(self): return str(self.data) - def __repr__(self): return repr(self.data) - def __int__(self): return int(self.data) - def __long__(self): return long(self.data) - def __float__(self): return float(self.data) - def __complex__(self): return complex(self.data) - def __hash__(self): return hash(self.data) - - def __cmp__(self, string): - if isinstance(string, UserString): - return cmp(self.data, string.data) - else: - return cmp(self.data, string) - def __contains__(self, char): - return char in self.data - - def __len__(self): return len(self.data) - def __getitem__(self, index): return self.__class__(self.data[index]) - def __getslice__(self, start, end): - start = max(start, 0); end = max(end, 0) - return self.__class__(self.data[start:end]) - - def __add__(self, other): - if isinstance(other, UserString): - return self.__class__(self.data + other.data) - elif is_String(other): - return self.__class__(self.data + other) - else: - return self.__class__(self.data + str(other)) - def __radd__(self, other): - if is_String(other): - return self.__class__(other + self.data) - else: - return self.__class__(str(other) + self.data) - def __mul__(self, n): - return self.__class__(self.data*n) - __rmul__ = __mul__ diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py index 7b74ab4..959bf52 100644 --- a/src/engine/SCons/compat/__init__.py +++ b/src/engine/SCons/compat/__init__.py @@ -55,10 +55,10 @@ 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 underscore (for -example, _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() +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 underscore), after which all of the "import {module}" statements in the rest of our code will find our pre-loaded compatibility module. @@ -86,14 +86,14 @@ except NameError: try: # Python 2.2 and 2.3 can use the copy of the 2.[45] sets module # that we grabbed. - import_as('_sets', 'sets') + import_as('_scons_sets', 'sets') except (ImportError, SyntaxError): # Python 1.5 (ImportError, no __future_ module) and 2.1 # (SyntaxError, no generators in __future__) will blow up # trying to import the 2.[45] sets module, so back off to a # custom sets module that can be discarded easily when we # stop supporting those versions. - import_as('_sets15', 'sets') + import_as('_scons_sets15', 'sets') import __builtin__ import sets __builtin__.set = sets.Set @@ -102,10 +102,10 @@ try: import subprocess except ImportError: # Pre-2.4 Python has no subprocess module. - import_as('_subprocess', 'subprocess') + import_as('_scons_subprocess', 'subprocess') try: import UserString except ImportError: # Pre-1.6 Python has no UserString module. - import_as('_UserString', 'UserString') + import_as('_scons_UserString', 'UserString') diff --git a/src/engine/SCons/compat/_scons_UserString.py b/src/engine/SCons/compat/_scons_UserString.py new file mode 100644 index 0000000..4adffa2 --- /dev/null +++ b/src/engine/SCons/compat/_scons_UserString.py @@ -0,0 +1,92 @@ +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +__doc__ = """ +A user-defined wrapper around string objects + +This class is "borrowed" from the Python 2.2 UserString and modified +slightly for use with SCons. It is *NOT* guaranteed to be fully compliant +with the standard UserString class from all later versions of Python. +In particular, it does not necessarily contain all of the methods found +in later versions. +""" + +import types + +StringType = types.StringType + +if hasattr(types, 'UnicodeType'): + UnicodeType = types.UnicodeType + def is_String(obj): + return type(obj) in (StringType, UnicodeType) +else: + def is_String(obj): + return type(obj) is StringType + +class UserString: + def __init__(self, seq): + if is_String(seq): + self.data = seq + elif isinstance(seq, UserString): + self.data = seq.data[:] + else: + self.data = str(seq) + def __str__(self): return str(self.data) + def __repr__(self): return repr(self.data) + def __int__(self): return int(self.data) + def __long__(self): return long(self.data) + def __float__(self): return float(self.data) + def __complex__(self): return complex(self.data) + def __hash__(self): return hash(self.data) + + def __cmp__(self, string): + if isinstance(string, UserString): + return cmp(self.data, string.data) + else: + return cmp(self.data, string) + def __contains__(self, char): + return char in self.data + + def __len__(self): return len(self.data) + def __getitem__(self, index): return self.__class__(self.data[index]) + def __getslice__(self, start, end): + start = max(start, 0); end = max(end, 0) + return self.__class__(self.data[start:end]) + + def __add__(self, other): + if isinstance(other, UserString): + return self.__class__(self.data + other.data) + elif is_String(other): + return self.__class__(self.data + other) + else: + return self.__class__(self.data + str(other)) + def __radd__(self, other): + if is_String(other): + return self.__class__(other + self.data) + else: + return self.__class__(str(other) + self.data) + def __mul__(self, n): + return self.__class__(self.data*n) + __rmul__ = __mul__ diff --git a/src/engine/SCons/compat/_scons_sets.py b/src/engine/SCons/compat/_scons_sets.py new file mode 100644 index 0000000..32a0dd6 --- /dev/null +++ b/src/engine/SCons/compat/_scons_sets.py @@ -0,0 +1,577 @@ +"""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. + +from __future__ import generators +try: + from itertools import ifilter, ifilterfalse +except ImportError: + # Code to make the module run under Py2.2 + def ifilter(predicate, iterable): + if predicate is None: + def predicate(x): + return x + for x in iterable: + if predicate(x): + yield x + def ifilterfalse(predicate, iterable): + if predicate is None: + def predicate(x): + return x + for x in iterable: + if not predicate(x): + yield x + try: + True, False + except NameError: + True, False = (0==0, 0!=0) + +__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, sorted=False): + elements = self._data.keys() + if sorted: + 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. + """ + 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 = ifilter(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 ifilterfalse(otherdata.has_key, selfdata): + data[elt] = value + for elt in ifilterfalse(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 ifilterfalse(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 ifilterfalse(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 ifilterfalse(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 ifilter(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() diff --git a/src/engine/SCons/compat/_scons_sets15.py b/src/engine/SCons/compat/_scons_sets15.py new file mode 100644 index 0000000..b3d0bb3 --- /dev/null +++ b/src/engine/SCons/compat/_scons_sets15.py @@ -0,0 +1,159 @@ +# +# A Set class that works all the way back to Python 1.5. From: +# +# Python Cookbook: Yet another Set class for Python +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/106469 +# Goncalo Rodriques +# +# This is a pure Pythonic implementation of a set class. The syntax +# and methods implemented are, for the most part, borrowed from +# PEP 218 by Greg Wilson. +# +# Note that this class violates the formal definition of a set() by adding +# a __getitem__() method so we can iterate over a set's elements under +# Python 1.5 and 2.1, which don't support __iter__() and iterator types. +# + +import string + +class Set: + """The set class. It can contain mutable objects.""" + + def __init__(self, seq = None): + """The constructor. It can take any object giving an iterator as an optional + argument to populate the new set.""" + self.elems = [] + if seq: + for elem in seq: + if elem not in self.elems: + self.elems.append(elem) + + def __str__(self): + return "{%s}" % string.join(map(str, self.elems), ", ") + + + def copy(self): + """Shallow copy of a set object.""" + return Set(self.elems) + + def __contains__(self, elem): + return elem in self.elems + + def __len__(self): + return len(self.elems) + + def __getitem__(self, index): + # Added so that Python 1.5 can iterate over the elements. + # The cookbook recipe's author didn't like this because there + # really isn't any order in a set object, but this is necessary + # to make the class work well enough for our purposes. + return self.elems[index] + + def items(self): + """Returns a list of the elements in the set.""" + return self.elems + + def add(self, elem): + """Add one element to the set.""" + if elem not in self.elems: + self.elems.append(elem) + + def remove(self, elem): + """Remove an element from the set. Return an error if elem is not in the set.""" + try: + self.elems.remove(elem) + except ValueError: + raise LookupError, "Object %s is not a member of the set." % str(elem) + + def discard(self, elem): + """Remove an element from the set. Do nothing if elem is not in the set.""" + try: + self.elems.remove(elem) + except ValueError: + pass + + def sort(self, func=cmp): + self.elems.sort(func) + + #Define an iterator for a set. + def __iter__(self): + return iter(self.elems) + + #The basic binary operations with sets. + def __or__(self, other): + """Union of two sets.""" + ret = self.copy() + for elem in other.elems: + if elem not in ret: + ret.elems.append(elem) + return ret + + def __sub__(self, other): + """Difference of two sets.""" + ret = self.copy() + for elem in other.elems: + ret.discard(elem) + return ret + + def __and__(self, other): + """Intersection of two sets.""" + ret = Set() + for elem in self.elems: + if elem in other.elems: + ret.elems.append(elem) + return ret + + def __add__(self, other): + """Symmetric difference of two sets.""" + ret = Set() + temp = other.copy() + for elem in self.elems: + if elem in temp.elems: + temp.elems.remove(elem) + else: + ret.elems.append(elem) + #Add remaining elements. + for elem in temp.elems: + ret.elems.append(elem) + return ret + + def __mul__(self, other): + """Cartesian product of two sets.""" + ret = Set() + for elemself in self.elems: + x = map(lambda other, s=elemself: (s, other), other.elems) + ret.elems.extend(x) + return ret + + #Some of the binary comparisons. + def __lt__(self, other): + """Returns 1 if the lhs set is contained but not equal to the rhs set.""" + if len(self.elems) < len(other.elems): + temp = other.copy() + for elem in self.elems: + if elem in temp.elems: + temp.remove(elem) + else: + return 0 + return len(temp.elems) == 0 + else: + return 0 + + def __le__(self, other): + """Returns 1 if the lhs set is contained in the rhs set.""" + if len(self.elems) <= len(other.elems): + ret = 1 + for elem in self.elems: + if elem not in other.elems: + ret = 0 + break + return ret + else: + return 0 + + def __eq__(self, other): + """Returns 1 if the sets are equal.""" + if len(self.elems) != len(other.elems): + return 0 + else: + return len(self - other) == 0 diff --git a/src/engine/SCons/compat/_scons_subprocess.py b/src/engine/SCons/compat/_scons_subprocess.py new file mode 100644 index 0000000..df7e702 --- /dev/null +++ b/src/engine/SCons/compat/_scons_subprocess.py @@ -0,0 +1,1280 @@ +# 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 string +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: + import threading + import msvcrt + if 0: # <-- change this to use pywin32 instead of the _subprocess driver + 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: + from _subprocess import * + class STARTUPINFO: + dwFlags = 0 + hStdInput = None + hStdOutput = None + hStdError = None + wShowWindow = 0 + class pywintypes: + error = IOError +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 + +# True/False does not exist on 2.2.0 +try: + False +except NameError: + False = 0 + True = 1 + +try: + isinstance(1, int) +except TypeError: + def is_int(obj): + return type(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 = (types.StringType, types.UnicodeType) + except AttributeError: + types.StringTypes = (types.StringType,) + def is_string(obj): + return type(obj) in types.StringTypes +else: + def is_string(obj): + return isinstance(obj, types.StringTypes) + +_active = [] + +def _cleanup(): + for inst in _active[:]: + if inst.poll(_deadstate=sys.maxint) >= 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 = apply(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 string.join(result, '') + + +try: + object +except NameError: + class object: + pass + +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.maxint) + 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 apply(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 xrange(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 = string.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 + bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512)) + 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 = string.join(stdout, '') + if stderr is not None: + stderr = string.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() diff --git a/src/engine/SCons/compat/_sets.py b/src/engine/SCons/compat/_sets.py deleted file mode 100644 index 32a0dd6..0000000 --- a/src/engine/SCons/compat/_sets.py +++ /dev/null @@ -1,577 +0,0 @@ -"""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. - -from __future__ import generators -try: - from itertools import ifilter, ifilterfalse -except ImportError: - # Code to make the module run under Py2.2 - def ifilter(predicate, iterable): - if predicate is None: - def predicate(x): - return x - for x in iterable: - if predicate(x): - yield x - def ifilterfalse(predicate, iterable): - if predicate is None: - def predicate(x): - return x - for x in iterable: - if not predicate(x): - yield x - try: - True, False - except NameError: - True, False = (0==0, 0!=0) - -__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, sorted=False): - elements = self._data.keys() - if sorted: - 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. - """ - 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 = ifilter(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 ifilterfalse(otherdata.has_key, selfdata): - data[elt] = value - for elt in ifilterfalse(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 ifilterfalse(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 ifilterfalse(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 ifilterfalse(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 ifilter(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() diff --git a/src/engine/SCons/compat/_sets15.py b/src/engine/SCons/compat/_sets15.py deleted file mode 100644 index b3d0bb3..0000000 --- a/src/engine/SCons/compat/_sets15.py +++ /dev/null @@ -1,159 +0,0 @@ -# -# A Set class that works all the way back to Python 1.5. From: -# -# Python Cookbook: Yet another Set class for Python -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/106469 -# Goncalo Rodriques -# -# This is a pure Pythonic implementation of a set class. The syntax -# and methods implemented are, for the most part, borrowed from -# PEP 218 by Greg Wilson. -# -# Note that this class violates the formal definition of a set() by adding -# a __getitem__() method so we can iterate over a set's elements under -# Python 1.5 and 2.1, which don't support __iter__() and iterator types. -# - -import string - -class Set: - """The set class. It can contain mutable objects.""" - - def __init__(self, seq = None): - """The constructor. It can take any object giving an iterator as an optional - argument to populate the new set.""" - self.elems = [] - if seq: - for elem in seq: - if elem not in self.elems: - self.elems.append(elem) - - def __str__(self): - return "{%s}" % string.join(map(str, self.elems), ", ") - - - def copy(self): - """Shallow copy of a set object.""" - return Set(self.elems) - - def __contains__(self, elem): - return elem in self.elems - - def __len__(self): - return len(self.elems) - - def __getitem__(self, index): - # Added so that Python 1.5 can iterate over the elements. - # The cookbook recipe's author didn't like this because there - # really isn't any order in a set object, but this is necessary - # to make the class work well enough for our purposes. - return self.elems[index] - - def items(self): - """Returns a list of the elements in the set.""" - return self.elems - - def add(self, elem): - """Add one element to the set.""" - if elem not in self.elems: - self.elems.append(elem) - - def remove(self, elem): - """Remove an element from the set. Return an error if elem is not in the set.""" - try: - self.elems.remove(elem) - except ValueError: - raise LookupError, "Object %s is not a member of the set." % str(elem) - - def discard(self, elem): - """Remove an element from the set. Do nothing if elem is not in the set.""" - try: - self.elems.remove(elem) - except ValueError: - pass - - def sort(self, func=cmp): - self.elems.sort(func) - - #Define an iterator for a set. - def __iter__(self): - return iter(self.elems) - - #The basic binary operations with sets. - def __or__(self, other): - """Union of two sets.""" - ret = self.copy() - for elem in other.elems: - if elem not in ret: - ret.elems.append(elem) - return ret - - def __sub__(self, other): - """Difference of two sets.""" - ret = self.copy() - for elem in other.elems: - ret.discard(elem) - return ret - - def __and__(self, other): - """Intersection of two sets.""" - ret = Set() - for elem in self.elems: - if elem in other.elems: - ret.elems.append(elem) - return ret - - def __add__(self, other): - """Symmetric difference of two sets.""" - ret = Set() - temp = other.copy() - for elem in self.elems: - if elem in temp.elems: - temp.elems.remove(elem) - else: - ret.elems.append(elem) - #Add remaining elements. - for elem in temp.elems: - ret.elems.append(elem) - return ret - - def __mul__(self, other): - """Cartesian product of two sets.""" - ret = Set() - for elemself in self.elems: - x = map(lambda other, s=elemself: (s, other), other.elems) - ret.elems.extend(x) - return ret - - #Some of the binary comparisons. - def __lt__(self, other): - """Returns 1 if the lhs set is contained but not equal to the rhs set.""" - if len(self.elems) < len(other.elems): - temp = other.copy() - for elem in self.elems: - if elem in temp.elems: - temp.remove(elem) - else: - return 0 - return len(temp.elems) == 0 - else: - return 0 - - def __le__(self, other): - """Returns 1 if the lhs set is contained in the rhs set.""" - if len(self.elems) <= len(other.elems): - ret = 1 - for elem in self.elems: - if elem not in other.elems: - ret = 0 - break - return ret - else: - return 0 - - def __eq__(self, other): - """Returns 1 if the sets are equal.""" - if len(self.elems) != len(other.elems): - return 0 - else: - return len(self - other) == 0 diff --git a/src/engine/SCons/compat/_subprocess.py b/src/engine/SCons/compat/_subprocess.py deleted file mode 100644 index fc06347..0000000 --- a/src/engine/SCons/compat/_subprocess.py +++ /dev/null @@ -1,1268 +0,0 @@ -# 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 string -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: - import threading - import msvcrt - if 0: # <-- change this to use pywin32 instead of the _subprocess driver - 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: - from _subprocess import * - class STARTUPINFO: - dwFlags = 0 - hStdInput = None - hStdOutput = None - hStdError = None - wShowWindow = 0 - class pywintypes: - error = IOError -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 - -# True/False does not exist on 2.2.0 -try: - False -except NameError: - False = 0 - True = 1 - -try: - isinstance(1, int) -except TypeError: - def is_int(obj): - return type(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 = (types.StringType, types.UnicodeType) - except AttributeError: - types.StringTypes = (types.StringType,) - def is_string(obj): - return type(obj) in types.StringTypes -else: - def is_string(obj): - return isinstance(obj, types.StringTypes) - -_active = [] - -def _cleanup(): - for inst in _active[:]: - if inst.poll(_deadstate=sys.maxint) >= 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 = apply(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 string.join(result, '') - - -try: - object -except NameError: - class object: - pass - -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.maxint) - 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 apply(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 xrange(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. - 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 = string.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 - bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512)) - 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 = string.join(stdout, '') - if stderr is not None: - stderr = string.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() diff --git a/src/script/scons.bat b/src/script/scons.bat index c0a304d..f927bb1 100644 --- a/src/script/scons.bat +++ b/src/script/scons.bat @@ -7,7 +7,7 @@ python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, ' @REM no way to set exit status of this script for 9x/Me goto endscons :WinNT -set path=%path%;%~dp0 +@REM set path=%path%;%~dp0 python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-__VERSION__'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-__VERSION__'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %* if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endscons if errorlevel 9009 echo you do not have python in your PATH diff --git a/src/setup.cfg b/src/setup.cfg index 4b097ac..a234e22 100644 --- a/src/setup.cfg +++ b/src/setup.cfg @@ -3,4 +3,4 @@ group = Development/Tools [bdist_wininst] title = SCons - a software construction tool -install-script = scons-post-install.py +#install-script = scons-post-install.py diff --git a/src/setup.py b/src/setup.py index 53b19fa..d79aed7 100644 --- a/src/setup.py +++ b/src/setup.py @@ -29,13 +29,7 @@ import stat import string import sys -Version = "0.96.94" - -man_pages = [ - 'scons.1', - 'sconsign.1', - 'scons-time.1', -] +Version = "0.96.96" man_pages = [ 'scons.1', @@ -367,10 +361,10 @@ scripts = [ 'script/scons.bat', ] -if is_win32: - scripts = scripts + [ - 'script/scons-post-install.py' - ] +#if is_win32: +# scripts = scripts + [ +# 'script/scons-post-install.py' +# ] arguments = { 'name' : "scons", diff --git a/src/test_strings.py b/src/test_strings.py index b609f59..a3ed6e8 100644 --- a/src/test_strings.py +++ b/src/test_strings.py @@ -54,18 +54,28 @@ build_local = build_path('scons-local', 'scons-local-'+scons_version) build_src = build_path('scons-src') class Checker: - def __init__(self, directory, search_list = [], remove_list=[]): + def __init__(self, directory, + search_list = [], + remove_list = [], + remove_patterns = []): self.directory = directory self.search_list = search_list self.remove_dict = {} for r in remove_list: self.remove_dict[os.path.join(directory, r)] = 1 + self.remove_patterns = remove_patterns def directory_exists(self): return os.path.exists(self.directory) - def remove_path(self, path): - return self.remove_dict.get(path) + def remove_this(self, name, path): + if self.remove_dict.get(path): + return 1 + else: + for pattern in self.remove_patterns: + if fnmatch.fnmatch(name, pattern): + return 1 + return 0 def search_this(self, path): if self.search_list: @@ -79,7 +89,7 @@ class Checker: def visit(self, result, dirname, names): make_path_tuple = lambda n, d=dirname: (n, os.path.join(d, n)) for name, path in map(make_path_tuple, names): - if self.remove_path(path): + if self.remove_this(name, path): names.remove(name) elif self.search_this(path): body = open(path, 'r').read() @@ -114,9 +124,9 @@ check_list = [ 'src', search_list = [ '*.py' ], remove_list = [ - 'engine/SCons/compat/_sets.py', - 'engine/SCons/compat/_sets15.py', - 'engine/SCons/compat/_subprocess.py', + 'engine/SCons/compat/_scons_sets.py', + 'engine/SCons/compat/_scons_sets15.py', + 'engine/SCons/compat/_scons_subprocess.py', 'engine/SCons/Conftest.py', 'engine/SCons/dblite.py', 'engine/SCons/Optik', @@ -137,9 +147,9 @@ check_list = [ 'debian', 'dist', 'gentoo', - 'engine/SCons/compat/_sets.py', - 'engine/SCons/compat/_sets15.py', - 'engine/SCons/compat/_subprocess.py', + 'engine/SCons/compat/_scons_sets.py', + 'engine/SCons/compat/_scons_sets15.py', + 'engine/SCons/compat/_scons_subprocess.py', 'engine/SCons/Conftest.py', 'engine/SCons/dblite.py', 'engine/SCons/Optik', @@ -147,14 +157,18 @@ check_list = [ 'os_spawnv_fix.diff', 'setup.cfg', ], + # We run epydoc on the *.py files, which generates *.pyc files. + remove_patterns = [ + '*.pyc' + ] ), CheckExpandedCopyright( build_local, remove_list = [ - 'SCons/compat/_sets.py', - 'SCons/compat/_sets15.py', - 'SCons/compat/_subprocess.py', + 'SCons/compat/_scons_sets.py', + 'SCons/compat/_scons_sets15.py', + 'SCons/compat/_scons_subprocess.py', 'SCons/Conftest.py', 'SCons/dblite.py', 'SCons/Optik', @@ -192,9 +206,9 @@ check_list = [ 'src/engine/MANIFEST.in', 'src/engine/MANIFEST-xml.in', 'src/engine/setup.cfg', - 'src/engine/SCons/compat/_sets.py', - 'src/engine/SCons/compat/_sets15.py', - 'src/engine/SCons/compat/_subprocess.py', + 'src/engine/SCons/compat/_scons_sets.py', + 'src/engine/SCons/compat/_scons_sets15.py', + 'src/engine/SCons/compat/_scons_subprocess.py', 'src/engine/SCons/Conftest.py', 'src/engine/SCons/dblite.py', 'src/engine/SCons/Optik', diff --git a/test/CPPDEFINES.py b/test/CPPDEFINES.py index 61aaecd..c38f857 100644 --- a/test/CPPDEFINES.py +++ b/test/CPPDEFINES.py @@ -50,13 +50,13 @@ test.write('SConstruct', """\ test_list = [ 'xyz', ['x', 'y', 'z'], - ['x', ['y', 123], 'z'], + ['x', ['y', 123], 'z', ('int', '$INTEGER')], { 'c' : 3, 'b': None, 'a' : 1 }, ] -env = Environment(CPPDEFPREFIX='-D', CPPDEFSUFFIX='') +env = Environment(CPPDEFPREFIX='-D', CPPDEFSUFFIX='', INTEGER=0) for i in test_list: print env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS') -env = Environment(CPPDEFPREFIX='|', CPPDEFSUFFIX='|') +env = Environment(CPPDEFPREFIX='|', CPPDEFSUFFIX='|', INTEGER=1) for i in test_list: print env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS') """) @@ -65,18 +65,18 @@ expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n", read_str = """\ -Dxyz -Dx -Dy -Dz --Dx -Dy=123 -Dz +-Dx -Dy=123 -Dz -Dint=0 -Da=1 -Db -Dc=3 |xyz| |x| |y| |z| -|x| |y=123| |z| +|x| |y=123| |z| |int=1| |a=1| |b| |c=3| """) test.run(arguments = '.', stdout=expect) test.write('SConstruct', """\ -foo = Environment(CPPDEFINES = ['FOO', ('VAL', 7)]) +foo = Environment(CPPDEFINES = ['FOO', ('VAL', '$VALUE')], VALUE=7) bar = Environment(CPPDEFINES = {'BAR':None, 'VAL':8}) baz = Environment(CPPDEFINES = ['BAZ', ('VAL', 9)]) f = foo.Object(target = 'foo', source = 'prog.c') diff --git a/test/CPPPATH.py b/test/CPPPATH.py deleted file mode 100644 index c9400f7..0000000 --- a/test/CPPPATH.py +++ /dev/null @@ -1,325 +0,0 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os -import sys -import TestSCons - -_exe = TestSCons._exe - -prog = 'prog' + _exe -subdir_prog = os.path.join('subdir', 'prog' + _exe) -variant_prog = os.path.join('variant', 'prog' + _exe) - -args = prog + ' ' + subdir_prog + ' ' + variant_prog - -test = TestSCons.TestSCons() - -test.subdir('foobar', - 'include', - 'subdir', - ['subdir', 'include'], - 'inc2') - -test.write('SConstruct', """ -env = Environment(CPPPATH = ['$FOO', '${TARGET.dir}', '${SOURCE.dir}'], - FOO='include') -obj = env.Object(target='foobar/prog', source='subdir/prog.c') -env.Program(target='prog', source=obj) -SConscript('subdir/SConscript', "env") - -BuildDir('variant', 'subdir', 0) -include = Dir('include') -env = Environment(CPPPATH=[include, '#foobar', '#subdir']) -SConscript('variant/SConscript', "env") -""") - -test.write(['subdir', 'SConscript'], -""" -Import("env") -env.Program(target='prog', source='prog.c') -""") - -test.write(['include', 'foo.h'], -r""" -#define FOO_STRING "include/foo.h 1\n" -#include -""") - -test.write(['include', 'bar.h'], -r""" -#define BAR_STRING "include/bar.h 1\n" -""") - -test.write(['subdir', 'sss.h'], -r""" -#define SSS_STRING "subdir/sss.h\n" -""") - -test.write(['foobar', 'ttt.h'], -r""" -#define TTT_STRING "foobar/ttt.h\n" -""") - -test.write(['subdir', 'ttt.h'], -r""" -#define TTT_STRING "subdir/ttt.h\n" -""") - -test.write(['subdir', 'prog.c'], -r""" -#include -#include -#include -#include - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("subdir/prog.c\n"); - printf(FOO_STRING); - printf(BAR_STRING); - printf(SSS_STRING); - printf(TTT_STRING); - return 0; -} -""") - -test.write(['subdir', 'include', 'foo.h'], -r""" -#define FOO_STRING "subdir/include/foo.h 1\n" -#include "bar.h" -""") - -test.write(['subdir', 'include', 'bar.h'], -r""" -#define BAR_STRING "subdir/include/bar.h 1\n" -""") - - - -test.run(arguments = args) - -test.run(program = test.workpath(prog), - stdout = """\ -subdir/prog.c -include/foo.h 1 -include/bar.h 1 -subdir/sss.h -foobar/ttt.h -""") - -test.run(program = test.workpath(subdir_prog), - stdout = """\ -subdir/prog.c -subdir/include/foo.h 1 -subdir/include/bar.h 1 -subdir/sss.h -subdir/ttt.h -""") - -test.run(program = test.workpath(variant_prog), - stdout = """\ -subdir/prog.c -include/foo.h 1 -include/bar.h 1 -subdir/sss.h -foobar/ttt.h -""") - - - -# Make sure we didn't duplicate the source file in the variant subdirectory. -test.fail_test(os.path.exists(test.workpath('variant', 'prog.c'))) - -test.up_to_date(arguments = args) - -test.write(['include', 'foo.h'], -r""" -#define FOO_STRING "include/foo.h 2\n" -#include "bar.h" -""") - -test.run(arguments = args) - -test.run(program = test.workpath(prog), - stdout = """\ -subdir/prog.c -include/foo.h 2 -include/bar.h 1 -subdir/sss.h -foobar/ttt.h -""") - -test.run(program = test.workpath(subdir_prog), - stdout = """\ -subdir/prog.c -subdir/include/foo.h 1 -subdir/include/bar.h 1 -subdir/sss.h -subdir/ttt.h -""") - -test.run(program = test.workpath(variant_prog), - stdout = """\ -subdir/prog.c -include/foo.h 2 -include/bar.h 1 -subdir/sss.h -foobar/ttt.h -""") - - - -# Make sure we didn't duplicate the source file in the variant subdirectory. -test.fail_test(os.path.exists(test.workpath('variant', 'prog.c'))) - -test.up_to_date(arguments = args) - - - -# -test.write(['include', 'bar.h'], -r""" -#define BAR_STRING "include/bar.h 2\n" -""") - -test.run(arguments = args) - -test.run(program = test.workpath(prog), - stdout = """\ -subdir/prog.c -include/foo.h 2 -include/bar.h 2 -subdir/sss.h -foobar/ttt.h -""") - -test.run(program = test.workpath(subdir_prog), - stdout = """\ -subdir/prog.c -subdir/include/foo.h 1 -subdir/include/bar.h 1 -subdir/sss.h -subdir/ttt.h -""") - -test.run(program = test.workpath(variant_prog), - stdout = """\ -subdir/prog.c -include/foo.h 2 -include/bar.h 2 -subdir/sss.h -foobar/ttt.h -""") - -# Make sure we didn't duplicate the source file in the variant subdirectory. -test.fail_test(os.path.exists(test.workpath('variant', 'prog.c'))) - -test.up_to_date(arguments = args) - - - -# Change CPPPATH and make sure we don't rebuild because of it. -test.write('SConstruct', """ -env = Environment(CPPPATH = Split('inc2 include ${TARGET.dir} ${SOURCE.dir}')) -obj = env.Object(target='foobar/prog', source='subdir/prog.c') -env.Program(target='prog', source=obj) -SConscript('subdir/SConscript', "env") - -BuildDir('variant', 'subdir', 0) -include = Dir('include') -env = Environment(CPPPATH=['inc2', include, '#foobar', '#subdir']) -SConscript('variant/SConscript', "env") -""") - -test.up_to_date(arguments = args) - - - -# -test.write(['inc2', 'foo.h'], -r""" -#define FOO_STRING "inc2/foo.h 1\n" -#include -""") - -test.run(arguments = args) - -test.run(program = test.workpath(prog), - stdout = """\ -subdir/prog.c -inc2/foo.h 1 -include/bar.h 2 -subdir/sss.h -foobar/ttt.h -""") - -test.run(program = test.workpath(subdir_prog), - stdout = """\ -subdir/prog.c -subdir/include/foo.h 1 -subdir/include/bar.h 1 -subdir/sss.h -subdir/ttt.h -""") - -test.run(program = test.workpath(variant_prog), - stdout = """\ -subdir/prog.c -include/foo.h 2 -include/bar.h 2 -subdir/sss.h -foobar/ttt.h -""") - -test.up_to_date(arguments = args) - - - -# Check that neither a null-string CPPPATH nor a -# a CPPPATH containing null values blows up. -test.write('SConstruct', """ -env = Environment(CPPPATH = '') -env.Library('one', source = 'empty1.c') -env = Environment(CPPPATH = [None]) -env.Library('two', source = 'empty2.c') -env = Environment(CPPPATH = ['']) -env.Library('three', source = 'empty3.c') -""") - -test.write('empty1.c', "int a=0;\n") -test.write('empty2.c', "int b=0;\n") -test.write('empty3.c', "int c=0;\n") - -test.run(arguments = '.', - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) - - - -test.pass_test() diff --git a/test/CPPPATH/CPPPATH.py b/test/CPPPATH/CPPPATH.py new file mode 100644 index 0000000..80b4aa7 --- /dev/null +++ b/test/CPPPATH/CPPPATH.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os.path + +import TestSCons + +_exe = TestSCons._exe + +prog = 'prog' + _exe +subdir_prog = os.path.join('subdir', 'prog' + _exe) +variant_prog = os.path.join('variant', 'prog' + _exe) + +args = prog + ' ' + subdir_prog + ' ' + variant_prog + +test = TestSCons.TestSCons() + +test.subdir('foobar', + 'include', + 'subdir', + ['subdir', 'include'], + 'inc2') + +test.write('SConstruct', """ +env = Environment(CPPPATH = ['$FOO', '${TARGET.dir}', '${SOURCE.dir}'], + FOO='include') +obj = env.Object(target='foobar/prog', source='subdir/prog.c') +env.Program(target='prog', source=obj) +SConscript('subdir/SConscript', "env") + +BuildDir('variant', 'subdir', 0) +include = Dir('include') +env = Environment(CPPPATH=[include, '#foobar', '#subdir']) +SConscript('variant/SConscript', "env") +""") + +test.write(['subdir', 'SConscript'], +""" +Import("env") +env.Program(target='prog', source='prog.c') +""") + +test.write(['include', 'foo.h'], +r""" +#define FOO_STRING "include/foo.h 1\n" +#include +""") + +test.write(['include', 'bar.h'], +r""" +#define BAR_STRING "include/bar.h 1\n" +""") + +test.write(['subdir', 'sss.h'], +r""" +#define SSS_STRING "subdir/sss.h\n" +""") + +test.write(['foobar', 'ttt.h'], +r""" +#define TTT_STRING "foobar/ttt.h\n" +""") + +test.write(['subdir', 'ttt.h'], +r""" +#define TTT_STRING "subdir/ttt.h\n" +""") + +test.write(['subdir', 'prog.c'], +r""" +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("subdir/prog.c\n"); + printf(FOO_STRING); + printf(BAR_STRING); + printf(SSS_STRING); + printf(TTT_STRING); + return 0; +} +""") + +test.write(['subdir', 'include', 'foo.h'], +r""" +#define FOO_STRING "subdir/include/foo.h 1\n" +#include "bar.h" +""") + +test.write(['subdir', 'include', 'bar.h'], +r""" +#define BAR_STRING "subdir/include/bar.h 1\n" +""") + + + +test.run(arguments = args) + +test.run(program = test.workpath(prog), + stdout = """\ +subdir/prog.c +include/foo.h 1 +include/bar.h 1 +subdir/sss.h +foobar/ttt.h +""") + +test.run(program = test.workpath(subdir_prog), + stdout = """\ +subdir/prog.c +subdir/include/foo.h 1 +subdir/include/bar.h 1 +subdir/sss.h +subdir/ttt.h +""") + +test.run(program = test.workpath(variant_prog), + stdout = """\ +subdir/prog.c +include/foo.h 1 +include/bar.h 1 +subdir/sss.h +foobar/ttt.h +""") + + + +# Make sure we didn't duplicate the source file in the variant subdirectory. +test.must_not_exist(test.workpath('variant', 'prog.c')) + +test.up_to_date(arguments = args) + +test.write(['include', 'foo.h'], +r""" +#define FOO_STRING "include/foo.h 2\n" +#include "bar.h" +""") + +test.run(arguments = args) + +test.run(program = test.workpath(prog), + stdout = """\ +subdir/prog.c +include/foo.h 2 +include/bar.h 1 +subdir/sss.h +foobar/ttt.h +""") + +test.run(program = test.workpath(subdir_prog), + stdout = """\ +subdir/prog.c +subdir/include/foo.h 1 +subdir/include/bar.h 1 +subdir/sss.h +subdir/ttt.h +""") + +test.run(program = test.workpath(variant_prog), + stdout = """\ +subdir/prog.c +include/foo.h 2 +include/bar.h 1 +subdir/sss.h +foobar/ttt.h +""") + + + +# Make sure we didn't duplicate the source file in the variant subdirectory. +test.must_not_exist(test.workpath('variant', 'prog.c')) + +test.up_to_date(arguments = args) + + + +# +test.write(['include', 'bar.h'], +r""" +#define BAR_STRING "include/bar.h 2\n" +""") + +test.run(arguments = args) + +test.run(program = test.workpath(prog), + stdout = """\ +subdir/prog.c +include/foo.h 2 +include/bar.h 2 +subdir/sss.h +foobar/ttt.h +""") + +test.run(program = test.workpath(subdir_prog), + stdout = """\ +subdir/prog.c +subdir/include/foo.h 1 +subdir/include/bar.h 1 +subdir/sss.h +subdir/ttt.h +""") + +test.run(program = test.workpath(variant_prog), + stdout = """\ +subdir/prog.c +include/foo.h 2 +include/bar.h 2 +subdir/sss.h +foobar/ttt.h +""") + +# Make sure we didn't duplicate the source file in the variant subdirectory. +test.must_not_exist(test.workpath('variant', 'prog.c')) + +test.up_to_date(arguments = args) + + + +# Change CPPPATH and make sure we don't rebuild because of it. +test.write('SConstruct', """ +env = Environment(CPPPATH = Split('inc2 include ${TARGET.dir} ${SOURCE.dir}')) +obj = env.Object(target='foobar/prog', source='subdir/prog.c') +env.Program(target='prog', source=obj) +SConscript('subdir/SConscript', "env") + +BuildDir('variant', 'subdir', 0) +include = Dir('include') +env = Environment(CPPPATH=['inc2', include, '#foobar', '#subdir']) +SConscript('variant/SConscript', "env") +""") + +test.up_to_date(arguments = args) + + + +# +test.write(['inc2', 'foo.h'], +r""" +#define FOO_STRING "inc2/foo.h 1\n" +#include +""") + +test.run(arguments = args) + +test.run(program = test.workpath(prog), + stdout = """\ +subdir/prog.c +inc2/foo.h 1 +include/bar.h 2 +subdir/sss.h +foobar/ttt.h +""") + +test.run(program = test.workpath(subdir_prog), + stdout = """\ +subdir/prog.c +subdir/include/foo.h 1 +subdir/include/bar.h 1 +subdir/sss.h +subdir/ttt.h +""") + +test.run(program = test.workpath(variant_prog), + stdout = """\ +subdir/prog.c +include/foo.h 2 +include/bar.h 2 +subdir/sss.h +foobar/ttt.h +""") + +test.up_to_date(arguments = args) + + + +test.pass_test() diff --git a/test/CPPPATH/Dir.py b/test/CPPPATH/Dir.py new file mode 100644 index 0000000..1059467 --- /dev/null +++ b/test/CPPPATH/Dir.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that CPPPATH values with Dir nodes work correctly. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('inc1', 'inc2', 'inc3', ['inc3', 'subdir']) + +test.write('SConstruct', """ +env = Environment(CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir'], + INC2 = Dir('inc2'), + INC3 = Dir('inc3')) +env.Program('prog.c') +""") + +test.write('prog.c', """\ +#include +#include + +#include "one.h" +#include "two.h" +#include "three.h" +int +main(int argc, char *argv[]) +{ + printf("%s\\n", ONE); + printf("%s\\n", TWO); + printf("%s\\n", THREE); + return (0); +} +""") + +test.write(['inc1', 'one.h'], """\ +#define ONE "1" +""") + +test.write(['inc2', 'two.h'], """\ +#define TWO "2" +""") + +test.write(['inc3', 'subdir', 'three.h'], """\ +#define THREE "3" +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") + +test.pass_test() diff --git a/test/CPPPATH/expand-object.py b/test/CPPPATH/expand-object.py new file mode 100644 index 0000000..5e1b06f --- /dev/null +++ b/test/CPPPATH/expand-object.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Make sure that $CPPPATH expands correctly if one of the subsidiary +expansions contains a stringable non-Node object. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +class XXX: + def __init__(self, value): + self.value = value + def __str__(self): + return 'XXX-' + self.value + '-XXX' +env = Environment(CPPPATH = ['#', + '$BUILDDIR', + '/tmp/xyzzy'], + BUILDDIR = '#scons_build/$EXPANSION', + EXPANSION = XXX('win32')) +env.Object('foo.c') +""") + +test.write('foo.c', """\ +#include +void +foo(void) +{ + printf("foo.c\\n"); +} +""") + +test.run(arguments = '.') + +test.must_exist(test.workpath('foo' + TestSCons._obj)) + +test.pass_test() diff --git a/test/CPPPATH/nested-lists.py b/test/CPPPATH/nested-lists.py new file mode 100644 index 0000000..8ec97bc --- /dev/null +++ b/test/CPPPATH/nested-lists.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that CPPPATH values consisting of nested lists work correctly. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('inc1', 'inc2', 'inc3') + +test.write('SConstruct', """ +env = Environment(CPPPATH = ['inc1', ['inc2', ['inc3']]]) +env.Program('prog.c') +""") + +test.write('prog.c', """\ +#include +#include + +#include "one.h" +#include "two.h" +#include "three.h" +int +main(int argc, char *argv[]) +{ + printf("%s\\n", ONE); + printf("%s\\n", TWO); + printf("%s\\n", THREE); + return (0); +} +""") + +test.write(['inc1', 'one.h'], """\ +#define ONE "1" +""") + +test.write(['inc2', 'two.h'], """\ +#define TWO "2" +""") + +test.write(['inc3', 'three.h'], """\ +#define THREE "3" +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") + +test.pass_test() diff --git a/test/CPPPATH/null.py b/test/CPPPATH/null.py new file mode 100644 index 0000000..19895db --- /dev/null +++ b/test/CPPPATH/null.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that neither a null-string CPPPATH nor a +a CPPPATH containing null values blows up. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +env = Environment(CPPPATH = '') +env.Library('one', source = 'empty1.c') +env = Environment(CPPPATH = [None]) +env.Library('two', source = 'empty2.c') +env = Environment(CPPPATH = ['']) +env.Library('three', source = 'empty3.c') +""") + +test.write('empty1.c', "int a=0;\n") +test.write('empty2.c', "int b=0;\n") +test.write('empty3.c', "int c=0;\n") + +test.run(arguments = '.', + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) + + + +test.pass_test() diff --git a/test/CXX/CC-variables.py b/test/CXX/CC-variables.py index 93aa315..fea3dde 100644 --- a/test/CXX/CC-variables.py +++ b/test/CXX/CC-variables.py @@ -34,7 +34,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ -env = Environment(tools = ['g++']) +env = Environment(tools = ['g++'], CXX = 'g++') env.Object(target = 'test.obj', source = 'test.cxx') env.MergeFlags('+for_CCFLAGS -Wp,-for_CPPFLAGS') """) diff --git a/test/CacheDir/up-to-date-q.py b/test/CacheDir/up-to-date-q.py index 07123c9..0a49164 100644 --- a/test/CacheDir/up-to-date-q.py +++ b/test/CacheDir/up-to-date-q.py @@ -60,7 +60,7 @@ int main(){ return 0; } sconstruct = """ import os -CacheDir('%s') +CacheDir(r'%s') Program('foo', 'foo.c') """ % test.workpath('cache') diff --git a/test/Errors/preparation.py b/test/Errors/preparation.py new file mode 100644 index 0000000..3f98894 --- /dev/null +++ b/test/Errors/preparation.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that we print a useful message (and exit non-zero) if an external +error occurs while deciding if a Node is current or not. +""" + +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +install = test.workpath('install') +install_file = test.workpath('install', 'file') +work_file = test.workpath('work', 'file') + +test.subdir('install', 'work') + +test.write(['work', 'SConstruct'], """\ +Alias("install", Install(r"%(install)s", File('file'))) + +# Make a directory where we expect the File() to be. This causes an +# IOError or OSError when we try to open it to read its signature. +import os +os.mkdir(r'%(work_file)s') +""" % locals()) + +if sys.platform == 'win32': + error_message = "Permission denied" +else: + error_message = "Is a directory" + +expect = """\ +scons: *** [%(install_file)s] %(work_file)s: %(error_message)s +""" % locals() + +test.run(chdir = 'work', + arguments = 'install', + status = 2, + stderr = expect) + +test.pass_test() diff --git a/test/Intel/icpc-link.py b/test/Intel/icpc-link.py new file mode 100644 index 0000000..068e0f3 --- /dev/null +++ b/test/Intel/icpc-link.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Simple "Hello, world" test of linking with the Intel C++ compiler, icpc. + +This tests for a bug (1415) where our initialization of the linker to +$CC prevented automatic linking of C++ source. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +icpc = test.detect_tool('intelc', prog='icpc') +if not icpc: + test.skip_test("Could not find 'icpc' from 'intelc' Tool; skipping test(s).\n") + +test.write('SConstruct', """\ +env = Environment(tools=['default', 'intelc']) +env.Program('hw', 'hw.cpp') +""") + +test.write('hw.cpp', """\ +#include +int +main() +{ + std::cout<<"hw\\n"; + return 0; +} +""") + +test.run(arguments = '.') + +test.pass_test() diff --git a/test/Java/JAVAC.py b/test/Java/JAVAC.py index 7c06324..09639ea 100644 --- a/test/Java/JAVAC.py +++ b/test/Java/JAVAC.py @@ -24,6 +24,10 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +""" +Test setting the JAVAC variable. +""" + import os import os.path import string @@ -93,223 +97,4 @@ line 3 -ENV = test.java_ENV() - -if test.detect_tool('javac', ENV=ENV): - where_javac = test.detect('JAVAC', 'javac', ENV=ENV) -else: - where_javac = test.where_is('javac') -if not where_javac: - test.skip_test("Could not find Java javac, skipping test(s).\n") - - - -test.write("wrapper.py", """\ -import os -import string -import sys -open('%s', 'ab').write("wrapper.py %%s\\n" %% string.join(sys.argv[1:])) -os.system(string.join(sys.argv[1:], " ")) -""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\')) - -test.write('SConstruct', """ -foo = Environment(tools = ['javac'], - JAVAC = r'%(where_javac)s') -javac = foo.Dictionary('JAVAC') -bar = foo.Clone(JAVAC = r'%(_python_)s wrapper.py ' + javac) -foo.Java(target = 'class1', source = 'com/sub/foo') -bar.Java(target = 'class2', source = 'com/sub/bar') -foo.Java(target = 'class3', source = ['src1', 'src2']) -""" % locals()) - -test.subdir('com', - ['com', 'sub'], - ['com', 'sub', 'foo'], - ['com', 'sub', 'bar'], - 'src1', - 'src2') - -test.write(['com', 'sub', 'foo', 'Example1.java'], """\ -package com.sub.foo; - -public class Example1 -{ - - public static void main(String[] args) - { - - } - -} -""") - -test.write(['com', 'sub', 'foo', 'Example2.java'], """\ -package com.other; - -public class Example2 -{ - - public static void main(String[] args) - { - - } - -} -""") - -test.write(['com', 'sub', 'foo', 'Example3.java'], """\ -package com.sub.foo; - -public class Example3 -{ - - public static void main(String[] args) - { - - } - -} -""") - -test.write(['com', 'sub', 'bar', 'Example4.java'], """\ -package com.sub.bar; - -public class Example4 -{ - - public static void main(String[] args) - { - - } - -} -""") - -test.write(['com', 'sub', 'bar', 'Example5.java'], """\ -package com.other; - -public class Example5 -{ - - public static void main(String[] args) - { - - } - -} -""") - -test.write(['com', 'sub', 'bar', 'Example6.java'], """\ -package com.sub.bar; - -public class Example6 -{ - - public static void main(String[] args) - { - - } - -} -""") - -test.write(['src1', 'Example7.java'], """\ -public class Example7 -{ - - public static void main(String[] args) - { - - } - -} -""") - -# Acid-test file for parsing inner Java classes, courtesy Chad Austin. -test.write(['src2', 'Test.java'], """\ -class Empty { -} - -interface Listener { - public void execute(); -} - -public -class -Test { - class Inner { - void go() { - use(new Listener() { - public void execute() { - System.out.println("In Inner"); - } - }); - } - String s1 = "class A"; - String s2 = "new Listener() { }"; - /* class B */ - /* new Listener() { } */ - } - - public static void main(String[] args) { - new Test().run(); - } - - void run() { - use(new Listener() { - public void execute() { - use(new Listener( ) { - public void execute() { - System.out.println("Inside execute()"); - } - }); - } - }); - - new Inner().go(); - } - - void use(Listener l) { - l.execute(); - } -} - -class Private { - void run() { - new Listener() { - public void execute() { - } - }; - } -} -""") - -test.run(arguments = '.') - -expected_wrapper_out = "wrapper.py %s -d class2 -sourcepath com/sub/bar com/sub/bar/Example4.java com/sub/bar/Example5.java com/sub/bar/Example6.java\n" -expected_wrapper_out = string.replace(expected_wrapper_out, '/', os.sep) -test.must_match('wrapper.out', expected_wrapper_out % where_javac) - -test.must_exist(test.workpath('class1', 'com', 'sub', 'foo', 'Example1.class')) -test.must_exist(test.workpath('class1', 'com', 'other', 'Example2.class')) -test.must_exist(test.workpath('class1', 'com', 'sub', 'foo', 'Example3.class')) - -test.must_exist(test.workpath('class2', 'com', 'sub', 'bar', 'Example4.class')) -test.must_exist(test.workpath('class2', 'com', 'other', 'Example5.class')) -test.must_exist(test.workpath('class2', 'com', 'sub', 'bar', 'Example6.class')) - -test.must_exist(test.workpath('class3', 'Example7.class')) - -test.must_exist(test.workpath('class3', 'Empty.class')) -test.must_exist(test.workpath('class3', 'Listener.class')) -test.must_exist(test.workpath('class3', 'Private.class')) -test.must_exist(test.workpath('class3', 'Private$1.class')) -test.must_exist(test.workpath('class3', 'Test.class')) -test.must_exist(test.workpath('class3', 'Test$1.class')) -test.must_exist(test.workpath('class3', 'Test$2.class')) -test.must_exist(test.workpath('class3', 'Test$3.class')) -test.must_exist(test.workpath('class3', 'Test$Inner.class')) - -test.up_to_date(arguments = '.') - test.pass_test() diff --git a/test/Java/live.py b/test/Java/live.py new file mode 100644 index 0000000..5ad2194 --- /dev/null +++ b/test/Java/live.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test Java compilation with a live "javac" compiler. +""" + +import os +import os.path +import string +import sys +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +ENV = test.java_ENV() + +if test.detect_tool('javac', ENV=ENV): + where_javac = test.detect('JAVAC', 'javac', ENV=ENV) +else: + where_javac = test.where_is('javac') +if not where_javac: + test.skip_test("Could not find Java javac, skipping test(s).\n") + + + +test.write('SConstruct', """ +env = Environment(tools = ['javac'], + JAVAC = r'%(where_javac)s') +env.Java(target = 'class1', source = 'com/sub/foo') +env.Java(target = 'class2', source = 'com/sub/bar') +env.Java(target = 'class3', source = ['src1', 'src2']) +env.Java(target = 'class4', source = ['src4']) +env.Java(target = 'class5', source = ['src5']) +""" % locals()) + +test.subdir('com', + ['com', 'sub'], + ['com', 'sub', 'foo'], + ['com', 'sub', 'bar'], + 'src1', + 'src2', + 'src4', + 'src5') + +test.write(['com', 'sub', 'foo', 'Example1.java'], """\ +package com.sub.foo; + +public class Example1 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com', 'sub', 'foo', 'Example2.java'], """\ +package com.other; + +public class Example2 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com', 'sub', 'foo', 'Example3.java'], """\ +package com.sub.foo; + +public class Example3 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com', 'sub', 'bar', 'Example4.java'], """\ +package com.sub.bar; + +public class Example4 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com', 'sub', 'bar', 'Example5.java'], """\ +package com.other; + +public class Example5 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com', 'sub', 'bar', 'Example6.java'], """\ +package com.sub.bar; + +public class Example6 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['src1', 'Example7.java'], """\ +public class Example7 +{ + + public static void main(String[] args) + { + + } + +} +""") + +# Acid-test file for parsing inner Java classes, courtesy Chad Austin. +test.write(['src2', 'Test.java'], """\ +class Empty { +} + +interface Listener { + public void execute(); +} + +public +class +Test { + class Inner { + void go() { + use(new Listener() { + public void execute() { + System.out.println("In Inner"); + } + }); + } + String s1 = "class A"; + String s2 = "new Listener() { }"; + /* class B */ + /* new Listener() { } */ + } + + public static void main(String[] args) { + new Test().run(); + } + + void run() { + use(new Listener() { + public void execute() { + use(new Listener( ) { + public void execute() { + System.out.println("Inside execute()"); + } + }); + } + }); + + new Inner().go(); + } + + void use(Listener l) { + l.execute(); + } +} + +class Private { + void run() { + new Listener() { + public void execute() { + } + }; + } +} +""") + +# Testing nested anonymous inner classes, courtesy Brandon Mansfield. +test.write(['src4', 'NestedExample.java'], """\ +// import java.util.*; + +public class NestedExample +{ + public NestedExample() + { + Thread t = new Thread() { + public void start() + { + Thread t = new Thread() { + public void start() + { + try {Thread.sleep(200);} + catch (Exception e) {} + } + }; + while (true) + { + try {Thread.sleep(200);} + catch (Exception e) {} + } + } + }; + } + + + public static void main(String argv[]) + { + NestedExample e = new NestedExample(); + } +} +""") + +# Test not finding an anonymous class when the second token after a +# "new" is a closing brace. This duplicates a test from the unit tests, +# but lets us make sure that we correctly determine that everything is +# up-to-date after the build. +test.write(['src5', 'TestSCons.java'], """\ +class TestSCons { + public static void main(String[] args) { + Foo[] fooArray = new Foo[] { new Foo() }; + } +} + +class Foo { } +""") + +test.run(arguments = '.') + +def get_class_files(dir): + def find_class_files(arg, dirname, fnames): + for fname in fnames: + if fname[-6:] == '.class': + arg.append(os.path.join(dirname, fname)) + result = [] + os.path.walk(dir, find_class_files, result) + result.sort() + return result + +classes_1 = get_class_files(test.workpath('class1')) +classes_2 = get_class_files(test.workpath('class2')) +classes_3 = get_class_files(test.workpath('class3')) +classes_4 = get_class_files(test.workpath('class4')) +classes_5 = get_class_files(test.workpath('class5')) + +expect_1 = [ + test.workpath('class1', 'com', 'other', 'Example2.class'), + test.workpath('class1', 'com', 'sub', 'foo', 'Example1.class'), + test.workpath('class1', 'com', 'sub', 'foo', 'Example3.class'), +] + +expect_2 = [ + test.workpath('class2', 'com', 'other', 'Example5.class'), + test.workpath('class2', 'com', 'sub', 'bar', 'Example4.class'), + test.workpath('class2', 'com', 'sub', 'bar', 'Example6.class'), +] + +expect_3 = [ + test.workpath('class3', 'Empty.class'), + test.workpath('class3', 'Example7.class'), + test.workpath('class3', 'Listener.class'), + test.workpath('class3', 'Private$1.class'), + test.workpath('class3', 'Private.class'), + test.workpath('class3', 'Test$1.class'), + test.workpath('class3', 'Test$2.class'), + test.workpath('class3', 'Test$3.class'), + test.workpath('class3', 'Test$Inner.class'), + test.workpath('class3', 'Test.class'), +] + +expect_4 = [ + test.workpath('class4', 'NestedExample$1.class'), + test.workpath('class4', 'NestedExample$2.class'), + test.workpath('class4', 'NestedExample.class'), +] + +expect_5 = [ + test.workpath('class5', 'Foo.class'), + test.workpath('class5', 'TestSCons.class'), +] + +def classes_must_match(dir, expect, got): + if expect != got: + sys.stderr.write("Expected the following class files in '%s':\n" % dir) + for c in expect: + sys.stderr.write(' %s\n' % c) + sys.stderr.write("Got the following class files in '%s':\n" % dir) + for c in got: + sys.stderr.write(' %s\n' % c) + test.fail_test() + +classes_must_match('class1', expect_1, classes_1) +classes_must_match('class2', expect_2, classes_2) +classes_must_match('class3', expect_3, classes_3) +classes_must_match('class4', expect_4, classes_4) + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/MSVS/common-prefix.py b/test/MSVS/common-prefix.py index d40060c..4034a4a 100644 --- a/test/MSVS/common-prefix.py +++ b/test/MSVS/common-prefix.py @@ -69,9 +69,9 @@ vcproj_template = """\ \t\t\t> \t\t\t \t\t\t" -c "" -C "" -f SConstruct Test.exe" -# PROP BASE Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct Test.exe" +# PROP BASE Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" +# PROP BASE Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" # PROP BASE Target_File "Test.exe" # PROP BASE Bsc_Name "" # PROP BASE Target_Dir "" @@ -86,8 +86,8 @@ CFG=Test - Win32 Release # PROP Use_Debug_Libraries 0 # PROP Output_Dir "" # PROP Intermediate_Dir "" -# PROP Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct Test.exe" -# PROP Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct Test.exe" +# PROP Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" +# PROP Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" # PROP Target_File "Test.exe" # PROP Bsc_Name "" # PROP Target_Dir "" diff --git a/test/MSVS/vs-7.0-files.py b/test/MSVS/vs-7.0-files.py index 8f98166..b9827e7 100644 --- a/test/MSVS/vs-7.0-files.py +++ b/test/MSVS/vs-7.0-files.py @@ -87,9 +87,9 @@ expected_vcprojfile = """\ \t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE"> \t\t\t \t\t \t diff --git a/test/MSVS/vs-7.1-files.py b/test/MSVS/vs-7.1-files.py index 3ad2e97..057f34f 100644 --- a/test/MSVS/vs-7.1-files.py +++ b/test/MSVS/vs-7.1-files.py @@ -87,9 +87,9 @@ expected_vcprojfile = """\ \t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE"> \t\t\t \t\t \t diff --git a/test/MSVS/vs-8.0-files.py b/test/MSVS/vs-8.0-files.py index 1d3c469..d7aa0d9 100644 --- a/test/MSVS/vs-8.0-files.py +++ b/test/MSVS/vs-8.0-files.py @@ -87,9 +87,9 @@ expected_vcprojfile = """\ \t\t\t> \t\t\t +int main(int argc, char **argv) { + QApplication qapp(argc, argv); + return 0; +} +''',".cpp","QT_") + context.Result(result) + return result + +def CheckForQt(context): + # list is currently POSIX centric - what happens with Windows? + potential_qt_dirs = [ + "/usr/share/qt3", # Debian unstable + "/usr/share/qt", + "/usr", + "/usr/local", + "/usr/lib/qt3", # Suse + "/usr/lib/qt", + "/usr/qt/3", # Gentoo + "/usr/pkg/qt3" # pkgsrc (NetBSD) + ] + + if os.environ.has_key('QTDIR'): + potential_qt_dirs.insert(0, os.environ[ 'QTDIR' ]) + + if env[ 'qt_directory' ] != "/": + uic_path = os.path.join(env['qt_directory'], 'bin', 'uic') + if os.path.isfile(uic_path): + potential_qt_dirs.insert(0, env[ 'qt_directory' ]) + else: + print "QT not found. Invalid qt_directory value - failed to find uic." + return 0 + + for i in potential_qt_dirs: + context.env.Replace(QTDIR = i) + if CheckForQtAt(context, i): + # additional checks to validate QT installation + if not os.path.isfile(os.path.join(i, 'bin', 'uic')): + print "QT - failed to find uic." + return 0 + if not os.path.isfile(os.path.join(i, 'bin', 'moc')): + print "QT - failed to find moc." + return 0 + if not os.path.exists(os.path.join(i, 'lib')): + print "QT - failed to find QT lib path." + return 0 + if not os.path.exists(os.path.join(i, 'include')): + print "QT - failed to find QT include path." + return 0 + return 1 + else: + if i==env['qt_directory']: + print "QT directory not valid. Failed QT test build." + return 0 + return 0 + +def AttemptLinkWithVariables(context, variables, code, extension, prefix): + return DoWithVariables(variables, + prefix, + lambda c=context, code=code, e=extension: + c.TryLink(code, e)) + +env = Environment(CPPPATH=['.'], LIBPATH=['.'], LIBS=[]) + +opts = Options('lprof.conf') +opts.Add(PathOption("qt_directory", "Path to Qt directory", "/")) +opts.Update(env) + +env['QT_LIB'] = 'qt-mt' +config = env.Configure(custom_tests = { + 'CheckForQt' : CheckForQt, +}) + +if not config.CheckForQt(): + print "Failed to find valid QT environment." + Exit(1) + +env.Tool('qt', ['$TOOL_PATH']) +""") + +test.run(arguments = '.') + +test.pass_test() diff --git a/test/TEX/auxiliaries.py b/test/TEX/auxiliaries.py new file mode 100644 index 0000000..e34ddaa --- /dev/null +++ b/test/TEX/auxiliaries.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that sections of LaTeX output that use auxiliary files (a +bibliography in our configuration below) are consistent when re-run +after modifying the input file. + +This checks for a bug that was triggered by the presence of auxiliary +files which were detected by SCons but then removed prior to invoking +TeX, causing the auxiliary sections to be excluded from the output. +That was fixed (courtesy Joel B. Mohler) by making all the relevant +auxiliary files Precious(). + +Test configuration courtesy Dmitry Mikhin. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +dvips = test.where_is('dvips') +latex = test.where_is('latex') + +if not dvips or not latex: + test.skip_test("Could not find dvips or latex; skipping test(s).\n") + + +test.subdir(['docs']) + +test.write(['SConstruct'], """\ +env = Environment(tools = ['pdftex', 'dvipdf', 'dvips', 'tex', 'latex'], + ENV = {}, + BUILD_DIR = '#build/docs') + +# Use 'duplicate=1' because LaTeX toolchain does not work properly for +# input/output files outside of the current directory + +env.BuildDir('$BUILD_DIR', 'docs', duplicate=1) +env.SConscript('$BUILD_DIR/SConscript', exports = ['env']) +""") + +test.write(['docs', 'SConscript'], """\ +Import('env') +envc = env.Clone() + +test_dvi = envc.DVI(source='test.tex') +test_ps = envc.PostScript(source='test.tex') +test_pdf = envc.PDF(source='test.tex') + +envc.Default(test_dvi) +envc.Default(test_ps) +envc.Default(test_pdf) +""") + +test.write(['docs', 'my.bib'], """\ +@ARTICLE{Mikhin, + author = "Dmitry {\uppercase{Y}u}. Mikhin", + title = "Blah!", + journal = "Some yellow paper", + year = "2007", + volume = "7", + number = "3", + pages = "1--2" +} +""") + +tex_input = r"""\documentclass{article} + +\title{BUG IN SCONS} + +\author{Dmitry Yu. Mikhin} + +\begin{document} + +\maketitle + + +\begin{abstract} +\noindent A bug in BibTeX processing? +\end{abstract} + + +\section{The problem} + +Provide a citation here: \cite{Mikhin}. + + +\bibliography{my} +\bibliographystyle{unsrtnat} + +\end{document} +""" + +test.write(['docs', 'test.tex'], tex_input) + +test.run(stderr=None) + +pdf_output_1 = test.read(['build', 'docs', 'test.pdf']) +ps_output_1 = test.read(['build', 'docs', 'test.ps']) + +# Adding blank lines will cause SCons to re-run the builds, but the +# actual contents of the output files shouldn't be any different. +# This assumption won't work if it's ever used with a toolchain that does +# something to the output like put a commented-out timestamp in a header. +test.write(['docs', 'test.tex'], tex_input + "\n\n\n") + +test.run(stderr=None) + +pdf_output_2 = test.read(['build', 'docs', 'test.pdf']) +ps_output_2 = test.read(['build', 'docs', 'test.ps']) + +test.fail_test(pdf_output_1 != pdf_output_2) +test.fail_test(ps_output_1 != ps_output_2) + +test.pass_test() diff --git a/test/TEX/bibtex-latex-rerun.py b/test/TEX/bibtex-latex-rerun.py new file mode 100644 index 0000000..2293216 --- /dev/null +++ b/test/TEX/bibtex-latex-rerun.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that we re-run LaTeX after running BibTeX in response to +changes in a .bib file. + +Thanks to Rob Managan for the patch that fixed this, and to Joel B. Mohler +for code clean up and packaging the test case. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write(['SConstruct'], """\ +PDF( 'bibtest.tex' ) +""") + +test.write(['bibtest.tex'], r""" +\documentclass{article} +\begin{document} +Learn about cool math in \cite{koblitz:elliptic_curves}. +\bibliographystyle{alpha} +\bibliography{sources} +\end{document} +""") + +sources_bib_content = r""" +@book{koblitz:elliptic_curves, + author = "Neal Koblitz", + title = "Elliptic Curves and Modular Forms", + year = "%s", + publisher = "Springer-Verlag New York Inc." +} +""" + + + +test.write('sources.bib', sources_bib_content % '1981') + +test.run() + +pdf_output_1 = test.read('bibtest.pdf') + + + +test.write('sources.bib', sources_bib_content % '1982') + +test.run() + +pdf_output_2 = test.read('bibtest.pdf') + +# If the PDF file is the same as it was previously, then it didn't +# pick up the change from 1981 to 1982, so fail. +test.fail_test(pdf_output_1 == pdf_output_2) + + + +# Double-check: clean everything and rebuild from scratch, which +# should force the PDF file to be the 1982 version. + +test.run(arguments = '-c') + +test.run() + +pdf_output_3 = test.read('bibtest.pdf') + +# If the PDF file is now different than the second run, then something +# else odd has happened, so fail. (Note that this test will be incorrect +# if the tool does something like insert a timestamp in the PDF file.) +test.fail_test(pdf_output_2 != pdf_output_3) + + + +test.pass_test() diff --git a/test/TEX/multi-run.py b/test/TEX/multi-run.py index f827ac9..d4e2d79 100644 --- a/test/TEX/multi-run.py +++ b/test/TEX/multi-run.py @@ -42,7 +42,7 @@ latex = test.where_is('latex') if not tex and not latex: test.skip_test("Could not find tex or latex; skipping test(s).\n") -test.subdir('work1', 'work2') +test.subdir('work1', 'work2', 'work4') input_file = r""" @@ -55,6 +55,15 @@ As stated in \cite{X}, this is a bug-a-boo. \end{document} """ +input_file2 = r""" +\documentclass{article} +\begin{document} +Hello world. +% \bibliography{fooref} +% \bibliographystyle{plain} +\end{document} +""" + bibfile = r""" @Article{X, author = "Mr. X", @@ -86,6 +95,8 @@ PDF( "foo.tex" ) print foo_log test.fail_test(1) + + if latex: test.write(['work2', 'SConstruct'], """\ @@ -106,4 +117,16 @@ PDF( "foo.ltx" ) print foo_log test.fail_test(1) + + + test.write(['work4', 'SConstruct'], """\ +DVI( "foo.ltx" ) +""") + test.write(['work4', 'foo.ltx'], input_file2) + + test.run(chdir = 'work4', arguments = '.') + + test.up_to_date(chdir = 'work4', arguments = '.') + + test.pass_test() diff --git a/test/gnutools.py b/test/gnutools.py index 4bcea00..2180217 100644 --- a/test/gnutools.py +++ b/test/gnutools.py @@ -31,10 +31,13 @@ Testing the gnu tool chain, i.e. the tools 'gcc', 'g++' and 'gnulink'. import TestSCons import string import sys + _python_ = TestSCons._python_ _exe = TestSCons._exe -_dll = TestSCons._dll -dll_ = TestSCons.dll_ + +def dll(s): + return TestSCons.dll_ + s + TestSCons._dll + test = TestSCons.TestSCons() test.subdir('gnutools') @@ -111,40 +114,32 @@ env.SharedLibrary('c-and-cpp', Split('cfile1.c cppfile1.cpp')) test.run(chdir='work1') -def testObject(test, obj, command, flags): +def testObject(test, obj, expect): contents = test.read(test.workpath('work1', obj)) line1 = string.split(contents,'\n')[0] - items = string.split(line1, ' ') - cmd = '' - for i in items: - if i != '': - if cmd: - cmd = cmd + ' ' + i - else: - cmd = i - res = ((flags and (cmd == command + ' ' + flags)) or - (not flags and (cmd == command))) - if not res: print "'"+obj+command+flags+"'"+"!='"+str(line1)+"'" - return res - -if sys.platform == 'cygwin': - fpic = '' + actual = string.join(string.split(line1)) + if not expect == actual: + print "%s: %s != %s\n" % (obj, repr(expect), repr(actual)) + test.fail_test() + +if sys.platform in ('win32', 'cygwin'): + c_fpic = '' else: - fpic = ' -fPIC' - -test.fail_test(not testObject(test, 'cfile1.o', 'gcc', '-c') or - not testObject(test, 'cfile2.o', 'gcc', '-c') or - not testObject(test, 'cppfile1.o', 'g++', '-c') or - not testObject(test, 'cppfile2.o', 'g++', '-c') or - not testObject(test, 'cfile1.os', 'gcc', '-c' + fpic) or - not testObject(test, 'cfile2.os', 'gcc', '-c' + fpic) or - not testObject(test, 'cppfile1.os', 'g++', '-c' + fpic) or - not testObject(test, 'cppfile2.os', 'g++', '-c' + fpic) or - not testObject(test, 'c-only' + _exe, 'gcc', '') or - not testObject(test, 'cpp-only' + _exe, 'g++', '') or - not testObject(test, 'c-and-cpp' + _exe, 'g++', '') or - not testObject(test, dll_ + 'c-only' + _dll, 'gcc', '-shared') or - not testObject(test, dll_ + 'cpp-only' + _dll, 'g++', '-shared') or - not testObject(test, dll_ + 'c-and-cpp' + _dll, 'g++', '-shared')) + c_fpic = ' -fPIC' + +testObject(test, 'cfile1.o', 'gcc -c') +testObject(test, 'cfile2.o', 'gcc -c') +testObject(test, 'cppfile1.o', 'g++ -c') +testObject(test, 'cppfile2.o', 'g++ -c') +testObject(test, 'cfile1.os', 'gcc -c' + c_fpic) +testObject(test, 'cfile2.os', 'gcc -c' + c_fpic) +testObject(test, 'cppfile1.os', 'g++ -c -fPIC') +testObject(test, 'cppfile2.os', 'g++ -c -fPIC') +testObject(test, 'c-only' + _exe, 'gcc') +testObject(test, 'cpp-only' + _exe, 'g++') +testObject(test, 'c-and-cpp' + _exe, 'g++') +testObject(test, dll('c-only'), 'gcc -shared') +testObject(test, dll('cpp-only'), 'g++ -shared') +testObject(test, dll('c-and-cpp'), 'g++ -shared') test.pass_test() diff --git a/test/import.py b/test/import.py index 92797df..d267f00 100644 --- a/test/import.py +++ b/test/import.py @@ -52,6 +52,7 @@ platforms = [ for platform in platforms: test.write('SConstruct', """ +print "Platform %(platform)s" env = Environment(platform = '%(platform)s') import SCons.Platform.%(platform)s x = SCons.Platform.%(platform)s.generate @@ -198,6 +199,7 @@ error_output = { # An SConstruct for importing Tool names that have illegal characters # for Python variable names. indirect_import = """\ +print "Tool %(tool)s (indirect)" env = Environment(tools = ['%(tool)s']) SCons = __import__('SCons.Tool.%(tool)s', globals(), locals(), []) @@ -208,6 +210,7 @@ m.generate(env) # An SConstruct for importing Tool names "normally." direct_import = """\ +print "Tool %(tool)s (direct)" env = Environment(tools = ['%(tool)s']) import SCons.Tool.%(tool)s diff --git a/test/option--debug.py b/test/option--debug.py deleted file mode 100644 index a56f261..0000000 --- a/test/option--debug.py +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import sys -import string -import re -import time - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx') -env.Program('foo', Split('foo.c bar.c')) -""") - -test.write('foo.c', r""" -#include "foo.h" -int main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("f1.c\n"); - exit (0); -} -""") - -test.write('bar.c', """ -#include "bar.h" -""") - -test.write('foo.h', """ -#ifndef FOO_H -#define FOO_H -#include "bar.h" -#endif -""") - -test.write('bar.h', """ -#ifndef BAR_H -#define BAR_H -#include "foo.h" -#endif -""") - -############################ -# test --debug=pdb - -test.run(arguments = "--debug=pdb", stdin = "n\ns\nq\n") -test.fail_test(string.find(test.stdout(), "(Pdb)") == -1) -test.fail_test(string.find(test.stdout(), "SCons") == -1) - -############################ -# test --debug=presub - -test.write('cat.py', """\ -import sys -open(sys.argv[2], "wb").write(open(sys.argv[1], "rb").read()) -sys.exit(0) -""") - -test.write('SConstruct', """\ -def cat(env, source, target): - target = str(target[0]) - source = map(str, source) - f = open(target, "wb") - for src in source: - f.write(open(src, "rb").read()) - f.close() -FILE = Builder(action="$FILECOM") -TEMP = Builder(action="$TEMPCOM") -LIST = Builder(action="$LISTCOM") -FUNC = Builder(action=cat) -env = Environment(PYTHON='%(_python_)s', - BUILDERS = {'FILE':FILE, 'TEMP':TEMP, 'LIST':LIST, 'FUNC':FUNC}, - FILECOM="$PYTHON cat.py $SOURCES $TARGET", - TEMPCOM="$PYTHON cat.py $SOURCES temp\\n$PYTHON cat.py temp $TARGET", - LISTCOM=["$PYTHON cat.py $SOURCES temp", "$PYTHON cat.py temp $TARGET"], - FUNCCOM=cat) -env.Command('file01.out', 'file01.in', "$FILECOM") -env.Command('file02.out', 'file02.in', ["$FILECOM"]) -env.Command('file03.out', 'file03.in', "$TEMPCOM") -env.Command('file04.out', 'file04.in', ["$TEMPCOM"]) -env.Command('file05.out', 'file05.in', "$LISTCOM") -env.Command('file06.out', 'file06.in', ["$LISTCOM"]) -env.Command('file07.out', 'file07.in', cat) -env.Command('file08.out', 'file08.in', "$FUNCCOM") -env.Command('file09.out', 'file09.in', ["$FUNCCOM"]) -env.FILE('file11.out', 'file11.in') -env.FILE('file12.out', 'file12.in') -env.TEMP('file13.out', 'file13.in') -env.TEMP('file14.out', 'file14.in') -env.LIST('file15.out', 'file15.in') -env.LIST('file16.out', 'file16.in') -env.FUNC('file17.out', 'file17.in') -env.FUNC('file18.out', 'file18.in') -""" % locals()) - -test.write('file01.in', "file01.in\n") -test.write('file02.in', "file02.in\n") -test.write('file03.in', "file03.in\n") -test.write('file04.in', "file04.in\n") -test.write('file05.in', "file05.in\n") -test.write('file06.in', "file06.in\n") -test.write('file07.in', "file07.in\n") -test.write('file08.in', "file08.in\n") -test.write('file09.in', "file09.in\n") -test.write('file11.in', "file11.in\n") -test.write('file12.in', "file12.in\n") -test.write('file13.in', "file13.in\n") -test.write('file14.in', "file14.in\n") -test.write('file15.in', "file15.in\n") -test.write('file16.in', "file16.in\n") -test.write('file17.in', "file17.in\n") -test.write('file18.in', "file18.in\n") - -expect = """\ -Building file01.out with action: - $PYTHON cat.py $SOURCES $TARGET -%(_python_)s cat.py file01.in file01.out -Building file02.out with action: - $PYTHON cat.py $SOURCES $TARGET -%(_python_)s cat.py file02.in file02.out -Building file03.out with action: - $PYTHON cat.py $SOURCES temp -%(_python_)s cat.py file03.in temp -Building file03.out with action: - $PYTHON cat.py temp $TARGET -%(_python_)s cat.py temp file03.out -Building file04.out with action: - $PYTHON cat.py $SOURCES temp -%(_python_)s cat.py file04.in temp -Building file04.out with action: - $PYTHON cat.py temp $TARGET -%(_python_)s cat.py temp file04.out -Building file05.out with action: - $PYTHON cat.py $SOURCES temp -%(_python_)s cat.py file05.in temp -Building file05.out with action: - $PYTHON cat.py temp $TARGET -%(_python_)s cat.py temp file05.out -Building file06.out with action: - $PYTHON cat.py $SOURCES temp -%(_python_)s cat.py file06.in temp -Building file06.out with action: - $PYTHON cat.py temp $TARGET -%(_python_)s cat.py temp file06.out -Building file07.out with action: - cat(target, source, env) -cat(["file07.out"], ["file07.in"]) -Building file08.out with action: - cat(target, source, env) -cat(["file08.out"], ["file08.in"]) -Building file09.out with action: - cat(target, source, env) -cat(["file09.out"], ["file09.in"]) -Building file11.out with action: - $PYTHON cat.py $SOURCES $TARGET -%(_python_)s cat.py file11.in file11.out -Building file12.out with action: - $PYTHON cat.py $SOURCES $TARGET -%(_python_)s cat.py file12.in file12.out -Building file13.out with action: - $PYTHON cat.py $SOURCES temp -%(_python_)s cat.py file13.in temp -Building file13.out with action: - $PYTHON cat.py temp $TARGET -%(_python_)s cat.py temp file13.out -Building file14.out with action: - $PYTHON cat.py $SOURCES temp -%(_python_)s cat.py file14.in temp -Building file14.out with action: - $PYTHON cat.py temp $TARGET -%(_python_)s cat.py temp file14.out -Building file15.out with action: - $PYTHON cat.py $SOURCES temp -%(_python_)s cat.py file15.in temp -Building file15.out with action: - $PYTHON cat.py temp $TARGET -%(_python_)s cat.py temp file15.out -Building file16.out with action: - $PYTHON cat.py $SOURCES temp -%(_python_)s cat.py file16.in temp -Building file16.out with action: - $PYTHON cat.py temp $TARGET -%(_python_)s cat.py temp file16.out -Building file17.out with action: - cat(target, source, env) -cat(["file17.out"], ["file17.in"]) -Building file18.out with action: - cat(target, source, env) -cat(["file18.out"], ["file18.in"]) -""" % locals() - -test.run(arguments = "--debug=presub .", stdout=test.wrap_stdout(expect)) - -test.must_match('file01.out', "file01.in\n") -test.must_match('file02.out', "file02.in\n") -test.must_match('file03.out', "file03.in\n") -test.must_match('file04.out', "file04.in\n") -test.must_match('file05.out', "file05.in\n") -test.must_match('file06.out', "file06.in\n") -test.must_match('file07.out', "file07.in\n") -test.must_match('file08.out', "file08.in\n") -test.must_match('file09.out', "file09.in\n") -test.must_match('file11.out', "file11.in\n") -test.must_match('file12.out', "file12.in\n") -test.must_match('file13.out', "file13.in\n") -test.must_match('file14.out', "file14.in\n") -test.must_match('file15.out', "file15.in\n") -test.must_match('file16.out', "file16.in\n") -test.must_match('file17.out', "file17.in\n") -test.must_match('file18.out', "file18.in\n") - -test.pass_test() diff --git a/test/option/debug-pdb.py b/test/option/debug-pdb.py new file mode 100644 index 0000000..fa703d5 --- /dev/null +++ b/test/option/debug-pdb.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import string + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +env = Environment() +""") + +test.run(arguments = "--debug=pdb", stdin = "n\ns\nq\n") +test.fail_test(string.find(test.stdout(), "(Pdb)") == -1) +test.fail_test(string.find(test.stdout(), "SCons") == -1) + +test.pass_test() diff --git a/test/option/debug-presub.py b/test/option/debug-presub.py new file mode 100644 index 0000000..cdbefbd --- /dev/null +++ b/test/option/debug-presub.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('cat.py', """\ +import sys +open(sys.argv[2], "wb").write(open(sys.argv[1], "rb").read()) +sys.exit(0) +""") + +test.write('SConstruct', """\ +def cat(env, source, target): + target = str(target[0]) + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() +FILE = Builder(action="$FILECOM") +TEMP = Builder(action="$TEMPCOM") +LIST = Builder(action="$LISTCOM") +FUNC = Builder(action=cat) +env = Environment(PYTHON=r'%(_python_)s', + BUILDERS = {'FILE':FILE, 'TEMP':TEMP, 'LIST':LIST, 'FUNC':FUNC}, + FILECOM="$PYTHON cat.py $SOURCES $TARGET", + TEMPCOM="$PYTHON cat.py $SOURCES temp\\n$PYTHON cat.py temp $TARGET", + LISTCOM=["$PYTHON cat.py $SOURCES temp", "$PYTHON cat.py temp $TARGET"], + FUNCCOM=cat) +env.Command('file01.out', 'file01.in', "$FILECOM") +env.Command('file02.out', 'file02.in', ["$FILECOM"]) +env.Command('file03.out', 'file03.in', "$TEMPCOM") +env.Command('file04.out', 'file04.in', ["$TEMPCOM"]) +env.Command('file05.out', 'file05.in', "$LISTCOM") +env.Command('file06.out', 'file06.in', ["$LISTCOM"]) +env.Command('file07.out', 'file07.in', cat) +env.Command('file08.out', 'file08.in', "$FUNCCOM") +env.Command('file09.out', 'file09.in', ["$FUNCCOM"]) +env.FILE('file11.out', 'file11.in') +env.FILE('file12.out', 'file12.in') +env.TEMP('file13.out', 'file13.in') +env.TEMP('file14.out', 'file14.in') +env.LIST('file15.out', 'file15.in') +env.LIST('file16.out', 'file16.in') +env.FUNC('file17.out', 'file17.in') +env.FUNC('file18.out', 'file18.in') + +env2 = Environment(PYTHON=r'%(_python_)s', + CCCOM="$PYTHON cat.py $SOURCES $TARGET") +env2.Object('file20.obj', 'file20.c') +""" % locals()) + +test.write('file01.in', "file01.in\n") +test.write('file02.in', "file02.in\n") +test.write('file03.in', "file03.in\n") +test.write('file04.in', "file04.in\n") +test.write('file05.in', "file05.in\n") +test.write('file06.in', "file06.in\n") +test.write('file07.in', "file07.in\n") +test.write('file08.in', "file08.in\n") +test.write('file09.in', "file09.in\n") +test.write('file11.in', "file11.in\n") +test.write('file12.in', "file12.in\n") +test.write('file13.in', "file13.in\n") +test.write('file14.in', "file14.in\n") +test.write('file15.in', "file15.in\n") +test.write('file16.in', "file16.in\n") +test.write('file17.in', "file17.in\n") +test.write('file18.in', "file18.in\n") + +test.write('file20.c', "file20.c\n") + +expect = """\ +Building file01.out with action: + $PYTHON cat.py $SOURCES $TARGET +%(_python_)s cat.py file01.in file01.out +Building file02.out with action: + $PYTHON cat.py $SOURCES $TARGET +%(_python_)s cat.py file02.in file02.out +Building file03.out with action: + $PYTHON cat.py $SOURCES temp +%(_python_)s cat.py file03.in temp +Building file03.out with action: + $PYTHON cat.py temp $TARGET +%(_python_)s cat.py temp file03.out +Building file04.out with action: + $PYTHON cat.py $SOURCES temp +%(_python_)s cat.py file04.in temp +Building file04.out with action: + $PYTHON cat.py temp $TARGET +%(_python_)s cat.py temp file04.out +Building file05.out with action: + $PYTHON cat.py $SOURCES temp +%(_python_)s cat.py file05.in temp +Building file05.out with action: + $PYTHON cat.py temp $TARGET +%(_python_)s cat.py temp file05.out +Building file06.out with action: + $PYTHON cat.py $SOURCES temp +%(_python_)s cat.py file06.in temp +Building file06.out with action: + $PYTHON cat.py temp $TARGET +%(_python_)s cat.py temp file06.out +Building file07.out with action: + cat(target, source, env) +cat(["file07.out"], ["file07.in"]) +Building file08.out with action: + cat(target, source, env) +cat(["file08.out"], ["file08.in"]) +Building file09.out with action: + cat(target, source, env) +cat(["file09.out"], ["file09.in"]) +Building file11.out with action: + $PYTHON cat.py $SOURCES $TARGET +%(_python_)s cat.py file11.in file11.out +Building file12.out with action: + $PYTHON cat.py $SOURCES $TARGET +%(_python_)s cat.py file12.in file12.out +Building file13.out with action: + $PYTHON cat.py $SOURCES temp +%(_python_)s cat.py file13.in temp +Building file13.out with action: + $PYTHON cat.py temp $TARGET +%(_python_)s cat.py temp file13.out +Building file14.out with action: + $PYTHON cat.py $SOURCES temp +%(_python_)s cat.py file14.in temp +Building file14.out with action: + $PYTHON cat.py temp $TARGET +%(_python_)s cat.py temp file14.out +Building file15.out with action: + $PYTHON cat.py $SOURCES temp +%(_python_)s cat.py file15.in temp +Building file15.out with action: + $PYTHON cat.py temp $TARGET +%(_python_)s cat.py temp file15.out +Building file16.out with action: + $PYTHON cat.py $SOURCES temp +%(_python_)s cat.py file16.in temp +Building file16.out with action: + $PYTHON cat.py temp $TARGET +%(_python_)s cat.py temp file16.out +Building file17.out with action: + cat(target, source, env) +cat(["file17.out"], ["file17.in"]) +Building file18.out with action: + cat(target, source, env) +cat(["file18.out"], ["file18.in"]) +Building file20.obj with action: + $PYTHON cat.py $SOURCES $TARGET +%(_python_)s cat.py file20.c file20.obj +""" % locals() + +test.run(arguments = "--debug=presub .", stdout=test.wrap_stdout(expect)) + +test.must_match('file01.out', "file01.in\n") +test.must_match('file02.out', "file02.in\n") +test.must_match('file03.out', "file03.in\n") +test.must_match('file04.out', "file04.in\n") +test.must_match('file05.out', "file05.in\n") +test.must_match('file06.out', "file06.in\n") +test.must_match('file07.out', "file07.in\n") +test.must_match('file08.out', "file08.in\n") +test.must_match('file09.out', "file09.in\n") +test.must_match('file11.out', "file11.in\n") +test.must_match('file12.out', "file12.in\n") +test.must_match('file13.out', "file13.in\n") +test.must_match('file14.out', "file14.in\n") +test.must_match('file15.out', "file15.in\n") +test.must_match('file16.out', "file16.in\n") +test.must_match('file17.out', "file17.in\n") +test.must_match('file18.out', "file18.in\n") + +test.must_match('file20.obj', "file20.c\n") + +test.pass_test() diff --git a/test/option/debug-time.py b/test/option/debug-time.py index b1471ba..51cfacf 100644 --- a/test/option/debug-time.py +++ b/test/option/debug-time.py @@ -30,49 +30,51 @@ import string import re import time +_python_ = TestSCons._python_ + test = TestSCons.TestSCons() -test.write('SConstruct', """ -env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx') -env.Program('foo', Split('foo.c bar.c')) +test.write('sleep_cat.py', """\ +import sys +import time +time.sleep(int(sys.argv[1])) +fp = open(sys.argv[2], 'wb') +for arg in sys.argv[3:]: + fp.write(open(arg, 'rb').read()) +fp.close() +sys.exit(0) """) -test.write('foo.c', r""" -#include -#include -#include "foo.h" -int main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("f1.c\n"); - exit (0); -} -""") +test.write('SConstruct', """ +env = Environment(PYTHON = r'%(_python_)s', + SLEEP_CAT = r'sleep_cat.py', + CATCOM = '$PYTHON $SLEEP_CAT $SECONDS $TARGET $SOURCES', + SECONDS = ARGUMENTS.get('SLEEP', '0')) +f1 = env.Command('f1.out', 'f1.in', '$CATCOM') +f2 = env.Command('f2.out', 'f2.in', '$CATCOM') +f3 = env.Command('f3.out', 'f3.in', '$CATCOM') +f4 = env.Command('f4.out', 'f4.in', '$CATCOM') +env.Command('output', [f1, f2, f3, f4], '$CATCOM') +""" % locals()) -test.write('bar.c', """ -#include "bar.h" -""") +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") +test.write('f3.in', "f3.in\n") +test.write('f4.in', "f4.in\n") -test.write('foo.h', """ -#ifndef FOO_H -#define FOO_H -#include "bar.h" -#endif -""") -test.write('bar.h', """ -#ifndef BAR_H -#define BAR_H -#include "foo.h" -#endif -""") -def num(match, line): - return float(re.search(match, line).group(1)) +def num(s, match): + return float(re.search(match, s).group(1)) + +def within_tolerance(expected, actual, tolerance): + return abs((expected-actual)/actual) <= tolerance + + -# Try to make things a little more equal by measuring Python overhead -# executing a minimal file, and reading the scons.py script itself from -# disk so that it's already been cached. +# Try to make our results a little more accurate and repeatable by +# measuring Python overhead executing a minimal file, and reading the +# scons.py script itself from disk so that it's already been cached. test.write('pass.py', "pass\n") test.read(test.program) @@ -80,55 +82,85 @@ start_time = time.time() test.run(program=TestSCons.python, arguments=test.workpath('pass.py')) overhead = time.time() - start_time + + start_time = time.time() -test.run(arguments = "--debug=time .") +test.run(arguments = "-j1 --debug=time . SLEEP=0") complete_time = time.time() - start_time + + expected_total_time = complete_time - overhead -lines = string.split(test.stdout(), '\n') -expected_command_time = 0.0 -for cmdline in filter(lambda x: x[:23] == "Command execution time:", lines): - n = num(r'Command execution time: (\d+\.\d+) seconds', cmdline) - expected_command_time = expected_command_time + n +pattern = r'Command execution time: (\d+\.\d+) seconds' +times = map(float, re.findall(pattern, test.stdout())) +expected_command_time = reduce(lambda x, y: x + y, times, 0.0) -stdout = test.stdout() -total_time = num(r'Total build time: (\d+\.\d+) seconds', stdout) -sconscript_time = num(r'Total SConscript file execution time: (\d+\.\d+) seconds', stdout) -scons_time = num(r'Total SCons execution time: (\d+\.\d+) seconds', stdout) -command_time = num(r'Total command execution time: (\d+\.\d+) seconds', stdout) +stdout = test.stdout() -def within_tolerance(expected, actual, tolerance): - return abs((expected-actual)/actual) <= tolerance +total_time = num(stdout, r'Total build time: (\d+\.\d+) seconds') +sconscript_time = num(stdout, r'Total SConscript file execution time: (\d+\.\d+) seconds') +scons_time = num(stdout, r'Total SCons execution time: (\d+\.\d+) seconds') +command_time = num(stdout, r'Total command execution time: (\d+\.\d+) seconds') failures = [] if not within_tolerance(expected_command_time, command_time, 0.01): failures.append("""\ -SCons reported a total command execution time of %s, -but command execution times really totalled %s, +SCons -j1 reported a total command execution time of %(command_time)s, +but command execution times really totalled %(expected_command_time)s, outside of the 1%% tolerance. -""" % (command_time, expected_command_time)) +""" % locals()) added_times = sconscript_time+scons_time+command_time if not within_tolerance(total_time, added_times, 0.01): failures.append("""\ -SCons reported a total build time of %s, -but the various execution times actually totalled %s, +SCons -j1 reported a total build time of %(total_time)s, +but the various execution times actually totalled %(added_times)s, outside of the 1%% tolerance. -""" % (total_time, added_times)) +""" % locals()) if not within_tolerance(total_time, expected_total_time, 0.15): failures.append("""\ -SCons reported total build time of %s, -but the actual measured build time was %s -(end-to-end time of %s less Python overhead of %s), +SCons -j1 reported total build time of %(total_time)s, +but the actual measured build time was %(expected_total_time)s +(end-to-end time of %(complete_time)s less Python overhead of %(overhead)s), outside of the 15%% tolerance. -""" % (total_time, expected_total_time, complete_time, overhead)) +""" % locals()) if failures: print string.join([test.stdout()] + failures, '\n') test.fail_test(1) + + +test.run(arguments = "-c") + +test.run(arguments = "-j4 --debug=time . SLEEP=1") + + + +stdout = test.stdout() + +total_time = num(stdout, r'Total build time: (\d+\.\d+) seconds') +sconscript_time = num(stdout, r'Total SConscript file execution time: (\d+\.\d+) seconds') +scons_time = num(stdout, r'Total SCons execution time: (\d+\.\d+) seconds') +command_time = num(stdout, r'Total command execution time: (\d+\.\d+) seconds') + +failures = [] + +added_times = sconscript_time+scons_time+command_time +if not within_tolerance(total_time, added_times, 0.01): + failures.append("""\ +SCons -j4 reported a total build time of %(total_time)s, +but the various execution times actually totalled %(added_times)s, +outside of the 1%% tolerance. +""" % locals()) + +if failures: + print string.join([test.stdout()] + failures, '\n') + test.fail_test(1) + + test.pass_test() diff --git a/test/site_scons/no-site-dir.py b/test/site_scons/no-site-dir.py index 6f2f26e..3fc5a6d 100644 --- a/test/site_scons/no-site-dir.py +++ b/test/site_scons/no-site-dir.py @@ -71,9 +71,14 @@ e=Environment() print e.subst('no site: M4 is $M4, M4_MINE is $M4_MINE') """) -test.run(arguments = '-Q --no-site-dir .', -stdout = """no site: M4 is m4, M4_MINE is -scons: `.' is up to date.\n""") +test.run(arguments = '-Q --no-site-dir .') + +not_expected = """Hi there, I am in site_scons/site_init.py! +no site: M4 is my_m4, M4_MINE is 1 +scons: `.' is up to date. +""" + +test.fail_test(test.stdout() == not_expected) diff --git a/test/site_scons/sys-path.py b/test/site_scons/sys-path.py new file mode 100644 index 0000000..7af45aa --- /dev/null +++ b/test/site_scons/sys-path.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that the site_scons dir is added to sys.path as an +absolute path, so it will work from a subdir. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('site_scons') +test.subdir('sub1') + +test.write(['site_scons', 'testmod1.py'], """ +print "Imported site_scons/testmod1.py." +""") +test.write(['site_scons', 'testmod2.py'], """ +print "Imported site_scons/testmod2.py." +""") + +test.write(['sub1', 'SConscript'], """ +import sys +import testmod2 # This call did not work before the fix + +""") + +test.write('SConstruct', """ +import sys +import testmod1 # this always worked +SConscript('sub1/SConscript') +""") + +test.run(arguments = '-Q .', + stdout = """Imported site_scons/testmod1.py. +Imported site_scons/testmod2.py. +scons: `.' is up to date.\n""") + +test.pass_test() + +# end of file -- cgit v0.12