From 090f90eeeeeb5b66ad8d4dab94fe61fc73acf0a2 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Sun, 11 Feb 2007 05:02:05 +0000 Subject: [PATCH] Merged revisions 1784-1824 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r1786 | stevenknight | 2007-01-25 15:29:15 -0600 (Thu, 25 Jan 2007) | 1 line 0.96.D555 - Doc string updates for TaskMaster to better describe the architecture. Get rid of some no-longer-used code. ........ r1787 | stevenknight | 2007-01-25 23:24:31 -0600 (Thu, 25 Jan 2007) | 1 line 0.96.D556 - Packaging changes to support building packages in an arbitrary directory. ........ r1788 | stevenknight | 2007-01-30 20:35:39 -0600 (Tue, 30 Jan 2007) | 1 line 0.96.D557 - Make the Scanner.Base class able to handle Scanner.Selector functionality (i.e., a dictionary to select other scanners) and prepare to deprecate Scanner.Selector and Scanner.Scanner() in the future. ........ r1789 | stevenknight | 2007-01-30 20:45:23 -0600 (Tue, 30 Jan 2007) | 1 line 0.96.D558 - Add support for a site-scons subdirectory. (Gary Oberbrunner) ........ r1790 | stevenknight | 2007-01-31 00:36:20 -0600 (Wed, 31 Jan 2007) | 1 line 0.96.D559 - Clean up various module imports and other pychecker-detected problems. ........ r1791 | stevenknight | 2007-01-31 11:51:04 -0600 (Wed, 31 Jan 2007) | 1 line 0.96.D560 - Fix detection of Java anonymous classes if a newline precedes the opening brace. (Leanid Nazdrynau) ........ r1792 | stevenknight | 2007-02-02 11:57:59 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D561 - Add a --bootstrap_src option to bootstrap.py; search the bootstrap.py directory by default. (Greg Noel) ........ r1793 | stevenknight | 2007-02-02 18:04:52 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D562 - Don't check the build/*/gentoo directories for copyright strings. ........ r1794 | stevenknight | 2007-02-02 18:12:43 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D563 - Fix pychecker clean-ups on later Python versions. ........ r1795 | stevenknight | 2007-02-02 18:38:47 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D564 - Add a NoCache() function to allow marking targets as not being suitable for propagating/ to (or retrieving from) CacheDir(). (Dave Vitek) ........ r1796 | stevenknight | 2007-02-02 20:08:33 -0600 (Fri, 02 Feb 2007) | 1 line 0.96.D565 - Add a --tree= option, to make the interface to dumping dependency graphs a little clearner, and give it a 'prune' option to avoid repeating the dependency walk for Nodes we've already visited. ........ r1797 | stevenknight | 2007-02-03 20:37:25 -0600 (Sat, 03 Feb 2007) | 1 line 0.96.D566 - Packaging fix: don't add '#' to the beginning of a build_dir name if it's already an absolute path. ........ r1798 | stevenknight | 2007-02-03 20:53:42 -0600 (Sat, 03 Feb 2007) | 1 line 0.96.D567 - Add backwards-compatibility for set() types, and restore the line that used them in the compatibility _subprocess.py module. ........ r1799 | stevenknight | 2007-02-05 10:30:28 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D568 - Capture outline and build configuration for a possible future Developer's Guide. ........ r1800 | stevenknight | 2007-02-05 11:03:37 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D569 - Exclude the new developer guide MANIFEST from Copyright string checks. ........ r1801 | stevenknight | 2007-02-05 11:16:04 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D570 - Track fix in upstream subprocess.py. (Ralf W. Grosse-Kunstleve) ........ r1802 | stevenknight | 2007-02-05 20:05:59 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D571 - Change the Windows installer to register scons.bat as an 'App Path', so the directory doesn't need to be added to %PATH%. ........ r1803 | stevenknight | 2007-02-05 21:33:01 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D572 - Prepare SConf for use with the subprocess module by refactoring the Unbuffered class. ........ r1804 | stevenknight | 2007-02-05 22:37:09 -0600 (Mon, 05 Feb 2007) | 1 line 0.96.D573 - Get rid of left-over Node.Node.{pre,post}_actions attributes. ........ r1805 | stevenknight | 2007-02-06 12:04:14 -0600 (Tue, 06 Feb 2007) | 1 line 0.96.D574 - Remove leftover print in the test/CacheDir/NoCache.py test. ........ r1806 | stevenknight | 2007-02-06 17:11:03 -0600 (Tue, 06 Feb 2007) | 1 line 0.96.D575 - Support the ability to evaluate a ${} construction variable to select the spawner function. ........ r1809 | stevenknight | 2007-02-07 00:30:10 -0600 (Wed, 07 Feb 2007) | 1 line 0.96.D576 - Documentation fixes and updates. ........ r1810 | stevenknight | 2007-02-07 15:51:20 -0600 (Wed, 07 Feb 2007) | 1 line 0.96.D577 - Don't expect a bootstrap.py runtest.py file to be generated when swig -noproxy is used. ........ r1811 | stevenknight | 2007-02-07 16:55:06 -0600 (Wed, 07 Feb 2007) | 1 line 0.96.D578 - Make --srcdir a synonym for -Y/--repository. ........ r1812 | stevenknight | 2007-02-07 23:46:22 -0600 (Wed, 07 Feb 2007) | 1 line 0.96.D579 - More doc updates: PathAccept, #include+Repository double-quote limitation. ........ r1817 | stevenknight | 2007-02-08 12:46:42 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D580 - Fix use of toolpath with BuildDir. ........ r1818 | stevenknight | 2007-02-08 12:59:23 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D581 - Enable build of the text version of the User's Guide. ........ r1819 | stevenknight | 2007-02-08 14:21:31 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D582 - Fix handling of Java inner classes with JARCHDIR. ........ r1820 | stevenknight | 2007-02-08 14:39:33 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D583 - Add linking with -lm to the dmd.py Tool smart linker. (Anonymous) ........ r1821 | stevenknight | 2007-02-08 17:26:29 -0600 (Thu, 08 Feb 2007) | 1 line 0.96.D584 - Fix ParseConfig() when *FLAGS variables have been replaced with strings. ........ r1822 | stevenknight | 2007-02-09 12:21:17 -0600 (Fri, 09 Feb 2007) | 1 line 0.96.D585 - Fix installation of file names beginning with #. (Dave Weber) ........ r1823 | stevenknight | 2007-02-09 14:14:47 -0600 (Fri, 09 Feb 2007) | 1 line 0.96.D586 - Post-review improvements to recent toolpath and MergeFlags() changes. (Gary Oberbrunner, Greg Noel) ........ r1824 | stevenknight | 2007-02-10 00:53:13 -0600 (Sat, 10 Feb 2007) | 1 line 0.96.D587 - Commonize flags initialization between the c and c++ Tools. ........ git-svn-id: http://scons.tigris.org/svn/scons/trunk@1826 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- HOWTO/release.txt | 6 + HOWTO/subrelease.txt | 8 + QMTest/SConscript | 11 +- SConstruct | 99 +-- bin/check | 26 - bin/sconsoutput.py | 11 +- bootstrap.py | 64 +- config | 2 +- doc/SConscript | 23 +- doc/developer/MANIFEST | 9 + doc/developer/architecture.sgml | 40 ++ doc/developer/branches.sgml | 40 ++ doc/developer/copyright.sgml | 32 + doc/developer/cycle.sgml | 40 ++ doc/developer/main.sgml | 107 ++++ doc/developer/packaging.sgml | 40 ++ doc/developer/preface.sgml | 175 ++++++ doc/developer/sourcetree.sgml | 40 ++ doc/developer/testing.sgml | 40 ++ doc/man/scons.1 | 272 ++++++++- doc/user/ENV.sgml | 2 +- doc/user/alias.sgml | 14 +- doc/user/builders-built-in.sgml | 4 +- doc/user/builders-writing.sgml | 2 +- doc/user/caching.sgml | 14 +- doc/user/command-line.sgml | 118 ++-- doc/user/depends.sgml | 34 +- doc/user/environments.sgml | 32 +- doc/user/hierarchy.sgml | 30 +- doc/user/install.sgml | 14 +- doc/user/less-simple.sgml | 30 +- doc/user/libraries.sgml | 28 +- doc/user/nodes.in | 6 +- doc/user/nodes.sgml | 15 +- doc/user/preface.sgml | 4 +- doc/user/repositories.in | 374 ++++++++---- doc/user/repositories.sgml | 363 +++++++---- doc/user/separate.sgml | 8 +- doc/user/simple.sgml | 10 +- doc/user/sourcecode.sgml | 8 +- doc/user/troubleshoot.sgml | 115 ++-- doc/user/variants.sgml | 6 +- runtest.py | 19 +- src/CHANGES.txt | 62 ++ src/engine/MANIFEST.in | 2 + src/engine/SCons/Action.py | 16 +- src/engine/SCons/ActionTests.py | 15 +- src/engine/SCons/BuilderTests.py | 2 +- src/engine/SCons/Conftest.py | 1 - src/engine/SCons/Defaults.py | 1 - src/engine/SCons/Environment.py | 66 +- src/engine/SCons/EnvironmentTests.py | 9 +- src/engine/SCons/Executor.py | 4 +- src/engine/SCons/Job.py | 2 +- src/engine/SCons/Node/FS.py | 13 +- src/engine/SCons/Node/__init__.py | 10 +- src/engine/SCons/Options/BoolOption.py | 6 +- src/engine/SCons/Options/BoolOptionTests.py | 6 +- src/engine/SCons/Options/PackageOption.py | 14 +- .../SCons/Options/PackageOptionTests.py | 5 +- src/engine/SCons/Options/__init__.py | 4 +- src/engine/SCons/PathList.py | 1 + src/engine/SCons/Platform/darwin.py | 1 - src/engine/SCons/Platform/win32.py | 1 - src/engine/SCons/SConf.py | 11 +- src/engine/SCons/SConsign.py | 2 - src/engine/SCons/Scanner/Dir.py | 2 - src/engine/SCons/Scanner/ScannerTests.py | 64 +- src/engine/SCons/Scanner/__init__.py | 48 +- src/engine/SCons/Script/Main.py | 156 +++-- src/engine/SCons/Script/SConscript.py | 4 +- src/engine/SCons/Script/__init__.py | 10 +- src/engine/SCons/Subst.py | 9 +- src/engine/SCons/SubstTests.py | 29 +- src/engine/SCons/Taskmaster.py | 241 +++++--- src/engine/SCons/TaskmasterTests.py | 24 - src/engine/SCons/Tool/BitKeeper.py | 2 - src/engine/SCons/Tool/JavaCommon.py | 6 +- src/engine/SCons/Tool/JavaCommonTests.py | 21 + src/engine/SCons/Tool/ToolTests.py | 2 + src/engine/SCons/Tool/__init__.py | 7 +- src/engine/SCons/Tool/c++.py | 4 + src/engine/SCons/Tool/cc.py | 35 +- src/engine/SCons/Tool/cvf.py | 1 - src/engine/SCons/Tool/dmd.py | 2 + src/engine/SCons/Tool/dvi.py | 2 +- src/engine/SCons/Tool/f77.py | 1 + src/engine/SCons/Tool/f90.py | 1 + src/engine/SCons/Tool/f95.py | 1 + src/engine/SCons/Tool/fortran.py | 1 + src/engine/SCons/Tool/intelc.py | 2 +- src/engine/SCons/Tool/jar.py | 5 +- src/engine/SCons/Tool/mingw.py | 1 + src/engine/SCons/Tool/msvs.py | 7 +- src/engine/SCons/Tool/mwcc.py | 4 +- src/engine/SCons/Tool/mwld.py | 4 +- src/engine/SCons/Tool/pdf.py | 2 +- src/engine/SCons/Tool/qt.py | 13 +- src/engine/SCons/Tool/swig.py | 4 +- src/engine/SCons/Tool/tex.py | 2 +- src/engine/SCons/Tool/zip.py | 1 + src/engine/SCons/Util.py | 54 +- src/engine/SCons/UtilTests.py | 6 +- src/engine/SCons/Warnings.py | 7 +- src/engine/SCons/__init__.py | 2 - src/engine/SCons/compat/__init__.py | 19 + src/engine/SCons/compat/_sets.py | 577 ++++++++++++++++++ src/engine/SCons/compat/_sets15.py | 159 +++++ src/engine/SCons/compat/_subprocess.py | 24 +- src/engine/SCons/cpp.py | 1 - src/script/scons-post-install.py | 76 +++ src/script/scons.bat | 1 + src/setup.cfg | 1 + src/setup.py | 31 +- src/test_pychecker.py | 145 +++++ src/test_strings.py | 11 + test/CXX/CC-variables.py | 50 ++ test/CacheDir/NoCache.py | 75 +++ test/Install/no-top-relative.py | 53 ++ test/Java/JARCHDIR.py | 10 +- test/Options/BoolOption.py | 10 +- test/Options/PackageOption.py | 20 +- test/Options/help.py | 34 +- test/ParseConfig.py | 29 +- test/SPAWN.py | 37 +- test/SWIG/SWIG.py | 177 +----- test/SWIG/implicit-dependencies.py | 110 ++++ test/SWIG/live.py | 164 +++++ test/SWIG/noproxy.py | 108 ++++ test/SWIG/remove-modules.py | 92 +++ test/Scanner/dictionary.py | 199 ++++++ test/Script-import.py | 4 +- test/option/debug-stree.py | 57 +- test/option/repository.py | 61 ++ test/option/srcdir.py | 61 ++ test/option/tree-all.py | 190 ++++++ test/option/tree-derived.py | 144 +++++ test/site_scons/basic.py | 69 +++ test/site_scons/no-site-dir.py | 82 +++ test/site_scons/nonexistent.py | 47 ++ test/site_scons/override.py | 62 ++ test/site_scons/site-dir.py | 73 +++ test/toolpath/BuildDir.py | 71 +++ test/{toolpath.py => toolpath/basic.py} | 0 144 files changed, 5351 insertions(+), 1159 deletions(-) delete mode 100644 bin/check create mode 100644 doc/developer/MANIFEST create mode 100644 doc/developer/architecture.sgml create mode 100644 doc/developer/branches.sgml create mode 100644 doc/developer/copyright.sgml create mode 100644 doc/developer/cycle.sgml create mode 100644 doc/developer/main.sgml create mode 100644 doc/developer/packaging.sgml create mode 100644 doc/developer/preface.sgml create mode 100644 doc/developer/sourcetree.sgml create mode 100644 doc/developer/testing.sgml create mode 100644 src/engine/SCons/compat/_sets.py create mode 100644 src/engine/SCons/compat/_sets15.py create mode 100644 src/script/scons-post-install.py create mode 100644 src/test_pychecker.py create mode 100644 test/CXX/CC-variables.py create mode 100644 test/CacheDir/NoCache.py create mode 100644 test/Install/no-top-relative.py create mode 100644 test/SWIG/implicit-dependencies.py create mode 100644 test/SWIG/live.py create mode 100644 test/SWIG/noproxy.py create mode 100644 test/SWIG/remove-modules.py create mode 100644 test/Scanner/dictionary.py create mode 100644 test/option/repository.py create mode 100644 test/option/srcdir.py create mode 100644 test/option/tree-all.py create mode 100644 test/option/tree-derived.py create mode 100644 test/site_scons/basic.py create mode 100644 test/site_scons/no-site-dir.py create mode 100644 test/site_scons/nonexistent.py create mode 100644 test/site_scons/override.py create mode 100644 test/site_scons/site-dir.py create mode 100644 test/toolpath/BuildDir.py rename test/{toolpath.py => toolpath/basic.py} (100%) diff --git a/HOWTO/release.txt b/HOWTO/release.txt index cff44b9e..64c62afc 100644 --- a/HOWTO/release.txt +++ b/HOWTO/release.txt @@ -45,6 +45,12 @@ Things to do to release a new X.Y version of SCons: Check in any changes necessary to make everything work + Update the user's guide + + sh bin/docdiff + + sh bin/docupdate + END THE CURRENT DEVELOPMENT BRANCH ae_p scons.0 diff --git a/HOWTO/subrelease.txt b/HOWTO/subrelease.txt index fbba52a2..7b9254b1 100644 --- a/HOWTO/subrelease.txt +++ b/HOWTO/subrelease.txt @@ -8,6 +8,14 @@ see the document HOWTO/release.txt. Things to do to release a new X.Y.Z version of SCons: + BEFORE STARTING THE SUB-BRANCH: + + Update the user's guide on the parent + + sh bin/docdiff + + sh bin/docupdate + START THE NEW SUB-BRANCH FOR SUBRELEASE aenbr -p scons.0.{94} {1} diff --git a/QMTest/SConscript b/QMTest/SConscript index 309f0dc2..3aa7d9cb 100644 --- a/QMTest/SConscript +++ b/QMTest/SConscript @@ -27,7 +27,7 @@ import os.path -Import('env') +Import('build_dir', 'env') files = [ 'classes.qmc', @@ -51,9 +51,10 @@ for file in files: # Guarantee that real copies of these files always exist in # build/QMTest. If there's a symlink there, then this is an Aegis # build and we blow them away now so that they'll get "built" later. - p = os.path.join('build', 'QMTest', file) + p = os.path.join(build_dir, 'QMTest', file) if os.path.islink(p): os.unlink(p) - sp = '#' + p - env.Command(sp, file, copy) - Local(sp) + if not os.path.isabs(p): + p = '#' + p + env.Command(p, file, copy) + Local(p) diff --git a/SConstruct b/SConstruct index 374cef0c..76ec535d 100644 --- a/SConstruct +++ b/SConstruct @@ -48,8 +48,6 @@ project = 'scons' default_version = '0.96.94' copyright = "Copyright (c) %s The SCons Foundation" % copyright_years -Default('.') - SConsignFile() # @@ -155,7 +153,11 @@ for key in ['AEGIS_PROJECT', 'LOGNAME', 'PYTHONPATH']: if os.environ.has_key(key): ENV[key] = os.environ[key] -cwd_build = os.path.join(os.getcwd(), "build") +build_dir = ARGUMENTS.get('BUILDDIR', 'build') +if not os.path.isabs(build_dir): + build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir)) + +Default('.', build_dir) packaging_flavors = [ 'deb', @@ -168,17 +170,17 @@ packaging_flavors = [ 'local-zip', ] -test_deb_dir = os.path.join(cwd_build, "test-deb") -test_rpm_dir = os.path.join(cwd_build, "test-rpm") -test_tar_gz_dir = os.path.join(cwd_build, "test-tar-gz") -test_src_tar_gz_dir = os.path.join(cwd_build, "test-src-tar-gz") -test_local_tar_gz_dir = os.path.join(cwd_build, "test-local-tar-gz") -test_zip_dir = os.path.join(cwd_build, "test-zip") -test_src_zip_dir = os.path.join(cwd_build, "test-src-zip") -test_local_zip_dir = os.path.join(cwd_build, "test-local-zip") +test_deb_dir = os.path.join(build_dir, "test-deb") +test_rpm_dir = os.path.join(build_dir, "test-rpm") +test_tar_gz_dir = os.path.join(build_dir, "test-tar-gz") +test_src_tar_gz_dir = os.path.join(build_dir, "test-src-tar-gz") +test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz") +test_zip_dir = os.path.join(build_dir, "test-zip") +test_src_zip_dir = os.path.join(build_dir, "test-src-zip") +test_local_zip_dir = os.path.join(build_dir, "test-local-zip") -unpack_tar_gz_dir = os.path.join(cwd_build, "unpack-tar-gz") -unpack_zip_dir = os.path.join(cwd_build, "unpack-zip") +unpack_tar_gz_dir = os.path.join(build_dir, "unpack-tar-gz") +unpack_zip_dir = os.path.join(build_dir, "unpack-zip") if platform == "win32": tar_hflag = '' @@ -305,6 +307,7 @@ env = Environment( ENV = ENV, BUILD = build_id, + BUILDDIR = build_dir, BUILDSYS = build_system, COPYRIGHT = copyright, DATE = date, @@ -501,14 +504,15 @@ scons = { 'sconsign.1', 'scons-time.1', 'script/scons.bat', + 'script/scons-post-install.py', 'setup.cfg', 'setup.py', ], 'filemap' : { - 'scons.1' : '../build/doc/man/scons.1', - 'sconsign.1' : '../build/doc/man/sconsign.1', - 'scons-time.1' : '../build/doc/man/scons-time.1', + 'scons.1' : '$BUILDDIR/doc/man/scons.1', + 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1', + 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1', }, 'buildermap' : { @@ -525,7 +529,7 @@ scons = { }, } -scripts = ['scons', 'sconsign'] +scripts = ['scons', 'sconsign', 'scons-time'] src_deps = [] src_files = [] @@ -541,7 +545,7 @@ for p in [ scons ]: if p.has_key('src_subdir'): src = os.path.join(src, p['src_subdir']) - build = os.path.join('build', pkg) + build = os.path.join(build_dir, pkg) tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version) platform_tar_gz = os.path.join(build, @@ -629,8 +633,10 @@ for p in [ scons ]: # for b in src_files: s = p['filemap'].get(b, b) + if not s[0] == '$' and not os.path.isabs(s): + s = os.path.join(src, s) builder = p['buildermap'].get(b, env.SCons_revision) - x = builder(os.path.join(build, b), os.path.join(src, s)) + x = builder(os.path.join(build, b), s) Local(x) # @@ -720,7 +726,7 @@ for p in [ scons ]: # # Generate portage files for submission to Gentoo Linux. # - gentoo = os.path.join('build', 'gentoo') + gentoo = os.path.join(build, 'gentoo') ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version) digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version) env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision) @@ -791,10 +797,10 @@ for p in [ scons ]: ]) if rpmbuild: - topdir = os.path.join(os.getcwd(), build, 'build', + topdir = os.path.join(build, 'build', 'bdist.' + platform, 'rpm') - buildroot = os.path.join(os.getcwd(), 'build', 'rpm-buildroot') + buildroot = os.path.join(build_dir, 'rpm-buildroot') BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version) RPMSdir = os.path.join(topdir, 'RPMS', 'noarch') @@ -846,7 +852,7 @@ for p in [ scons ]: if dh_builddeb and fakeroot: # Our Debian packaging builds directly into build/dist, # so we don't need to add the .debs to install_targets. - deb = os.path.join('build', 'dist', "%s_%s-1_all.deb" % (pkg, version)) + deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version)) for d in p['debian_deps']: b = env.SCons_revision(os.path.join(build, d), d) env.Depends(deb, b) @@ -898,38 +904,38 @@ for p in [ scons ]: # s_l_v = '%s-local-%s' % (pkg, version) - local = os.path.join('build', pkg + '-local') - cwd_local = os.path.join(os.getcwd(), local) - cwd_local_slv = os.path.join(os.getcwd(), local, s_l_v) + local = pkg + '-local' + build_dir_local = os.path.join(build_dir, local) + build_dir_local_slv = os.path.join(build_dir, local, s_l_v) - local_tar_gz = os.path.join('build', 'dist', "%s.tar.gz" % s_l_v) - local_zip = os.path.join('build', 'dist', "%s.zip" % s_l_v) + local_tar_gz = os.path.join(build_dir, 'dist', "%s.tar.gz" % s_l_v) + local_zip = os.path.join(build_dir, 'dist', "%s.zip" % s_l_v) commands = [ - Delete(local), + Delete(build_dir_local), '$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \ - (cwd_local, cwd_local_slv), + (build_dir_local, build_dir_local_slv), ] for script in scripts: #commands.append("mv %s/%s %s/%s.py" % (local, script, local, script)) - local_script = os.path.join(local, script) + local_script = os.path.join(build_dir_local, script) commands.append(Move(local_script + '.py', local_script)) rf = filter(lambda x: not x in scripts, raw_files) rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf) for script in scripts: rf.append("%s.py" % script) - local_targets = map(lambda x, s=local: os.path.join(s, x), rf) + local_targets = map(lambda x, s=build_dir_local: os.path.join(s, x), rf) env.Command(local_targets, build_src_files, commands) - scons_LICENSE = os.path.join(local, 'scons-LICENSE') + scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE') l = env.SCons_revision(scons_LICENSE, 'LICENSE-local') local_targets.append(l) Local(l) - scons_README = os.path.join(local, 'scons-README') + scons_README = os.path.join(build_dir_local, 'scons-README') l = env.SCons_revision(scons_README, 'README-local') local_targets.append(l) Local(l) @@ -937,7 +943,7 @@ for p in [ scons ]: if gzip: env.Command(local_tar_gz, local_targets, - "cd %s && tar czf $( ${TARGET.abspath} $) *" % local) + "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local) unpack_targets = map(lambda x, d=test_local_tar_gz_dir: os.path.join(d, x), @@ -950,7 +956,7 @@ for p in [ scons ]: if zipit: env.Command(local_zip, local_targets, zipit, - CD = local, PSV = '.') + CD = build_dir_local, PSV = '.') unpack_targets = map(lambda x, d=test_local_zip_dir: os.path.join(d, x), @@ -966,13 +972,13 @@ for p in [ scons ]: # And, lastly, install the appropriate packages in the # appropriate subdirectory. # - b_d_files = env.Install(os.path.join('build', 'dist'), install_targets) + b_d_files = env.Install(os.path.join(build_dir, 'dist'), install_targets) Local(b_d_files) # # # -Export('env') +Export('build_dir', 'env') SConscript('QMTest/SConscript') @@ -992,17 +998,18 @@ for file in files: # Guarantee that real copies of these files always exist in # build/. If there's a symlink there, then this is an Aegis # build and we blow them away now so that they'll get "built" later. - p = os.path.join('build', file) + p = os.path.join(build_dir, file) if os.path.islink(p): os.unlink(p) - sp = '#' + p - env.Command(sp, file, copy) + if not os.path.isabs(p): + p = '#' + p + sp = env.Command(p, file, copy) Local(sp) # # Documentation. # -Export('env', 'whereis') +Export('build_dir', 'env', 'whereis') SConscript('doc/SConscript') @@ -1040,12 +1047,12 @@ if change: if sfiles: ps = "%s-src" % project psv = "%s-%s" % (ps, version) - b_ps = os.path.join('build', ps) - b_psv = os.path.join('build', psv) + b_ps = os.path.join(build_dir, ps) + b_psv = os.path.join(build_dir, psv) b_psv_stamp = b_psv + '-stamp' - src_tar_gz = os.path.join('build', 'dist', '%s.tar.gz' % psv) - src_zip = os.path.join('build', 'dist', '%s.zip' % psv) + src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv) + src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv) Local(src_tar_gz, src_zip) diff --git a/bin/check b/bin/check deleted file mode 100644 index f32494e4..00000000 --- a/bin/check +++ /dev/null @@ -1,26 +0,0 @@ -#! /bin/sh - -# This script runs pychecker on the SCons source and creates a file called -# checks.txt with the results. It must be run in the src/engine directory. - -base=`basename $PWD` -if [ "$base" != "engine" ]; then - echo "You must run this script from the engine directory." - exit -fi - -DEVDIR=../../doc/developer -SRCFILE=../../bin/files -CHKFILE=checks.txt - -rm -f $CHKFILE -for f in `cat $SRCFILE` ; do - echo >> $CHKFILE - echo " --- $f ---" >> $CHKFILE - env PYTHONPATH=. pychecker -T -z -Z --deprecated $f >> $CHKFILE -done - -if [ -e $CHKFILE ]; then - sed -e "s|$PWD/||" $CHKFILE > /tmp/tmpchk - mv -f /tmp/tmpchk $CHKFILE -fi diff --git a/bin/sconsoutput.py b/bin/sconsoutput.py index 974646c4..a9c502b4 100644 --- a/bin/sconsoutput.py +++ b/bin/sconsoutput.py @@ -101,8 +101,8 @@ import string import sys import time -sys.path.append(os.path.join(os.getcwd(), 'etc')) -sys.path.append(os.path.join(os.getcwd(), 'build', 'etc')) +sys.path.append(os.path.join(os.getcwd(), 'QMTest')) +sys.path.append(os.path.join(os.getcwd(), 'build', 'QMTest')) scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py') if not os.path.exists(scons_py): @@ -341,7 +341,7 @@ def JarCom(target, source, env): ToolList = { 'posix' : [('cc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']), ('link', ['LINKCOM', 'SHLINKCOM'], Cat, []), - ('ar', 'ARCOM', Cat, []), + ('ar', ['ARCOM', 'RANLIBCOM'], Cat, []), ('tar', 'TARCOM', Null, []), ('zip', 'ZIPCOM', Null, []), ('BitKeeper', 'BITKEEPERCOM', Cat, []), @@ -416,7 +416,10 @@ def command_scons(args, c, test, dict): os.environ.update(save_vals) for key in delete_keys: del(os.environ[key]) - out = string.replace(test.stdout(), test.workpath('ROOT'), '') + out = test.stdout() + out = string.replace(out, test.workpath('ROOT'), '') + out = string.replace(out, test.workpath('WORK/SConstruct'), + '/home/my/project/SConstruct') lines = string.split(out, '\n') if lines: while lines[-1] == '': diff --git a/bootstrap.py b/bootstrap.py index 9a88c1c8..1620bf3e 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -23,7 +23,6 @@ import os import os.path -import getopt import string import sys @@ -50,14 +49,31 @@ All of these begin with the string "bootstrap_": bootstrap.py script only updates the bootstrap copy if the content of the source copy is different. + --bootstrap_src=DIR + + Searches for the SCons files relative to the specified DIR, + then relative to the directory in which this bootstrap.py + script is found. + --bootstrap_update Only updates the bootstrap subdirectory, and then exits. In addition to the above options, the bootstrap.py script understands -the -Y and --repository= options, which are used under Aegis to specify -a search path for the source files that may not have been copied in to -the Aegis change. +the following SCons options: + + -C, --directory + + Changes to the specified directory before invoking SCons. + Because we change directory right away to the specified directory, + the SCons script itself doesn't need to, so this option gets + "eaten" by the bootstrap.py script. + + -Y, --repository + + These options are used under Aegis to specify a search path + for the source files that may not have been copied in to the + Aegis change. This is essentially a minimal build of SCons to bootstrap ourselves into executing it for the full build of all the packages, as specified in our @@ -66,18 +82,23 @@ local SConstruct file. bootstrap_dir = 'bootstrap' pass_through_args = [] -search = ['.'] update_only = None requires_an_argument = 'bootstrap.py: %s requires an argument\n' -command_line_args = sys.argv[1:] - def must_copy(dst, src): if not os.path.exists(dst): return 1 return open(dst, 'rb').read() != open(src, 'rb').read() +search = [os.path.dirname(sys.argv[0])] +if search[0] == '': search[0] = '.' + +# Note: We don't use the getopt module to process the command-line +# arguments because we'd have to teach it about all of the SCons options. + +command_line_args = sys.argv[1:] + while command_line_args: arg = command_line_args.pop(0) @@ -87,7 +108,6 @@ while command_line_args: except IndexError: sys.stderr.write(requires_an_argument % arg) sys.exit(1) - elif arg[:16] == '--bootstrap_dir=': bootstrap_dir = arg[16:] @@ -95,9 +115,31 @@ while command_line_args: def must_copy(dst, src): return 1 + elif arg == '--bootstrap_src': + try: + search.insert(0, command_line_args.pop(0)) + except IndexError: + sys.stderr.write(requires_an_argument % arg) + sys.exit(1) + elif arg[:16] == '--bootstrap_src=': + search.insert(0, arg[16:]) + elif arg == '--bootstrap_update': update_only = 1 + elif arg in ('-C', '--directory'): + try: + dir = command_line_args.pop(0) + except IndexError: + sys.stderr.write(requires_an_argument % arg) + sys.exit(1) + else: + os.chdir(dir) + elif arg[:2] == '-C': + os.chdir(arg[2:]) + elif arg[:12] == '--directory=': + os.chdir(arg[12:]) + elif arg in ('-Y', '--repository'): try: dir = command_line_args.pop(0) @@ -107,11 +149,9 @@ while command_line_args: else: search.append(dir) pass_through_args.extend([arg, dir]) - elif arg[:2] == '-Y': search.append(arg[2:]) pass_through_args.append(arg) - elif arg[:13] == '--repository=': search.append(arg[13:]) pass_through_args.append(arg) @@ -122,8 +162,8 @@ while command_line_args: def find(file, search=search): for dir in search: f = os.path.join(dir, file) - if os.path.exists(f): - return os.path.normpath(f) + if os.path.exists(f): + return os.path.normpath(f) sys.stderr.write("could not find `%s' in search path:\n" % file) sys.stderr.write("\t" + string.join(search, "\n\t") + "\n") sys.exit(2) diff --git a/config b/config index b055bdb2..3af477ce 100644 --- a/config +++ b/config @@ -50,7 +50,7 @@ * * Look in aesub(5) for more information about command substitutions. */ -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}"; +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}"; /* * SCons removes its targets before constructing them, which qualifies it diff --git a/doc/SConscript b/doc/SConscript index 43d34036..bf620241 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -29,18 +29,18 @@ import os.path import re import string -Import('env', 'whereis') +Import('build_dir', 'env', 'whereis') env = env.Clone() env.TargetSignatures('content') -build = os.path.join('#build', 'doc') +build = os.path.join(build_dir, 'doc') # # # -doc_tar_gz = os.path.join('#build', +doc_tar_gz = os.path.join(build_dir, 'dist', 'scons-doc-%s.tar.gz' % env.Dictionary('VERSION')) @@ -210,12 +210,21 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. 'pdf' : 1, 'text' : 0, }, + # For whenever (if ever?) we start putting developer guide + # information in a printable document instead of the wiki. + #'developer' : { + # 'htmlindex' : 'book1.html', + # 'html' : 1, + # 'ps' : 1, + # 'pdf' : 1, + # 'text' : 0, + #}, 'user' : { 'htmlindex' : 'book1.html', 'html' : 1, 'ps' : 1, 'pdf' : 1, - 'text' : 0, + 'text' : 1, 'graphics' : [ 'SCons-win32-install-1.jpg', 'SCons-win32-install-2.jpg', @@ -268,7 +277,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. # Hard-coding the scons-src path is a bit of a hack. This can # be reworked when a better solution presents itself. - scons_src_main = os.path.join('#build', 'scons-src', 'doc', main) + scons_src_main = os.path.join(build_dir, 'scons-src', 'doc', main) env.Ignore(scons_src_main, version_sgml) htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc) @@ -379,7 +388,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. man_page_list = ['scons.1', 'sconsign.1', 'scons-time.1'] for m in man_page_list: - orig_env.SCons_revision(os.path.join(build, 'man', m), + x = orig_env.SCons_revision(os.path.join(build, 'man', m), os.path.join('man', m)) man_i_files = ['builders.man', 'tools.man', 'variables.man'] @@ -425,7 +434,7 @@ for man_1 in man_page_list: return 0 cmds = [ - "( cd build/doc/man && cp %s .. )" % string.join(man_i_files), + "( cd %s/man && cp %s .. )" % (build, string.join(man_i_files)), "( cd ${SOURCE.dir} && man2html ${SOURCE.file} ) > $TARGET", Action(strip_to_first_html_tag), ] diff --git a/doc/developer/MANIFEST b/doc/developer/MANIFEST new file mode 100644 index 00000000..12a4de0b --- /dev/null +++ b/doc/developer/MANIFEST @@ -0,0 +1,9 @@ +architecture.sgml +branches.sgml +copyright.sgml +cycle.sgml +main.sgml +packaging.sgml +preface.sgml +sourcetree.sgml +testing.sgml diff --git a/doc/developer/architecture.sgml b/doc/developer/architecture.sgml new file mode 100644 index 00000000..0fc357fd --- /dev/null +++ b/doc/developer/architecture.sgml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Architecture + + + + XXX + + +
diff --git a/doc/developer/branches.sgml b/doc/developer/branches.sgml new file mode 100644 index 00000000..abba3986 --- /dev/null +++ b/doc/developer/branches.sgml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ &SCons; Branches + + + + XXX + + +
diff --git a/doc/developer/copyright.sgml b/doc/developer/copyright.sgml new file mode 100644 index 00000000..d926f5b7 --- /dev/null +++ b/doc/developer/copyright.sgml @@ -0,0 +1,32 @@ + + +
+ + + SCons Developer's Guide Copyright (c) 2007 Steven Knight + + +
diff --git a/doc/developer/cycle.sgml b/doc/developer/cycle.sgml new file mode 100644 index 00000000..629a1a8f --- /dev/null +++ b/doc/developer/cycle.sgml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Development Cycle + + + + XXX + + +
diff --git a/doc/developer/main.sgml b/doc/developer/main.sgml new file mode 100644 index 00000000..949287a6 --- /dev/null +++ b/doc/developer/main.sgml @@ -0,0 +1,107 @@ + + + + %version; + + + %scons; + + + + + + + + + + +]> + + + + SCons Developer's Guide &buildversion; + + + Steven + Knight + + + Revision &buildrevision; (&builddate;) + + 2007 + + + 2007 + Steven Knight + + + + ©right; + + + version &buildversion; + + + + + Preface + &preface; + + + + Development Cycle + &cycle; + + + + Source Tree + &sourcetree; + + + + Testing + &testing; + + + + Branches + &branches; + + + + Packaging + &packaging; + + + + Architecture + &architecture; + + + diff --git a/doc/developer/packaging.sgml b/doc/developer/packaging.sgml new file mode 100644 index 00000000..3860ee77 --- /dev/null +++ b/doc/developer/packaging.sgml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Packaging + + + + XXX + + +
diff --git a/doc/developer/preface.sgml b/doc/developer/preface.sgml new file mode 100644 index 00000000..5784cee3 --- /dev/null +++ b/doc/developer/preface.sgml @@ -0,0 +1,175 @@ + + + + + This document assumes that you already know how &SCons; + and that you want to learn how to work on the code. + + + +
+ &SCons; Principles + + + + There are a few overriding principles + we try to live up to in designing and implementing &SCons: + + + + + + + Correctness + + + + + First and foremost, + by default, &SCons; guarantees a correct build + even if it means sacrificing performance a little. + We strive to guarantee the build is correct + regardless of how the software being built is structured, + how it may have been written, + or how unusual the tools are that build it. + + + + + + + Performance + + + + + Given that the build is correct, + we try to make &SCons; build software + as quickly as possible. + In particular, wherever we may have needed to slow + down the default &SCons; behavior to guarantee a correct build, + we also try to make it easy to speed up &SCons; + through optimization options that let you trade off + guaranteed correctness in all end cases for + a speedier build in the usual cases. + + + + + + + Convenience + + + + + &SCons; tries to do as much for you out of the box as reasonable, + including detecting the right tools on your system + and using them correctly to build the software. + + + + + + + + + + In a nutshell, we try hard to make &SCons; just + "do the right thing" and build software correctly, + with a minimum of hassles. + + + +
+ +
+ Acknowledgements + + + + &SCons; would not exist without a lot of help + from a lot of people, + many of whom may not even be aware + that they helped or served as inspiration. + So in no particular order, + and at the risk of leaving out someone: + + + + + + First and foremost, + &SCons; owes a tremendous debt to Bob Sidebotham, + the original author of the classic Perl-based &Cons; tool + which Bob first released to the world back around 1996. + Bob's work on Cons classic provided the underlying architecture + and model of specifying a build configuration + using a real scripting language. + My real-world experience working on Cons + informed many of the design decisions in SCons, + including the improved parallel build support, + making Builder objects easily definable by users, + and separating the build engine from the wrapping interface. + + + + + + Greg Wilson was instrumental in getting + &SCons; started as a real project + when he initiated the Software Carpentry design + competition in February 2000. + Without that nudge, + marrying the advantages of the Cons classic + architecture with the readability of Python + might have just stayed no more than a nice idea. + + + + + + Thanks to Peter Miller + for his splendid change management system, &Aegis;, + which has provided the &SCons; project + with a robust development methodology from day one, + and which showed me how you could + integrate incremental regression tests into + a practical development cycle + (years before eXtreme Programming arrived on the scene). + + + + + + And last, thanks to Guido van Rossum + for his elegant scripting language, + which is the basis not only for the &SCons; implementation, + but for the interface itself. + + + +
diff --git a/doc/developer/sourcetree.sgml b/doc/developer/sourcetree.sgml new file mode 100644 index 00000000..be1c45a3 --- /dev/null +++ b/doc/developer/sourcetree.sgml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Source Tree + + + + XXX + + +
diff --git a/doc/developer/testing.sgml b/doc/developer/testing.sgml new file mode 100644 index 00000000..c577c5c9 --- /dev/null +++ b/doc/developer/testing.sgml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Testing + + + + XXX + + +
diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 0a76affd..862cde55 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -92,6 +92,26 @@ rules exist for building common software components (executable programs, object files, libraries), so that for most software projects, only the target and input files need be specified. +Before reading the +.I SConstruct +file, +.B scons +adds looks for a dir named +.I site_scons +in the dir containing the +.I SConstruct +file; it adds that +.I site_scons +to sys.path, reads the file +.IR site_scons/site_init.py , +and adds the directory +.I site_scons/site_tools +to the default toolpath, if those exist. See the +.I --no-site-dir +and +.I --site-dir +options for more details. + .B scons reads and executes the SConscript files as Python scripts, so you may use normal Python scripting capabilities @@ -570,8 +590,11 @@ This only works when run under Python 2.1 or later. .TP --debug=dtree -Print the dependency tree -after each top-level target is built. This prints out only derived files. +A synonym for the newer +.B --tree=derived +option. +This will be deprecated in some future release +and ultimately removed. .TP --debug=explain @@ -650,9 +673,11 @@ when encountering an otherwise unexplained error. .TP --debug=stree -Print the dependency tree along with status information. This is the -same as the debug=tree option, but additional status information is -provided for each node in the tree. +A synonym for the newer +.B --tree=all,status +option. +This will be deprecated in some future release +and ultimately removed. .TP --debug=time @@ -663,10 +688,11 @@ SConscript files, and the total time spent executing SCons itself. .TP --debug=tree -Print the dependency tree -after each top-level target is built. This prints out the complete -dependency tree including implicit dependencies and ignored -dependencies. +A synonym for the newer +.B --tree=all +option. +This will be deprecated in some future release +and ultimately removed. .TP .RI --diskcheck= types @@ -748,6 +774,18 @@ 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 @@ -939,6 +977,18 @@ Also suppresses SCons status messages. Ignored for compatibility with GNU .BR make . +.TP +.RI --site-dir= dir +Uses the named dir as the site dir rather than the default +.I site_scons +dir. This dir will get prepended to +.IR sys.path , +the module +.IR dir /site_init.py +will get loaded if it exists, and +.IR dir /site_tools +will get added to the default toolpath. + .TP -t, --touch Ignored for compatibility with GNU @@ -957,6 +1007,56 @@ A file name of .B - may be used to specify the standard output. +.TP +.RI -tree= options +Prints a tree of the dependencies +after each top-level target is built. +This prints out some or all of the tree, +in various formats, +depending on the +.I options +specified: + +.TP +--tree=all +Print the entire dependency tree +after each top-level target is built. +This prints out the complete dependency tree, +including implicit dependencies and ignored dependencies. + +.TP +--tree=derived +Restricts the tree output to only derived (target) files, +not source files. + +.TP +--tree=status +Prints status information for each displayed node. + +.TP +--tree=prune +Prunes the tree to avoid repeating dependency information +for nodes that have already been displayed. +Any node that has already been displayed +will have its name printed in +.BR "[square brackets]" , +as an indication that the dependencies +for that node can be found by searching +for the relevant output higher up in the tree. + +.IP +Multiple options may be specified, +separated by commas: + +.ES +# Prints only derived files, with status information: +scons --tree=derived,status + +# Prints all dependencies of target, with status information +# and pruning dependencies of already-visited Nodes: +scons --tree=all,prune,status target +.EE + .TP -u, --up, --search-up Walks up the directory structure until an @@ -993,6 +1093,10 @@ Then exit. Print a message containing the working directory before and after other processing. +.TP +--no-print-directory +Turn off -w, even if it was turned on implicitly. + .TP .RI --warn= type ", --warn=no-" type Enable or disable warnings. @@ -1003,6 +1107,20 @@ specifies the type of warnings to be enabled or disabled: --warn=all, --warn=no-all Enables or disables all warnings. +.TP +--warn=cache-write-error, --warn=no-cache-write-error +Enables or disables warnings about errors trying to +write a copy of a built file to a specified +.BR CacheDir (). +These warnings are disabled by default. + +.TP +--warn=corrupt-sconsign, --warn=no-corrupt-sconsign +Enables or disables warnings about unfamiliar signature data in +.B .sconsign +files. +These warnings are enabled by default. + .TP --warn=dependency, --warn=no-dependency Enables or disables warnings about dependencies. @@ -1014,13 +1132,63 @@ Enables or disables warnings about use of deprecated features. These warnings are enabled by default. .TP ---warn=missing-sconscript, --warn=no-missing-sconscript +--warn=duplicate-environment, --warn=no-duplicate-environment Enables or disables warnings about missing SConscript files. + +.TP +--warn=misleading-keywords, --warn=no-misleading-keywords +Enables or disables warnings about use of the misspelled keywords +.B targets +and +.B sources +when calling Builders. +(Note the last +.B s +characters, the correct spellings are +.B target +and +.B source.) These warnings are enabled by default. .TP ---no-print-directory -Turn off -w, even if it was turned on implicitly. +--warn=missing-sconscript, --warn=no-missing-sconscript +Enables or disables warnings about attempts to specify a build +of a target with two different construction environments +that use the same action. +These warnings are enabled by default. + +.TP +--warn=no-md5-module, --warn=no-no-md5-module +Enables or disables warnings about the version of Python +not having an MD5 checksum module available. +These warnings are enabled by default. + +.TP +--warn=no-metaclass-support, --warn=no-no-metaclass-support +Enables or disables warnings about the version of Python +not supporting metaclasses when the +.B --debug=memoizer +option is used. +These warnings are enabled by default. + +.TP +--warn=no-parallel-support, --warn=no-no-parallel-support +Enables or disables warnings about the version of Python +not being able to support parallel builds when the +.B -j +option is used. +These warnings are enabled by default. + +.TP +--warn=reserved-variable, --warn=no-reserved-variable +Enables or disables warnings about attempts to set the +reserved construction variable names +.BR TARGET , +.BR TARGETS , +.BR SOURCE +or +.BR SOURCES . +These warnings are disabled by default. .\" .TP .\" .RI --write-filenames= file @@ -1045,7 +1213,7 @@ Turn off -w, even if it was turned on implicitly. .\" Warn when an undefined variable is referenced. .TP -.RI -Y " repository" ", --repository=" repository +.RI -Y " repository" ", --repository=" repository ", --srcdir=" repository Search the specified repository for any input and target files not found in the local directory hierarchy. Multiple .B -Y @@ -2174,6 +2342,12 @@ that are equivalent regardless of whether a given derived file has been built in-place or retrieved from the cache. +The +.BR NoCache () +method can be used to disable caching of specific files. This can be +useful if inputs and/or outputs of some tool are impossible to +predict or prohibitively large. + '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP .RI Clean( targets ", " files_or_dirs ) @@ -2282,6 +2456,7 @@ env.Command('baz.out', 'baz.in', rename ]) .EE +.IP Note that the .BR Command () function will usually assume, by default, @@ -2304,6 +2479,7 @@ env['DISTDIR'] = 'destination/directory' env.Command(env.Dir('$DISTDIR')), None, make_distdir) .EE +.IP (Also note that SCons will usually automatically create any directory necessary to hold a target file, so you normally don't need to create directories by hand.) @@ -2513,6 +2689,7 @@ This SConstruct: env=Environment() print env.Dump('CCCOM') .EE +.IP will print: .ES '$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' @@ -2522,6 +2699,7 @@ will print: env=Environment() print env.Dump() .EE +.IP will print: .ES { 'AR': 'ar', @@ -3024,6 +3202,46 @@ env.MergeFlags(['-O3', '!pkg-config libpng12 --cflags --libs']) .EE +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP +.RI NoCache( target ", ...)" +.TP +.RI env.NoCache( target ", ...)" +Specifies a list of files which should +.I not +be cached whenever the +.BR CacheDir () +method has been activated. +The specified targets may be a list +or an individual target. +Multiple 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 +.BR NoCache () +method, or as a list. +.BR NoCache () +will also accept the return value of any of the construction environment +Builder methods. + +Calling +.BR NoCache () +on directories and other non-File Node types has no effect because +only File Nodes are cached. + +Examples: + +.ES +NoCache('foo.elf') +NoCache(env.Program('hello', 'hello.c')) +.EE + '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP .RI NoClean( target ", ...)" @@ -3214,6 +3432,7 @@ and added to the following construction variables: + CCFLAGS, LINKFLAGS .EE +.IP Any other strings not associated with options are assumed to be the names of libraries and added to the @@ -5347,10 +5566,15 @@ which verifies that the specified path exists; which verifies that the specified path is an existing file; .BR PathOption.PathIsDir , which verifies that the specified path is an existing directory; -and .BR PathOption.PathIsDirCreate , -which verifies that the specified path is a directory, -and will create the specified directory if the path does not exist. +which verifies that the specified path is a directory +and will create the specified directory if the path does not exist; +and +.BR PathOption.PathAccept , +which simply accepts the specific path name argument without validation, +and which is suitable if you want your users +to be able to specify a directory path that will be +created as part of the build process, for example. You may supply your own .I validator function, @@ -5636,6 +5860,7 @@ env.Append(BUILDERS = {'MakeDirectory':MakeDirectoryBuilder}) env.MakeDirectory('new_directory', []) .EE +.IP Note that the call to the MakeDirectory Builder needs to specify an empty source list to make the string represent the builder's target; @@ -6739,16 +6964,23 @@ new file types for implicit dependencies. Scanner accepts the following arguments: .IP function -A Python function that will process +This can be either: +1) a Python function that will process the Node (file) and return a list of strings (file names) representing the implicit -dependencies found in the contents. -The function takes three or four arguments: +dependencies found in the contents; +or: +2) a dictionary that maps keys +(typically the file suffix, but see below for more discussion) +to other Scanners that should be called. + +If the argument is actually a Python function, +the function must take three or four arguments: def scanner_function(node, env, path): - def scanner_function(node, env, path, arg): + def scanner_function(node, env, path, arg=None): The .B node diff --git a/doc/user/ENV.sgml b/doc/user/ENV.sgml index aa65ebd9..d8432763 100644 --- a/doc/user/ENV.sgml +++ b/doc/user/ENV.sgml @@ -57,7 +57,7 @@ on a POSIX system is /usr/local/bin:/bin:/usr/bin. The default value of the &PATH; environment variable - on a Win32 system comes from the Windows registry + on a Windows system comes from the Windows registry value for the command interpreter. If you want to execute any commands--compilers, linkers, etc.--that are not in these default locations, diff --git a/doc/user/alias.sgml b/doc/user/alias.sgml index 4471605e..f285d70d 100644 --- a/doc/user/alias.sgml +++ b/doc/user/alias.sgml @@ -46,7 +46,7 @@ % scons -Q install - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello" @@ -86,12 +86,12 @@ % scons -Q install-bin - cc -c -o foo.o foo.c + cc -o foo.o -c foo.c cc -o foo foo.o Install file: "foo" as "/usr/bin/foo" % scons -Q install-lib - cc -c -o bar.o bar.c - ar r libbar.a bar.o + cc -o bar.o -c bar.c + ar rc libbar.a bar.o ranlib libbar.a Install file: "libbar.a" as "/usr/lib/libbar.a" % scons -Q -c / @@ -102,11 +102,11 @@ Removed libbar.a Removed /usr/lib/libbar.a % scons -Q install - cc -c -o foo.o foo.c + cc -o foo.o -c foo.c cc -o foo foo.o Install file: "foo" as "/usr/bin/foo" - cc -c -o bar.o bar.c - ar r libbar.a bar.o + cc -o bar.o -c bar.c + ar rc libbar.a bar.o ranlib libbar.a Install file: "libbar.a" as "/usr/lib/libbar.a" diff --git a/doc/user/builders-built-in.sgml b/doc/user/builders-built-in.sgml index 84877a33..9c0a3b87 100644 --- a/doc/user/builders-built-in.sgml +++ b/doc/user/builders-built-in.sgml @@ -144,8 +144,8 @@ % scons -Q - cc -c -o goodbye.o goodbye.c - cc -c -o hello.o hello.c + cc -o goodbye.o -c goodbye.c + cc -o hello.o -c hello.c cc -o hello hello.o goodbye.o -L/usr/dir1 -Ldir2 -lfoo1 -lfoo2 diff --git a/doc/user/builders-writing.sgml b/doc/user/builders-writing.sgml index 9085c0e5..327650a8 100644 --- a/doc/user/builders-writing.sgml +++ b/doc/user/builders-writing.sgml @@ -266,7 +266,7 @@ This functionality could be invoked as in the following example: % scons -Q foobuild < file.input > file.foo - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o diff --git a/doc/user/caching.sgml b/doc/user/caching.sgml index eac69115..d87e4932 100644 --- a/doc/user/caching.sgml +++ b/doc/user/caching.sgml @@ -82,7 +82,7 @@ % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o @@ -124,13 +124,13 @@ % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o Removed hello % scons -Q --cache-show - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -177,7 +177,7 @@ % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o @@ -189,7 +189,7 @@ Removed hello.o Removed hello % scons -Q --cache-disable - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -228,13 +228,13 @@ % scons -Q --cache-disable - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q -c Removed hello.o Removed hello % scons -Q --cache-disable - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q --cache-force scons: `.' is up to date. diff --git a/doc/user/command-line.sgml b/doc/user/command-line.sgml index 565d9f89..1953690e 100644 --- a/doc/user/command-line.sgml +++ b/doc/user/command-line.sgml @@ -133,11 +133,11 @@ % scons -Q - cc -c -o foo.o foo.c + cc -o foo.o -c foo.c cc -o foo foo.o % scons -Q bar Don't forget to copy `bar' to the archive! - cc -c -o bar.o bar.c + cc -o bar.o -c bar.c cc -o bar bar.o @@ -192,12 +192,12 @@ % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q scons: `hello' is up to date. % scons -Q goodbye - cc -c -o goodbye.o goodbye.c + cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o @@ -214,9 +214,9 @@ % scons -Q . - cc -c -o goodbye.o goodbye.c + cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -266,12 +266,12 @@ % scons -Q - cc -c -o prog1.o prog1.c + cc -o prog1.o -c prog1.c cc -o prog1 prog1.o - cc -c -o prog3.o prog3.c + cc -o prog3.o -c prog3.c cc -o prog3 prog3.o % scons -Q . - cc -c -o prog2.o prog2.c + cc -o prog2.o -c prog2.c cc -o prog2 prog2.o @@ -298,14 +298,14 @@ % scons -Q - cc -c -o prog1/foo.o prog1/foo.c - cc -c -o prog1/main.o prog1/main.c + cc -o prog1/foo.o -c prog1/foo.c + cc -o prog1/main.o -c prog1/main.c cc -o prog1/main prog1/main.o prog1/foo.o % scons -Q scons: `prog1' is up to date. % scons -Q . - cc -c -o prog2/bar.o prog2/bar.c - cc -c -o prog2/main.o prog2/main.c + cc -o prog2/bar.o -c prog2/bar.c + cc -o prog2/main.o -c prog2/main.c cc -o prog2/main prog2/main.o prog2/bar.o @@ -335,9 +335,9 @@ % scons -Q scons: *** No targets specified and no Default() targets found. Stop. % scons -Q . - cc -c -o prog1.o prog1.c + cc -o prog1.o -c prog1.c cc -o prog1 prog1.o - cc -c -o prog2.o prog2.c + cc -o prog2.o -c prog2.c cc -o prog2 prog2.o @@ -382,7 +382,7 @@ DEFAULT_TARGETS is ['prog1'] scons: done reading SConscript files. scons: Building targets ... - cc -c -o prog1.o prog1.c + cc -o prog1.o -c prog1.c cc -o prog1 prog1.o scons: done building targets. @@ -418,9 +418,9 @@ DEFAULT_TARGETS is now ['prog1', 'prog2'] scons: done reading SConscript files. scons: Building targets ... - cc -c -o prog1.o prog1.c + cc -o prog1.o -c prog1.c cc -o prog1 prog1.o - cc -c -o prog2.o prog2.c + cc -o prog2.o -c prog2.c cc -o prog2 prog2.o scons: done building targets. @@ -509,11 +509,11 @@ % scons -Q BUILD_TARGETS is ['prog1'] - cc -c -o prog1.o prog1.c + cc -o prog1.o -c prog1.c cc -o prog1 prog1.o % scons -Q prog2 BUILD_TARGETS is ['prog2'] - cc -c -o prog2.o prog2.c + cc -o prog2.o -c prog2.c cc -o prog2 prog2.o % scons -Q -c . BUILD_TARGETS is ['.'] @@ -593,12 +593,12 @@ % scons -Q debug=0 - cc -c -o prog.o prog.c + cc -o prog.o -c prog.c cc -o prog prog.o % scons -Q debug=0 scons: `.' is up to date. % scons -Q debug=1 - cc -g -c -o prog.o prog.c + cc -o prog.o -c -g prog.c cc -o prog prog.o % scons -Q debug=1 scons: `.' is up to date. @@ -693,8 +693,8 @@ % scons -Q RELEASE=1 - cc -DRELEASE_BUILD=1 -c -o bar.o bar.c - cc -DRELEASE_BUILD=1 -c -o foo.o 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 @@ -808,8 +808,8 @@ % scons -Q - cc -DRELEASE_BUILD=1 -c -o bar.o bar.c - cc -DRELEASE_BUILD=1 -c -o foo.o foo.c + cc -o bar.o -c -D['RELEASE_BUILD=', 1] bar.c + cc -o foo.o -c -D['RELEASE_BUILD=', 1] foo.c cc -o foo foo.o bar.o @@ -832,8 +832,8 @@ % scons -Q - cc -DRELEASE_BUILD=0 -c -o bar.o bar.c - cc -DRELEASE_BUILD=0 -c -o foo.o foo.c + cc -o bar.o -c -D['RELEASE_BUILD=', 0] bar.c + cc -o foo.o -c -D['RELEASE_BUILD=', 0] foo.c cc -o foo foo.o bar.o @@ -898,12 +898,12 @@ % scons -Q RELEASE=yes foo.o - cc -DRELEASE_BUILD=1 -c -o foo.o foo.c + cc -o foo.o -c -D['RELEASE_BUILD=', True] foo.c % scons -Q RELEASE=t foo.o - cc -DRELEASE_BUILD=1 -c -o foo.o foo.c + cc -o foo.o -c -D['RELEASE_BUILD=', True] foo.c @@ -929,12 +929,12 @@ % scons -Q RELEASE=no foo.o - cc -DRELEASE_BUILD=0 -c -o foo.o foo.c + cc -o foo.o -c -D['RELEASE_BUILD=', False] foo.c % scons -Q RELEASE=f foo.o - cc -DRELEASE_BUILD=0 -c -o foo.o foo.c + cc -o foo.o -c -D['RELEASE_BUILD=', False] foo.c @@ -961,7 +961,7 @@ scons: *** Error converting option: RELEASE Invalid value for boolean option: bad_value - File "SConstruct", line 4, in ? + File "/home/my/project/SConstruct", line 4, in ? @@ -1004,11 +1004,11 @@ % scons -Q COLOR=red foo.o - cc -DCOLOR="red" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="red" foo.c % scons -Q COLOR=blue foo.o - cc -DCOLOR="blue" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=green foo.o - cc -DCOLOR="green" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="green" foo.c @@ -1024,7 +1024,7 @@ % scons -Q COLOR=magenta foo.o scons: *** Invalid value for option COLOR: magenta - File "SConstruct", line 5, in ? + File "/home/my/project/SConstruct", line 5, in ? @@ -1063,7 +1063,7 @@ % scons -Q COLOR=navy foo.o - cc -DCOLOR="blue" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="blue" foo.c @@ -1080,15 +1080,15 @@ % scons -Q COLOR=Red foo.o scons: *** Invalid value for option COLOR: Red - File "SConstruct", line 5, in ? + File "/home/my/project/SConstruct", line 5, in ? % scons -Q COLOR=BLUE foo.o scons: *** Invalid value for option COLOR: BLUE - File "SConstruct", line 5, in ? + File "/home/my/project/SConstruct", line 5, in ? % scons -Q COLOR=nAvY foo.o scons: *** Invalid value for option COLOR: nAvY - File "SConstruct", line 5, in ? + File "/home/my/project/SConstruct", line 5, in ? @@ -1120,13 +1120,13 @@ % scons -Q COLOR=Red foo.o - cc -DCOLOR="Red" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="Red" foo.c % scons -Q COLOR=BLUE foo.o - cc -DCOLOR="BLUE" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="BLUE" foo.c % scons -Q COLOR=nAvY foo.o - cc -DCOLOR="blue" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=green foo.o - cc -DCOLOR="green" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="green" foo.c @@ -1164,11 +1164,11 @@ % scons -Q COLOR=Red foo.o - cc -DCOLOR="red" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="red" foo.c % scons -Q COLOR=nAvY foo.o - cc -DCOLOR="blue" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=GREEN foo.o - cc -DCOLOR="green" -c -o foo.o foo.c + cc -o foo.o -c -DCOLOR="green" foo.c @@ -1207,9 +1207,9 @@ % scons -Q COLORS=red,blue foo.o - cc -DCOLORS="red blue" -c -o foo.o foo.c + cc -o foo.o -c -DCOLORS="red blue" foo.c % scons -Q COLORS=blue,green,red foo.o - cc -DCOLORS="blue green red" -c -o foo.o foo.c + cc -o foo.o -c -DCOLORS="blue green red" foo.c @@ -1224,9 +1224,9 @@ % scons -Q COLORS=all foo.o - cc -DCOLORS="red green blue" -c -o foo.o foo.c + cc -o foo.o -c -DCOLORS="red green blue" foo.c % scons -Q COLORS=none foo.o - cc -DCOLORS="" -c -o foo.o foo.c + cc -o foo.o -c -DCOLORS="" foo.c @@ -1241,7 +1241,7 @@ scons: *** Error converting option: COLORS Invalid value(s) for option: magenta - File "SConstruct", line 5, in ? + File "/home/my/project/SConstruct", line 5, in ? @@ -1281,7 +1281,7 @@ % scons -Q foo.o - cc -DCONFIG_FILE="/etc/my_config" -c -o foo.o foo.c + cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c % scons -Q CONFIG=/usr/local/etc/other_config foo.o scons: `foo.o' is up to date. @@ -1298,7 +1298,7 @@ % scons -Q CONFIG=/does/not/exist foo.o scons: *** Path for option CONFIG does not exist: /does/not/exist - File "SConstruct", line 6, in ? + File "/home/my/project/SConstruct", line 6, in ? @@ -1426,13 +1426,13 @@ % scons -Q foo.o - cc -DPACKAGE="/opt/location" -c -o foo.o foo.c + cc -o foo.o -c -DPACKAGE="/opt/location" foo.c % scons -Q PACKAGE=/usr/local/location foo.o - cc -DPACKAGE="/usr/local/location" -c -o foo.o foo.c + cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c % scons -Q PACKAGE=yes foo.o - cc -DPACKAGE="1" -c -o foo.o foo.c + cc -o foo.o -c -D['PACKAGE="', True, '"'] foo.c % scons -Q PACKAGE=no foo.o - cc -DPACKAGE="0" -c -o foo.o foo.c + cc -o foo.o -c -D['PACKAGE="', False, '"'] foo.c diff --git a/doc/user/depends.sgml b/doc/user/depends.sgml index 3d740251..10a93b69 100644 --- a/doc/user/depends.sgml +++ b/doc/user/depends.sgml @@ -61,7 +61,7 @@ operating system on which the build is performed (as reported by C % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q scons: `.' is up to date. @@ -80,7 +80,7 @@ operating system on which the build is performed (as reported by C % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. @@ -132,7 +132,7 @@ operating system on which the build is performed (as reported by C % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % touch hello.c % scons -Q hello @@ -157,12 +157,12 @@ operating system on which the build is performed (as reported by C % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % edit hello.c [CHANGE THE CONTENTS OF hello.c] % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -212,11 +212,11 @@ operating system on which the build is performed (as reported by C % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % touch hello.c % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -259,12 +259,12 @@ operating system on which the build is performed (as reported by C % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % edit hello.c [CHANGE THE CONTENTS OF hello.c] % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -346,12 +346,12 @@ operating system on which the build is performed (as reported by C % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % edit hello.c [CHANGE A COMMENT IN hello.c] % scons -Q hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c scons: `hello' is up to date. @@ -433,14 +433,14 @@ operating system on which the build is performed (as reported by C % scons -Q hello - cc -I. -c -o hello.o hello.c + cc -o hello.o -c -I. hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. % edit hello.h [CHANGE THE CONTENTS OF hello.h] % scons -Q hello - cc -I. -c -o hello.o hello.c + cc -o hello.o -c -I. hello.c cc -o hello hello.o @@ -499,7 +499,7 @@ operating system on which the build is performed (as reported by C % scons -Q hello - cc -Iinclude -I/home/project/inc -c -o hello.o hello.c + cc -o hello.o -c -Iinclude -I/home/project/inc hello.c cc -o hello hello.o @@ -567,7 +567,7 @@ operating system on which the build is performed (as reported by C % scons -Q --implicit-cache hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. @@ -626,7 +626,7 @@ operating system on which the build is performed (as reported by C % scons -Q --implicit-deps-changed hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. @@ -664,7 +664,7 @@ operating system on which the build is performed (as reported by C % scons -Q --implicit-deps-unchanged hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q hello scons: `hello' is up to date. diff --git a/doc/user/environments.sgml b/doc/user/environments.sgml index 90f6e3f5..9404c233 100644 --- a/doc/user/environments.sgml +++ b/doc/user/environments.sgml @@ -125,7 +125,7 @@ UNIX system are: ENV => { 'PATH' => '/bin:/usr/bin' }, -And on a Win32 system (Windows NT), the default construction variables +And on a Windows system (Windows NT), the default construction variables are (unless the default rule style is set using the B method): @@ -473,7 +473,7 @@ environment undisturbed. % scons -Q - gcc -O2 -c -o foo.o foo.c + gcc -o foo.o -c -O2 foo.c gcc -o foo foo.o @@ -505,9 +505,9 @@ environment undisturbed. % scons -Q - cc -g -c -o bar.o bar.c + cc -o bar.o -c -g bar.c cc -o bar bar.o - cc -O2 -c -o foo.o foo.c + cc -o foo.o -c -O2 foo.c cc -o foo foo.o @@ -540,7 +540,7 @@ environment undisturbed. % scons -Q scons: *** Two environments with different actions were specified for the same target: foo.o - File "SConstruct", line 6, in ? + File "/home/my/project/SConstruct", line 6, in ? @@ -593,9 +593,9 @@ environment undisturbed. % scons -Q - cc -g -c -o foo-dbg.o foo.c + cc -o foo-dbg.o -c -g foo.c cc -o foo-dbg foo-dbg.o - cc -O2 -c -o foo-opt.o foo.c + cc -o foo-opt.o -c -O2 foo.c cc -o foo-opt foo-opt.o @@ -653,11 +653,11 @@ environment undisturbed. % scons -Q - gcc -c -o foo.o foo.c + gcc -o foo.o -c foo.c gcc -o foo foo.o - gcc -g -c -o foo-dbg.o foo.c + gcc -o foo-dbg.o -c -g foo.c gcc -o foo-dbg foo-dbg.o - gcc -O2 -c -o foo-opt.o foo.c + gcc -o foo-opt.o -c -O2 foo.c gcc -o foo-opt foo-opt.o @@ -728,7 +728,7 @@ environment undisturbed. - And on Win32: + And on Windows: @@ -889,7 +889,7 @@ environment undisturbed. % scons -Q - cc -DDEFINE2 -c -o foo.o foo.c + cc -o foo.o -c -DDEFINE2 foo.c cc -o foo foo.o @@ -965,9 +965,9 @@ environment undisturbed. CCFLAGS = -DDEFINE2 scons: done reading SConscript files. scons: Building targets ... - cc -DDEFINE2 -c -o bar.o bar.c + cc -o bar.o -c -DDEFINE2 bar.c cc -o bar bar.o - cc -DDEFINE2 -c -o foo.o foo.c + cc -o foo.o -c -DDEFINE2 foo.c cc -o foo foo.o scons: done building targets. @@ -1014,7 +1014,7 @@ environment undisturbed. % scons -Q - cc -DMY_VALUE -DLAST -c -o foo.o foo.c + cc -o foo.o -c -DMY_VALUE -DLAST foo.c cc -o foo foo.o @@ -1071,7 +1071,7 @@ environment undisturbed. % scons -Q - cc -DFIRST -DMY_VALUE -c -o foo.o foo.c + cc -o foo.o -c -DFIRST -DMY_VALUE foo.c cc -o foo foo.o diff --git a/doc/user/hierarchy.sgml b/doc/user/hierarchy.sgml index 047545d9..0ce24300 100644 --- a/doc/user/hierarchy.sgml +++ b/doc/user/hierarchy.sgml @@ -317,13 +317,13 @@ make no difference to the build. % scons -Q - cc -c -o prog1/foo1.o prog1/foo1.c - cc -c -o prog1/foo2.o prog1/foo2.c - cc -c -o prog1/main.o prog1/main.c + cc -o prog1/foo1.o -c prog1/foo1.c + cc -o prog1/foo2.o -c prog1/foo2.c + cc -o prog1/main.o -c prog1/main.c cc -o prog1/prog1 prog1/main.o prog1/foo1.o prog1/foo2.o - cc -c -o prog2/bar1.o prog2/bar1.c - cc -c -o prog2/bar2.o prog2/bar2.c - cc -c -o prog2/main.o prog2/main.c + cc -o prog2/bar1.o -c prog2/bar1.c + cc -o prog2/bar2.o -c prog2/bar2.c + cc -o prog2/main.o -c prog2/main.c cc -o prog2/prog2 prog2/main.o prog2/bar1.o prog2/bar2.o @@ -382,9 +382,9 @@ make no difference to the build. % scons -Q - cc -c -o lib/foo1.o lib/foo1.c - cc -c -o src/prog/foo2.o src/prog/foo2.c - cc -c -o src/prog/main.o src/prog/main.c + cc -o lib/foo1.o -c lib/foo1.c + cc -o src/prog/foo2.o -c src/prog/foo2.c + cc -o src/prog/main.o -c src/prog/main.c cc -o src/prog/prog src/prog/main.o lib/foo1.o src/prog/foo2.o @@ -423,9 +423,9 @@ make no difference to the build. % scons -Q - cc -c -o src/prog/foo2.o src/prog/foo2.c - cc -c -o src/prog/main.o src/prog/main.c - cc -c -o /usr/joe/lib/foo1.o /usr/joe/lib/foo1.c + cc -o src/prog/foo2.o -c src/prog/foo2.c + cc -o src/prog/main.o -c src/prog/main.c + cc -o /usr/joe/lib/foo1.o -c /usr/joe/lib/foo1.c cc -o src/prog/prog src/prog/main.o /usr/joe/lib/foo1.o src/prog/foo2.o @@ -716,9 +716,9 @@ make no difference to the build. % scons -Q - cc -c -o bar/bar.o bar/bar.c - cc -c -o foo/foo.o foo/foo.c - ar r libprog.a foo/foo.o bar/bar.o + cc -o bar/bar.o -c bar/bar.c + cc -o foo/foo.o -c foo/foo.c + ar rc libprog.a foo/foo.o bar/bar.o ranlib libprog.a diff --git a/doc/user/install.sgml b/doc/user/install.sgml index 430b336d..2a6d1b89 100644 --- a/doc/user/install.sgml +++ b/doc/user/install.sgml @@ -58,7 +58,7 @@ % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q /usr/bin Install file: "hello" as "/usr/bin/hello" @@ -95,7 +95,7 @@ % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o % scons -Q install Install file: "hello" as "/usr/bin/hello" @@ -144,10 +144,10 @@ % scons -Q install - cc -c -o goodbye.o goodbye.c + cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o Install file: "goodbye" as "/usr/bin/goodbye" - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello" @@ -184,7 +184,7 @@ % scons -Q install - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello-new" @@ -226,10 +226,10 @@ % scons -Q install - cc -c -o goodbye.o goodbye.c + cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o Install file: "goodbye" as "/usr/bin/goodbye-new" - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o Install file: "hello" as "/usr/bin/hello-new" diff --git a/doc/user/less-simple.sgml b/doc/user/less-simple.sgml index 1aa993d7..ad208124 100644 --- a/doc/user/less-simple.sgml +++ b/doc/user/less-simple.sgml @@ -87,7 +87,7 @@ % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o new_hello hello.o @@ -135,9 +135,9 @@ % scons -Q - cc -c -o file1.o file1.c - cc -c -o file2.o file2.c - cc -c -o main.o main.c + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o main.o -c main.c cc -o main main.o file1.o file2.o @@ -175,9 +175,9 @@ % scons -Q - cc -c -o file1.o file1.c - cc -c -o file2.o file2.c - cc -c -o main.o main.c + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o main.o -c main.c cc -o program main.o file1.o file2.o @@ -459,10 +459,10 @@ % scons -Q - cc -c -o bar1.o bar1.c - cc -c -o bar2.o bar2.c + cc -o bar1.o -c bar1.c + cc -o bar2.o -c bar2.c cc -o bar bar1.o bar2.o - cc -c -o foo.o foo.c + cc -o foo.o -c foo.c cc -o foo foo.o @@ -522,12 +522,12 @@ % scons -Q - cc -c -o bar1.o bar1.c - cc -c -o bar2.o bar2.c - cc -c -o common1.o common1.c - cc -c -o common2.o common2.c + cc -o bar1.o -c bar1.c + cc -o bar2.o -c bar2.c + cc -o common1.o -c common1.c + cc -o common2.o -c common2.c cc -o bar bar1.o bar2.o common1.o common2.o - cc -c -o foo.o foo.c + cc -o foo.o -c foo.c cc -o foo foo.o common1.o common2.o diff --git a/doc/user/libraries.sgml b/doc/user/libraries.sgml index aab30450..ca2cb973 100644 --- a/doc/user/libraries.sgml +++ b/doc/user/libraries.sgml @@ -57,10 +57,10 @@ % scons -Q - cc -c -o f1.o f1.c - cc -c -o f2.o f2.c - cc -c -o f3.o f3.c - ar r libfoo.a f1.o f2.o f3.o + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o ranlib libfoo.a @@ -139,10 +139,10 @@ % scons -Q - cc -c -o f1.os f1.c - cc -c -o f2.os f2.c - cc -c -o f3.os f3.c - cc -shared -o libfoo.so f1.os f2.os f3.os + cc -o f1.os -c f1.c + cc -o f2.os -c f2.c + cc -o f3.os -c f3.c + cc -o libfoo.so -shared f1.os f2.os f3.os @@ -212,12 +212,12 @@ % scons -Q - cc -c -o f1.o f1.c - cc -c -o f2.o f2.c - cc -c -o f3.o f3.c - ar r libfoo.a f1.o f2.o f3.o + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o ranlib libfoo.a - cc -c -o prog.o prog.c + cc -o prog.o -c prog.c cc -o prog prog.o -L. -lfoo -lbar @@ -345,7 +345,7 @@ % scons -Q - cc -c -o prog.o prog.c + cc -o prog.o -c prog.c cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm diff --git a/doc/user/nodes.in b/doc/user/nodes.in index f5faf5ae..4b8aa0f9 100644 --- a/doc/user/nodes.in +++ b/doc/user/nodes.in @@ -310,7 +310,7 @@ - + scons -Q @@ -328,7 +328,7 @@ - + hello_c = File('hello.c') contents = hello_c.read() @@ -346,7 +346,7 @@ - + scons -Q diff --git a/doc/user/nodes.sgml b/doc/user/nodes.sgml index 114e9e0a..c8756c55 100644 --- a/doc/user/nodes.sgml +++ b/doc/user/nodes.sgml @@ -115,8 +115,8 @@ % scons -Q - cc -DGOODBYE -c -o goodbye.o goodbye.c - cc -DHELLO -c -o hello.o hello.c + cc -o goodbye.o -c -DGOODBYE goodbye.c + cc -o hello.o -c -DHELLO hello.c cc -o hello hello.o goodbye.o @@ -254,7 +254,7 @@ % scons -Q The object file is: hello.o The program file is: hello - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -312,9 +312,8 @@ % scons -Q - The object file is: hello.o - The program file is: hello - cc -c -o hello.o hello.c + hello does not exist! + cc -o hello.o -c hello.c cc -o hello hello.o @@ -332,7 +331,7 @@ - + hello_c = File('hello.c') contents = hello_c.read() @@ -350,7 +349,7 @@ - + scons -Q diff --git a/doc/user/preface.sgml b/doc/user/preface.sgml index 47dbd983..ba5d1a34 100644 --- a/doc/user/preface.sgml +++ b/doc/user/preface.sgml @@ -345,12 +345,12 @@ &SCons; has received contributions from many other people, of course: - Matt Balvin (extending long command-line support on Win32), + Matt Balvin (extending long command-line support on Windows), Allen Bierbaum (extensions and fixes to Options), Steve Christensen (help text sorting and function action signature fixes), Michael Cook (avoiding losing signal bits from executed commands), Derrick 'dman' Hudson (), - Alex Jacques (work on the Win32 scons.bat file), + Alex Jacques (work on the Windows scons.bat file), Stephen Kennedy (performance enhancements), Lachlan O'Dea (SharedObject() support for masm and normalized paths for the WhereIs() function), diff --git a/doc/user/repositories.in b/doc/user/repositories.in index d2c92369..a667a911 100644 --- a/doc/user/repositories.in +++ b/doc/user/repositories.in @@ -21,118 +21,6 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---> - - @@ -170,11 +58,13 @@ subdirectories under the repository tree. a directory copy of the source code tree. (Note that this is not the sort of repository maintained by a source code management system - like BitKeeper, CVS, or Subversion. + like BitKeeper, CVS, or Subversion.) + You use the &Repository; method to tell &SCons; to search one or more central code repositories (in order) @@ -187,7 +77,7 @@ subdirectories under the repository tree. env = Environment() env.Program('hello.c') - Repository('/usr/repository1', '/usr/repository2') + Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') int main() { printf("Hello, world!\n"); } @@ -256,17 +146,15 @@ subdirectories under the repository tree. env = Environment() env.Program('hello.c') - Repository('/usr/repository1', '/usr/repository2') + Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') - + int main() { printf("Hello, world!\n"); } scons -Q - gcc -c /usr/repository1/hello.c -o hello.o - gcc -o hello hello.o @@ -281,9 +169,9 @@ subdirectories under the repository tree. env = Environment() env.Program('hello.c') - Repository('/usr/repository1', '/usr/repository2') + Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2') - + int main() { printf("Hello, world!\n"); } @@ -298,6 +186,252 @@ subdirectories under the repository tree. +
+ Finding <literal>#include</literal> files in repositories + + + + We've already seen that SCons will scan the contents of + 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, + &SCons; will actually search the corresponding directories + in any repository trees and establish the + correct dependencies on any + #include files that it finds + in repository directory. + + + + + + Unless the C compiler also knows about these directories + in the repository trees, though, + it will be unable to find the #include files. + If, for example, the &hello_c; file in + our previous example includes the &hello.h; + in its current directory, + and the &hello.h; only exists in the repository: + + + + + % scons -Q + cc -o hello.o -c hello.c + hello.c:1: hello.h: No such file or directory + + + + + In order to inform the C compiler about the repositories, + &SCons; will add appropriate + -I flags to the compilation commands + for each directory in the &cv-CPPPATH; list. + So if we add the current directory to the + construction environment &cv-CPPPATH; like so: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('hello.c') + Repository('__ROOT__/usr/repository1') + + + int main() { printf("Hello, world!\n"); } + + + + + + Then re-executing &SCons; yields: + + + + + scons -Q + + + + + The order of the -I options replicates, + for the C preprocessor, + the same repository-directory search path + that &SCons; uses for its own dependency analysis. + If there are multiple repositories and multiple &cv-CPPPATH; + directories, &SCons; will add the repository directories + to the beginning of each &cv-CPPPATH; directory, + rapidly multiplying the number of -I flags. + If, for example, the &cv-CPPPATH; contains three directories + (and shorter repository path names!): + + + + + + env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3']) + env.Program('hello.c') + Repository('__ROOT__/r1', '__ROOT__/r2') + + + int main() { printf("Hello, world!\n"); } + + + + + + Then we'll end up with nine -I options + on the command line, + three (for each of the &cv-CPPPATH; directories) + times three (for the local directory plus the two repositories): + + + + + scons -Q + + + + +
+ Limitations on <literal>#include</literal> files in repositories + + + + &SCons; relies on the C compiler's + -I options to control the order in which + the preprocessor will search the repository directories + for #include files. + This causes a problem, however, with how the C preprocessor + handles #include lines with + the file name included in double-quotes. + + + + + + As we've seen, + &SCons; will compile the &hello_c; file from + the repository if it doesn't exist in + the local directory. + If, however, the &hello_c; file in the repository contains + a #include line with the file name in + double quotes: + + + + + #include "hello.h" + int + main(int argc, char *argv[]) + { + printf(HELLO_MESSAGE); + return (0); + } + + + + + Then the C preprocessor will always + use a &hello_h; file from the repository directory first, + even if there is a &hello_h; file in the local directory, + despite the fact that the command line specifies + -I as the first option: + + + + + + env = Environment(CPPPATH = ['.']) + env.Program('hello.c') + Repository('__ROOT__/usr/repository1') + + + int main() { printf("Hello, world!\n"); } + + + + + scons -Q + + + + + This behavior of the C preprocessor--always search + for a #include file in double-quotes + first in the same directory as the source file, + and only then search the -I--can + not, in general, be changed. + In other words, it's a limitation + that must be lived with if you want to use + code repositories in this way. + There are three ways you can possibly + work around this C preprocessor behavior: + + + + + + + + + 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). + Make sure the option is used for all construction + environments that use C preprocessing! + + + + + + + + Change all occurrences of #include "file.h" + to #include &lt;file.h&gt;. + Use of #include with angle brackets + does not have the same behavior--the -I + directories are searched first + for #include files--which + gives &SCons; direct control over the list of + directories the C preprocessor will search. + + + + + + + + Require that everyone working with compilation from + repositories check out and work on entire directories of files, + not individual files. + (If you use local wrapper scripts around + your source code control system's command, + you could add logic to enforce this restriction there. + + + + + + +
+ +
+
Finding the &SConstruct; file in repositories diff --git a/doc/user/repositories.sgml b/doc/user/repositories.sgml index 03928536..c659aa22 100644 --- a/doc/user/repositories.sgml +++ b/doc/user/repositories.sgml @@ -21,118 +21,6 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---> - - @@ -170,11 +58,13 @@ subdirectories under the repository tree. a directory copy of the source code tree. (Note that this is not the sort of repository maintained by a source code management system - like BitKeeper, CVS, or Subversion. + like BitKeeper, CVS, or Subversion.) + You use the &Repository; method to tell &SCons; to search one or more central code repositories (in order) @@ -236,7 +126,7 @@ subdirectories under the repository tree. % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -253,10 +143,8 @@ subdirectories under the repository tree. % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c /usr/repository1/hello.c cc -o hello hello.o - gcc -c /usr/repository1/hello.c -o hello.o - gcc -o hello hello.o @@ -271,14 +159,247 @@ subdirectories under the repository tree. % scons -Q - cc -c -o hello.o hello.c + cc -o hello.o -c /usr/repository2/hello.c + cc -o hello hello.o + + + + + + +
+ +
+ Finding <literal>#include</literal> files in repositories + + + + We've already seen that SCons will scan the contents of + 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, + &SCons; will actually search the corresponding directories + in any repository trees and establish the + correct dependencies on any + #include files that it finds + in repository directory. + + + + + + Unless the C compiler also knows about these directories + in the repository trees, though, + it will be unable to find the #include files. + If, for example, the &hello_c; file in + our previous example includes the &hello;.h; + in its current directory, + and the &hello;.h; only exists in the repository: + + + + + % scons -Q + cc -o hello.o -c hello.c + hello.c:1: hello.h: No such file or directory + + + + + In order to inform the C compiler about the repositories, + &SCons; will add appropriate + -I flags to the compilation commands + for each directory in the &cv-CPPPATH; list. + So if we add the current directory to the + construction environment &cv-CPPPATH; like so: + + + + + env = Environment(CPPPATH = ['.']) + env.Program('hello.c') + Repository('/usr/repository1') + + + + + Then re-executing &SCons; yields: + + + + + % scons -Q + cc -o hello.o -c -I. -I/usr/repository1 hello.c cc -o hello hello.o + The order of the -I options replicates, + for the C preprocessor, + the same repository-directory search path + that &SCons; uses for its own dependency analysis. + If there are multiple repositories and multiple &cv-CPPPATH; + directories, &SCons; will add the repository directories + to the beginning of each &cv-CPPPATH; directory, + rapidly multiplying the number of -I flags. + If, for example, the &cv-CPPPATH; contains three directories + (and shorter repository path names!): + + + env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3']) + env.Program('hello.c') + Repository('/r1', '/r2') + + + + + Then we'll end up with nine -I options + on the command line, + three (for each of the &cv-CPPPATH; directories) + times three (for the local directory plus the two repositories): + + + + + % scons -Q + cc -o hello.o -c -Idir1 -I/r1/dir1 -I/r2/dir1 -Idir2 -I/r1/dir2 -I/r2/dir2 -Idir3 -I/r1/dir3 -I/r2/dir3 hello.c + cc -o hello hello.o + + + + +
+ Limitations on <literal>#include</literal> files in repositories + + + + &SCons; relies on the C compiler's + -I options to control the order in which + the preprocessor will search the repository directories + for #include files. + This causes a problem, however, with how the C preprocessor + handles #include lines with + the file name included in double-quotes. + + + + + + As we've seen, + &SCons; will compile the &hello_c; file from + the repository if it doesn't exist in + the local directory. + If, however, the &hello_c; file in the repository contains + a #include line with the file name in + double quotes: + + + + + #include "hello.h" + int + main(int argc, char *argv[]) + { + printf(HELLO_MESSAGE); + return (0); + } + + + + + Then the C preprocessor will always + use a &hello_h; file from the repository directory first, + even if there is a &hello_h; file in the local directory, + despite the fact that the command line specifies + -I as the first option: + + + + + + + % scons -Q + cc -o hello.o -c -I. -I/usr/repository1 /usr/repository1/hello.c + cc -o hello hello.o + + + + + This behavior of the C preprocessor--always search + for a #include file in double-quotes + first in the same directory as the source file, + and only then search the -I--can + not, in general, be changed. + In other words, it's a limitation + that must be lived with if you want to use + code repositories in this way. + There are three ways you can possibly + work around this C preprocessor behavior: + + + + + + + + + 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). + Make sure the option is used for all construction + environments that use C preprocessing! + + + + + + + + Change all occurrences of #include "file.h" + to #include <file.h>. + Use of #include with angle brackets + does not have the same behavior--the -I + directories are searched first + for #include files--which + gives &SCons; direct control over the list of + directories the C preprocessor will search. + + + + + + + + Require that everyone working with compilation from + repositories check out and work on entire directories of files, + not individual files. + (If you use local wrapper scripts around + your source code control system's command, + you could add logic to enforce this restriction there. + + + + + + +
+
@@ -346,9 +467,9 @@ subdirectories under the repository tree. % cd /usr/repository1 % scons -Q - cc -c -o file1.o file1.c - cc -c -o file2.o file2.c - cc -c -o hello.o hello.c + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o hello.o -c hello.c cc -o hello hello.o file1.o file2.o diff --git a/doc/user/separate.sgml b/doc/user/separate.sgml index 1c04f8ae..5f0341d6 100644 --- a/doc/user/separate.sgml +++ b/doc/user/separate.sgml @@ -154,7 +154,7 @@ program using the F path name. % ls src SConscript hello.c % scons -Q - cc -c -o build/hello.o build/hello.c + cc -o build/hello.o -c build/hello.c cc -o build/hello build/hello.o % ls build SConscript hello hello.c hello.o @@ -350,7 +350,7 @@ program using the F path name. % ls src hello.c % scons -Q - cc -c -o build/hello.o build/hello.c + cc -o build/hello.o -c build/hello.c cc -o build/hello build/hello.o % ls build hello hello.c hello.o @@ -380,7 +380,7 @@ program using the F path name. % ls src hello.c % scons -Q - cc -c -o build/hello.o src/hello.c + cc -o build/hello.o -c src/hello.c cc -o build/hello build/hello.o % ls build hello hello.o @@ -429,7 +429,7 @@ program using the F path name. % ls src SConscript hello.c % scons -Q - cc -c -o build/hello.o build/hello.c + cc -o build/hello.o -c build/hello.c cc -o build/hello build/hello.o % ls build SConscript hello hello.c hello.o diff --git a/doc/user/simple.sgml b/doc/user/simple.sgml index fc68d170..9d3617d4 100644 --- a/doc/user/simple.sgml +++ b/doc/user/simple.sgml @@ -91,7 +91,7 @@ scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o scons: done building targets. @@ -179,7 +179,7 @@ scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c scons: done building targets. @@ -275,7 +275,7 @@ scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o scons: done building targets. % scons -c @@ -469,9 +469,9 @@ Finished calling Program() scons: done reading SConscript files. scons: Building targets ... - cc -c -o goodbye.o goodbye.c + cc -o goodbye.o -c goodbye.c cc -o goodbye goodbye.o - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o scons: done building targets. diff --git a/doc/user/sourcecode.sgml b/doc/user/sourcecode.sgml index 6ea0b21f..6cb41626 100644 --- a/doc/user/sourcecode.sgml +++ b/doc/user/sourcecode.sgml @@ -52,7 +52,7 @@ % scons -Q bk get - bk get hello.c - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -77,7 +77,7 @@ % scons -Q cvs -d /usr/local/CVS co - cvs -d /usr/local/CVS co hello.c - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -102,7 +102,7 @@ % scons -Q co - co hello.c - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o @@ -127,7 +127,7 @@ % scons -Q sccs get - sccs get hello.c - cc -c -o hello.o hello.c + cc -o hello.o -c hello.c cc -o hello hello.o diff --git a/doc/user/troubleshoot.sgml b/doc/user/troubleshoot.sgml index 5cd1c9e5..f019baa2 100644 --- a/doc/user/troubleshoot.sgml +++ b/doc/user/troubleshoot.sgml @@ -133,15 +133,15 @@ % scons -Q - cc -c -o file1.o file1.c - cc -c -o file2.o file2.c - cc -c -o file3.o file3.c + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o file3.o -c file3.c cc -o prog file1.o file2.o file3.o % edit file2.c [CHANGE THE CONTENTS OF file2.c] % scons -Q --debug=explain scons: rebuilding `file2.o' because `file2.c' changed - cc -c -o file2.o file2.c + cc -o file2.o -c file2.c scons: rebuilding `prog' because `file2.o' changed cc -o prog file1.o file2.o file3.o @@ -167,17 +167,17 @@ % scons -Q - cc -I. -c -o file1.o file1.c - cc -I. -c -o file2.o file2.c - cc -I. -c -o file3.o file3.c + cc -o file1.o -c -I. file1.c + cc -o file2.o -c -I. file2.c + cc -o file3.o -c -I. file3.c cc -o prog file1.o file2.o file3.o % edit hello.h [CHANGE THE CONTENTS OF hello.h] % scons -Q --debug=explain scons: rebuilding `file1.o' because `hello.h' changed - cc -I. -c -o file1.o file1.c + cc -o file1.o -c -I. file1.c scons: rebuilding `file3.o' because `hello.h' changed - cc -I. -c -o file3.o file3.c + cc -o file3.o -c -I. file3.c scons: rebuilding `prog' because: `file1.o' changed `file3.o' changed @@ -226,6 +226,8 @@ % scons scons: Reading SConscript files ... { 'BUILDERS': {}, + 'CONFIGUREDIR': '#/.sconf_temp', + 'CONFIGURELOG': '#/config.log', 'CPPSUFFIXES': [ '.c', '.C', '.cxx', @@ -240,48 +242,52 @@ '.F', '.fpp', '.FPP', + '.m', + '.mm', '.S', '.spp', '.SPP'], 'DSUFFIXES': ['.d'], - 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0x829dcb4>, - 'ENV': {'PATH': '/usr/local/bin:/bin:/usr/bin'}, - 'ESCAPE': <function escape at 0x837d2a4>, - 'File': <SCons.Defaults.Variable_Method_Caller instance at 0x829e0fc>, + 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43bec>, + 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c0c>, + 'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'}, + 'ESCAPE': <function escape at 0xb7b66c34>, + 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c2c>, 'IDLSUFFIXES': ['.idl', '.IDL'], - 'INSTALL': <function copyFunc at 0x829db9c>, + 'INSTALL': <function installFunc at 0xb7c41f0c>, + 'INSTALLSTR': <function installStr at 0xb7c41f44>, + 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'], 'LIBPREFIX': 'lib', 'LIBPREFIXES': '$LIBPREFIX', 'LIBSUFFIX': '.a', 'LIBSUFFIXES': ['$LIBSUFFIX', '$SHLIBSUFFIX'], + 'MAXLINELENGTH': 128072, 'OBJPREFIX': '', 'OBJSUFFIX': '.o', - 'PDFPREFIX': '', - 'PDFSUFFIX': '.pdf', 'PLATFORM': 'posix', 'PROGPREFIX': '', 'PROGSUFFIX': '', - 'PSPAWN': <function piped_env_spawn at 0x837d384>, - 'PSPREFIX': '', - 'PSSUFFIX': '.ps', - 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0x829e46c>, + 'PSPAWN': <function piped_env_spawn at 0xb7b66fb4>, + 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c4c>, 'SCANNERS': [], 'SHELL': 'sh', 'SHLIBPREFIX': '$LIBPREFIX', 'SHLIBSUFFIX': '.so', 'SHOBJPREFIX': '$OBJPREFIX', 'SHOBJSUFFIX': '$OBJSUFFIX', - 'SPAWN': <function spawnvpe_spawn at 0x8377fdc>, - 'TEMPFILE': <class SCons.Defaults.NullCmdGenerator at 0x829ddec>, + 'SPAWN': <function spawnvpe_spawn at 0xb7b66a74>, + 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7bd37ac>, + 'TEMPFILEPREFIX': '@', 'TOOLS': [], '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', - '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET)} $)', - '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET)} $)', + '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', '__RPATH': '$_RPATH', - '_concat': <function _concat at 0x829dc0c>, - '_defines': <function _defines at 0x829dc7c>, - '_stripixes': <function _stripixes at 0x829dc44>} + '_concat': <function _concat at 0xb7c41fb4>, + '_defines': <function _defines at 0xb7c47064>, + '_installStr': <function installStr at 0xb7c41f44>, + '_stripixes': <function _stripixes at 0xb7c4702c>} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. @@ -298,14 +304,17 @@ C:\>scons scons: Reading SConscript files ... - { 'BUILDERS': {'Object': <SCons.Memoize.MultiStepBuilder object at 0x83493e4>, 'SharedObject': <SCons.Memoize.MultiStepBuilder object at 0x8349fec>, 'StaticObject': <SCons.Memoize.MultiStepBuilder object at 0x83493e4>, 'PCH': <SCons.Memoize.BuilderBase object at 0x83418cc>, 'RES': <SCons.Memoize.BuilderBase object at 0x8367cec>}, + { '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>}, 'CC': 'cl', - 'CCCOM': <SCons.Memoize.FunctionAction object at 0x8340454>, + 'CCCOM': <SCons.Action.FunctionAction instance at 0xb7b6086c>, 'CCCOMFLAGS': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS', 'CCFLAGS': ['/nologo'], 'CCPCHFLAGS': ['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'], 'CCPDBFLAGS': ['${(PDB and "/Z7") or ""}'], 'CFILESUFFIX': '.c', + 'CFLAGS': [], + 'CONFIGUREDIR': '#/.sconf_temp', + 'CONFIGURELOG': '#/config.log', 'CPPDEFPREFIX': '/D', 'CPPDEFSUFFIX': '', 'CPPSUFFIXES': [ '.c', @@ -322,6 +331,8 @@ '.F', '.fpp', '.FPP', + '.m', + '.mm', '.S', '.spp', '.SPP'], @@ -330,17 +341,21 @@ 'CXXFILESUFFIX': '.cc', 'CXXFLAGS': ['$CCFLAGS', '$(', '/TP', '$)'], 'DSUFFIXES': ['.d'], - 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0x829dcb4>, + 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58bec>, + 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c0c>, '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'}, - 'ESCAPE': <function <lambda> at 0x82339ec>, - 'File': <SCons.Defaults.Variable_Method_Caller instance at 0x829e0fc>, + 'PATHEXT': '.COM;.EXE;.BAT;.CMD', + 'SystemRoot': 'C:/WINDOWS'}, + 'ESCAPE': <function escape at 0xb7bc917c>, + 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c2c>, 'IDLSUFFIXES': ['.idl', '.IDL'], 'INCPREFIX': '/I', 'INCSUFFIX': '', - 'INSTALL': <function copyFunc at 0x829db9c>, + 'INSTALL': <function installFunc at 0xb7c56f0c>, + 'INSTALLSTR': <function installStr at 0xb7c56f44>, + 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'], 'LIBPREFIX': '', 'LIBPREFIXES': ['$LIBPREFIX'], 'LIBSUFFIX': '.lib', @@ -352,22 +367,19 @@ 'OBJSUFFIX': '.obj', 'PCHCOM': '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS', 'PCHPDBFLAGS': ['${(PDB and "/Yd") or ""}'], - 'PDFPREFIX': '', - 'PDFSUFFIX': '.pdf', 'PLATFORM': 'win32', 'PROGPREFIX': '', 'PROGSUFFIX': '.exe', - 'PSPAWN': <function piped_spawn at 0x8372bc4>, - 'PSPREFIX': '', - 'PSSUFFIX': '.ps', + 'PSPAWN': <function piped_spawn at 0xb7bc90d4>, 'RC': 'rc', 'RCCOM': '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES', 'RCFLAGS': [], - 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0x829e46c>, + 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c4c>, 'SCANNERS': [], 'SHCC': '$CC', - 'SHCCCOM': <SCons.Memoize.FunctionAction object at 0x83494bc>, + 'SHCCCOM': <SCons.Action.FunctionAction instance at 0xb7b608cc>, 'SHCCFLAGS': ['$CCFLAGS'], + 'SHCFLAGS': ['$CFLAGS'], 'SHCXX': '$CXX', 'SHCXXCOM': '$SHCXX $SHCXXFLAGS $CCCOMFLAGS', 'SHCXXFLAGS': ['$CXXFLAGS'], @@ -376,17 +388,19 @@ 'SHLIBSUFFIX': '.dll', 'SHOBJPREFIX': '$OBJPREFIX', 'SHOBJSUFFIX': '$OBJSUFFIX', - 'SPAWN': <function spawn at 0x8374c34>, + 'SPAWN': <function spawn at 0xb7bc9144>, 'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1, - 'TEMPFILE': <class SCons.Platform.win32.TempFileMunge at 0x835edc4>, + 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7be87ac>, + 'TEMPFILEPREFIX': '@', 'TOOLS': ['msvc'], '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', - '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET)} $)', - '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET)} $)', + '_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 0x829dc0c>, - '_defines': <function _defines at 0x829dc7c>, - '_stripixes': <function _stripixes at 0x829dc44>} + '_concat': <function _concat at 0xb7c56fb4>, + '_defines': <function _defines at 0xb7c5c064>, + '_installStr': <function installStr at 0xb7c56f44>, + '_stripixes': <function _stripixes at 0xb7c5c02c>} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. @@ -431,7 +445,7 @@ % scons scons: Reading SConscript files ... - {'PATH': '/usr/local/bin:/bin:/usr/bin'} + {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. @@ -450,7 +464,8 @@ { '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'} + 'PATHEXT': '.COM;.EXE;.BAT;.CMD', + 'SystemRoot': 'C:/WINDOWS'} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. diff --git a/doc/user/variants.sgml b/doc/user/variants.sgml index 57d96e02..67278595 100644 --- a/doc/user/variants.sgml +++ b/doc/user/variants.sgml @@ -91,9 +91,9 @@ is pretty smart about rebuilding things when you change options. % scons -Q OS=linux Install file: "build/linux/world/world.h" as "export/linux/include/world.h" - cc -Iexport/linux/include -c -o build/linux/hello/hello.o build/linux/hello/hello.c - cc -Iexport/linux/include -c -o build/linux/world/world.o build/linux/world/world.c - ar r build/linux/world/libworld.a build/linux/world/world.o + cc -o build/linux/hello/hello.o -c -Iexport/linux/include build/linux/hello/hello.c + cc -o build/linux/world/world.o -c -Iexport/linux/include build/linux/world/world.c + ar rc build/linux/world/libworld.a build/linux/world/world.o ranlib build/linux/world/libworld.a Install file: "build/linux/world/libworld.a" as "export/linux/lib/libworld.a" cc -o build/linux/hello/hello build/linux/hello/hello.o -Lexport/linux/lib -lworld diff --git a/runtest.py b/runtest.py index 7ee1fea5..0d9519e9 100644 --- a/runtest.py +++ b/runtest.py @@ -99,8 +99,11 @@ import time if not hasattr(os, 'WEXITSTATUS'): os.WEXITSTATUS = lambda x: x +cwd = os.getcwd() + all = 0 baseline = 0 +builddir = os.path.join(cwd, 'build') debug = '' execute_tests = 1 format = None @@ -118,14 +121,13 @@ python = None sp = None spe = None -cwd = os.getcwd() - helpstr = """\ Usage: runtest.py [OPTIONS] [TEST ...] Options: -a, --all Run all tests. --aegis Print results in Aegis format. -b BASE, --baseline BASE Run test scripts against baseline BASE. + --builddir DIR Directory in which packages were built. -d, --debug Run test scripts under the Python debugger. -f FILE, --file FILE Run tests in specified FILE. -h, --help Print this message and exit. @@ -160,7 +162,7 @@ Options: """ opts, args = getopt.getopt(sys.argv[1:], "ab:df:hlno:P:p:qv:Xx:t", - ['all', 'aegis', 'baseline=', + ['all', 'aegis', 'baseline=', 'builddir=', 'debug', 'file=', 'help', 'list', 'no-exec', 'noqmtest', 'output=', 'package=', 'passed', 'python=', @@ -173,6 +175,10 @@ for o, a in opts: all = 1 elif o in ['-b', '--baseline']: baseline = a + elif o in ['--builddir']: + builddir = a + if not os.path.isabs(builddir): + builddir = os.path.normpath(os.path.join(cwd, builddir)) elif o in ['-d', '--debug']: for dir in sys.path: pdb = os.path.join(dir, 'pdb.py') @@ -288,6 +294,7 @@ if sp is None: if spe is None: spe = [] +sp.append(builddir) sp.append(cwd) # @@ -413,7 +420,7 @@ if package: sys.stderr.write("Unknown package '%s'\n" % package) sys.exit(2) - test_dir = os.path.join(cwd, 'build', 'test-%s' % package) + test_dir = os.path.join(builddir, 'test-%s' % package) if dir[package] is None: scons_script_dir = test_dir @@ -433,7 +440,7 @@ if package: scons_lib_dir = os.path.join(test_dir, dir[package], 'lib', l) pythonpath_dir = scons_lib_dir - scons_runtest_dir = os.path.join(cwd, 'build') + scons_runtest_dir = builddir else: sd = None @@ -616,7 +623,7 @@ if qmtest: qmtest_args = [ qmtest, ] if format == '--aegis': - dir = os.path.join(cwd, 'build') + dir = builddir if not os.path.isdir(dir): dir = cwd qmtest_args.extend(['-D', dir]) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index c4671e51..b17ba4e2 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -10,29 +10,91 @@ RELEASE 0.97 - XXX + From Anatoly: + + - Add the scons.org URL and a package description to the setup.py + arguments. + + - Have the Windows installer add a registry entry for scons.bat in the + "App Paths" key, so scons.bat can be executed without adding the + directory to the %PATH%. (Python itself works this way.) + From Anonymous: - Fix looking for default paths in Visual Studio 8.0 (and later). + - Add -lm to the list of default D libraries for linking. + From Matt Doar: - Provide a more complete write-your-own-Scanner example in the man page. + From Ralf W. Grosse-Kunstleve: + + - Contributed upstream Python change to our copied subprocess.py module + for more efficient standard input processing. + From Steven Knight: - Fix the Node.FS.Base.rel_path() method when the two nodes are on different drive letters. (This caused an infinite loop when trying to write .sconsign files.) + - Fully support Scanners that use a dictionary to map file suffixes + to other scanners. + + - Support delayed evaluation of the $SPAWN variable to allow selection + of a function via ${} string expansions. + + - Add --srcdir as a synonym for -Y/--repository. + + - Document limitations of #include "file.h" with Repository(). + + - Fix use of a toolpath under the source directory of a BuildDir(). + + - Fix env.Install() with a file name portion that begins with '#'. + + - Fix ParseConfig()'s handling of multiple options in a string that's + replaced a *FLAGS construction variable. + + - Have the C++ tools initialize common C compilation variables ($CCFLAGS, + $SHCCFLAGS and $_CCCOMCOM) even if the 'cc' Tool isn't loaded. + + From Leanid Nazdrynau: + + - Fix detection of Java anonymous classes if a newline precedes the + opening brace. + From Gary Oberbrunner: - Document use of ${} to execute arbitrary Python code. + - Add support for: + 1) automatically adding a site_scons subdirectory (in the top-level + SConstruct directory) to sys.path (PYTHONPATH); + 2) automatically importing site_scons/site_init.py; + 3) automatically adding site_scons/site_tools to the toolpath. + From John Pye: - Change ParseConfig() to preserve white space in arguments passed in as a list. + From a smith: + + - Fix adding explicitly-named Java inner class files (and any + other file names that may contain a '$') to Jar files. + + From David Vitek: + + - Add a NoCache() function to mark targets as unsuitable for propagating + to (or retrieving from) a CacheDir(). + + From Ben Webb: + + - If the swig -noproxy option is used, it won't generate a .py file, + so don't emit it as a target that we expect to be built. + RELEASE 0.96.94 - Sun, 07 Jan 2007 18:36:20 -0600 diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 029217e9..2484877b 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -2,6 +2,8 @@ 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/builtins.py diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 503dc9f7..dd7009c7 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -100,12 +100,12 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import dis import os import os.path -import re import string import sys from SCons.Debug import logInstanceCreation import SCons.Errors +import SCons.Executor import SCons.Util class _Null: @@ -403,7 +403,8 @@ class CommandAction(_ActionAction): def strfunction(self, target, source, env): if not self.cmdstr is None: - c = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source) + from SCons.Subst import SUBST_RAW + c = env.subst(self.cmdstr, SUBST_RAW, target, source) if c: return c cmd_list, ignore, silent = self.process(target, source, env) @@ -421,7 +422,10 @@ class CommandAction(_ActionAction): externally. """ from SCons.Subst import escape_list - from SCons.Util import is_String, is_List, flatten + import SCons.Util + flatten = SCons.Util.flatten + is_String = SCons.Util.is_String + is_List = SCons.Util.is_List try: shell = env['SHELL'] @@ -432,6 +436,9 @@ class CommandAction(_ActionAction): spawn = env['SPAWN'] except KeyError: raise SCons.Errors.UserError('Missing SPAWN construction variable.') + else: + if is_String(spawn): + spawn = env.subst(spawn, raw=1, conv=lambda x: x) escape = env.get('ESCAPE', lambda x: x) @@ -618,7 +625,8 @@ class FunctionAction(_ActionAction): if self.cmdstr is None: return None if not self.cmdstr is _null: - c = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source) + from SCons.Subst import SUBST_RAW + c = env.subst(self.cmdstr, SUBST_RAW, target, source) if c: return c def array(a): diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 2c6d9159..83396106 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -135,11 +135,13 @@ class Environment: for k, v in kw.items(): self.d[k] = v # Just use the underlying scons_subst*() utility methods. - def subst(self, strSubst, raw=0, target=[], source=[]): - return SCons.Subst.scons_subst(strSubst, self, raw, target, source, self.d) + def subst(self, strSubst, raw=0, target=[], source=[], conv=None): + return SCons.Subst.scons_subst(strSubst, self, raw, + target, source, self.d, conv=conv) subst_target_source = subst - def subst_list(self, strSubst, raw=0, target=[], source=[]): - return SCons.Subst.scons_subst_list(strSubst, self, raw, target, source, self.d) + def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None): + return SCons.Subst.scons_subst_list(strSubst, self, raw, + target, source, self.d, conv=conv) def __getitem__(self, item): return self.d[item] def __setitem__(self, item, value): @@ -1150,6 +1152,11 @@ class CommandActionTestCase(unittest.TestCase): a([], [], e) assert t.executed == [ 'xyzzy' ], t.executed + a = SCons.Action.CommandAction(["xyzzy"]) + e = Environment(SPAWN = '$FUNC', FUNC = func) + a([], [], e) + assert t.executed == [ 'xyzzy' ], t.executed + a = SCons.Action.CommandAction(["xyzzy"]) e = Environment(SPAWN = func, SHELL = 'fake shell') a([], [], e) diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index acf07220..77ac9f41 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -864,7 +864,7 @@ class BuilderTestCase(unittest.TestCase): def func(self): pass - scanner = SCons.Scanner.Scanner(func, name='fooscan') + scanner = SCons.Scanner.Base(func, name='fooscan') b1 = SCons.Builder.Builder(action='bld', target_scanner=scanner) b2 = SCons.Builder.Builder(action='bld', target_scanner=scanner) diff --git a/src/engine/SCons/Conftest.py b/src/engine/SCons/Conftest.py index ddb1a992..81a8ee4d 100644 --- a/src/engine/SCons/Conftest.py +++ b/src/engine/SCons/Conftest.py @@ -341,7 +341,6 @@ def CheckLib(context, libs, func_name = None, header = None, sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. """ - from SCons.Debug import Trace # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 96c3cf84..11bace3f 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -40,7 +40,6 @@ import os import os.path import shutil import stat -import string import time import types import sys diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 6c392a5c..e5eb40c9 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -38,7 +38,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import copy import os import os.path -import popen2 import string from UserDict import UserDict @@ -47,6 +46,7 @@ import SCons.Builder from SCons.Debug import logInstanceCreation import SCons.Defaults import SCons.Errors +import SCons.Memoize import SCons.Node import SCons.Node.Alias import SCons.Node.FS @@ -54,7 +54,6 @@ import SCons.Node.Python import SCons.Platform import SCons.SConsign import SCons.Sig -import SCons.Sig.TimeStamp import SCons.Subst import SCons.Tool import SCons.Util @@ -508,17 +507,17 @@ class SubstitutionEnvironment: the result of that evaluation is then added to the dict. """ dict = { - 'ASFLAGS' : [], - 'CFLAGS' : [], - 'CCFLAGS' : [], + 'ASFLAGS' : SCons.Util.CLVar(''), + 'CFLAGS' : SCons.Util.CLVar(''), + 'CCFLAGS' : SCons.Util.CLVar(''), 'CPPDEFINES' : [], - 'CPPFLAGS' : [], + 'CPPFLAGS' : SCons.Util.CLVar(''), 'CPPPATH' : [], - 'FRAMEWORKPATH' : [], - 'FRAMEWORKS' : [], + 'FRAMEWORKPATH' : SCons.Util.CLVar(''), + 'FRAMEWORKS' : SCons.Util.CLVar(''), 'LIBPATH' : [], 'LIBS' : [], - 'LINKFLAGS' : [], + 'LINKFLAGS' : SCons.Util.CLVar(''), 'RPATH' : [], } @@ -620,7 +619,7 @@ class SubstitutionEnvironment: if arg[2:]: append_define(arg[2:]) else: - appencd_next_arg_to = 'CPPDEFINES' + append_next_arg_to = 'CPPDEFINES' elif arg == '-framework': append_next_arg_to = 'FRAMEWORKS' elif arg[:14] == '-frameworkdir=': @@ -665,7 +664,7 @@ class SubstitutionEnvironment: apply(self.Append, (), args) return self for key, value in args.items(): - if value == '': + if not value: continue try: orig = self[key] @@ -673,10 +672,24 @@ class SubstitutionEnvironment: orig = value else: if not orig: - orig = [] - elif not SCons.Util.is_List(orig): - orig = [orig] - orig = orig + value + orig = value + elif value: + # Add orig and value. The logic here was lifted from + # part of env.Append() (see there for a lot of comments + # about the order in which things are tried) and is + # used mainly to handle coercion of strings to CLVar to + # "do the right thing" given (e.g.) an original CCFLAGS + # string variable like '-pipe -Wall'. + try: + orig = orig + value + except (KeyError, TypeError): + try: + add_to_orig = orig.append + except AttributeError: + value.insert(0, orig) + orig = value + else: + add_to_orig(value) t = [] if key[-4:] == 'PATH': ### keep left-most occurence @@ -1314,12 +1327,15 @@ class Base(SubstitutionEnvironment): del kw[k] apply(self.Replace, (), kw) + def _find_toolpath_dir(self, tp): + return self.fs.Dir(self.subst(tp)).srcnode().abspath + def Tool(self, tool, toolpath=None, **kw): if SCons.Util.is_String(tool): tool = self.subst(tool) if toolpath is None: toolpath = self.get('toolpath', []) - toolpath = map(self.subst, toolpath) + toolpath = map(self._find_toolpath_dir, toolpath) tool = apply(SCons.Tool.Tool, (tool, toolpath), kw) tool(self) @@ -1514,6 +1530,15 @@ class Base(SubstitutionEnvironment): t.set_noclean() return tlist + def NoCache(self, *targets): + """Tags a target so that it will not be cached""" + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_nocache() + return tlist + def Entry(self, name, *args, **kw): """ """ @@ -1575,7 +1600,10 @@ class Base(SubstitutionEnvironment): tgt = [] for dnode in dnodes: for src in sources: - target = self.fs.Entry(src.name, dnode) + # Prepend './' so the lookup doesn't interpret an initial + # '#' on the file name portion as meaning the Node should + # be relative to the top-level SConstruct directory. + target = self.fs.Entry('.'+os.sep+src.name, dnode) tgt.extend(InstallBuilder(self, target, src)) return tgt @@ -1631,7 +1659,7 @@ class Base(SubstitutionEnvironment): arg = self.subst(arg) nargs.append(arg) nkw = self.subst_kw(kw) - return apply(SCons.Scanner.Scanner, nargs, nkw) + return apply(SCons.Scanner.Base, nargs, nkw) def SConsignFile(self, name=".sconsign", dbm_module=None): if not name is None: @@ -1746,7 +1774,7 @@ class OverrideEnvironment(Base): def __getattr__(self, name): return getattr(self.__dict__['__subject'], name) def __setattr__(self, name, value): - return setattr(self.__dict__['__subject'], name, value) + setattr(self.__dict__['__subject'], name, value) # Methods that make this class act like a dictionary. def __getitem__(self, key): diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index c015bc1e..70f90264 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -695,7 +695,7 @@ sys.exit(1) "-pthread " + \ "-mno-cygwin -mwindows " + \ "-arch i386 -isysroot /tmp +DD64 " + \ - "-DFOO -DBAR=value" + "-DFOO -DBAR=value -D BAZ" d = env.ParseFlags(s) @@ -705,7 +705,7 @@ sys.exit(1) '-pthread', '-mno-cygwin', ('-arch', 'i386'), ('-isysroot', '/tmp'), '+DD64'], d['CCFLAGS'] - assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value']], d['CPPDEFINES'] + assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES'] assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS'] assert d['CPPPATH'] == ['/usr/include/fum', 'bar'], d['CPPPATH'] assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH'] @@ -725,7 +725,7 @@ sys.exit(1) """ env = SubstitutionEnvironment() env.MergeFlags('') - assert env['CCFLAGS'] == [], env['CCFLAGS'] + assert not env.has_key('CCFLAGS'), env['CCFLAGS'] env.MergeFlags('-X') assert env['CCFLAGS'] == ['-X'], env['CCFLAGS'] env.MergeFlags('-X') @@ -2805,6 +2805,9 @@ def generate(env): for tnode in tgt: assert tnode.builder == InstallBuilder + tgt = env.Install('export', 'subdir/#file') + assert str(tgt[0]) == os.path.normpath('export/#file'), str(tgt[0]) + env.File('export/foo1') exc_caught = None diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py index 4b150102..12114bc9 100644 --- a/src/engine/SCons/Executor.py +++ b/src/engine/SCons/Executor.py @@ -33,7 +33,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import string from SCons.Debug import logInstanceCreation -import SCons.Util +import SCons.Memoize class Executor: @@ -63,8 +63,10 @@ class Executor: self._memo = {} def set_action_list(self, action): + import SCons.Util if not SCons.Util.is_List(action): if not action: + import SCons.Errors raise SCons.Errors.UserError, "Executor must have an action." action = [action] self.action_list = action diff --git a/src/engine/SCons/Job.py b/src/engine/SCons/Job.py index 8dde905a..9832f146 100644 --- a/src/engine/SCons/Job.py +++ b/src/engine/SCons/Job.py @@ -170,7 +170,7 @@ else: self.resultsQueue = Queue.Queue(0) # Create worker threads - for i in range(num): + for _ in range(num): Worker(self.requestQueue, self.resultsQueue) def put(self, obj): diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 9e0e6f67..4d269d2f 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -47,6 +47,7 @@ import cStringIO import SCons.Action from SCons.Debug import logInstanceCreation import SCons.Errors +import SCons.Memoize import SCons.Node import SCons.Subst import SCons.Util @@ -249,6 +250,8 @@ CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) def CachePushFunc(target, source, env): t = target[0] + if t.nocache: + return fs = t.fs cachedir, cachefile = t.cachepath() if fs.exists(cachefile): @@ -829,7 +832,7 @@ class Entry(Base): morph this Entry.""" try: self = self.disambiguate(must_exist=1) - except SCons.Errors.UserError, e: + except SCons.Errors.UserError: # There was nothing on disk with which to disambiguate # this entry. Leave it as an Entry, but return a null # string so calls to get_contents() in emitters and the @@ -1054,7 +1057,7 @@ class FS(LocalFS): path_norm = string.split(_my_normcase(name), os.sep) first_orig = path_orig.pop(0) # strip first element - first_norm = path_norm.pop(0) # strip first element + unused = path_norm.pop(0) # strip first element drive, path_first = os.path.splitdrive(first_orig) if path_first: @@ -1477,9 +1480,11 @@ class Dir(Base): return result def get_env_scanner(self, env, kw={}): + import SCons.Defaults return SCons.Defaults.DirEntryScanner def get_target_scanner(self): + import SCons.Defaults return SCons.Defaults.DirEntryScanner def get_found_includes(self, env, scanner, path): @@ -2033,6 +2038,8 @@ class File(Base): Returns true iff the node was successfully retrieved. """ + if self.nocache: + return None b = self.is_derived() if not b and not self.has_src_builder(): return None @@ -2298,7 +2305,7 @@ class File(Base): return str(self.rfile()) def cachepath(self): - if not self.fs.CachePath: + if self.nocache or not self.fs.CachePath: return None, None ninfo = self.get_binfo().ninfo if not hasattr(ninfo, 'bsig'): diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 405010c9..fa682a20 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -52,6 +52,7 @@ import UserList from SCons.Debug import logInstanceCreation import SCons.Executor +import SCons.Memoize import SCons.SConsign import SCons.Util @@ -202,14 +203,13 @@ class Node: self.state = no_state self.precious = None self.noclean = 0 + self.nocache = 0 self.always_build = None self.found_includes = {} self.includes = None self.attributes = self.Attrs() # Generic place to stick information about the Node. self.side_effect = 0 # true iff this node is a side effect self.side_effects = [] # the side effects of building this target - self.pre_actions = [] - self.post_actions = [] self.linked = 0 # is this node linked to the build directory? self.clear_memoized_values() @@ -765,6 +765,12 @@ class Node: # output in Util.py can use it as an index. self.noclean = noclean and 1 or 0 + def set_nocache(self, nocache = 1): + """Set the Node's nocache value.""" + # Make sure nocache is an integer so the --debug=stree + # output in Util.py can use it as an index. + self.nocache = nocache and 1 or 0 + def set_always_build(self, always_build = 1): """Set the Node's always_build value.""" self.always_build = always_build diff --git a/src/engine/SCons/Options/BoolOption.py b/src/engine/SCons/Options/BoolOption.py index f38bf029..80b607d4 100644 --- a/src/engine/SCons/Options/BoolOption.py +++ b/src/engine/SCons/Options/BoolOption.py @@ -36,18 +36,16 @@ Usage example: __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -__all__ = ('BoolOption', 'True', 'False') +__all__ = ('BoolOption') import string +import SCons.compat import SCons.Errors __true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' ) __false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none') -# we need this since SCons should work version indepentant -True, False = 1, 0 - def _text2bool(val): """ diff --git a/src/engine/SCons/Options/BoolOptionTests.py b/src/engine/SCons/Options/BoolOptionTests.py index 845f251e..07b5b795 100644 --- a/src/engine/SCons/Options/BoolOptionTests.py +++ b/src/engine/SCons/Options/BoolOptionTests.py @@ -23,6 +23,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import SCons.compat + import sys import unittest @@ -91,8 +93,8 @@ class BoolOptionTestCase(unittest.TestCase): o = opts.options[0] env = { - 'T' : SCons.Options.True, - 'F' : SCons.Options.False, + 'T' : True, + 'F' : False, 'N' : 'xyzzy', } diff --git a/src/engine/SCons/Options/PackageOption.py b/src/engine/SCons/Options/PackageOption.py index 9ecb42a3..3b4f0cec 100644 --- a/src/engine/SCons/Options/PackageOption.py +++ b/src/engine/SCons/Options/PackageOption.py @@ -52,15 +52,15 @@ Usage example: __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -__all__ = ('PackageOption', 'True', 'False') +__all__ = ('PackageOption') import string -from BoolOption import True, False +import SCons.compat import SCons.Errors -__enable_strings = (str(True), 'yes', 'true', 'on', 'enable', 'search') -__disable_strings = (str(False), 'no', 'false', 'off', 'disable') +__enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search') +__disable_strings = ('0', 'no', 'false', 'off', 'disable') def _converter(val): """ @@ -78,12 +78,10 @@ def _validator(key, val, env, searchfunc): """ # todo: write validator, check for path import os - if env[key] == False: - pass - elif env[key] == True: + if env[key] is True: if searchfunc: env[key] = searchfunc(key, val) - elif not os.path.exists(val): + elif env[key] and not os.path.exists(val): raise SCons.Errors.UserError( 'Path does not exist for option %s: %s' % (key, val)) diff --git a/src/engine/SCons/Options/PackageOptionTests.py b/src/engine/SCons/Options/PackageOptionTests.py index 7c868e5b..68f14e5d 100644 --- a/src/engine/SCons/Options/PackageOptionTests.py +++ b/src/engine/SCons/Options/PackageOptionTests.py @@ -23,12 +23,13 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import SCons.compat + import sys import unittest import SCons.Errors import SCons.Options -from SCons.Options.BoolOption import True, False import TestCmd @@ -96,7 +97,7 @@ class PackageOptionTestCase(unittest.TestCase): o = opts.options[0] - env = {'F':0, 'T':1, 'X':'x'} + env = {'F':False, 'T':True, 'X':'x'} exists = test.workpath('exists') does_not_exist = test.workpath('does_not_exist') diff --git a/src/engine/SCons/Options/__init__.py b/src/engine/SCons/Options/__init__.py index 5c30be60..66c21435 100644 --- a/src/engine/SCons/Options/__init__.py +++ b/src/engine/SCons/Options/__init__.py @@ -36,7 +36,7 @@ import SCons.Errors import SCons.Util import SCons.Warnings -from BoolOption import BoolOption, True, False # okay +from BoolOption import BoolOption # okay from EnumOption import EnumOption # okay from ListOption import ListOption # naja from PackageOption import PackageOption # naja @@ -227,8 +227,6 @@ class Options: of the options. """ - help_text = "" - if sort: options = self.options[:] options.sort(lambda x,y,func=sort: func(x.key,y.key)) diff --git a/src/engine/SCons/PathList.py b/src/engine/SCons/PathList.py index b757bd3b..81b81359 100644 --- a/src/engine/SCons/PathList.py +++ b/src/engine/SCons/PathList.py @@ -35,6 +35,7 @@ Do the Right Thing (almost) regardless of how the variable is specified. import os import string +import SCons.Memoize import SCons.Util # diff --git a/src/engine/SCons/Platform/darwin.py b/src/engine/SCons/Platform/darwin.py index 7883795a..fc4c773f 100644 --- a/src/engine/SCons/Platform/darwin.py +++ b/src/engine/SCons/Platform/darwin.py @@ -33,7 +33,6 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import posix -import os def generate(env): posix.generate(env) diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index 148d9df8..8d35a8d1 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -93,7 +93,6 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: ret = exitvalmap[e[0]] except KeyError: - result = 127 sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1])) if stderr != None: stderr.write("scons: %s: %s\n" % (cmd, e[1])) diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index 5c20f264..21305b93 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -39,6 +39,7 @@ import types import SCons.Action import SCons.Builder import SCons.Errors +import SCons.Job import SCons.Node.FS import SCons.Taskmaster import SCons.Util @@ -141,7 +142,6 @@ def _createSource( target, source, env ): fd.write(source[0].get_contents()) fd.close() def _stringSource( target, source, env ): - import string return (str(target[0]) + ' <-\n |' + string.replace( source[0].get_contents(), '\n', "\n |" ) ) @@ -188,6 +188,11 @@ class Streamer: Return everything written to orig since the Streamer was created. """ return self.s.getvalue() + + def flush(self): + if self.orig: + self.orig.flush() + self.s.flush() class SConfBuildTask(SCons.Taskmaster.Task): @@ -229,7 +234,6 @@ class SConfBuildTask(SCons.Taskmaster.Task): except AttributeError: # Earlier versions of Python don't have sys.excepthook... def excepthook(type, value, tb): - import traceback traceback.print_tb(tb) print type, value apply(excepthook, self.exc_info()) @@ -597,7 +601,8 @@ class SConf: else: _ac_config_logs[self.logfile] = None log_mode = "w" - self.logstream = open(str(self.logfile), log_mode) + fp = open(str(self.logfile), log_mode) + self.logstream = SCons.Util.Unbuffered(fp) # logfile may stay in a build directory, so we tell # the build system not to override it with a eventually # existing file with the same name in the source directory diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py index 67b2affa..dcd6979e 100644 --- a/src/engine/SCons/SConsign.py +++ b/src/engine/SCons/SConsign.py @@ -32,8 +32,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import cPickle import os import os.path -import string -import time import SCons.dblite import SCons.Sig diff --git a/src/engine/SCons/Scanner/Dir.py b/src/engine/SCons/Scanner/Dir.py index fb23d1b1..535150a6 100644 --- a/src/engine/SCons/Scanner/Dir.py +++ b/src/engine/SCons/Scanner/Dir.py @@ -23,8 +23,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import string - import SCons.Node.FS import SCons.Scanner diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py index bd8546f9..30dc1dfd 100644 --- a/src/engine/SCons/Scanner/ScannerTests.py +++ b/src/engine/SCons/Scanner/ScannerTests.py @@ -82,29 +82,37 @@ class FindPathDirsTestCase(unittest.TestCase): class ScannerTestCase(unittest.TestCase): def test_creation(self): - """Test creation of Scanner objects through the Scanner() function""" + """Test creation of Scanner objects""" def func(self): pass - s = SCons.Scanner.Scanner(func) + s = SCons.Scanner.Base(func) + assert isinstance(s, SCons.Scanner.Base), s + s = SCons.Scanner.Base({}) assert isinstance(s, SCons.Scanner.Base), s - s = SCons.Scanner.Scanner({}) - assert isinstance(s, SCons.Scanner.Selector), s - s = SCons.Scanner.Scanner(func, name='fooscan') + s = SCons.Scanner.Base(func, name='fooscan') assert str(s) == 'fooscan', str(s) - s = SCons.Scanner.Scanner({}, name='barscan') + s = SCons.Scanner.Base({}, name='barscan') assert str(s) == 'barscan', str(s) - s = SCons.Scanner.Scanner(func, name='fooscan', argument=9) + s = SCons.Scanner.Base(func, name='fooscan', argument=9) assert str(s) == 'fooscan', str(s) assert s.argument == 9, s.argument - s = SCons.Scanner.Scanner({}, name='fooscan', argument=888) + s = SCons.Scanner.Base({}, name='fooscan', argument=888) assert str(s) == 'fooscan', str(s) assert s.argument == 888, s.argument class BaseTestCase(unittest.TestCase): + class skey_node: + def __init__(self, key): + self.key = key + def scanner_key(self): + return self.key + def rexists(self): + return 1 + def func(self, filename, env, target, *args): self.filename = filename self.env = env @@ -132,6 +140,29 @@ class BaseTestCase(unittest.TestCase): else: self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been") + def test___call__dict(self): + """Test calling Scanner.Base objects with a dictionary""" + called = [] + def s1func(node, env, path, called=called): + called.append('s1func') + called.append(node) + return [] + def s2func(node, env, path, called=called): + called.append('s2func') + called.append(node) + return [] + s1 = SCons.Scanner.Base(s1func) + s2 = SCons.Scanner.Base(s2func) + selector = SCons.Scanner.Base({'.x' : s1, '.y' : s2}) + nx = self.skey_node('.x') + env = DummyEnvironment() + selector(nx, env, []) + assert called == ['s1func', nx], called + del called[:] + ny = self.skey_node('.y') + selector(ny, env, []) + assert called == ['s2func', ny], called + def test_path(self): """Test the Scanner.Base path() method""" def pf(env, cwd, target, source, argument=None): @@ -277,6 +308,23 @@ class BaseTestCase(unittest.TestCase): s = scanner.select('.x') assert s is scanner, s + selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2}) + s = selector.select(self.skey_node('.x')) + assert s == 1, s + s = selector.select(self.skey_node('.y')) + assert s == 2, s + s = selector.select(self.skey_node('.z')) + assert s is None, s + + def test_add_scanner(self): + """Test the Scanner.Base add_scanner() method""" + selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2}) + s = selector.select(self.skey_node('.z')) + assert s is None, s + selector.add_scanner('.z', 3) + s = selector.select(self.skey_node('.z')) + assert s == 3, s + def test___str__(self): """Test the Scanner.Base __str__() method""" scanner = SCons.Scanner.Base(function = self.func) diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py index 679efca5..db93f615 100644 --- a/src/engine/SCons/Scanner/__init__.py +++ b/src/engine/SCons/Scanner/__init__.py @@ -45,9 +45,17 @@ class _Null: _null = _Null def Scanner(function, *args, **kw): - """Public interface factory function for creating different types + """ + Public interface factory function for creating different types of Scanners based on the different types of "functions" that may - be supplied.""" + be supplied. + + TODO: Deprecate this some day. We've moved the functionality + inside the Base class and really don't need this factory function + any more. It was, however, used by some of our Tool modules, so + the call probably ended up in various people's custom modules + patterned on SCons code. + """ if SCons.Util.is_Dict(function): return apply(Selector, (function,) + args, kw) else: @@ -83,7 +91,7 @@ class Base: function, name = "NONE", argument = _null, - skeys = [], + skeys = _null, path_function = None, node_class = SCons.Node.FS.Entry, node_factory = None, @@ -159,7 +167,14 @@ class Base: self.path_function = path_function self.name = name self.argument = argument + + if skeys is _null: + if SCons.Util.is_Dict(function): + skeys = function.keys() + else: + skeys = [] self.skeys = skeys + self.node_class = node_class self.node_factory = node_factory self.scan_check = scan_check @@ -188,10 +203,13 @@ class Base: if self.scan_check and not self.scan_check(node, env): return [] + self = self.select(node) + if not self.argument is _null: list = self.function(node, env, path, self.argument) else: list = self.function(node, env, path) + kw = {} if hasattr(node, 'dir'): kw['directory'] = node.dir @@ -221,12 +239,19 @@ class Base: self.skeys.append(skey) def get_skeys(self, env=None): - if SCons.Util.is_String(self.skeys): + if env and SCons.Util.is_String(self.skeys): return env.subst_list(self.skeys)[0] return self.skeys def select(self, node): - return self + if SCons.Util.is_Dict(self.function): + key = node.scanner_key() + try: + return self.function[key] + except KeyError: + return None + else: + return self def _recurse_all_nodes(self, nodes): return nodes @@ -236,15 +261,27 @@ class Base: recurse_nodes = _recurse_no_nodes + def add_scanner(self, skey, scanner): + self.function[skey] = scanner + self.add_skey(skey) + class Selector(Base): """ A class for selecting a more specific scanner based on the scanner_key() (suffix) for a specific Node. + + TODO: This functionality has been moved into the inner workings of + the Base class, and this class will be deprecated at some point. + (It was never exposed directly as part of the public interface, + although it is used by the Scanner() factory function that was + used by various Tool modules and therefore was likely a template + for custom modules that may be out there.) """ def __init__(self, dict, *args, **kw): apply(Base.__init__, (self, None,)+args, kw) self.dict = dict + self.skeys = dict.keys() def __call__(self, node, env, path = ()): return self.select(node)(node, env, path) @@ -257,6 +294,7 @@ class Selector(Base): def add_scanner(self, skey, scanner): self.dict[skey] = scanner + self.add_skey(skey) class Current(Base): diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 96f15261..6a0ad812 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -36,6 +36,8 @@ it goes here. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import SCons.compat + import os import os.path import random @@ -62,6 +64,7 @@ import SCons.Node import SCons.Node.FS from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError import SCons.SConf +import SCons.Script import SCons.Sig import SCons.Taskmaster import SCons.Util @@ -180,15 +183,8 @@ class BuildTask(SCons.Taskmaster.Task): def postprocess(self): if self.top: t = self.targets[0] - if print_tree: - print - SCons.Util.print_tree(t, get_all_children) - if print_stree: - print - SCons.Util.print_tree(t, get_all_children, showtags=2) - if print_dtree: - print - SCons.Util.print_tree(t, get_derived_children) + for tp in tree_printers: + tp.display(t) if print_includes: tree = t.render_include_tree() if tree: @@ -291,18 +287,37 @@ class QuestionTask(SCons.Taskmaster.Task): def executed(self): pass + +class TreePrinter: + def __init__(self, derived=False, prune=False, status=False): + self.derived = derived + self.prune = prune + self.status = status + def get_all_children(self, node): + return node.all_children() + def get_derived_children(self, node): + children = node.all_children(None) + return filter(lambda x: x.has_builder(), children) + def display(self, t): + if self.derived: + func = self.get_derived_children + else: + func = self.get_all_children + s = self.status and 2 or 0 + SCons.Util.print_tree(t, func, prune=self.prune, showtags=s) + + # Global variables +tree_printers = [] + keep_going_on_error = 0 -print_dtree = 0 print_explanations = 0 print_includes = 0 print_objects = 0 print_memoizer = 0 print_stacktrace = 0 -print_stree = 0 print_time = 0 -print_tree = 0 ignore_errors = 0 sconscript_time = 0 command_time = 0 @@ -390,12 +405,6 @@ memory_stats = MemStats() # utility functions -def get_all_children(node): return node.all_children() - -def get_derived_children(node): - children = node.all_children(None) - return filter(lambda x: x.has_builder(), children) - def _scons_syntax_error(e): """Handle syntax errors. Print out a message and show where the error occurred. @@ -538,10 +547,10 @@ def _SConstruct_exists(dirname=''): def _set_globals(options): global keep_going_on_error, ignore_errors - global count_stats, print_dtree + global count_stats global print_explanations, print_includes, print_memoizer - global print_objects, print_stacktrace, print_stree - global print_time, print_tree + global print_objects, print_stacktrace, print_time + global tree_printers global memory_stats keep_going_on_error = options.keep_going @@ -555,7 +564,7 @@ def _set_globals(options): if "count" in debug_values: count_stats.enable(sys.stdout) if "dtree" in debug_values: - print_dtree = 1 + tree_printers.append(TreePrinter(derived=True)) if "explain" in debug_values: print_explanations = 1 if "findlibs" in debug_values: @@ -573,11 +582,11 @@ def _set_globals(options): if "stacktrace" in debug_values: print_stacktrace = 1 if "stree" in debug_values: - print_stree = 1 + tree_printers.append(TreePrinter(status=True)) if "time" in debug_values: print_time = 1 if "tree" in debug_values: - print_tree = 1 + tree_printers.append(TreePrinter()) ignore_errors = options.ignore_errors def _create_path(plist): @@ -589,6 +598,47 @@ def _create_path(plist): path = path + '/' + d return path +def _load_site_scons_dir(topdir, site_dir_name=None): + """Load the site_scons dir under topdir. + Adds site_scons to sys.path, imports site_scons/site_init.py, + and adds site_scons/site_tools to default toolpath.""" + if site_dir_name: + err_if_not_found = True # user specified: err if missing + else: + site_dir_name = "site_scons" + err_if_not_found = False + + site_dir = os.path.join(topdir.path, site_dir_name) + if not os.path.exists(site_dir): + if err_if_not_found: + raise SCons.Errors.UserError, "site dir %s not found."%site_dir + return + + site_init_filename = "site_init.py" + site_init_modname = "site_init" + site_tools_dirname = "site_tools" + sys.path = [site_dir] + sys.path + site_init_file = os.path.join(site_dir, site_init_filename) + site_tools_dir = os.path.join(site_dir, site_tools_dirname) + if os.path.exists(site_init_file): + import imp + try: + fp, pathname, description = imp.find_module(site_init_modname, + [site_dir]) + try: + imp.load_module(site_init_modname, fp, pathname, description) + finally: + if fp: + fp.close() + except ImportError, e: + sys.stderr.write("Can't import site init file '%s': %s\n"%(site_init_file, e)) + raise + except Exception, e: + sys.stderr.write("Site init file '%s' raised exception: %s\n"%(site_init_file, e)) + raise + if os.path.exists(site_tools_dir): + SCons.Tool.DefaultToolpath.append(os.path.abspath(site_tools_dir)) + def version_string(label, module): fmt = "\t%s: v%s.%s, %s, by %s on %s\n" return fmt % (label, @@ -676,7 +726,9 @@ class OptParser(OptionParser): "pdb", "presub", "stacktrace", "stree", "time", "tree"] - deprecated_debug_options = [ "nomemoizer", ] + deprecated_debug_options = { + "nomemoizer" : ' and has no effect', + } def opt_debug(option, opt, value, parser, debug_options=debug_options, deprecated_debug_options=deprecated_debug_options): if value in debug_options: @@ -686,8 +738,9 @@ class OptParser(OptionParser): except AttributeError: parser.values.debug = [] parser.values.debug.append(value) - elif value in deprecated_debug_options: - w = "The --debug=%s option is deprecated and has no effect." % value + elif value in deprecated_debug_options.keys(): + msg = deprecated_debug_options[value] + w = "The --debug=%s option is deprecated%s." % (value, msg) delayed_warnings.append((SCons.Warnings.DeprecatedWarning, w)) else: raise OptionValueError("Warning: %s is not a valid debug type" % value) @@ -773,6 +826,10 @@ class OptParser(OptionParser): '--recon', action="store_true", dest='noexec', default=0, help="Don't build; just print commands.") + self.add_option('--no-site-dir', action="store_true", + dest='no_site_dir', default=0, + help="Don't search or use the usual site_scons dir.") + self.add_option('--profile', action="store", dest="profile_file", metavar="FILE", help="Profile SCons and put results in FILE.") @@ -790,10 +847,36 @@ class OptParser(OptionParser): self.add_option('-s', '--silent', '--quiet', action="store_true", default=0, help="Don't print commands.") + self.add_option('--site-dir', action="store", + dest='site_dir', metavar="DIR", + help="Use DIR instead of the usual site_scons dir.") + self.add_option('--taskmastertrace', action="store", dest="taskmastertrace_file", metavar="FILE", help="Trace Node evaluation to FILE.") + tree_options = ["all", "derived", "prune", "status"] + + def opt_tree(option, opt, value, parser, tree_options=tree_options): + tp = TreePrinter() + for o in string.split(value, ','): + if o == 'all': + tp.derived = False + elif o == 'derived': + tp.derived = True + elif o == 'prune': + tp.prune = True + elif o == 'status': + tp.status = True + else: + raise OptionValueError("Warning: %s is not a valid --tree option" % o) + tree_printers.append(tp) + + self.add_option('--tree', action="callback", type="string", + callback=opt_tree, nargs=1, metavar="OPTIONS", + help="Print a dependency tree in various formats: " + "%s." % string.join(tree_options, ", ")) + self.add_option('-u', '--up', '--search-up', action="store_const", dest="climb_up", default=0, const=1, help="Search up directory tree for SConstruct, " @@ -811,7 +894,8 @@ class OptParser(OptionParser): metavar="WARNING-SPEC", help="Enable or disable warnings.") - self.add_option('-Y', '--repository', nargs=1, action="append", + self.add_option('-Y', '--repository', '--srcdir', + nargs=1, action="append", help="Search REPOSITORY for source and target files.") self.add_option('-e', '--environment-overrides', action="callback", @@ -1076,6 +1160,11 @@ def _main(args, parser): if options.cache_show: fs.cache_show = 1 + if options.site_dir: + _load_site_scons_dir(d, options.site_dir) + elif not options.no_site_dir: + _load_site_scons_dir(d) + if options.include_dir: sys.path = options.include_dir + sys.path @@ -1092,16 +1181,7 @@ def _main(args, parser): SCons.Script._Add_Targets(targets) SCons.Script._Add_Arguments(xmit_args) - class Unbuffered: - def __init__(self, file): - self.file = file - def write(self, arg): - self.file.write(arg) - self.file.flush() - def __getattr__(self, attr): - return getattr(self.file, attr) - - sys.stdout = Unbuffered(sys.stdout) + sys.stdout = SCons.Util.Unbuffered(sys.stdout) memory_stats.append('before reading SConscript files:') count_stats.append(('pre-', 'read')) diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 749be6d7..d25e44c6 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -301,12 +301,12 @@ def SConscript_exception(file=sys.stderr): def annotate(node): """Annotate a node with the stack frame describing the SConscript file and line number that created it.""" - tb = exc_tb = sys.exc_info()[2] + tb = sys.exc_info()[2] while tb and not tb.tb_frame.f_locals.has_key(stack_bottom): tb = tb.tb_next if not tb: # We did not find any exec of an SConscript file: what?! - raise InternalError, "could not find SConscript stack frame" + raise SCons.Errors.InternalError, "could not find SConscript stack frame" node.creator = traceback.extract_stack(tb)[0] # The following line would cause each Node to be annotated using the diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 067f540e..35bbbf7d 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -109,12 +109,12 @@ OptParser = Main.OptParser SConscriptSettableOptions = Main.SConscriptSettableOptions keep_going_on_error = Main.keep_going_on_error -print_dtree = Main.print_dtree +#print_dtree = Main.print_dtree print_explanations = Main.print_explanations print_includes = Main.print_includes print_objects = Main.print_objects print_time = Main.print_time -print_tree = Main.print_tree +#print_tree = Main.print_tree memory_stats = Main.memory_stats ignore_errors = Main.ignore_errors #sconscript_time = Main.sconscript_time @@ -289,6 +289,7 @@ GlobalDefaultEnvironmentFunctions = [ 'Depends', 'Dir', 'NoClean', + 'NoCache', 'Entry', 'Execute', 'File', @@ -341,6 +342,7 @@ GlobalDefaultBuilders = [ for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)) +del name # There are a handful of variables that used to live in the # Script/SConscript.py module that some SConscript files out there were @@ -352,6 +354,10 @@ for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: # this way by hanging some attributes off the "SConscript" object here. SConscript = _SConscript.DefaultEnvironmentCall('SConscript') +# Make SConscript look enough like the module it used to be so +# that pychecker doesn't barf. +SConscript.__name__ = 'SConscript' + SConscript.Arguments = ARGUMENTS SConscript.ArgList = ARGLIST SConscript.BuildTargets = BUILD_TARGETS diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py index 2a993bee..989f1dd5 100644 --- a/src/engine/SCons/Subst.py +++ b/src/engine/SCons/Subst.py @@ -442,7 +442,9 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={ # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). - s = str(s) + if self.mode == SUBST_RAW: + return s + s = self.conv(s) return self.substitute(s, lvars) elif s is None: return '' @@ -646,7 +648,10 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). - s = str(s) + if self.mode == SUBST_RAW: + self.append(s) + return + s = self.conv(s) self.substitute(s, lvars, within_list) elif s is None: self.this_word() diff --git a/src/engine/SCons/SubstTests.py b/src/engine/SCons/SubstTests.py index e8419f1d..7ba24779 100644 --- a/src/engine/SCons/SubstTests.py +++ b/src/engine/SCons/SubstTests.py @@ -145,6 +145,8 @@ class SubstTestCase(unittest.TestCase): MyNode("/bar/ack.cpp"), MyNode("../foo/ack.c") ] + callable_object = TestCallable('callable-1') + loc = { 'xxx' : None, 'null' : '', @@ -203,7 +205,7 @@ class SubstTestCase(unittest.TestCase): 'SSS' : '$RRR', # Test callables that don't match the calling arguments. - 'CALLABLE' : TestCallable('callable-1'), + 'CALLABLE' : callable_object, } env = DummyEnv(loc) @@ -514,6 +516,16 @@ class SubstTestCase(unittest.TestCase): else: raise AssertionError, "did not catch expected UserError" + # Test that the combination of SUBST_RAW plus a pass-through + # conversion routine allows us to fetch a function through the + # dictionary. CommandAction uses this to allow delayed evaluation + # of $SPAWN variables. + x = lambda x: x + r = scons_subst("$CALLABLE", env, mode=SUBST_RAW, conv=x, gvars=gvars) + assert r is callable_object, repr(r) + r = scons_subst("$CALLABLE", env, mode=SUBST_RAW, gvars=gvars) + assert r == 'callable-1', repr(r) + # Test how we handle overriding the internal conversion routines. def s(obj): return obj @@ -594,6 +606,8 @@ class SubstTestCase(unittest.TestCase): MyNode("/bar/ack.cpp"), MyNode("../foo/ack.c") ] + callable_object = TestCallable('callable-2') + def _defines(defs): l = [] for d in defs: @@ -653,7 +667,7 @@ class SubstTestCase(unittest.TestCase): 'SSS' : '$RRR', # Test callable objects that don't match our calling arguments. - 'CALLABLE' : TestCallable('callable-2'), + 'CALLABLE' : callable_object, '_defines' : _defines, 'DEFS' : [ ('Q1', '"q1"'), ('Q2', '"$AAA"') ], @@ -792,8 +806,6 @@ class SubstTestCase(unittest.TestCase): # Test callables that don't match our calling arguments. '$CALLABLE', [['callable-2']], - # Test - # Test handling of quotes. # XXX Find a way to handle this in the future. #'aaa "bbb ccc" ddd', [['aaa', 'bbb ccc', 'ddd']], @@ -991,6 +1003,15 @@ class SubstTestCase(unittest.TestCase): else: raise AssertionError, "did not catch expected SyntaxError" + # Test that the combination of SUBST_RAW plus a pass-through + # conversion routine allows us to fetch a function through the + # dictionary. + x = lambda x: x + r = scons_subst_list("$CALLABLE", env, mode=SUBST_RAW, conv=x, gvars=gvars) + assert r == [[callable_object]], repr(r) + r = scons_subst_list("$CALLABLE", env, mode=SUBST_RAW, gvars=gvars) + assert r == [['callable-2']], repr(r) + # Test we handle overriding the internal conversion routines. def s(obj): return obj diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 04ed19a8..7439168a 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -41,7 +41,8 @@ interface and the SCons build engine. There are two key classes here: which has Task subclasses that handle its specific behavior, like printing "`foo' is up to date" when a top-level target doesn't need to be built, and handling the -c option by removing - targets as its "build" action. + targets as its "build" action. There is also a separate subclass + for suppressing this output when the -q option is used. The Taskmaster instantiates a Task object for each (set of) target(s) that it decides need to be evaluated and/or built. @@ -58,6 +59,8 @@ import SCons.Errors StateString = SCons.Node.StateString + + # A subsystem for recording stats about how different Nodes are handled by # the main Taskmaster loop. There's no external control here (no need for # a --debug= option); enable it by changing the value of CollectStats. @@ -68,7 +71,7 @@ class Stats: """ A simple class for holding statistics about the disposition of a Node by the Taskmaster. If we're collecting statistics, each Node - processed by the Taskmaster gets one of these attached, in which + processed by the Taskmaster gets one of these attached, in which case the Taskmaster records its decision each time it processes the Node. (Ideally, that's just once per Node.) """ @@ -100,8 +103,11 @@ def dump_stats(): for n in StatsNodes: print (fmt % n.stats.__dict__) + str(n) + + class Task: - """Default SCons build engine task. + """ + Default SCons build engine task. This controls the interaction of the actual building of node and the rest of the engine. @@ -116,7 +122,8 @@ class Task: Note that it's generally a good idea for sub-classes to call these methods explicitly to update state, etc., rather than - roll their own interaction with Taskmaster from scratch.""" + roll their own interaction with Taskmaster from scratch. + """ def __init__(self, tm, targets, top, node): self.tm = tm self.targets = targets @@ -125,15 +132,26 @@ class Task: self.exc_clear() def display(self, message): - """Allow the calling interface to display a message + """ + Hook to allow the calling interface to display a message. + + This hook gets called as part of preparing a task for execution + (that is, a Node to be built). As part of figuring out what Node + should be built next, the actually target list may be altered, + along with a message describing the alteration. The calling + interface can subclass Task and provide a concrete implementation + of this method to see those messages. """ pass def prepare(self): - """Called just before the task is executed. + """ + Called just before the task is executed. - This unlinks all targets and makes all directories before - building anything.""" + This is mainly intended to give the target Nodes a chance to + unlink underlying files and make all necessary directories before + the Action is actually called to build the targets. + """ # Now that it's the appropriate time, give the TaskMaster a # chance to raise any exceptions it encountered while preparing @@ -155,11 +173,13 @@ class Task: return self.node def execute(self): - """Called to execute the task. + """ + Called to execute the task. This method is called from multiple threads in a parallel build, so only do thread safe stuff here. Do thread unsafe stuff in - prepare(), executed() or failed().""" + prepare(), executed() or failed(). + """ try: everything_was_cached = 1 @@ -183,13 +203,13 @@ class Task: sys.exc_info()) def executed(self): - """Called when the task has been successfully executed. + """ + Called when the task has been successfully executed. - This may have been a do-nothing operation (to preserve - build order), so check the node's state before updating - things. Most importantly, this calls back to the - Taskmaster to put any node tasks waiting on this one - back on the pending list.""" + This may have been a do-nothing operation (to preserve build + order), so we have to check the node's state before deciding + whether it was "built" or just "visited." + """ for t in self.targets: if t.get_state() == SCons.Node.executing: t.set_state(SCons.Node.executed) @@ -197,17 +217,18 @@ class Task: else: t.visited() - self.tm.executed(self.node) - def failed(self): - """Default action when a task fails: stop the build.""" + """ + Default action when a task fails: stop the build. + """ self.fail_stop() def fail_stop(self): - """Explicit stop-the-build failure.""" + """ + Explicit stop-the-build failure. + """ for t in self.targets: t.set_state(SCons.Node.failed) - self.tm.failed(self.node) self.tm.stop() # We're stopping because of a build failure, but give the @@ -217,7 +238,8 @@ class Task: self.top = 1 def fail_continue(self): - """Explicit continue-the-build failure. + """ + Explicit continue-the-build failure. This sets failure status on the target nodes and all of their dependent parent nodes. @@ -228,10 +250,9 @@ class Task: def set_state(node): node.set_state(SCons.Node.failed) t.call_for_all_waiting_parents(set_state) - self.tm.executed(self.node) - def make_ready_all(self): - """Mark all targets in a task ready for execution. + """ + Marks all targets in a task ready for execution. This is used when the interface needs every target Node to be visited--the canonical example being the "scons -c" option. @@ -243,7 +264,8 @@ class Task: s.set_state(SCons.Node.executing) def make_ready_current(self): - """Mark all targets in a task ready for execution if any target + """ + Marks all targets in a task ready for execution if any target is not current. This is the default behavior for building only what's necessary. @@ -261,11 +283,28 @@ class Task: make_ready = make_ready_current def postprocess(self): - """Post process a task after it's been executed.""" + """ + Post-processes a task after it's been executed. + + This examines all the targets just built (or not, we don't care + if the build was successful, or even if there was no build + because everything was up-to-date) to see if they have any + waiting parent Nodes, or Nodes waiting on a common side effect, + that can be put back on the candidates list. + """ + + # We may have built multiple targets, some of which may have + # common parents waiting for this build. Count up how many + # targets each parent was waiting for so we can subtract the + # values later, and so we *don't* put waiting side-effect Nodes + # back on the candidates list if the Node is also a waiting + # parent. + parents = {} for t in self.targets: for p in t.waiting_parents.keys(): parents[p] = parents.get(p, 0) + 1 + for t in self.targets: for s in t.side_effects: if s.get_state() == SCons.Node.executing: @@ -276,21 +315,47 @@ class Task: for p in s.waiting_s_e.keys(): if p.ref_count == 0: self.tm.candidates.append(p) + for p, subtract in parents.items(): p.ref_count = p.ref_count - subtract if p.ref_count == 0: self.tm.candidates.append(p) + for t in self.targets: t.postprocess() + # Exception handling subsystem. + # + # Exceptions that occur while walking the DAG or examining Nodes + # must be raised, but must be raised at an appropriate time and in + # a controlled manner so we can, if necessary, recover gracefully, + # possibly write out signature information for Nodes we've updated, + # etc. This is done by having the Taskmaster tell us about the + # exception, and letting + def exc_info(self): + """ + Returns info about a recorded exception. + """ return self.exception def exc_clear(self): + """ + Clears any recorded exception. + + This also changes the "exception_raise" attribute to point + to the appropriate do-nothing method. + """ self.exception = (None, None, None) self.exception_raise = self._no_exception_to_raise def exception_set(self, exception=None): + """ + Records an exception to be raised at the appropriate time. + + This also changes the "exception_raise" attribute to point + to the method that will, in fact + """ if not exception: exception = sys.exc_info() self.exception = exception @@ -300,14 +365,17 @@ class Task: pass def _exception_raise(self): - """Raise a pending exception that was recorded while - getting a Task ready for execution.""" - self.tm.exception_raise(self.exc_info()) - - -def order(dependencies): - """Re-order a list of dependencies (if we need to).""" - return dependencies + """ + Raises a pending exception that was recorded while getting a + Task ready for execution. + """ + exc = self.exc_info()[:] + try: + exc_type, exc_value, exc_traceback = exc + except ValueError: + exc_type, exc_value = exc + exc_traceback = None + raise exc_type, exc_value, exc_traceback def find_cycle(stack): @@ -322,24 +390,41 @@ def find_cycle(stack): class Taskmaster: - """A generic Taskmaster for handling a bunch of targets. - - Classes that override methods of this class should call - the base class method, so this class can do its thing. + """ + The Taskmaster for walking the dependency DAG. """ - def __init__(self, targets=[], tasker=Task, order=order, trace=None): + def __init__(self, targets=[], tasker=Task, order=None, trace=None): self.top_targets = targets[:] self.top_targets.reverse() self.candidates = [] self.tasker = tasker - self.ready = None # the next task that is ready to be executed + if not order: + order = lambda l: l self.order = order self.message = None self.trace = trace self.next_candidate = self.find_next_candidate def find_next_candidate(self): + """ + Returns the next candidate Node for (potential) evaluation. + + The candidate list (really a stack) initially consists of all of + the top-level (command line) targets provided when the Taskmaster + was initialized. While we walk the DAG, visiting Nodes, all the + children that haven't finished processing get pushed on to the + candidate list. Each child can then be popped and examined in + turn for whether *their* children are all up-to-date, in which + case a Task will be created for their actual evaluation and + potential building. + + Here is where we also allow candidate Nodes to alter the list of + Nodes that should be examined. This is used, for example, when + invoking SCons in a source directory. A source directory Node can + return its corresponding build directory Node, essentially saying, + "Hey, you really need to build this thing over here instead." + """ try: return self.candidates.pop() except IndexError: @@ -358,13 +443,32 @@ class Taskmaster: return node def no_next_candidate(self): + """ + Stops Taskmaster processing by not returning a next candidate. + """ return None def _find_next_ready_node(self): - """Find the next node that is ready to be built""" - - if self.ready: - return + """ + Finds the next node that is ready to be built. + + This is *the* main guts of the DAG walk. We loop through the + list of candidates, looking for something that has no un-built + children (i.e., that is a leaf Node or has dependencies that are + all leaf Nodes or up-to-date). Candidate Nodes are re-scanned + (both the target Node itself and its sources, which are always + scanned in the context of a given target) to discover implicit + dependencies. A Node that must wait for some children to be + built will be put back on the candidates list after the children + have finished building. A Node that has been put back on the + candidates list in this way may have itself (or its sources) + re-scanned, in order to handle generated header files (e.g.) and + the implicit dependencies therein. + + Note that this method does not do any signature calculation or + up-to-date check itself. All of that is handled by the Task + class. This is purely concerned with the dependency graph walk. + """ self.ready_exc = None @@ -373,8 +477,7 @@ class Taskmaster: while 1: node = self.next_candidate() if node is None: - self.ready = None - break + return None node = node.disambiguate() state = node.get_state() @@ -405,9 +508,8 @@ class Taskmaster: exc_value = sys.exc_info()[1] e = SCons.Errors.ExplicitExit(node, exc_value.code) self.ready_exc = (SCons.Errors.ExplicitExit, e) - self.ready = node if T: T.write(' SystemExit\n') - break + return node except KeyboardInterrupt: if T: T.write(' KeyboardInterrupt\n') raise @@ -417,10 +519,9 @@ class Taskmaster: # BuildDir, or a Scanner threw something). Arrange to # raise the exception when the Task is "executed." self.ready_exc = sys.exc_info() - self.ready = node if S: S.problem = S.problem + 1 if T: T.write(' exception\n') - break + return node if T and children: c = map(str, children) @@ -516,7 +617,7 @@ class Taskmaster: continue # Skip this node if it has side-effects that are currently being - # built themselves or waiting for something else being built. + # built themselves or waiting for something else being built. side_effects = filter(lambda N: N.get_state() == SCons.Node.executing, node.side_effects) @@ -531,17 +632,20 @@ class Taskmaster: # The default when we've gotten through all of the checks above: # this node is ready to be built. - self.ready = node if S: S.build = S.build + 1 if T: T.write(' evaluating %s\n' % node) - break + return node - def next_task(self): - """Return the next task to be executed.""" + return None - self._find_next_ready_node() + def next_task(self): + """ + Returns the next task to be executed. - node = self.ready + This simply asks for the next Node to be evaluated, and then wraps + it in the specific Task subclass with which we were initialized. + """ + node = self._find_next_ready_node() if node is None: return None @@ -563,27 +667,12 @@ class Taskmaster: if self.ready_exc: task.exception_set(self.ready_exc) - self.ready = None self.ready_exc = None return task def stop(self): - """Stop the current build completely.""" + """ + Stops the current build completely. + """ self.next_candidate = self.no_next_candidate - self.ready = None - - def failed(self, node): - pass - - def executed(self, node): - pass - - def exception_raise(self, exception): - exc = exception[:] - try: - exc_type, exc_value, exc_traceback = exc - except ValueError: - exc_type, exc_value = exc - exc_traceback = None - raise exc_type, exc_value, exc_traceback diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index 1803eee2..f74cf347 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -681,16 +681,6 @@ class TaskmasterTestCase(unittest.TestCase): assert built_text == "MyTM.stop()" assert tm.next_task() is None - def test_failed(self): - """Test when a task has failed - """ - n1 = Node("n1") - tm = SCons.Taskmaster.Taskmaster([n1]) - t = tm.next_task() - assert t.targets == [n1], map(str, t.targets) - tm.failed(n1) - assert t.targets == [n1], map(str, t.targets) - def test_executed(self): """Test when a task has been executed """ @@ -974,20 +964,6 @@ class TaskmasterTestCase(unittest.TestCase): else: assert 0, "did not catch expected exception" - t.exception_set(("exception 4", "XYZZY")) - def fw_exc(exc): - raise 'exception_forwarded', exc - tm.exception_raise = fw_exc - try: - t.exception_raise() - except: - exc_type, exc_value = sys.exc_info()[:2] - assert exc_type == 'exception_forwarded', exc_type - assert exc_value[0] == "exception 4", exc_value[0] - assert exc_value[1] == "XYZZY", exc_value[1] - else: - assert 0, "did not catch expected exception" - def test_postprocess(self): """Test postprocessing targets to give them a chance to clean up """ diff --git a/src/engine/SCons/Tool/BitKeeper.py b/src/engine/SCons/Tool/BitKeeper.py index 2c2bfdda..b6561f4f 100644 --- a/src/engine/SCons/Tool/BitKeeper.py +++ b/src/engine/SCons/Tool/BitKeeper.py @@ -34,8 +34,6 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import os.path - import SCons.Action import SCons.Builder import SCons.Util diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 77363f04..09ce1d17 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -148,8 +148,10 @@ if java_parsing: self.outer_state = outer_state self.tokens_to_find = 2 def parseToken(self, token): - # This is an anonymous class if and only if the next token - # is a bracket + # This is an anonymous class if and only if the next + # non-whitespace token is a bracket + if token == '\n': + return self if token == '{': self.outer_state.addAnonClass() elif token in ['"', "'"]: diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py index 6d9fc43b..4a7f9cfc 100644 --- a/src/engine/SCons/Tool/JavaCommonTests.py +++ b/src/engine/SCons/Tool/JavaCommonTests.py @@ -265,6 +265,27 @@ public enum a {} assert classes == ['a'], classes + def test_anon_classes(self): + """Test anonymous classes""" + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\ +public abstract class TestClass +{ + public void completed() + { + new Thread() + { + }.start(); + + new Thread() + { + }.start(); + } +} +""") + assert pkg_dir == None, pkg_dir + assert classes == ['TestClass$1', 'TestClass$2', 'TestClass'], classes + + if __name__ == "__main__": suite = unittest.TestSuite() diff --git a/src/engine/SCons/Tool/ToolTests.py b/src/engine/SCons/Tool/ToolTests.py index 5ddac193..52c032fe 100644 --- a/src/engine/SCons/Tool/ToolTests.py +++ b/src/engine/SCons/Tool/ToolTests.py @@ -45,6 +45,8 @@ class ToolTestCase(unittest.TestCase): return self.dict[key] def __setitem__(self, key, val): self.dict[key] = val + def has_key(self, key): + return self.dict.has_key(key) env = Environment() env['BUILDERS'] = {} env['ENV'] = {} diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index d36478d4..b2e2eff9 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -41,6 +41,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import imp import sys +import SCons.Builder import SCons.Errors import SCons.Scanner import SCons.Scanner.C @@ -48,11 +49,13 @@ import SCons.Scanner.D import SCons.Scanner.LaTeX import SCons.Scanner.Prog +DefaultToolpath=[] + CScanner = SCons.Scanner.C.CScanner() DScanner = SCons.Scanner.D.DScanner() LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() ProgramScanner = SCons.Scanner.Prog.ProgramScanner() -SourceFileScanner = SCons.Scanner.Scanner({}, name='SourceFileScanner') +SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner') CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", ".h", ".H", ".hxx", ".hpp", ".hh", @@ -78,7 +81,7 @@ for suffix in LaTeXSuffixes: class Tool: def __init__(self, name, toolpath=[], **kw): self.name = name - self.toolpath = toolpath + self.toolpath = toolpath + DefaultToolpath # remember these so we can merge them into the call self.init_kw = kw diff --git a/src/engine/SCons/Tool/c++.py b/src/engine/SCons/Tool/c++.py index a44fa6de..d9370b05 100644 --- a/src/engine/SCons/Tool/c++.py +++ b/src/engine/SCons/Tool/c++.py @@ -60,6 +60,8 @@ def generate(env): Add Builders and construction variables for Visual Age C++ compilers to an Environment. """ + import SCons.Tool + import SCons.Tool.cc static_obj, shared_obj = SCons.Tool.createObjBuilders(env) for suffix in CXXSuffixes: @@ -67,6 +69,8 @@ def generate(env): shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + SCons.Tool.cc.add_common_cc_variables(env) env['CXX'] = 'c++' env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS') diff --git a/src/engine/SCons/Tool/cc.py b/src/engine/SCons/Tool/cc.py index 62b945f6..d1a287ab 100644 --- a/src/engine/SCons/Tool/cc.py +++ b/src/engine/SCons/Tool/cc.py @@ -40,6 +40,28 @@ CSuffixes = ['.c', '.m'] if not SCons.Util.case_sensitive_suffixes('.c', '.C'): CSuffixes.append('.C') +def add_common_cc_variables(env): + """ + Add underlying common "C compiler" variables that + are used by multiple tools (specifically, c++). + """ + if not env.has_key('_CCCOMCOM'): + env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' + # It's a hack to test for darwin here, but the alternative + # of creating an applecc.py to contain this seems overkill. + # Maybe someday the Apple platform will require more setup and + # this logic will be moved. + env['FRAMEWORKS'] = SCons.Util.CLVar('') + env['FRAMEWORKPATH'] = SCons.Util.CLVar('') + if env['PLATFORM'] == 'darwin': + env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' + + if not env.has_key('CCFLAGS'): + env['CCFLAGS'] = SCons.Util.CLVar('') + + if not env.has_key('SHCCFLAGS'): + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + def generate(env): """ Add Builders and construction variables for C compilers to an Environment. @@ -51,22 +73,13 @@ def generate(env): shared_obj.add_action(suffix, SCons.Defaults.ShCAction) static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' - # It's a hack to test for darwin here, but the alternative of creating - # an applecc.py to contain this seems overkill. Maybe someday the Apple - # platform will require more setup and this logic will be moved. - env['FRAMEWORKS'] = SCons.Util.CLVar('') - env['FRAMEWORKPATH'] = SCons.Util.CLVar('') - if env['PLATFORM'] == 'darwin': - env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' + + add_common_cc_variables(env) env['CC'] = 'cc' - env['CCFLAGS'] = SCons.Util.CLVar('') env['CFLAGS'] = SCons.Util.CLVar('') env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' env['SHCC'] = '$CC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' diff --git a/src/engine/SCons/Tool/cvf.py b/src/engine/SCons/Tool/cvf.py index 28a1915b..4bca52bb 100644 --- a/src/engine/SCons/Tool/cvf.py +++ b/src/engine/SCons/Tool/cvf.py @@ -29,7 +29,6 @@ Tool-specific initialization for the Compaq Visual Fortran compiler. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import SCons.Util import fortran compilers = ['f90'] diff --git a/src/engine/SCons/Tool/dmd.py b/src/engine/SCons/Tool/dmd.py index cae264b7..64ffb680 100644 --- a/src/engine/SCons/Tool/dmd.py +++ b/src/engine/SCons/Tool/dmd.py @@ -201,6 +201,8 @@ def generate(env): env.Append(LIBS = ['phobos']) if 'pthread' not in libs: env.Append(LIBS = ['pthread']) + if 'm' not in libs: + env.Append(LIBS = ['m']) return defaultLinker env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink diff --git a/src/engine/SCons/Tool/dvi.py b/src/engine/SCons/Tool/dvi.py index fce98509..78fb4c2d 100644 --- a/src/engine/SCons/Tool/dvi.py +++ b/src/engine/SCons/Tool/dvi.py @@ -36,7 +36,7 @@ DVIBuilder = None def generate(env): try: - bld = env['BUILDERS']['DVI'] + env['BUILDERS']['DVI'] except KeyError: global DVIBuilder diff --git a/src/engine/SCons/Tool/f77.py b/src/engine/SCons/Tool/f77.py index 75c3c2dd..bd1e870e 100644 --- a/src/engine/SCons/Tool/f77.py +++ b/src/engine/SCons/Tool/f77.py @@ -54,6 +54,7 @@ F77Scan = SCons.Scanner.Fortran.FortranScan("F77PATH") for suffix in F77Suffixes + F77PPSuffixes: SCons.Tool.SourceFileScanner.add_scanner(suffix, F77Scan) +del suffix # fVLG = fortran.VariableListGenerator diff --git a/src/engine/SCons/Tool/f90.py b/src/engine/SCons/Tool/f90.py index cb450b6d..fab4ccb9 100644 --- a/src/engine/SCons/Tool/f90.py +++ b/src/engine/SCons/Tool/f90.py @@ -54,6 +54,7 @@ F90Scan = SCons.Scanner.Fortran.FortranScan("F90PATH") for suffix in F90Suffixes + F90PPSuffixes: SCons.Tool.SourceFileScanner.add_scanner(suffix, F90Scan) +del suffix # fVLG = fortran.VariableListGenerator diff --git a/src/engine/SCons/Tool/f95.py b/src/engine/SCons/Tool/f95.py index 7adc80b3..94786c84 100644 --- a/src/engine/SCons/Tool/f95.py +++ b/src/engine/SCons/Tool/f95.py @@ -53,6 +53,7 @@ F95Scan = SCons.Scanner.Fortran.FortranScan("F95PATH") for suffix in F95Suffixes + F95PPSuffixes: SCons.Tool.SourceFileScanner.add_scanner(suffix, F95Scan) +del suffix # fVLG = fortran.VariableListGenerator diff --git a/src/engine/SCons/Tool/fortran.py b/src/engine/SCons/Tool/fortran.py index b83e7d3b..8494fd69 100644 --- a/src/engine/SCons/Tool/fortran.py +++ b/src/engine/SCons/Tool/fortran.py @@ -63,6 +63,7 @@ FortranScan = SCons.Scanner.Fortran.FortranScan("FORTRANPATH") for suffix in FortranSuffixes + FortranPPSuffixes: SCons.Tool.SourceFileScanner.add_scanner(suffix, FortranScan) +del suffix # def _fortranEmitter(target, source, env): diff --git a/src/engine/SCons/Tool/intelc.py b/src/engine/SCons/Tool/intelc.py index e668bf0c..d95a4bec 100644 --- a/src/engine/SCons/Tool/intelc.py +++ b/src/engine/SCons/Tool/intelc.py @@ -151,7 +151,7 @@ def get_intel_registry_value(valuename, version=None, abi=None): return v # or v.encode('iso-8859-1', 'replace') to remove unicode? except SCons.Util.RegError: raise MissingRegistryError, \ - "%s\\%s was not found in the registry."%(K, value) + "%s\\%s was not found in the registry."%(K, valuename) def get_all_compiler_versions(): diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index ed934127..cb0a8eb6 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -35,11 +35,14 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.Action import SCons.Builder +import SCons.Subst import SCons.Util def jarSources(target, source, env, for_signature): """Only include sources that are not a manifest file.""" jarchdir = env.subst('$JARCHDIR') + if jarchdir: + jarchdir = env.fs.Dir(jarchdir) result = [] for src in source: contents = src.get_contents() @@ -47,7 +50,7 @@ def jarSources(target, source, env, for_signature): if jarchdir: # If we are changing the dir with -C, then sources should # be relative to that directory. - src = src.get_path(src.fs.Dir(jarchdir)) + src = SCons.Subst.Literal(src.get_path(jarchdir)) result.append('-C') result.append(jarchdir) result.append(src) diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index 0639535c..cc7f5845 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -39,6 +39,7 @@ import string import SCons.Action import SCons.Builder +import SCons.Defaults import SCons.Tool import SCons.Util diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index e35c92ab..b84b2775 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -205,7 +205,7 @@ class _DSPGenerator: if len(buildtarget) == 1: bt = buildtarget[0] buildtarget = [] - for v in variants: + for _ in variants: buildtarget.append(bt) if not env.has_key('outdir') or env['outdir'] == None: @@ -1010,7 +1010,6 @@ class _GenerateV7DSW(_DSWGenerator): self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n') for name in confkeys: - name = name variant = self.configs[name].variant platform = self.configs[name].platform if self.version_num >= 8.0: @@ -1279,7 +1278,7 @@ def get_visualstudio8_suites(): try: idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Microsoft\VisualStudio\8.0') - id = SCons.Util.RegQueryValueEx(idk, 'InstallDir') + SCons.Util.RegQueryValueEx(idk, 'InstallDir') editions = { 'PRO': r'Setup\VS\Pro' } # ToDo: add standard and team editions edition_name = 'STD' for name, key_suffix in editions.items(): @@ -1297,7 +1296,7 @@ def get_visualstudio8_suites(): try: idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Microsoft\VCExpress\8.0') - id = SCons.Util.RegQueryValueEx(idk, 'InstallDir') + SCons.Util.RegQueryValueEx(idk, 'InstallDir') suites.append('EXPRESS') except SCons.Util.RegError: pass diff --git a/src/engine/SCons/Tool/mwcc.py b/src/engine/SCons/Tool/mwcc.py index a1ede44a..0d5ce82e 100644 --- a/src/engine/SCons/Tool/mwcc.py +++ b/src/engine/SCons/Tool/mwcc.py @@ -32,11 +32,11 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import SCons.Util import os import os.path import string +import SCons.Util def set_vars(env): """Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars @@ -155,6 +155,8 @@ CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] def generate(env): """Add Builders and construction variables for the mwcc to an Environment.""" + import SCons.Defaults + import SCons.Tool set_vars(env) diff --git a/src/engine/SCons/Tool/mwld.py b/src/engine/SCons/Tool/mwld.py index e2b1827c..e73c730a 100644 --- a/src/engine/SCons/Tool/mwld.py +++ b/src/engine/SCons/Tool/mwld.py @@ -33,7 +33,6 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.Tool -import SCons.Tool.mwcc def generate(env): @@ -60,7 +59,8 @@ def generate(env): def exists(env): - return mwcc.set_versions(env) + import SCons.Tool.mwcc + return SCons.Tool.mwcc.set_vars(env) def shlib_generator(target, source, env, for_signature): diff --git a/src/engine/SCons/Tool/pdf.py b/src/engine/SCons/Tool/pdf.py index 0f6468b0..b0cd126f 100644 --- a/src/engine/SCons/Tool/pdf.py +++ b/src/engine/SCons/Tool/pdf.py @@ -36,7 +36,7 @@ PDFBuilder = None def generate(env): try: - bld = env['BUILDERS']['PDF'] + env['BUILDERS']['PDF'] except KeyError: global PDFBuilder if PDFBuilder is None: diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 4d290c77..105f42e5 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -75,7 +75,6 @@ def checkMocIncluded(target, source, env): (str(moc), str(cpp))) def find_file(filename, paths, node_factory): - retval = None for dir in paths: node = node_factory(filename, dir) if node.rexists(): @@ -219,7 +218,6 @@ def uicEmitter(target, source, env): return target, source def uicScannerFunc(node, env, path): - dir = node.dir lookout = [] lookout.extend(env['CPPPATH']) lookout.append(str(node.rfile().dir)) @@ -231,18 +229,17 @@ def uicScannerFunc(node, env, path): result.append(dep) return result -uicScanner = SCons.Scanner.Scanner(uicScannerFunc, - name = "UicScanner", - node_class = SCons.Node.FS.File, - node_factory = SCons.Node.FS.File, - recursive = 0) +uicScanner = SCons.Scanner.Base(uicScannerFunc, + name = "UicScanner", + node_class = SCons.Node.FS.File, + node_factory = SCons.Node.FS.File, + recursive = 0) def generate(env): """Add Builders and construction variables for qt to an Environment.""" CLVar = SCons.Util.CLVar Action = SCons.Action.Action Builder = SCons.Builder.Builder - splitext = SCons.Util.splitext env.SetDefault(QTDIR = _detect(env), QT_BINPATH = os.path.join('$QTDIR', 'bin'), diff --git a/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py index a8e12a29..04c3b2ab 100644 --- a/src/engine/SCons/Tool/swig.py +++ b/src/engine/SCons/Tool/swig.py @@ -77,7 +77,6 @@ def recurse(path, searchPath): return found def _scanSwig(node, env, path): - import sys r = recurse(str(node), [os.path.abspath(os.path.dirname(str(node))), os.path.abspath(os.path.join("include", "swig"))]) return r @@ -85,7 +84,8 @@ def _swigEmitter(target, source, env): for src in source: src = str(src) mname = None - if "-python" in SCons.Util.CLVar(env.subst("$SWIGFLAGS")): + flags = SCons.Util.CLVar(env.subst("$SWIGFLAGS")) + if "-python" in flags and "-noproxy" not in flags: f = open(src) try: for l in f.readlines(): diff --git a/src/engine/SCons/Tool/tex.py b/src/engine/SCons/Tool/tex.py index 0329667f..1defd905 100644 --- a/src/engine/SCons/Tool/tex.py +++ b/src/engine/SCons/Tool/tex.py @@ -112,7 +112,7 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None # Now decide if latex needs to be run yet again. logfilename = basename + '.log' - for trial in range(int(env.subst('$LATEXRETRIES'))): + for _ in range(int(env.subst('$LATEXRETRIES'))): if not os.path.exists(logfilename): break content = open(logfilename, "rb").read() diff --git a/src/engine/SCons/Tool/zip.py b/src/engine/SCons/Tool/zip.py index f0d4ed0b..22f63fbb 100644 --- a/src/engine/SCons/Tool/zip.py +++ b/src/engine/SCons/Tool/zip.py @@ -36,6 +36,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path import SCons.Builder +import SCons.Defaults import SCons.Node.FS import SCons.Util diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 4bd050a1..eb38e44c 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -37,7 +37,6 @@ import os.path import re import string import sys -import stat import types from UserDict import UserDict @@ -234,9 +233,6 @@ def render_tree(root, child_func, prune=0, margin=[0], visited={}): rname = str(root) - if visited.has_key(rname): - return "" - children = child_func(root) retval = "" for pipe in margin[:-1]: @@ -245,6 +241,9 @@ def render_tree(root, child_func, prune=0, margin=[0], visited={}): else: retval = retval + " " + if visited.has_key(rname): + return retval + "+-[" + rname + "]\n" + retval = retval + "+-" + rname + "\n" if not prune: visited = copy.copy(visited) @@ -278,21 +277,19 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}): rname = str(root) - if visited.has_key(rname): - return - if showtags: if showtags == 2: - print ' E = exists' - print ' R = exists in repository only' - print ' b = implicit builder' - print ' B = explicit builder' - print ' S = side effect' - print ' P = precious' - print ' A = always build' - print ' C = current' - print ' N = no clean' + print ' E = exists' + print ' R = exists in repository only' + print ' b = implicit builder' + print ' B = explicit builder' + print ' S = side effect' + print ' P = precious' + print ' A = always build' + print ' C = current' + print ' N = no clean' + print ' H = no cache' print '' tags = ['['] @@ -305,6 +302,7 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}): tags.append(' A'[IDX(root.always_build)]) tags.append(' C'[IDX(root.current())]) tags.append(' N'[IDX(root.noclean)]) + tags.append(' H'[IDX(root.nocache)]) tags.append(']') else: @@ -314,6 +312,10 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}): return [" ","| "][m] margins = map(MMM, margin[:-1]) + if visited.has_key(rname): + print string.join(tags + margins + ['+-[', rname, ']'], '') + return + print string.join(tags + margins + ['+-', rname], '') if prune: @@ -567,6 +569,7 @@ elif os.name == 'os2': else: def WhereIs(file, path=None, pathext=None, reject=[]): + import stat if path is None: try: path = os.environ['PATH'] @@ -864,9 +867,10 @@ def unique(s): for x in s: u[x] = 1 except TypeError: - del u # move on to the next method + pass # move on to the next method else: return u.keys() + del u # We can't hash all the elements. Second fastest is to sort, # which brings the equal elements together; then duplicates are @@ -879,7 +883,7 @@ def unique(s): t = list(s) t.sort() except TypeError: - del t # move on to the next method + pass # move on to the next method else: assert n > 0 last = t[0] @@ -890,6 +894,7 @@ def unique(s): lasti = lasti + 1 i = i + 1 return t[:lasti] + del t # Brute force is all that's left. u = [] @@ -926,3 +931,16 @@ class LogicalLines: break result.append(line) return result + +class Unbuffered: + """ + A proxy class that wraps a file object, flushing after every write, + and delegating everything else to the wrapped object. + """ + def __init__(self, file): + self.file = file + def write(self, arg): + self.file.write(arg) + self.file.flush() + def __getattr__(self, attr): + return getattr(self.file, attr) diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index e2916621..db6d9f6e 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -56,6 +56,7 @@ class UtilTestCase(unittest.TestCase): def __init__(self, name, children=[]): self.children = children self.name = name + self.nocache = None def __str__(self): return self.name def exists(self): @@ -100,7 +101,7 @@ class UtilTestCase(unittest.TestCase): """ lines = string.split(expect, '\n')[:-1] - lines = map(lambda l: '[E BSPACN]'+l, lines) + lines = map(lambda l: '[E BSPACN ]'+l, lines) withtags = string.join(lines, '\n') + '\n' return foo, expect, withtags @@ -120,10 +121,11 @@ class UtilTestCase(unittest.TestCase): +-blat.h | +-stdlib.h +-bar.h + +-[stdlib.h] """ lines = string.split(expect, '\n')[:-1] - lines = map(lambda l: '[E BSPACN]'+l, lines) + lines = map(lambda l: '[E BSPACN ]'+l, lines) withtags = string.join(lines, '\n') + '\n' return blat_o, expect, withtags diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py index 1b13c96a..b313ee06 100644 --- a/src/engine/SCons/Warnings.py +++ b/src/engine/SCons/Warnings.py @@ -35,6 +35,7 @@ class Warning(SCons.Errors.UserError): pass +# NOTE: If you add a new warning class, add it to the man page, too! class CacheWriteErrorWarning(Warning): pass @@ -51,6 +52,9 @@ class DeprecatedWarning(Warning): class DuplicateEnvironmentWarning(Warning): pass +class MisleadingKeywordsWarning(Warning): + pass + class MissingSConscriptWarning(Warning): pass @@ -66,9 +70,6 @@ class NoParallelSupportWarning(Warning): class ReservedVariableWarning(Warning): pass -class MisleadingKeywordsWarning(Warning): - pass - _warningAsException = 0 # The below is a list of 2-tuples. The first element is a class object. diff --git a/src/engine/SCons/__init__.py b/src/engine/SCons/__init__.py index 20e47344..b5488419 100644 --- a/src/engine/SCons/__init__.py +++ b/src/engine/SCons/__init__.py @@ -38,5 +38,3 @@ __buildsys__ = "__BUILDSYS__" __date__ = "__DATE__" __developer__ = "__DEVELOPER__" - -import SCons.Memoize diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py index 6112db56..7b74ab46 100644 --- a/src/engine/SCons/compat/__init__.py +++ b/src/engine/SCons/compat/__init__.py @@ -79,6 +79,25 @@ def import_as(module, name): import builtins +try: + set +except NameError: + # Pre-2.4 Python has no native set type + try: + # Python 2.2 and 2.3 can use the copy of the 2.[45] sets module + # that we grabbed. + import_as('_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 __builtin__ + import sets + __builtin__.set = sets.Set + try: import subprocess except ImportError: diff --git a/src/engine/SCons/compat/_sets.py b/src/engine/SCons/compat/_sets.py new file mode 100644 index 00000000..32a0dd64 --- /dev/null +++ b/src/engine/SCons/compat/_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/_sets15.py b/src/engine/SCons/compat/_sets15.py new file mode 100644 index 00000000..b3d0bb32 --- /dev/null +++ b/src/engine/SCons/compat/_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/_subprocess.py b/src/engine/SCons/compat/_subprocess.py index aa17caef..fc06347d 100644 --- a/src/engine/SCons/compat/_subprocess.py +++ b/src/engine/SCons/compat/_subprocess.py @@ -356,6 +356,7 @@ import sys mswindows = (sys.platform == "win32") import os +import string import types import traceback @@ -565,7 +566,7 @@ def list2cmdline(seq): result.extend(bs_buf) result.append('"') - return ''.join(result) + return string.join(result, '') try: @@ -1048,12 +1049,8 @@ class Popen(object): # Close pipe fds. Make sure we don't close the same # fd more than once, or standard fds. - 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) + 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: @@ -1079,7 +1076,7 @@ class Popen(object): exc_lines = traceback.format_exception(exc_type, exc_value, tb) - exc_value.child_traceback = ''.join(exc_lines) + 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 @@ -1158,6 +1155,7 @@ class Popen(object): read_set.append(self.stderr) stderr = [] + input_offset = 0 while read_set or write_set: rlist, wlist, xlist = select.select(read_set, write_set, []) @@ -1165,9 +1163,9 @@ class Popen(object): # 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(), input[:512]) - input = input[bytes_written:] - if not input: + 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) @@ -1187,9 +1185,9 @@ class Popen(object): # All data exchanged. Translate lists into strings. if stdout is not None: - stdout = ''.join(stdout) + stdout = string.join(stdout, '') if stderr is not None: - stderr = ''.join(stderr) + stderr = string.join(stderr, '') # Translate newlines, if requested. We cannot let the file # object do the translation: It is based on stdio, which is diff --git a/src/engine/SCons/cpp.py b/src/engine/SCons/cpp.py index d96ef235..8620936f 100644 --- a/src/engine/SCons/cpp.py +++ b/src/engine/SCons/cpp.py @@ -32,7 +32,6 @@ import SCons.compat import os import re import string -import sys # # First "subsystem" of regular expressions that we set up: diff --git a/src/script/scons-post-install.py b/src/script/scons-post-install.py new file mode 100644 index 00000000..a622a6ac --- /dev/null +++ b/src/script/scons-post-install.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# scons-post-install - SCons post install script for Windows +# +# A script for configuring "App Paths" registry key so that SCons could +# be run from any directory the same way Python is. +# + +# +# SCons - a Software Constructor +# +# __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 sys + +scons_bat_path = os.path.join(sys.prefix, 'Scripts', 'scons.bat') + +app_paths_key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\SCons.bat' + +def install(): + if sys.platform == 'win32': + try: + import _winreg + except ImportError: + pass + else: + print 'Writing "App Paths" registry entry for %s' % scons_bat_path + _winreg.SetValue( + _winreg.HKEY_LOCAL_MACHINE, + app_paths_key, + _winreg.REG_SZ, + scons_bat_path) + print 'Done.' + + +def remove(): + if sys.platform == 'win32': + try: + import _winreg + except ImportError: + pass + else: + # print 'Remove "App Paths" registry entry' + _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, app_paths_key) + + +if len(sys.argv) > 1: + if sys.argv[1] == '-install': + install() + elif sys.argv[1] == '-remove': + remove() + +sys.exit(0) diff --git a/src/script/scons.bat b/src/script/scons.bat index 9e45a911..c0a304d5 100644 --- a/src/script/scons.bat +++ b/src/script/scons.bat @@ -7,6 +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 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 f04ca1bb..4b097ac4 100644 --- a/src/setup.cfg +++ b/src/setup.cfg @@ -3,3 +3,4 @@ group = Development/Tools [bdist_wininst] title = SCons - a software construction tool +install-script = scons-post-install.py diff --git a/src/setup.py b/src/setup.py index 9ad00694..53b19fa6 100644 --- a/src/setup.py +++ b/src/setup.py @@ -350,9 +350,35 @@ class install_data(_install_data): else: self.data_files = [] +description = """Open Source next-generation build tool. +Improved, cross-platform substitute for the classic Make +utility. In short, SCons is an easier, more reliable +and faster way to build software.""" + +scripts = [ + 'script/scons', + 'script/sconsign', + 'script/scons-time', + + # We include scons.bat in the list of scripts, even on UNIX systems, + # because we provide an option to allow it be installed explicitly, + # for example if you're installing from UNIX on a share that's + # accessible to Windows and you want the scons.bat. + 'script/scons.bat', +] + +if is_win32: + scripts = scripts + [ + 'script/scons-post-install.py' + ] + arguments = { 'name' : "scons", 'version' : Version, + 'description' : description, + 'author' : 'Steven Knight', + 'author_email' : 'knight@baldmt.com', + 'url' : "http://www.scons.org/", 'packages' : ["SCons", "SCons.compat", "SCons.Node", @@ -365,10 +391,7 @@ arguments = { "SCons.Tool"], 'package_dir' : {'' : 'engine'}, 'data_files' : [('man/man1', man_pages)], - 'scripts' : ['script/scons', - 'script/sconsign', - 'script/scons-time', - 'script/scons.bat'], + 'scripts' : scripts, 'cmdclass' : {'install' : install, 'install_lib' : install_lib, 'install_data' : install_data, diff --git a/src/test_pychecker.py b/src/test_pychecker.py new file mode 100644 index 00000000..368520f9 --- /dev/null +++ b/src/test_pychecker.py @@ -0,0 +1,145 @@ +#!/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__" + +""" +Use pychecker to catch various Python coding errors. +""" + +import os +import os.path +import string +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +test.skip_test('Not ready for clean pychecker output; skipping test.\n') + +try: + import pychecker +except ImportError: + pychecker = test.where_is('pychecker') + if not pychecker: + test.skip_test("Could not find 'pychecker'; skipping test(s).\n") + program = pychecker + default_arguments = [] +else: + pychecker = os.path.join(os.path.split(pychecker.__file__)[0], 'checker.py') + program = sys.executable + default_arguments = [pychecker] + +try: + cwd = os.environ['SCONS_CWD'] +except KeyError: + src_engine = os.environ['SCONS_LIB_DIR'] +else: + src_engine = os.path.join(cwd, 'build', 'scons-src', 'src', 'engine') + if not os.path.exists(src_engine): + src_engine = os.path.join(cwd, 'src', 'engine') + +src_engine_ = os.path.join(src_engine, '') + +MANIFEST = os.path.join(src_engine, 'MANIFEST.in') +files = string.split(open(MANIFEST).read()) + +files = filter(lambda f: f[-3:] == '.py', files) + +ignore = [ + 'SCons/compat/__init__.py', + 'SCons/compat/builtins.py', + 'SCons/compat/_subprocess.py', + 'SCons/Optik/__init__.py', + 'SCons/Optik/errors.py', + 'SCons/Optik/option.py', + 'SCons/Optik/option_parser.py', +] + +u = {} +for file in files: + u[file] = 1 +for file in ignore: + try: + del u[file] + except KeyError: + pass + +files = u.keys() + +files.sort() + +mismatches = [] + +default_arguments.extend([ + '--quiet', + '--limit=1000', +]) + +if sys.platform == 'win32': + default_arguments.extend([ + '--blacklist', '"pywintypes,pywintypes.error"', + ]) + +per_file_arguments = { + 'SCons/__init__.py' : [ + '--varlist', '"__revision__,__version__,__build__,__buildsys__,__date__,__developer__"', + ], +} + +pywintypes_warning = "warning: couldn't find real module for class pywintypes.error (module name: pywintypes)\n" + +os.environ['PYTHONPATH'] = src_engine + +for file in files: + + file = os.path.join(src_engine, file) + args = default_arguments + per_file_arguments.get(file, []) + [file] + + test.run(program=program, arguments=args, status=None, stderr=None) + + stdout = test.stdout() + stdout = string.replace(stdout, src_engine_, '') + + stderr = test.stderr() + stderr = string.replace(stderr, src_engine_, '') + stderr = string.replace(stderr, pywintypes_warning, '') + + if test.status or stdout or stderr: + mismatches.append('\n') + mismatches.append(string.join([program] + args) + '\n') + + mismatches.append('STDOUT =====================================\n') + mismatches.append(stdout) + + if stderr: + mismatches.append('STDERR =====================================\n') + mismatches.append(stderr) + +if mismatches: + print string.join(mismatches[1:], '') + test.fail_test() + +test.pass_test() diff --git a/src/test_strings.py b/src/test_strings.py index 92202315..b609f595 100644 --- a/src/test_strings.py +++ b/src/test_strings.py @@ -114,6 +114,8 @@ 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/Conftest.py', 'engine/SCons/dblite.py', @@ -134,6 +136,9 @@ check_list = [ 'configure-stamp', 'debian', 'dist', + 'gentoo', + 'engine/SCons/compat/_sets.py', + 'engine/SCons/compat/_sets15.py', 'engine/SCons/compat/_subprocess.py', 'engine/SCons/Conftest.py', 'engine/SCons/dblite.py', @@ -147,6 +152,8 @@ check_list = [ CheckExpandedCopyright( build_local, remove_list = [ + 'SCons/compat/_sets.py', + 'SCons/compat/_sets15.py', 'SCons/compat/_subprocess.py', 'SCons/Conftest.py', 'SCons/dblite.py', @@ -160,10 +167,12 @@ check_list = [ 'bin', 'config', 'debian', + 'gentoo', 'doc/design', 'doc/MANIFEST', 'doc/python10', 'doc/reference', + 'doc/developer/MANIFEST', 'doc/man/MANIFEST', 'doc/user/cons.pl', 'doc/user/MANIFEST', @@ -183,6 +192,8 @@ 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/Conftest.py', 'src/engine/SCons/dblite.py', diff --git a/test/CXX/CC-variables.py b/test/CXX/CC-variables.py new file mode 100644 index 00000000..93aa315a --- /dev/null +++ b/test/CXX/CC-variables.py @@ -0,0 +1,50 @@ +#!/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 initializing a construction environment with just the +g++ tool uses the CCFLAGS. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +env = Environment(tools = ['g++']) +env.Object(target = 'test.obj', source = 'test.cxx') +env.MergeFlags('+for_CCFLAGS -Wp,-for_CPPFLAGS') +""") + +test.write('test.cxx', "test.cxx\n") + +expect = """\ +g++ -o test.obj -c +for_CCFLAGS -Wp,-for_CPPFLAGS test.cxx +""" + +test.run(arguments = '-Q -n test.obj', stdout=expect) + +test.pass_test() diff --git a/test/CacheDir/NoCache.py b/test/CacheDir/NoCache.py new file mode 100644 index 00000000..44e51a01 --- /dev/null +++ b/test/CacheDir/NoCache.py @@ -0,0 +1,75 @@ +#!/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 NoCache environment method works. +""" + +import TestSCons, os.path + +test = TestSCons.TestSCons() + +test.subdir('cache', 'alpha', 'beta') + +sconstruct = """ +import os +CacheDir(r'%s') + + +# This is bad form, but the easiest way to produce a test case. +# Obviously, this could be cached if the inputs were passed in a +# reasonable fashion. +g = '%s' + +def ActionWithUndeclaredInputs(target,source,env): + open(target[0].abspath,'w').write(g) + +Command('foo_cached', [], ActionWithUndeclaredInputs) +NoCache(Command('foo_notcached', [], ActionWithUndeclaredInputs)) +Command('bar_cached', [], ActionWithUndeclaredInputs) +Command('bar_notcached', [], ActionWithUndeclaredInputs) +NoCache('bar_notcached') + +# Make sure NoCache doesn't vomit when applied to a Dir +NoCache(Command(Dir('aoeu'), [], Mkdir('$TARGET'))) +""" + +test.write('alpha/SConstruct', sconstruct % (test.workpath('cache'), 'alpha')) + +test.write('beta/SConstruct', sconstruct % (test.workpath('cache'), 'beta')) + +# First build, would populate the cache without NoCache +test.run(chdir = 'alpha', arguments = '.') + +# Second build, without NoCache there would be a cache hit +test.run(chdir = 'beta', arguments = '.') + +test.must_match(['beta','foo_cached'], 'alpha') +test.must_match(['beta','foo_notcached'], 'beta') +test.must_match(['beta','bar_cached'], 'alpha') +test.must_match(['beta','bar_notcached'], 'beta') + +test.pass_test() diff --git a/test/Install/no-top-relative.py b/test/Install/no-top-relative.py new file mode 100644 index 00000000..51538eac --- /dev/null +++ b/test/Install/no-top-relative.py @@ -0,0 +1,53 @@ +#!/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 can install a file if its file name portion begins +with a '#'. (A previous bug re-interpreted that as relative to +the top-level SConstruct directory.) + +Thanks to Dave Weber for the test case. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir(['test']) + +test.write(['SConstruct'], """\ +env = Environment() +i = env.Install("#/install", "#/test/#testfile.txt#"); +env.Default(i); +""") + +test.write(['test', '#testfile.txt#'], "test/#testfile.txt#\n") + +test.run() + +test.must_match(['install', '#testfile.txt#'], "test/#testfile.txt#\n") + +test.pass_test() diff --git a/test/Java/JARCHDIR.py b/test/Java/JARCHDIR.py index 3ee3deed..9250843a 100644 --- a/test/Java/JARCHDIR.py +++ b/test/Java/JARCHDIR.py @@ -58,7 +58,13 @@ env = Environment(tools = ['javac', 'jar'], JARCHDIR = dir) bin = env.Java(dir, Dir('./')) jar = env.Jar(File('c.jar', dir), bin) -Default(bin, jar) + +# Make sure we handle class files with $ in them, such as typically +# created for inner classes. +env = env.Clone(JARCHDIR = '.') +inner = env.Jar('inner.jar', 'Inner$$Class.class') + +Default(bin, jar, inner) """ % locals()) test.write('a.java', """\ @@ -71,6 +77,8 @@ package foo.bar; public class b {} """) +test.write('Inner$Class.class', "Inner$Class.class\n") + test.run(arguments = '.') test.pass_test() diff --git a/test/Options/BoolOption.py b/test/Options/BoolOption.py index 7af9bfd4..aab2b205 100644 --- a/test/Options/BoolOption.py +++ b/test/Options/BoolOption.py @@ -31,6 +31,12 @@ Test the BoolOption canned Option type. import os.path import string +try: + True, False +except NameError: + True = (0 == 0) + False = (0 != 0) + import TestSCons test = TestSCons.TestSCons() @@ -64,10 +70,10 @@ Default(env.Alias('dummy', None)) test.run() -check(['1', '0']) +check([str(True), str(False)]) test.run(arguments='warnings=0 profile=no profile=true') -check(['0', '1']) +check([str(False), str(True)]) expect_stderr = """ scons: *** Error converting option: warnings diff --git a/test/Options/PackageOption.py b/test/Options/PackageOption.py index 81f70032..d285338e 100644 --- a/test/Options/PackageOption.py +++ b/test/Options/PackageOption.py @@ -31,6 +31,12 @@ Test the PackageOption canned Option type. import os.path import string +try: + True, False +except NameError: + True = (0 == 0) + False = (0 != 0) + import TestSCons test = TestSCons.TestSCons() @@ -61,10 +67,16 @@ Default(env.Alias('dummy', None)) """) test.run() -check(['1']) -test.run(arguments='x11=no'); check(['0']) -test.run(arguments='x11=0'); check(['0']) -test.run(arguments=['x11=%s' % test.workpath()]); check([test.workpath()]) +check([str(True)]) + +test.run(arguments='x11=no') +check([str(False)]) + +test.run(arguments='x11=0') +check([str(False)]) + +test.run(arguments=['x11=%s' % test.workpath()]) +check([test.workpath()]) expect_stderr = """ scons: *** Path does not exist for option x11: /non/existing/path/ diff --git a/test/Options/help.py b/test/Options/help.py index 5ef56224..d3fa9ad0 100644 --- a/test/Options/help.py +++ b/test/Options/help.py @@ -31,6 +31,15 @@ Test the Options help messages. import os.path import string +try: + True, False +except NameError: + True = (0 == 0) + False = (0 != 0) + +str_True = str(True) +str_False = str(False) + import TestSCons test = TestSCons.TestSCons() @@ -50,7 +59,7 @@ from SCons.Options import BoolOption, EnumOption, ListOption, \ PackageOption, PathOption list_of_libs = Split('x11 gl qt ical') -qtdir = r'%(qtdir)s' +qtdir = r'%(qtpath)s' opts = Options(args=ARGUMENTS) opts.AddOptions( @@ -85,23 +94,23 @@ print env['warnings'] print env['profile'] Default(env.Alias('dummy', None)) -""" % {'qtdir': qtpath, 'libdirvar': libdirvar, 'libdir': libpath}) +""" % locals()) test.run(arguments='-h', stdout = """\ scons: Reading SConscript files ... -1 -0 +%(str_True)s +%(str_False)s scons: done reading SConscript files. warnings: compilation with -Wall and similiar (yes|no) default: 1 - actual: 1 + actual: %(str_True)s profile: create profiling informations (yes|no) default: 0 - actual: 0 + actual: %(str_False)s debug: debug output and symbols (yes|no|full) default: no @@ -124,19 +133,18 @@ shared: libraries to build as shared libraries x11: use X11 installed here (yes = search some places) ( yes | no | /path/to/x11 ) default: yes - actual: 1 + actual: %(str_True)s qtdir: where the root of Qt is installed ( /path/to/qtdir ) - default: %(qtdir)s - actual: %(qtdir)s + default: %(qtpath)s + actual: %(qtpath)s qt_libraries: where the Qt library is installed ( /path/to/qt_libraries ) - default: %(qtdir_lib)s - actual: %(libdir)s + default: %(libdirvar)s + actual: %(libpath)s Use scons -H for help about command-line options. -""" % {'qtdir': qtpath, 'qtdir_lib' : os.path.join('$qtdir', 'lib'), - 'libdirvar': libdirvar, 'libdir': libpath}) +""" % locals()) diff --git a/test/ParseConfig.py b/test/ParseConfig.py index f1427834..ed222764 100644 --- a/test/ParseConfig.py +++ b/test/ParseConfig.py @@ -55,8 +55,9 @@ test.write(test_config3, """\ print "-L foo -L lib_dir -isysroot /tmp -arch ppc -arch i386" """) -test.write('SConstruct', """ -env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], CCFLAGS = '') +test.write('SConstruct1', """ +env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], + CCFLAGS = '-pipe -Wall') env.ParseConfig([r'%(_python_)s', r"%(test_config1)s", "--libs --cflags"]) env.ParseConfig([r'%(_python_)s', r"%(test_config2)s", "--libs --cflags"]) print env['CPPPATH'] @@ -66,7 +67,8 @@ print env['CCFLAGS'] """ % locals()) test.write('SConstruct2', """ -env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], CCFLAGS = '', +env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], + CCFLAGS = '-pipe -Wall', PYTHON = r'%(_python_)s') env.ParseConfig(r"$PYTHON %(test_config1)s --libs --cflags") env.ParseConfig(r"$PYTHON %(test_config2)s --libs --cflags") @@ -77,7 +79,8 @@ print env['CCFLAGS'] """ % locals()) test.write('SConstruct3', """ -env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], CCFLAGS = '', +env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], + CCFLAGS = '-pipe -Wall', PYTHON = r'%(_python_)s') env.ParseConfig(r"$PYTHON %(test_config3)s --libs --cflags") print env['CPPPATH'] @@ -86,24 +89,24 @@ print map(lambda x: str(x), env['LIBS']) print env['CCFLAGS'] """ % locals()) -good_stdout = test.wrap_stdout(read_str = """\ +good_stdout = """\ ['/usr/include/fum', 'bar'] ['/usr/fax', 'foo', 'lib_dir'] ['xxx', 'abc'] -['-X', ('-arch', 'i386')] -""", build_str = "scons: `.' is up to date.\n") +['-pipe', '-Wall', '-X', ('-arch', 'i386')] +""" -stdout3 = test.wrap_stdout(read_str = """\ +stdout3 = """\ [] ['foo', 'lib_dir'] [] -[('-isysroot', '/tmp'), ('-arch', 'ppc'), ('-arch', 'i386')] -""", build_str = "scons: `.' is up to date.\n") +['-pipe', '-Wall', ('-isysroot', '/tmp'), ('-arch', 'ppc'), ('-arch', 'i386')] +""" -test.run(arguments = ".", stdout = good_stdout) +test.run(arguments = "-q -Q -f SConstruct1 .", stdout = good_stdout) -test.run(arguments = "-f SConstruct2 .", stdout = good_stdout) +test.run(arguments = "-q -Q -f SConstruct2 .", stdout = good_stdout) -test.run(arguments = "-f SConstruct3 .", stdout = stdout3) +test.run(arguments = "-q -Q -f SConstruct3 .", stdout = stdout3) test.pass_test() diff --git a/test/SPAWN.py b/test/SPAWN.py index f73ab112..2a3632eb 100644 --- a/test/SPAWN.py +++ b/test/SPAWN.py @@ -46,21 +46,42 @@ test.write('SConstruct', """ import os import string import sys -def my_spawn(sh, escape, cmd, args, env): - s = string.join(args + ['extra.txt']) +def my_spawn1(sh, escape, cmd, args, env): + s = string.join(args + ['extra1.txt']) if sys.platform in ['win32']: s = '"' + s + '"' os.system(s) -env = Environment(SPAWN = my_spawn) -env.Command('file.out', 'file.in', '%(_python_)s cat.py $TARGET $SOURCES') -env = Environment() +def my_spawn2(sh, escape, cmd, args, env): + s = string.join(args + ['extra2.txt']) + if sys.platform in ['win32']: + s = '"' + s + '"' + os.system(s) +env = Environment(MY_SPAWN1 = my_spawn1, + MY_SPAWN2 = my_spawn2, + COMMAND = r'%(_python_)s cat.py $TARGET $SOURCES') +env1 = env.Clone(SPAWN = my_spawn1) +env1.Command('file1.out', 'file1.in', '$COMMAND') + +env2 = env.Clone(SPAWN = '$MY_SPAWN2') +env2.Command('file2.out', 'file2.in', '$COMMAND') + +env3 = env.Clone(SPAWN = '${USE_TWO and MY_SPAWN2 or MY_SPAWN1}') +env3.Command('file3.out', 'file3.in', '$COMMAND', USE_TWO=0) +env3.Command('file4.out', 'file4.in', '$COMMAND', USE_TWO=1) """ % locals()) -test.write('file.in', "file.in\n") -test.write('extra.txt', "extra.txt\n") +test.write('file1.in', "file1.in\n") +test.write('file2.in', "file2.in\n") +test.write('file3.in', "file3.in\n") +test.write('file4.in', "file4.in\n") +test.write('extra1.txt', "extra1.txt\n") +test.write('extra2.txt', "extra2.txt\n") test.run(arguments = '.') -test.must_match('file.out', "file.in\nextra.txt\n") +test.must_match('file1.out', "file1.in\nextra1.txt\n") +test.must_match('file2.out', "file2.in\nextra2.txt\n") +test.must_match('file3.out', "file3.in\nextra1.txt\n") +test.must_match('file4.out', "file4.in\nextra2.txt\n") test.pass_test() diff --git a/test/SWIG/SWIG.py b/test/SWIG/SWIG.py index 791d0e0d..dfee5ef2 100644 --- a/test/SWIG/SWIG.py +++ b/test/SWIG/SWIG.py @@ -24,6 +24,10 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +""" +Verify that the swig tool generates file names that we expect. +""" + import os import string import sys @@ -37,19 +41,11 @@ if sys.platform =='darwin': python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python" _python_ = '"' + python + '"' else: - python = TestSCons.python _python_ = TestSCons._python_ _exe = TestSCons._exe _obj = TestSCons._obj -# swig-python expects specific filenames. -# the platform specific suffix won't necessarily work. -if sys.platform == 'win32': - _dll = '.dll' -else: - _dll = '.so' - test = TestSCons.TestSCons() @@ -105,168 +101,13 @@ swig test.run(arguments = '.', stderr = None) test.run(program = test.workpath('test1' + _exe), stdout = "test1.i\n") -test.fail_test(not os.path.exists(test.workpath('test1_wrap.c'))) -test.fail_test(not os.path.exists(test.workpath('test1_wrap' + _obj))) +test.must_exist(test.workpath('test1_wrap.c')) +test.must_exist(test.workpath('test1_wrap' + _obj)) -test.fail_test(test.read('test2_wrap.c') != "test2.i\n") +test.must_match('test2_wrap.c', "test2.i\n") test.run(program = test.workpath('test3' + _exe), stdout = "test3.i\n") -test.fail_test(not os.path.exists(test.workpath('test3_wrap.cc'))) -test.fail_test(not os.path.exists(test.workpath('test3_wrap' + _obj))) - - - -swig = test.where_is('swig') - -if swig: - - version = sys.version[:3] # see also sys.prefix documentation - - # handle testing on other platforms: - ldmodule_prefix = '_' - - frameworks = '' - platform_sys_prefix = sys.prefix - if sys.platform == 'darwin': - # OS X has a built-in Python but no static libpython - # so you should link to it using apple's 'framework' scheme. - # (see top of file for further explanation) - frameworks = '-framework Python' - platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version - - test.write("wrapper.py", -"""import os -import string -import sys -open('%s', 'wb').write("wrapper.py\\n") -os.system(string.join(sys.argv[1:], " ")) -""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\')) - - test.write('SConstruct', """ -foo = Environment(SWIGFLAGS='-python', - CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', - LDMODULEPREFIX='%(ldmodule_prefix)s', - LDMODULESUFFIX='%(_dll)s', - FRAMEWORKSFLAGS='%(frameworks)s', - ) - -swig = foo.Dictionary('SWIG') -bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig) -foo.LoadableModule(target = 'foo', source = ['foo.c', 'foo.i']) -bar.LoadableModule(target = 'bar', source = ['bar.c', 'bar.i']) -""" % locals()) - - test.write("foo.c", """\ -char * -foo_string() -{ - return "This is foo.c!"; -} -""") - - test.write("foo.i", """\ -%module foo -%{ -/* Put header files here (optional) */ -%} - -extern char *foo_string(); -""") - - test.write("bar.c", """\ -char * -bar_string() -{ - return "This is bar.c!"; -} -""") - - test.write("bar.i", """\ -%module \t bar -%{ -/* Put header files here (optional) */ -%} - -extern char *bar_string(); -""") - - test.run(arguments = ldmodule_prefix+'foo' + _dll) - - test.must_not_exist(test.workpath('wrapper.out')) - - test.run(program = python, stdin = """\ -import foo -print foo.foo_string() -""", stdout="""\ -This is foo.c! -""") - - test.up_to_date(arguments = ldmodule_prefix+'foo' + _dll) - - test.run(arguments = ldmodule_prefix+'bar' + _dll) - - test.must_match('wrapper.out', "wrapper.py\n") - - test.run(program = python, stdin = """\ -import foo -import bar -print foo.foo_string() -print bar.bar_string() -""", stdout="""\ -This is foo.c! -This is bar.c! -""") - - test.up_to_date(arguments = '.') - - # Test that swig-generated modules are removed - # The %module directive specifies the module name - test.write("module.i", """\ -%module modulename -""") - test.write('SConstruct', """ -foo = Environment(SWIGFLAGS='-python', - CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', - LDMODULEPREFIX='%(ldmodule_prefix)s', - LDMODULESUFFIX='%(_dll)s', - FRAMEWORKSFLAGS='%(frameworks)s', - ) - -foo.LoadableModule(target = 'modulename', source = ['module.i']) -""" % locals()) - test.run() - test.must_exist(test.workpath("modulename.py")) - test.run(arguments = "-c") - test.must_not_exist(test.workpath("modulename.py")) - - # Test that implicit dependencies are caught - - test.write("dependency.i", """\ -%module dependency -""") - test.write("dependent.i", """\ -%module dependent - -%include dependency.i -""") - test.write('SConstruct', """ -foo = Environment(SWIGFLAGS='-python', - CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', - LDMODULEPREFIX='%(ldmodule_prefix)s', - LDMODULESUFFIX='%(_dll)s', - FRAMEWORKSFLAGS='%(frameworks)s', - ) - -swig = foo.Dictionary('SWIG') -bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig) -foo.CFile(target = 'dependent', source = ['dependent.i']) -""" % locals()) - - test.run() - test.write("dependency.i", """%module dependency - -extern char *dependency_string(); -""") - test.not_up_to_date(arguments = "dependent_wrap.c") +test.must_exist(test.workpath('test3_wrap.cc')) +test.must_exist(test.workpath('test3_wrap' + _obj)) test.pass_test() diff --git a/test/SWIG/implicit-dependencies.py b/test/SWIG/implicit-dependencies.py new file mode 100644 index 00000000..55645a4e --- /dev/null +++ b/test/SWIG/implicit-dependencies.py @@ -0,0 +1,110 @@ +#!/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 SWIG implicit dependencies are caught. +""" + +import sys + +import TestSCons + +if sys.platform =='darwin': + # change to make it work with stock OS X python framework + # we can't link to static libpython because there isn't one on OS X + # so we link to a framework version. However, testing must also + # use the same version, or else you get interpreter errors. + python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python" + _python_ = '"' + python + '"' +else: + python = TestSCons.python + _python_ = TestSCons._python_ + +# swig-python expects specific filenames. +# the platform specific suffix won't necessarily work. +if sys.platform == 'win32': + _dll = '.dll' +else: + _dll = '.so' + +test = TestSCons.TestSCons() + +swig = test.where_is('swig') + +if not swig: + test.skip_test('Can not find installed "swig", skipping test.\n') + + + +version = sys.version[:3] # see also sys.prefix documentation + +# handle testing on other platforms: +ldmodule_prefix = '_' + +frameworks = '' +platform_sys_prefix = sys.prefix +if sys.platform == 'darwin': + # OS X has a built-in Python but no static libpython + # so you should link to it using apple's 'framework' scheme. + # (see top of file for further explanation) + frameworks = '-framework Python' + platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version + +test.write("dependency.i", """\ +%module dependency +""") + +test.write("dependent.i", """\ +%module dependent + +%include dependency.i +""") + +test.write('SConstruct', """ +foo = Environment(SWIGFLAGS='-python', + CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', + LDMODULEPREFIX='%(ldmodule_prefix)s', + LDMODULESUFFIX='%(_dll)s', + FRAMEWORKSFLAGS='%(frameworks)s', + ) + +swig = foo.Dictionary('SWIG') +bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig) +foo.CFile(target = 'dependent', source = ['dependent.i']) +""" % locals()) + +test.run() + +test.write("dependency.i", """%module dependency + +extern char *dependency_string(); +""") + +test.not_up_to_date(arguments = "dependent_wrap.c") + + + +test.pass_test() diff --git a/test/SWIG/live.py b/test/SWIG/live.py new file mode 100644 index 00000000..c6b45d22 --- /dev/null +++ b/test/SWIG/live.py @@ -0,0 +1,164 @@ +#!/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 SWIG behavior with a live, installed SWIG. +""" + +import string +import sys + +import TestSCons + +if sys.platform =='darwin': + # change to make it work with stock OS X python framework + # we can't link to static libpython because there isn't one on OS X + # so we link to a framework version. However, testing must also + # use the same version, or else you get interpreter errors. + python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python" + _python_ = '"' + python + '"' +else: + python = TestSCons.python + _python_ = TestSCons._python_ + +# swig-python expects specific filenames. +# the platform specific suffix won't necessarily work. +if sys.platform == 'win32': + _dll = '.dll' +else: + _dll = '.so' + +test = TestSCons.TestSCons() + +swig = test.where_is('swig') + +if not swig: + test.skip_test('Can not find installed "swig", skipping test.\n') + + + +version = sys.version[:3] # see also sys.prefix documentation + +# handle testing on other platforms: +ldmodule_prefix = '_' + +frameworks = '' +platform_sys_prefix = sys.prefix +if sys.platform == 'darwin': + # OS X has a built-in Python but no static libpython + # so you should link to it using apple's 'framework' scheme. + # (see top of file for further explanation) + frameworks = '-framework Python' + platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version + +test.write("wrapper.py", +"""import os +import string +import sys +open('%s', 'wb').write("wrapper.py\\n") +os.system(string.join(sys.argv[1:], " ")) +""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\')) + +test.write('SConstruct', """ +foo = Environment(SWIGFLAGS='-python', + CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', + LDMODULEPREFIX='%(ldmodule_prefix)s', + LDMODULESUFFIX='%(_dll)s', + FRAMEWORKSFLAGS='%(frameworks)s', + ) + +swig = foo.Dictionary('SWIG') +bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig) +foo.LoadableModule(target = 'foo', source = ['foo.c', 'foo.i']) +bar.LoadableModule(target = 'bar', source = ['bar.c', 'bar.i']) +""" % locals()) + +test.write("foo.c", """\ +char * +foo_string() +{ + return "This is foo.c!"; +} +""") + +test.write("foo.i", """\ +%module foo +%{ +/* Put header files here (optional) */ +%} + +extern char *foo_string(); +""") + +test.write("bar.c", """\ +char * +bar_string() +{ + return "This is bar.c!"; +} +""") + +test.write("bar.i", """\ +%module \t bar +%{ +/* Put header files here (optional) */ +%} + +extern char *bar_string(); +""") + +test.run(arguments = ldmodule_prefix+'foo' + _dll) + +test.must_not_exist(test.workpath('wrapper.out')) + +test.run(program = python, stdin = """\ +import foo +print foo.foo_string() +""", stdout="""\ +This is foo.c! +""") + +test.up_to_date(arguments = ldmodule_prefix+'foo' + _dll) + +test.run(arguments = ldmodule_prefix+'bar' + _dll) + +test.must_match('wrapper.out', "wrapper.py\n") + +test.run(program = python, stdin = """\ +import foo +import bar +print foo.foo_string() +print bar.bar_string() +""", stdout="""\ +This is foo.c! +This is bar.c! +""") + +test.up_to_date(arguments = '.') + + + +test.pass_test() diff --git a/test/SWIG/noproxy.py b/test/SWIG/noproxy.py new file mode 100644 index 00000000..c0f6da61 --- /dev/null +++ b/test/SWIG/noproxy.py @@ -0,0 +1,108 @@ +#!/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 SCons realizes the -noproxy option means no .py file will +be created. +""" + +import sys + +import TestSCons + +if sys.platform =='darwin': + # change to make it work with stock OS X python framework + # we can't link to static libpython because there isn't one on OS X + # so we link to a framework version. However, testing must also + # use the same version, or else you get interpreter errors. + python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python" + _python_ = '"' + python + '"' +else: + python = TestSCons.python + _python_ = TestSCons._python_ + +# swig-python expects specific filenames. +# the platform specific suffix won't necessarily work. +if sys.platform == 'win32': + _dll = '.dll' +else: + _dll = '.so' + +test = TestSCons.TestSCons() + +swig = test.where_is('swig') + +if not swig: + test.skip_test('Can not find installed "swig", skipping test.\n') + + + +version = sys.version[:3] # see also sys.prefix documentation + +# handle testing on other platforms: +ldmodule_prefix = '_' + +frameworks = '' +platform_sys_prefix = sys.prefix +if sys.platform == 'darwin': + # OS X has a built-in Python but no static libpython + # so you should link to it using apple's 'framework' scheme. + # (see top of file for further explanation) + frameworks = '-framework Python' + platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version + +test.write("dependency.i", """\ +%module dependency +""") + +test.write("dependent.i", """\ +%module dependent + +%include dependency.i +""") + +test.write('SConstruct', """ +foo = Environment(SWIGFLAGS=['-python', '-noproxy'], + CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', + LDMODULEPREFIX='%(ldmodule_prefix)s', + LDMODULESUFFIX='%(_dll)s', + FRAMEWORKSFLAGS='%(frameworks)s', + ) + +swig = foo.Dictionary('SWIG') +bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig) +foo.CFile(target = 'dependent', source = ['dependent.i']) +""" % locals()) + +test.run(arguments = '.') + +# If we mistakenly depend on the .py file that SWIG didn't create +# (suppressed by the -noproxy option) then the build won't be up-to-date. +test.up_to_date(arguments = '.') + + + +test.pass_test() diff --git a/test/SWIG/remove-modules.py b/test/SWIG/remove-modules.py new file mode 100644 index 00000000..cac96779 --- /dev/null +++ b/test/SWIG/remove-modules.py @@ -0,0 +1,92 @@ +#!/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 swig-generated modules are removed. +The %module directive specifies the module name. +""" + +import sys + +import TestSCons + +# swig-python expects specific filenames. +# the platform specific suffix won't necessarily work. +if sys.platform == 'win32': + _dll = '.dll' +else: + _dll = '.so' + +test = TestSCons.TestSCons() + +swig = test.where_is('swig') + +if not swig: + test.skip_test('Can not find installed "swig", skipping test.\n') + + + +version = sys.version[:3] # see also sys.prefix documentation + +# handle testing on other platforms: +ldmodule_prefix = '_' + +frameworks = '' +platform_sys_prefix = sys.prefix +if sys.platform == 'darwin': + # OS X has a built-in Python but no static libpython + # so you should link to it using apple's 'framework' scheme. + # (see top of file for further explanation) + frameworks = '-framework Python' + platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version + + +test.write("module.i", """\ +%module modulename +""") + +test.write('SConstruct', """ +foo = Environment(SWIGFLAGS='-python', + CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', + LDMODULEPREFIX='%(ldmodule_prefix)s', + LDMODULESUFFIX='%(_dll)s', + FRAMEWORKSFLAGS='%(frameworks)s', + ) + +foo.LoadableModule(target = 'modulename', source = ['module.i']) +""" % locals()) + +test.run() + +test.must_exist(test.workpath("modulename.py")) + +test.run(arguments = "-c") + +test.must_not_exist(test.workpath("modulename.py")) + + + +test.pass_test() diff --git a/test/Scanner/dictionary.py b/test/Scanner/dictionary.py new file mode 100644 index 00000000..62fda9b0 --- /dev/null +++ b/test/Scanner/dictionary.py @@ -0,0 +1,199 @@ +#!/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 +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('build.py', r""" +import sys +input = open(sys.argv[1], 'rb') +output = open(sys.argv[2], 'wb') + +include_prefix = 'include%s ' % sys.argv[1][-1] + +def process(infp, outfp): + for line in infp.readlines(): + if line[:len(include_prefix)] == include_prefix: + file = line[len(include_prefix):-1] + process(open(file, 'rb'), outfp) + else: + outfp.write(line) + +process(input, output) + +sys.exit(0) +""") + +# Execute a subsidiary SConscript just to make sure we can +# get at the Scanner keyword from there. + +test.write('SConstruct', """ +SConscript('SConscript') +""") + +test.write('SConscript', """ +import re + +include1_re = re.compile(r'^include1\s+(\S+)$', re.M) +include2_re = re.compile(r'^include2\s+(\S+)$', re.M) +include3_re = re.compile(r'^include3\s+(\S+)$', re.M) + +def kfile_scan1(node, env, scanpaths, arg=None): + contents = node.get_contents() + includes = include1_re.findall(contents) + return includes + +def kfile_scan2(node, env, scanpaths, arg=None): + contents = node.get_contents() + includes = include2_re.findall(contents) + return includes + +def kfile_scan3(node, env, scanpaths, arg=None): + contents = node.get_contents() + includes = include3_re.findall(contents) + return includes + +scan1 = Scanner(kfile_scan1) + +scan2 = Scanner(kfile_scan2) + +scan3 = Scanner(kfile_scan3) + +kscanner = Scanner({'.k1' : scan1, '.k2': scan2}) + +env = Environment(SCANNERS = [kscanner]) + +kscanner.add_scanner('.k3', scan3) + +env.Command('aaa', 'aaa.k1', r'%(_python_)s build.py $SOURCES $TARGET') +env.Command('bbb', 'bbb.k2', r'%(_python_)s build.py $SOURCES $TARGET') +env.Command('ccc', 'ccc.k3', r'%(_python_)s build.py $SOURCES $TARGET') +""" % locals()) + +test.write('aaa.k1', +"""aaa.k1 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('bbb.k2', +"""bbb.k2 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('ccc.k3', +"""ccc.k3 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('xxx', "xxx 1\n") +test.write('yyy', "yyy 1\n") +test.write('zzz', "zzz 1\n") + + + + +expect = test.wrap_stdout("""\ +%(_python_)s build.py aaa.k1 aaa +%(_python_)s build.py bbb.k2 bbb +%(_python_)s build.py ccc.k3 ccc +""" % locals()) + +test.run(stdout=expect) + +expect_aaa = 'aaa.k1 1\nline 2\nxxx 1\ninclude2 yyy\ninclude3 zzz\nline 6\n' +expect_bbb = 'bbb.k2 1\nline 2\ninclude1 xxx\nyyy 1\ninclude3 zzz\nline 6\n' +expect_ccc = 'ccc.k3 1\nline 2\ninclude1 xxx\ninclude2 yyy\nzzz 1\nline 6\n' + +test.must_match('aaa', expect_aaa) +test.must_match('bbb', expect_bbb) +test.must_match('ccc', expect_ccc) + +test.up_to_date(arguments = '.') + + + +test.write('zzz', "zzz 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py ccc.k3 ccc +""" % locals()) + +test.run(stdout=expect) + +expect_ccc = 'ccc.k3 1\nline 2\ninclude1 xxx\ninclude2 yyy\nzzz 2\nline 6\n' + +test.must_match('bbb', expect_bbb) + + + +test.write('yyy', "yyy 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py bbb.k2 bbb +""" % locals()) + +test.run(stdout=expect) + +expect_bbb = 'bbb.k2 1\nline 2\ninclude1 xxx\nyyy 2\ninclude3 zzz\nline 6\n' + +test.must_match('bbb', expect_bbb) + + + +test.write('xxx', "xxx 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py aaa.k1 aaa +""" % locals()) + +test.run(stdout=expect) + +expect_aaa = 'aaa.k1 1\nline 2\nxxx 2\ninclude2 yyy\ninclude3 zzz\nline 6\n' + +test.must_match('bbb', expect_bbb) + + + +test.pass_test() diff --git a/test/Script-import.py b/test/Script-import.py index 4dd8d914..544960bb 100644 --- a/test/Script-import.py +++ b/test/Script-import.py @@ -65,12 +65,12 @@ SCons.Script.OptParser SCons.Script.SConscriptSettableOptions SCons.Script.keep_going_on_error -SCons.Script.print_dtree +#SCons.Script.print_dtree SCons.Script.print_explanations SCons.Script.print_includes SCons.Script.print_objects SCons.Script.print_time -SCons.Script.print_tree +#SCons.Script.print_tree SCons.Script.memory_stats SCons.Script.ignore_errors #SCons.Script.sconscript_time diff --git a/test/option/debug-stree.py b/test/option/debug-stree.py index 8ffadc6d..21ca3866 100644 --- a/test/option/debug-stree.py +++ b/test/option/debug-stree.py @@ -73,40 +73,41 @@ test.write('bar.h', """ """) stree = """ -[E B C ]+-foo.xxx -[E B C ] +-foo.ooo -[E ] | +-foo.c -[E ] | +-foo.h -[E ] | +-bar.h -[E B C ] +-bar.ooo -[E ] +-bar.c -[E ] +-bar.h -[E ] +-foo.h +[E B C ]+-foo.xxx +[E B C ] +-foo.ooo +[E ] | +-foo.c +[E ] | +-foo.h +[E ] | +-bar.h +[E B C ] +-bar.ooo +[E ] +-bar.c +[E ] +-bar.h +[E ] +-foo.h """ test.run(arguments = "--debug=stree foo.xxx") test.fail_test(string.find(test.stdout(), stree) == -1) stree2 = """ - 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 - -[ B ]+-foo.xxx -[ B ] +-foo.ooo -[E ] | +-foo.c -[E ] | +-foo.h -[E ] | +-bar.h -[ B ] +-bar.ooo -[E ] +-bar.c -[E ] +-bar.h -[E ] +-foo.h + 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 + +[ B ]+-foo.xxx +[ B ] +-foo.ooo +[E ] | +-foo.c +[E ] | +-foo.h +[E ] | +-bar.h +[ B ] +-bar.ooo +[E ] +-bar.c +[E ] +-bar.h +[E ] +-foo.h """ test.run(arguments = '-c foo.xxx') diff --git a/test/option/repository.py b/test/option/repository.py new file mode 100644 index 00000000..d30cbec4 --- /dev/null +++ b/test/option/repository.py @@ -0,0 +1,61 @@ +#!/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 --srcdir option works to fetch things from a repository. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('repository', 'work1') + +repository = test.workpath('repository') + +test.write(['repository', 'SConstruct'], """\ +env = Environment() +env.Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) +""") + +test.write(['repository', 'file.in'], "repository/file.in\n") + +opts = '--repository ' + repository + +# Make the entire repository non-writable, so we'll detect +# if we try to write into it accidentally. +test.writable('repository', 0) + +test.run(chdir = 'work1', options = opts, arguments = '.') + +test.must_match(['work1', 'file.out'], "repository/file.in\n") + +test.up_to_date(chdir = 'work1', options = opts, arguments = '.') + + + +# +test.pass_test() diff --git a/test/option/srcdir.py b/test/option/srcdir.py new file mode 100644 index 00000000..b6bcc593 --- /dev/null +++ b/test/option/srcdir.py @@ -0,0 +1,61 @@ +#!/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 --srcdir option works to fetch things from a repository. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('repository', 'work1') + +repository = test.workpath('repository') + +test.write(['repository', 'SConstruct'], r""" +env = Environment() +env.Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) +""") + +test.write(['repository', 'file.in'], "repository/file.in\n") + +opts = '--srcdir ' + repository + +# Make the entire repository non-writable, so we'll detect +# if we try to write into it accidentally. +test.writable('repository', 0) + +test.run(chdir = 'work1', options = opts, arguments = '.') + +test.must_match(['work1', 'file.out'], "repository/file.in\n") + +test.up_to_date(chdir = 'work1', options = opts, arguments = '.') + + + +# +test.pass_test() diff --git a/test/option/tree-all.py b/test/option/tree-all.py new file mode 100644 index 00000000..92b90652 --- /dev/null +++ b/test/option/tree-all.py @@ -0,0 +1,190 @@ +#!/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 that the --tree=all option prints a tree representation of the +complete dependencies of a target. +""" + +import TestSCons +import sys +import string +import re +import time + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx') +env.Program('Foo', Split('Foo.c Bar.c')) +""") + +# N.B.: We use upper-case file names (Foo* and Bar*) so that the sorting +# order with our upper-case SConstruct file is the same on case-sensitive +# (UNIX/Linux) and case-insensitive (Windows) systems. + +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('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 +""") + +tree1 = """ ++-Foo.xxx + +-Foo.ooo + | +-Foo.c + | +-Foo.h + | +-Bar.h + +-Bar.ooo + +-Bar.c + +-Bar.h + +-Foo.h +""" + +test.run(arguments = "--tree=all Foo.xxx") +test.fail_test(string.find(test.stdout(), tree1) == -1) + +tree2 = """ ++-. + +-Bar.c + +-Bar.ooo + | +-Bar.c + | +-Bar.h + | +-Foo.h + +-Foo.c + +-Foo.ooo + | +-Foo.c + | +-Foo.h + | +-Bar.h + +-Foo.xxx + | +-Foo.ooo + | | +-Foo.c + | | +-Foo.h + | | +-Bar.h + | +-Bar.ooo + | +-Bar.c + | +-Bar.h + | +-Foo.h + +-SConstruct +""" + +test.run(arguments = "--tree=all .") +test.fail_test(string.find(test.stdout(), tree2) == -1) + +tree3 = """ ++-. + +-Bar.c + +-Bar.ooo + | +-[Bar.c] + | +-Bar.h + | +-Foo.h + +-Foo.c + +-Foo.ooo + | +-[Foo.c] + | +-[Foo.h] + | +-[Bar.h] + +-Foo.xxx + | +-[Foo.ooo] + | +-[Bar.ooo] + +-SConstruct +""" + +test.run(arguments = "--tree=all,prune .") +test.fail_test(string.find(test.stdout(), tree3) == -1) + +test.run(arguments = "--tree=prune .") +test.fail_test(string.find(test.stdout(), tree3) == -1) + +tree4 = """ + 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 + +[ B ]+-Foo.xxx +[ B ] +-Foo.ooo +[E ] | +-Foo.c +[E ] | +-Foo.h +[E ] | +-Bar.h +[ B ] +-Bar.ooo +[E ] +-Bar.c +[E ] +-Bar.h +[E ] +-Foo.h +""" + +test.run(arguments = '-c Foo.xxx') + +test.run(arguments = "--no-exec --tree=all,status Foo.xxx") +test.fail_test(string.find(test.stdout(), tree4) == -1) + +test.run(arguments = "--no-exec --tree=status Foo.xxx") +test.fail_test(string.find(test.stdout(), tree4) == -1) + +# Make sure we print the debug stuff even if there's a build failure. +test.write('Bar.h', """ +#ifndef BAR_H +#define BAR_H +#include "Foo.h" +#endif +THIS SHOULD CAUSE A BUILD FAILURE +""") + +test.run(arguments = "--tree=all Foo.xxx", + status = 2, + stderr = None) +test.fail_test(string.find(test.stdout(), tree1) == -1) + +test.pass_test() diff --git a/test/option/tree-derived.py b/test/option/tree-derived.py new file mode 100644 index 00000000..a10faa23 --- /dev/null +++ b/test/option/tree-derived.py @@ -0,0 +1,144 @@ +#!/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 that the --debug=dtree option correctly prints just the explicit +dependencies (sources or Depends()) of a target. +""" + +import TestSCons +import sys +import string +import re +import time + +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 +#include +#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 +""") + +dtree1 = """ ++-foo.xxx + +-foo.ooo + +-bar.ooo +""" + +test.run(arguments = "--tree=derived foo.xxx") +test.fail_test(string.find(test.stdout(), dtree1) == -1) + +dtree2 = """ ++-. + +-bar.ooo + +-foo.ooo + +-foo.xxx + +-foo.ooo + +-bar.ooo +""" + +test.run(arguments = "--tree=derived .") +test.fail_test(string.find(test.stdout(), dtree2) == -1) + +dtree3 = """ ++-. + +-bar.ooo + +-foo.ooo + +-foo.xxx + +-[foo.ooo] + +-[bar.ooo] +""" + +test.run(arguments = "--tree=derived,prune .") +test.fail_test(string.find(test.stdout(), dtree3) == -1) + +dtree4 = """ + 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 + +[ B ]+-foo.xxx +[ B ] +-foo.ooo +[ B ] +-bar.ooo +""" + +test.run(arguments = '-c foo.xxx') + +test.run(arguments = "--no-exec --tree=derived,status foo.xxx") +test.fail_test(string.find(test.stdout(), dtree4) == -1) + +# Make sure we print the debug stuff even if there's a build failure. +test.write('bar.h', """ +#ifndef BAR_H +#define BAR_H +#include "foo.h" +#endif +THIS SHOULD CAUSE A BUILD FAILURE +""") + +test.run(arguments = "--tree=derived foo.xxx", + status = 2, + stderr = None) +test.fail_test(string.find(test.stdout(), dtree1) == -1) + +test.pass_test() diff --git a/test/site_scons/basic.py b/test/site_scons/basic.py new file mode 100644 index 00000000..ed2e1d52 --- /dev/null +++ b/test/site_scons/basic.py @@ -0,0 +1,69 @@ +#!/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 + +""" +Verify basic functionality of the site_scons dir and +site_scons/site_init.py file: +Make sure the site_scons/site_init.py file gets loaded, +and make sure a tool can be loaded from the site_scons/site_tools subdir, +even when executing out of a subdirectory. +""" + +test = TestSCons.TestSCons() + +test.subdir('site_scons', ['site_scons', 'site_tools']) + +test.write(['site_scons', 'site_init.py'], """ +from SCons.Script import * +print "Hi there, I am in site_scons/site_init.py!" +""") + +test.write(['site_scons', 'site_tools', 'mytool.py'], """ +import SCons.Tool +def generate(env): + env['MYTOOL']='mytool' +def exists(env): + return 1 +""") + + +test.write('SConstruct', """ +e=Environment(tools=['default', 'mytool']) +print e.subst('My site tool is $MYTOOL') +""") + +test.run(arguments = '-Q .', + stdout = """Hi there, I am in site_scons/site_init.py! +My site tool is mytool +scons: `.' is up to date.\n""") + + + +test.pass_test() + +# end of file diff --git a/test/site_scons/no-site-dir.py b/test/site_scons/no-site-dir.py new file mode 100644 index 00000000..6f2f26ee --- /dev/null +++ b/test/site_scons/no-site-dir.py @@ -0,0 +1,82 @@ +#!/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 use of the --no-site-dir option: +the site_scons/site_init.py script should NOT be loaded. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('site_scons', ['site_scons', 'site_tools']) + +test.write(['site_scons', 'site_init.py'], """ +from SCons.Script import * +print "Hi there, I am in site_scons/site_init.py!" +""") + +test.write(['site_scons', 'site_tools', 'mytool.py'], """ +import SCons.Tool +def generate(env): + env['MYTOOL']='mytool' +def exists(env): + return 1 +""") + +test.write(['site_scons', 'site_tools', 'm4.py'], """ +import SCons.Tool +def generate(env): + env['M4']='my_m4' + env['M4_MINE']=1 +def exists(env): + return 1 +""") + +test.write('SConstruct', """ +e=Environment() +""") + +test.run(arguments = '-Q --no-site-dir .', + stdout = "scons: `.' is up to date.\n") + +# With --no-site-dir, shouldn't override default m4 tool + +test.write('SConstruct', """ +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.pass_test() + +# end of file diff --git a/test/site_scons/nonexistent.py b/test/site_scons/nonexistent.py new file mode 100644 index 00000000..21853046 --- /dev/null +++ b/test/site_scons/nonexistent.py @@ -0,0 +1,47 @@ +#!/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 specifying --site-dir= with a nonexistent directory +gives an error and nonzero status. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', "\n") + +test.run(arguments = '-Q --site-dir=whatever .', + stderr = r".*site dir .*whatever not found.*", + status = 2, + match = TestSCons.match_re_dotall) + + + +test.pass_test() + +# end of file diff --git a/test/site_scons/override.py b/test/site_scons/override.py new file mode 100644 index 00000000..ebe56a67 --- /dev/null +++ b/test/site_scons/override.py @@ -0,0 +1,62 @@ +#!/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 a that a tool module in site_tools overrides base tool. + +Use 'm4' as test tool since it's likely to be found, +and not commonly overridden by platform-specific stuff the way cc is. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('site_scons', ['site_scons', 'site_tools']) + +test.write(['site_scons', 'site_tools', 'm4.py'], """ +import SCons.Tool +def generate(env): + env['M4']='my_m4' + env['M4_MINE']=1 +def exists(env): + return 1 +""") + +test.write('SConstruct', """ +e=Environment() +print e.subst('M4 is $M4, M4_MINE is $M4_MINE') +""") +test.run(arguments = '-Q .', + stdout = """M4 is my_m4, M4_MINE is 1 +scons: `.' is up to date.\n""") + + + +test.pass_test() + +# end of file diff --git a/test/site_scons/site-dir.py b/test/site_scons/site-dir.py new file mode 100644 index 00000000..a05817e7 --- /dev/null +++ b/test/site_scons/site-dir.py @@ -0,0 +1,73 @@ +#!/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 --site-dir=otherdir loads the site_init.py script +from the other dir; +the usual site_scons/site_init.py should NOT be loaded. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('site_scons', ['site_scons', 'site_tools']) + +test.write(['site_scons', 'site_init.py'], """ +from SCons.Script import * +print "Hi there, I am in site_scons/site_init.py!" +""") + +test.write(['site_scons', 'site_tools', 'mytool.py'], """ +import SCons.Tool +def generate(env): + env['MYTOOL']='mytool' +def exists(env): + return 1 +""") + + + +test.subdir('alt_site', ['alt_site', 'site_tools']) + +test.write(['alt_site', 'site_init.py'], """ +from SCons.Script import * +print "Hi there, I am in alt_site/site_init.py!" +""") + +test.write('SConstruct', """ +e=Environment() +""") + +test.run(arguments = '-Q --site-dir=alt_site .', + stdout = """Hi there, I am in alt_site/site_init.py! +scons: `.' is up to date.\n""") + + + +test.pass_test() + +# end of file diff --git a/test/toolpath/BuildDir.py b/test/toolpath/BuildDir.py new file mode 100644 index 00000000..a8b8b8a4 --- /dev/null +++ b/test/toolpath/BuildDir.py @@ -0,0 +1,71 @@ +#!/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 toolpath works with BuildDir() for an SConscript. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir', ['subdir', 'src'], ['subdir', 'src', 'tools']) + +test.write('SConstruct', """\ +BuildDir('build', 'subdir', duplicate=0) +SConscript('build/SConscript') +""") + +test.write(['subdir', 'SConscript'], """\ +env = Environment(tools = ['MyBuilder'], toolpath = ['src/tools']) +env.MyCopy('src/file.out', 'src/file.in') +""") + +test.write(['subdir', 'src', 'file.in'], "subdir/src/file.in\n") + +test.write(['subdir', 'src', 'tools', 'MyBuilder.py'], """\ +from SCons.Script import Builder +def generate(env): + def my_copy(target, source, env): + content = open(str(source[0]), 'rb').read() + open(str(target[0]), 'wb').write(content) + env['BUILDERS']['MyCopy'] = Builder(action = my_copy) + +def exists(env): + return 1 +""") + +test.run() + +test.must_match(['build', 'src', 'file.out'], "subdir/src/file.in\n") + +# We should look for the underlying tool in both the build/src/tools +# (which doesn't exist) and subdir/src/tools (which still does). If we +# don't, the following would fail because the execution directory is +# now relative to the created BuildDir. +test.run() + +test.pass_test() diff --git a/test/toolpath.py b/test/toolpath/basic.py similarity index 100% rename from test/toolpath.py rename to test/toolpath/basic.py -- 2.26.2