From 8cb34cccc26935bce5d07ed3f51cc29fbbd1ab03 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Fri, 17 Aug 2007 03:17:04 +0000 Subject: Merged revisions 2136-2200,2202-2290,2292-2301 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r2145 | stevenknight | 2007-07-17 09:15:12 -0500 (Tue, 17 Jul 2007) | 3 lines Don't put null strings (from variable expansion) in a path list. (They get turned into the current directory on later expansion.) ........ r2146 | stevenknight | 2007-07-17 10:47:39 -0500 (Tue, 17 Jul 2007) | 3 lines Add support for optional arguments on command-line long options by specifying nargs='?'. ........ r2149 | stevenknight | 2007-07-17 15:22:24 -0500 (Tue, 17 Jul 2007) | 2 lines Remove left-over Optik mentions. ........ r2150 | stevenknight | 2007-07-17 15:39:34 -0500 (Tue, 17 Jul 2007) | 4 lines Add a $SWIGPATH variable for finding SWIG dependencies, with $SWIGINC{PREFIX,SUFFIX} for adding them to the command line. ........ r2154 | stevenknight | 2007-07-18 20:05:31 -0500 (Wed, 18 Jul 2007) | 2 lines Fix variable misspellings in the doc added for $SWIGOUTPUT. ........ r2155 | stevenknight | 2007-07-18 20:07:28 -0500 (Wed, 18 Jul 2007) | 2 lines Add the Python eggs info file to the RPM packaging build. ........ r2156 | stevenknight | 2007-07-18 20:15:08 -0500 (Wed, 18 Jul 2007) | 2 lines Convert documentation from DocBook SGML to XML. ........ r2158 | stevenknight | 2007-07-19 17:16:19 -0500 (Thu, 19 Jul 2007) | 3 lines Conditionally add the .egg-info the RPM file list only if the distutils in the version of Python that rpmbuild will execute knows about them. ........ r2161 | stevenknight | 2007-07-19 19:12:29 -0500 (Thu, 19 Jul 2007) | 5 lines Capture a test case (contributed by Tilo Prutz) where instantiation of a private class causes javac to generate an additional anonymous inner class file. (No solution yet, but there's no sense throwing away the preparatory work.) ........ r2162 | stevenknight | 2007-07-20 11:29:56 -0500 (Fri, 20 Jul 2007) | 3 lines Support passing a list of .java files as source to the Java() builder. (Leanid Nazdrynau) ........ r2163 | garyo | 2007-07-20 12:00:35 -0500 (Fri, 20 Jul 2007) | 1 line Fixed cut-n-paste error in Touch factory method doc in users guide. ........ r2167 | stevenknight | 2007-07-21 22:59:40 -0500 (Sat, 21 Jul 2007) | 2 lines Don't execute the SWIGOUTDIR test if swig isn't installed. ........ r2168 | stevenknight | 2007-07-21 23:14:17 -0500 (Sat, 21 Jul 2007) | 2 lines Fix the test's ability to run under a path name containing spaces. ........ r2171 | stevenknight | 2007-07-24 15:54:41 -0500 (Tue, 24 Jul 2007) | 2 lines Handle white space in key file names in the packaging build. ........ r2172 | stevenknight | 2007-07-24 21:41:15 -0500 (Tue, 24 Jul 2007) | 2 lines More efficient copying of construction environments. ........ r2173 | stevenknight | 2007-07-25 10:56:02 -0500 (Wed, 25 Jul 2007) | 2 lines Update the SCons build for Subversion and general clean-up. ........ r2174 | stevenknight | 2007-07-25 11:35:16 -0500 (Wed, 25 Jul 2007) | 3 lines Suppress the [brackets] around a node in the --tree=prune output if the node is a source. ........ r2175 | stevenknight | 2007-07-25 12:52:18 -0500 (Wed, 25 Jul 2007) | 3 lines Commonize the skip_test() method and make its behavior configurable via a TESTCOMMON_PASS_SKIPS environment variable. ........ r2178 | stevenknight | 2007-07-25 21:43:47 -0500 (Wed, 25 Jul 2007) | 3 lines Add $JAVACLASSPATH and $JAVASOURCEPATH construction variables. (Leanid Nazdrynau) ........ r2182 | stevenknight | 2007-07-30 12:10:20 -0500 (Mon, 30 Jul 2007) | 3 lines Refactor Builder suffix-adjusting into its own method, so we can (potentially) re-use it for Builders with attached source Builders. ........ r2183 | stevenknight | 2007-07-30 14:51:53 -0500 (Mon, 30 Jul 2007) | 2 lines More efficient source-builder suffix matching. ........ r2184 | stevenknight | 2007-07-30 16:01:42 -0500 (Mon, 30 Jul 2007) | 4 lines Encapsulate initialization of the default FS object by an accessor function in SCons.Node.FS. (This also gets rid of an unnecessary reference to SCons.Node.FS.default_fs in the LaTeX scanner.) ........ r2193 | stevenknight | 2007-07-30 18:24:07 -0500 (Mon, 30 Jul 2007) | 3 lines Fix interpretation of source arguments that have no suffix when the called Builder has both a src_suffix and a src_builder. ........ r2194 | stevenknight | 2007-07-31 10:25:31 -0500 (Tue, 31 Jul 2007) | 2 lines Increase the number of tries for random output from three to ten. ........ r2195 | stevenknight | 2007-07-31 10:52:28 -0500 (Tue, 31 Jul 2007) | 3 lines Skip the test gracefully if the zipfile module can't read the file it just wrote (which is the case for Python 2.1 on 64-bit systems). ........ r2196 | stevenknight | 2007-07-31 13:06:21 -0500 (Tue, 31 Jul 2007) | 2 lines Move the "import zipfile" so it doesn't fail on Python <= 2.0. ........ r2197 | stevenknight | 2007-07-31 14:51:50 -0500 (Tue, 31 Jul 2007) | 3 lines Commonize initialization of the various Java builders so they can be hooked up into a multi-stage Builder chain. (Leanid Nazdrynau) ........ r2198 | stevenknight | 2007-07-31 16:15:18 -0500 (Tue, 31 Jul 2007) | 3 lines Fix use of ${TARGET.dir} and ${SOURCE.dir} expansions in $FORTRANMODDIR $JARCHDIR, $JARFLAGS, $LEXFLAGS, $SWIGFLAGS, $SWIGOUTDIR and $YACCFLAGS. ........ r2199 | stevenknight | 2007-07-31 16:25:48 -0500 (Tue, 31 Jul 2007) | 2 lines Remove left-over Trace() call. ........ r2202 | stevenknight | 2007-08-01 12:31:48 -0500 (Wed, 01 Aug 2007) | 3 lines Bail out via test.skip_test() if wix ("candle") isn't found. Put the main body of code flush left instead of under an if: block. ........ r2203 | stevenknight | 2007-08-01 15:35:55 -0500 (Wed, 01 Aug 2007) | 5 lines Fix Tool.packaging.rpm.package() so it doesn't always overwrite $RPMFLAGS with -ta. Set --buildroot in RPM packaging tests so they don't overwrite each other when run simultaneously. ........ r2204 | stevenknight | 2007-08-01 15:37:36 -0500 (Wed, 01 Aug 2007) | 2 lines Fix a nested scope issue with the internal build_sources() function. ........ r2205 | stevenknight | 2007-08-01 15:46:08 -0500 (Wed, 01 Aug 2007) | 5 lines Normalize (X out) the CreationDate field inside embedded, compressed PostScript streams within the generated PDF files. Also normalize preceding Length field, since compression length is affected by different patterns of input, including the variable CreationDate value. ........ r2211 | stevenknight | 2007-08-02 08:52:06 -0500 (Thu, 02 Aug 2007) | 2 lines Add the new modules from branches/packaging to the SCons packaging build. ........ r2212 | stevenknight | 2007-08-02 19:59:01 -0500 (Thu, 02 Aug 2007) | 2 lines Fix the JAVACLASSPATH test when javah isn't on the default $PATH. ........ r2214 | stevenknight | 2007-08-03 15:05:21 -0500 (Fri, 03 Aug 2007) | 4 lines Hook up the Java builders into a multi-step chain underneath a Java() pseudo-builder (wrapper) that examines its arguments and calls the appropriate underlying file-or-dir builder. ........ r2215 | stevenknight | 2007-08-03 15:49:58 -0500 (Fri, 03 Aug 2007) | 2 lines Fix for old Python versions: use apply() instead of *args, **kw. ........ r2216 | stevenknight | 2007-08-03 16:49:31 -0500 (Fri, 03 Aug 2007) | 2 lines Hook up the SWIG builder as a source builder for .java files. ........ r2217 | stevenknight | 2007-08-03 17:28:19 -0500 (Fri, 03 Aug 2007) | 2 lines Don't use .endswith(), which didn't appear until later Python versions. ........ r2218 | stevenknight | 2007-08-03 17:29:38 -0500 (Fri, 03 Aug 2007) | 2 lines Replace tabs with spaces. ........ r2219 | stevenknight | 2007-08-04 08:06:23 -0500 (Sat, 04 Aug 2007) | 3 lines Initialize a loop-invariant lambda for matching .java suffixes outside the loop. ........ r2220 | stevenknight | 2007-08-07 15:06:13 -0500 (Tue, 07 Aug 2007) | 2 lines Refactor parallel class-generation loops into one. ........ r2221 | stevenknight | 2007-08-07 16:04:06 -0500 (Tue, 07 Aug 2007) | 5 lines Have the Java multi-step builder test actually check for generated files, and fix the generation of .java and .class file names, and interaction with the SWIG builder, so that the files are generated in the correct place. ........ r2222 | stevenknight | 2007-08-07 16:45:05 -0500 (Tue, 07 Aug 2007) | 3 lines Fix dependencies on SWIG-generated .java files so they don't have to be built in multiple passes. ........ r2226 | stevenknight | 2007-08-07 18:00:22 -0500 (Tue, 07 Aug 2007) | 2 lines Fix SWIG when used with BuildDir(). ........ r2227 | stevenknight | 2007-08-07 22:15:55 -0500 (Tue, 07 Aug 2007) | 5 lines User's guide updates: - Make the multiple files example match its text. - Expand a truncated sentence about being able to use Python function actions in the Command() Builder. ........ r2228 | stevenknight | 2007-08-07 23:25:18 -0500 (Tue, 07 Aug 2007) | 3 lines Don't generate an error if a #include file matches a same-named directory in $CPPPATH (or $FORTRANPATH, etc.). ........ r2229 | stevenknight | 2007-08-07 23:40:00 -0500 (Tue, 07 Aug 2007) | 2 lines Fix a code example. (Gary Oberbrunner) ........ r2230 | stevenknight | 2007-08-08 00:05:43 -0500 (Wed, 08 Aug 2007) | 3 lines Capture a test case to make sure AddPostAction() doesn't interfere with normal linking. (Matt Doar, Gary Oberbrunner) ........ r2233 | stevenknight | 2007-08-08 14:15:44 -0500 (Wed, 08 Aug 2007) | 2 lines Fix documentation typo in a construction variable cross-reference. ........ r2234 | stevenknight | 2007-08-08 17:03:25 -0500 (Wed, 08 Aug 2007) | 2 lines Changes to SCons packaging to support checkpoint releases. ........ r2235 | stevenknight | 2007-08-09 10:10:01 -0500 (Thu, 09 Aug 2007) | 2 lines Sidestep false negatives on heavily loaded systems. ........ r2236 | garyo | 2007-08-09 11:16:26 -0500 (Thu, 09 Aug 2007) | 1 line Allow unpackaged files (e.g. *.pyo) to exist in the build dir without being packaged in the RPM. Without this, on some systems the rpmbuild may error out. ........ r2237 | stevenknight | 2007-08-09 11:27:56 -0500 (Thu, 09 Aug 2007) | 5 lines Fix test/SWIG/build-dir.py so it works on old Python versions without distutils.sysconfig. Instead of just cutting-and-pasting initialization code from other SWIG tests, centralize it in some new TestSCons methods. ........ r2238 | garyo | 2007-08-09 11:30:58 -0500 (Thu, 09 Aug 2007) | 1 line Use docbook 4.3 instead of 4.4 for the XML doctype since some older(?) jade parsers can't handle new 4-byte Unicode chars in the 4.4 version of isogrk4.ent. ........ r2240 | stevenknight | 2007-08-09 16:35:06 -0500 (Thu, 09 Aug 2007) | 2 lines User's Guide updates (post packaging changes). ........ r2243 | stevenknight | 2007-08-10 10:31:51 -0500 (Fri, 10 Aug 2007) | 3 lines Fix the User's Guide build to use openjade, and to accomodate a change in the name of the main generated file (book1.html => index.html). ........ r2245 | stevenknight | 2007-08-10 11:09:16 -0500 (Fri, 10 Aug 2007) | 2 lines Update the {CHANGES,RELEASE}.txt datestamp lines. ........ r2253 | stevenknight | 2007-08-10 16:21:54 -0500 (Fri, 10 Aug 2007) | 2 lines Fix the wix Tool module's ability to handle null entries in $PATH. ........ r2261 | stevenknight | 2007-08-11 23:08:12 -0500 (Sat, 11 Aug 2007) | 3 lines Remove unnecessary files (.svnt/*, .{ae,cvs}ignore, www/*) from the scons-src packages. ........ r2262 | stevenknight | 2007-08-11 23:24:49 -0500 (Sat, 11 Aug 2007) | 2 lines Add missing __revision__ lines. ........ r2263 | stevenknight | 2007-08-11 23:33:42 -0500 (Sat, 11 Aug 2007) | 2 lines Skip the test if the MANIFEST file hasn't been built. ........ r2264 | stevenknight | 2007-08-11 23:36:30 -0500 (Sat, 11 Aug 2007) | 2 lines Add recent compatibility modules to the relevant exceptions lists. ........ r2265 | stevenknight | 2007-08-11 23:39:00 -0500 (Sat, 11 Aug 2007) | 3 lines Update __VERSION__ strings in the QMTest/*.py modules, so that packaging tests (src/test_*.py) will pass after builds of checkpoint releases. ........ r2266 | stevenknight | 2007-08-12 07:36:19 -0500 (Sun, 12 Aug 2007) | 2 lines Add a comment about why we construct the __VERSION__ string at run time. ........ r2267 | stevenknight | 2007-08-12 07:42:30 -0500 (Sun, 12 Aug 2007) | 2 lines Avoid reading the MANIFEST file twice. (Courtesy review by Greg Noel.) ........ r2268 | stevenknight | 2007-08-12 08:14:53 -0500 (Sun, 12 Aug 2007) | 3 lines Shift Install() and InstallAs() from being documented as functions to being documented as Builders. ........ r2269 | garyo | 2007-08-13 08:49:52 -0500 (Mon, 13 Aug 2007) | 1 line Tests: Skip some more Java tests if javac is not installed on the test machine so they don't get marked as failing. ........ r2270 | garyo | 2007-08-13 11:09:39 -0500 (Mon, 13 Aug 2007) | 1 line Fixed typo in test (shows up on non-Linux platforms). ........ r2271 | garyo | 2007-08-13 14:09:05 -0500 (Mon, 13 Aug 2007) | 4 lines Test portability fixes for Darwin/OSX and IRIX. This does not make all the tests pass on those OSes, but it takes care of some of the more obvious errors that I have time for right now. More to come. ........ r2272 | stevenknight | 2007-08-13 15:33:29 -0500 (Mon, 13 Aug 2007) | 2 lines Tab => space fix. ........ r2273 | stevenknight | 2007-08-13 15:33:52 -0500 (Mon, 13 Aug 2007) | 2 lines Test for swig, too, which is used to build from the .i file. ........ r2277 | garyo | 2007-08-14 10:40:00 -0500 (Tue, 14 Aug 2007) | 8 lines Test portability on IRIX: test/Actions/pre-post creates target file before building target, then IRIX CC does not chmod +x afterwards. I think this change is safe on all OSes. test/AS/ml.py: I think this is only supposed to be run on win32 (not skipped only on win32); the sense of the skip test was backwards. ........ r2278 | stevenknight | 2007-08-14 11:04:40 -0500 (Tue, 14 Aug 2007) | 2 lines Add -tt when running tests, to catch inconsistent tab usage. ........ r2279 | stevenknight | 2007-08-14 14:00:43 -0500 (Tue, 14 Aug 2007) | 2 lines Minor refactor of logic in File.retrieve_from_cache(). ........ r2280 | stevenknight | 2007-08-15 01:11:40 -0500 (Wed, 15 Aug 2007) | 2 lines Refactor CacheDir support into its own module. ........ r2281 | stevenknight | 2007-08-15 07:24:51 -0500 (Wed, 15 Aug 2007) | 2 lines Move the cachepath() method from FS.File to the CacheDir class. ........ r2282 | stevenknight | 2007-08-15 08:31:34 -0500 (Wed, 15 Aug 2007) | 2 lines Python 1.5.2 fix in the new Null class. ........ r2283 | stevenknight | 2007-08-15 10:45:53 -0500 (Wed, 15 Aug 2007) | 5 lines Refactor CacheDir unit tests to: - restore functionality that was dropped in the transition; - commonize creation of test Nodes and other (mock) objects - separate CacheDir tests from tests of CacheDir through Node.FS.File. ........ r2284 | stevenknight | 2007-08-15 11:46:38 -0500 (Wed, 15 Aug 2007) | 3 lines Replace the Executor.Null.NullEnvironment object with a real Null object, so it will absorb the CacheDir method calls as well. ........ r2285 | stevenknight | 2007-08-15 11:52:57 -0500 (Wed, 15 Aug 2007) | 5 lines Add a get_CacheDir() method to a construction environment, which will be used to fetch per-environment CacheDir specifications. (Right now all calls to it still just return the one attached to underlying default FS object.) ........ r2286 | stevenknight | 2007-08-15 15:15:46 -0500 (Wed, 15 Aug 2007) | 2 lines Support per-construction-environment configuration of CacheDir(). ........ r2287 | stevenknight | 2007-08-15 15:33:04 -0500 (Wed, 15 Aug 2007) | 2 lines Move the tests of CacheDir()-related command-line options into test/CacheDir. ........ r2293 | stevenknight | 2007-08-16 11:14:49 -0500 (Thu, 16 Aug 2007) | 3 lines Add the Package() builder description to the documentation build, fixing the XML so that it will build. ........ r2294 | stevenknight | 2007-08-16 12:51:19 -0500 (Thu, 16 Aug 2007) | 3 lines Reorganize packaging documentation: alphabetize the variable definitions (and function names), document Tag() as a function, not a builder. ........ r2296 | stevenknight | 2007-08-16 12:55:01 -0500 (Thu, 16 Aug 2007) | 2 lines Add a build command. ........ r2300 | stevenknight | 2007-08-16 16:49:13 -0500 (Thu, 16 Aug 2007) | 2 lines First cut at documenting packaging variables. ........ r2301 | stevenknight | 2007-08-16 16:51:21 -0500 (Thu, 16 Aug 2007) | 3 lines Construct the .src.rpm and .arch.rpm file names independnetly, not by trying to massage one into the other. ........ --- .svnt/conf | 4 + HOWTO/release.txt | 6 - HOWTO/subrelease.txt | 6 - QMTest/SConscript | 8 +- QMTest/TestCommon.py | 32 + QMTest/TestRuntest.py | 30 - QMTest/TestSCons.py | 161 ++- QMTest/TestSCons_time.py | 30 - QMTest/scons_tdb.py | 2 +- README | 2 +- SConstruct | 353 +++-- bin/docdiff | 8 +- bin/docrun | 4 +- bin/docupdate | 8 +- bin/linecount | 4 +- bin/scons-proc.py | 18 +- bin/sconsoutput.py | 6 + doc/.aeignore | 2 +- doc/SConscript | 64 +- doc/design/MANIFEST | 22 +- doc/design/acks.sgml | 179 --- doc/design/acks.xml | 179 +++ doc/design/bground.sgml | 86 -- doc/design/bground.xml | 86 ++ doc/design/copyright.sgml | 39 - doc/design/copyright.xml | 39 + doc/design/engine.sgml | 1964 -------------------------- doc/design/engine.xml | 1964 ++++++++++++++++++++++++++ doc/design/goals.sgml | 208 --- doc/design/goals.xml | 216 +++ doc/design/install.sgml | 28 - doc/design/install.xml | 28 + doc/design/intro.sgml | 111 -- doc/design/intro.xml | 111 ++ doc/design/issues.sgml | 195 --- doc/design/issues.xml | 195 +++ doc/design/main.sgml | 151 -- doc/design/main.xml | 158 +++ doc/design/native.sgml | 364 ----- doc/design/native.xml | 364 +++++ doc/design/overview.sgml | 498 ------- doc/design/overview.xml | 498 +++++++ doc/developer/MANIFEST | 18 +- doc/developer/architecture.sgml | 40 - doc/developer/architecture.xml | 40 + doc/developer/branches.sgml | 40 - doc/developer/branches.xml | 40 + doc/developer/copyright.sgml | 32 - doc/developer/copyright.xml | 32 + doc/developer/cycle.sgml | 40 - doc/developer/cycle.xml | 40 + doc/developer/main.sgml | 107 -- doc/developer/main.xml | 110 ++ doc/developer/packaging.sgml | 40 - doc/developer/packaging.xml | 40 + doc/developer/preface.sgml | 175 --- doc/developer/preface.xml | 175 +++ doc/developer/sourcetree.sgml | 40 - doc/developer/sourcetree.xml | 40 + doc/developer/testing.sgml | 40 - doc/developer/testing.xml | 40 + doc/man/scons.1 | 108 +- doc/python10/MANIFEST | 18 +- doc/python10/abstract.sgml | 32 - doc/python10/abstract.xml | 32 + doc/python10/acks.sgml | 27 - doc/python10/acks.xml | 27 + doc/python10/copyright.sgml | 32 - doc/python10/copyright.xml | 32 + doc/python10/design.sgml | 898 ------------ doc/python10/design.xml | 898 ++++++++++++ doc/python10/future.sgml | 170 --- doc/python10/future.xml | 170 +++ doc/python10/install.sgml | 179 --- doc/python10/install.xml | 179 +++ doc/python10/intro.sgml | 212 --- doc/python10/intro.xml | 212 +++ doc/python10/main.sgml | 218 --- doc/python10/main.xml | 221 +++ doc/python10/process.sgml | 353 ----- doc/python10/process.xml | 353 +++++ doc/reference/Alias.sgml | 41 - doc/reference/Alias.xml | 41 + doc/reference/CFile.sgml | 41 - doc/reference/CFile.xml | 41 + doc/reference/CXXFile.sgml | 41 - doc/reference/CXXFile.xml | 41 + doc/reference/Command.sgml | 73 - doc/reference/Command.xml | 73 + doc/reference/Install.sgml | 41 - doc/reference/Install.xml | 41 + doc/reference/InstallAs.sgml | 41 - doc/reference/InstallAs.xml | 41 + doc/reference/Library.sgml | 152 -- doc/reference/Library.xml | 152 ++ doc/reference/MANIFEST | 42 +- doc/reference/Object.sgml | 71 - doc/reference/Object.xml | 71 + doc/reference/PCH.sgml | 41 - doc/reference/PCH.xml | 41 + doc/reference/PDF.sgml | 41 - doc/reference/PDF.xml | 41 + doc/reference/PostScript.sgml | 41 - doc/reference/PostScript.xml | 41 + doc/reference/Program.sgml | 77 - doc/reference/Program.xml | 77 + doc/reference/RES.sgml | 41 - doc/reference/RES.xml | 41 + doc/reference/SharedLibrary.sgml | 41 - doc/reference/SharedLibrary.xml | 41 + doc/reference/SharedObject.sgml | 41 - doc/reference/SharedObject.xml | 41 + doc/reference/StaticLibrary.sgml | 41 - doc/reference/StaticLibrary.xml | 41 + doc/reference/StaticObject.sgml | 41 - doc/reference/StaticObject.xml | 41 + doc/reference/copyright.sgml | 32 - doc/reference/copyright.xml | 32 + doc/reference/errors.sgml | 41 - doc/reference/errors.xml | 41 + doc/reference/main.sgml | 204 --- doc/reference/main.xml | 207 +++ doc/reference/preface.sgml | 85 -- doc/reference/preface.xml | 85 ++ doc/scons.mod | 3 + doc/user/ENV.sgml | 208 --- doc/user/ENV.xml | 208 +++ doc/user/MANIFEST | 84 +- doc/user/actions.sgml | 240 ---- doc/user/actions.xml | 240 ++++ doc/user/alias.sgml | 112 -- doc/user/alias.xml | 112 ++ doc/user/ant.sgml | 52 - doc/user/ant.xml | 52 + doc/user/build-install.sgml | 693 --------- doc/user/build-install.xml | 693 +++++++++ doc/user/builders-built-in.sgml | 937 ------------ doc/user/builders-built-in.xml | 937 ++++++++++++ doc/user/builders-commands.in | 19 +- doc/user/builders-commands.sgml | 108 -- doc/user/builders-commands.xml | 125 ++ doc/user/builders-writing.in | 25 +- doc/user/builders-writing.sgml | 673 --------- doc/user/builders-writing.xml | 690 +++++++++ doc/user/builders.in | 3 +- doc/user/builders.sgml | 56 - doc/user/builders.xml | 57 + doc/user/caching.in | 2 +- doc/user/caching.sgml | 472 ------- doc/user/caching.xml | 492 +++++++ doc/user/command-line.sgml | 1486 ------------------- doc/user/command-line.xml | 1486 +++++++++++++++++++ doc/user/copyright.sgml | 32 - doc/user/copyright.xml | 32 + doc/user/depends.sgml | 917 ------------ doc/user/depends.xml | 917 ++++++++++++ doc/user/environments.sgml | 1145 --------------- doc/user/environments.xml | 1145 +++++++++++++++ doc/user/errors.sgml | 41 - doc/user/errors.xml | 41 + doc/user/example.sgml | 41 - doc/user/example.xml | 41 + doc/user/factories.in | 8 +- doc/user/factories.sgml | 427 ------ doc/user/factories.xml | 425 ++++++ doc/user/file-removal.sgml | 202 --- doc/user/file-removal.xml | 202 +++ doc/user/help.sgml | 153 -- doc/user/help.xml | 153 ++ doc/user/hierarchy.in | 4 +- doc/user/hierarchy.sgml | 742 ---------- doc/user/hierarchy.xml | 742 ++++++++++ doc/user/install.sgml | 237 ---- doc/user/install.xml | 237 ++++ doc/user/java.sgml | 413 ------ doc/user/java.xml | 413 ++++++ doc/user/less-simple.in | 12 +- doc/user/less-simple.sgml | 562 -------- doc/user/less-simple.xml | 562 ++++++++ doc/user/libraries.in | 4 +- doc/user/libraries.sgml | 421 ------ doc/user/libraries.xml | 421 ++++++ doc/user/main.in | 88 +- doc/user/main.sgml | 393 ------ doc/user/main.xml | 398 ++++++ doc/user/make.sgml | 121 -- doc/user/make.xml | 121 ++ doc/user/nodes.sgml | 373 ----- doc/user/nodes.xml | 373 +++++ doc/user/parseconfig.sgml | 72 - doc/user/parseconfig.xml | 72 + doc/user/preface.sgml | 425 ------ doc/user/preface.xml | 425 ++++++ doc/user/python.sgml | 154 -- doc/user/python.xml | 154 ++ doc/user/repositories.sgml | 595 -------- doc/user/repositories.xml | 595 ++++++++ doc/user/run.sgml | 375 ----- doc/user/run.xml | 375 +++++ doc/user/scanners.sgml | 317 ----- doc/user/scanners.xml | 317 +++++ doc/user/sconf.sgml | 486 ------- doc/user/sconf.xml | 486 +++++++ doc/user/separate.sgml | 461 ------ doc/user/separate.xml | 461 ++++++ doc/user/simple.in | 4 +- doc/user/simple.sgml | 551 -------- doc/user/simple.xml | 551 ++++++++ doc/user/sourcecode.sgml | 161 --- doc/user/sourcecode.xml | 161 +++ doc/user/tasks.sgml | 109 -- doc/user/tasks.xml | 109 ++ doc/user/tools.sgml | 38 - doc/user/tools.xml | 38 + doc/user/troubleshoot.sgml | 1240 ---------------- doc/user/troubleshoot.xml | 1232 ++++++++++++++++ doc/user/variables.sgml | 56 - doc/user/variables.xml | 56 + doc/user/variants.sgml | 134 -- doc/user/variants.xml | 134 ++ rpm/scons.spec.in | 3 +- src/CHANGES.txt | 68 +- src/RELEASE.txt | 64 +- src/engine/MANIFEST-xml.in | 3 + src/engine/MANIFEST.in | 30 +- src/engine/SCons/ActionTests.py | 2 +- src/engine/SCons/Builder.py | 71 +- src/engine/SCons/BuilderTests.py | 33 +- src/engine/SCons/CacheDir.py | 207 +++ src/engine/SCons/CacheDirTests.py | 295 ++++ src/engine/SCons/Defaults.py | 2 + src/engine/SCons/Environment.py | 64 +- src/engine/SCons/EnvironmentTests.py | 9 +- src/engine/SCons/Executor.py | 6 +- src/engine/SCons/Node/FS.py | 232 ++- src/engine/SCons/Node/FSTests.py | 264 +--- src/engine/SCons/Node/NodeTests.py | 3 +- src/engine/SCons/PathList.py | 3 +- src/engine/SCons/PathListTests.py | 6 +- src/engine/SCons/Scanner/LaTeX.py | 6 +- src/engine/SCons/Script/Main.py | 25 +- src/engine/SCons/Script/SConsOptions.py | 69 +- src/engine/SCons/Script/__init__.py | 6 +- src/engine/SCons/Tool/JavaCommonTests.py | 33 + src/engine/SCons/Tool/__init__.py | 77 + src/engine/SCons/Tool/aixlink.py | 2 +- src/engine/SCons/Tool/filesystem.py | 3 + src/engine/SCons/Tool/fortran.py | 4 +- src/engine/SCons/Tool/install.py | 8 +- src/engine/SCons/Tool/install.xml | 52 + src/engine/SCons/Tool/jar.py | 20 +- src/engine/SCons/Tool/jar.xml | 11 +- src/engine/SCons/Tool/javac.py | 167 ++- src/engine/SCons/Tool/javac.xml | 58 +- src/engine/SCons/Tool/javah.py | 24 +- src/engine/SCons/Tool/javah.xml | 6 +- src/engine/SCons/Tool/lex.py | 3 +- src/engine/SCons/Tool/packaging/__init__.py | 20 +- src/engine/SCons/Tool/packaging/__init__.xml | 490 ++++--- src/engine/SCons/Tool/packaging/rpm.py | 7 +- src/engine/SCons/Tool/swig.py | 70 +- src/engine/SCons/Tool/swig.xml | 86 ++ src/engine/SCons/Tool/wix.py | 3 + src/engine/SCons/Tool/yacc.py | 3 +- src/engine/SCons/Util.py | 97 +- src/engine/SCons/UtilTests.py | 8 +- src/engine/setup.py | 1 - src/setup.py | 6 +- src/test_interrupts.py | 9 +- src/test_pychecker.py | 4 - src/test_strings.py | 16 +- test/AS/ml.py | 2 +- test/AS/nasm.py | 2 +- test/Actions/addpost-link.py | 76 + test/Actions/pre-post.py | 4 +- test/AddOption/optional-arg.py | 102 ++ test/BuildDir/CPPPATH-subdir.py | 1 + test/CPPPATH/match-dir.py | 61 + test/CacheDir/BuildDir.py | 7 +- test/CacheDir/environment.py | 163 +++ test/CacheDir/option--cd.py | 129 ++ test/CacheDir/option--cf.py | 123 ++ test/CacheDir/option--cs.py | 194 +++ test/Case.py | 3 + test/Fortran/FORTRANMODDIR.py | 5 + test/Java/JARCHDIR.py | 46 + test/Java/JAVACCOM.py | 2 +- test/Java/JAVACCOMSTR.py | 6 +- test/Java/JAVACLASSPATH.py | 104 ++ test/Java/JAVASOURCEPATH.py | 81 ++ test/Java/Java-1.4.py | 93 +- test/Java/Java-1.5.py | 93 +- test/Java/Java-1.6.py | 93 +- test/Java/multi-step.py | 567 ++++++++ test/Java/source-files.py | 127 ++ test/Java/swig-dependencies.py | 135 ++ test/LEX/LEXFLAGS.py | 20 +- test/SWIG/SWIG.py | 15 +- test/SWIG/SWIGFLAGS.py | 69 + test/SWIG/SWIGOUTDIR.py | 11 + test/SWIG/SWIGPATH.py | 91 ++ test/SWIG/build-dir.py | 167 +++ test/SWIG/implicit-dependencies.py | 14 +- test/SWIG/live.py | 32 +- test/SWIG/noproxy.py | 30 +- test/SWIG/remove-modules.py | 22 +- test/Scanner/generated.py | 9 +- test/TEX/auxiliaries.py | 16 +- test/TEX/bibtex-latex-rerun.py | 17 +- test/YACC/YACCFLAGS.py | 18 +- test/import.py | 10 + test/long-lines.py | 7 + test/option--cd.py | 129 -- test/option--cf.py | 123 -- test/option--cs.py | 194 --- test/option--random.py | 4 +- test/option/debug-time.py | 13 +- test/option/tree-all.py | 8 +- test/option/tree-derived.py | 4 +- test/packaging/msi/file-placement.py | 126 +- test/packaging/msi/package.py | 83 +- test/packaging/option--package-type.py | 3 + test/packaging/rpm/cleanup.py | 5 + test/packaging/rpm/internationalization.py | 3 + test/packaging/rpm/package.py | 3 + test/packaging/rpm/tagging.py | 5 + test/scons-time/run/archive/zip.py | 24 + test/scons-time/run/option/quiet.py | 5 +- test/scons-time/run/option/verbose.py | 5 +- 329 files changed, 29720 insertions(+), 26223 deletions(-) delete mode 100644 doc/design/acks.sgml create mode 100644 doc/design/acks.xml delete mode 100644 doc/design/bground.sgml create mode 100644 doc/design/bground.xml delete mode 100644 doc/design/copyright.sgml create mode 100644 doc/design/copyright.xml delete mode 100644 doc/design/engine.sgml create mode 100644 doc/design/engine.xml delete mode 100644 doc/design/goals.sgml create mode 100644 doc/design/goals.xml delete mode 100644 doc/design/install.sgml create mode 100644 doc/design/install.xml delete mode 100644 doc/design/intro.sgml create mode 100644 doc/design/intro.xml delete mode 100644 doc/design/issues.sgml create mode 100644 doc/design/issues.xml delete mode 100644 doc/design/main.sgml create mode 100644 doc/design/main.xml delete mode 100644 doc/design/native.sgml create mode 100644 doc/design/native.xml delete mode 100644 doc/design/overview.sgml create mode 100644 doc/design/overview.xml delete mode 100644 doc/developer/architecture.sgml create mode 100644 doc/developer/architecture.xml delete mode 100644 doc/developer/branches.sgml create mode 100644 doc/developer/branches.xml delete mode 100644 doc/developer/copyright.sgml create mode 100644 doc/developer/copyright.xml delete mode 100644 doc/developer/cycle.sgml create mode 100644 doc/developer/cycle.xml delete mode 100644 doc/developer/main.sgml create mode 100644 doc/developer/main.xml delete mode 100644 doc/developer/packaging.sgml create mode 100644 doc/developer/packaging.xml delete mode 100644 doc/developer/preface.sgml create mode 100644 doc/developer/preface.xml delete mode 100644 doc/developer/sourcetree.sgml create mode 100644 doc/developer/sourcetree.xml delete mode 100644 doc/developer/testing.sgml create mode 100644 doc/developer/testing.xml delete mode 100644 doc/python10/abstract.sgml create mode 100644 doc/python10/abstract.xml delete mode 100644 doc/python10/acks.sgml create mode 100644 doc/python10/acks.xml delete mode 100644 doc/python10/copyright.sgml create mode 100644 doc/python10/copyright.xml delete mode 100644 doc/python10/design.sgml create mode 100644 doc/python10/design.xml delete mode 100644 doc/python10/future.sgml create mode 100644 doc/python10/future.xml delete mode 100644 doc/python10/install.sgml create mode 100644 doc/python10/install.xml delete mode 100644 doc/python10/intro.sgml create mode 100644 doc/python10/intro.xml delete mode 100644 doc/python10/main.sgml create mode 100644 doc/python10/main.xml delete mode 100644 doc/python10/process.sgml create mode 100644 doc/python10/process.xml delete mode 100644 doc/reference/Alias.sgml create mode 100644 doc/reference/Alias.xml delete mode 100644 doc/reference/CFile.sgml create mode 100644 doc/reference/CFile.xml delete mode 100644 doc/reference/CXXFile.sgml create mode 100644 doc/reference/CXXFile.xml delete mode 100644 doc/reference/Command.sgml create mode 100644 doc/reference/Command.xml delete mode 100644 doc/reference/Install.sgml create mode 100644 doc/reference/Install.xml delete mode 100644 doc/reference/InstallAs.sgml create mode 100644 doc/reference/InstallAs.xml delete mode 100644 doc/reference/Library.sgml create mode 100644 doc/reference/Library.xml delete mode 100644 doc/reference/Object.sgml create mode 100644 doc/reference/Object.xml delete mode 100644 doc/reference/PCH.sgml create mode 100644 doc/reference/PCH.xml delete mode 100644 doc/reference/PDF.sgml create mode 100644 doc/reference/PDF.xml delete mode 100644 doc/reference/PostScript.sgml create mode 100644 doc/reference/PostScript.xml delete mode 100644 doc/reference/Program.sgml create mode 100644 doc/reference/Program.xml delete mode 100644 doc/reference/RES.sgml create mode 100644 doc/reference/RES.xml delete mode 100644 doc/reference/SharedLibrary.sgml create mode 100644 doc/reference/SharedLibrary.xml delete mode 100644 doc/reference/SharedObject.sgml create mode 100644 doc/reference/SharedObject.xml delete mode 100644 doc/reference/StaticLibrary.sgml create mode 100644 doc/reference/StaticLibrary.xml delete mode 100644 doc/reference/StaticObject.sgml create mode 100644 doc/reference/StaticObject.xml delete mode 100644 doc/reference/copyright.sgml create mode 100644 doc/reference/copyright.xml delete mode 100644 doc/reference/errors.sgml create mode 100644 doc/reference/errors.xml delete mode 100644 doc/reference/main.sgml create mode 100644 doc/reference/main.xml delete mode 100644 doc/reference/preface.sgml create mode 100644 doc/reference/preface.xml delete mode 100644 doc/user/ENV.sgml create mode 100644 doc/user/ENV.xml delete mode 100644 doc/user/actions.sgml create mode 100644 doc/user/actions.xml delete mode 100644 doc/user/alias.sgml create mode 100644 doc/user/alias.xml delete mode 100644 doc/user/ant.sgml create mode 100644 doc/user/ant.xml delete mode 100644 doc/user/build-install.sgml create mode 100644 doc/user/build-install.xml delete mode 100644 doc/user/builders-built-in.sgml create mode 100644 doc/user/builders-built-in.xml delete mode 100644 doc/user/builders-commands.sgml create mode 100644 doc/user/builders-commands.xml delete mode 100644 doc/user/builders-writing.sgml create mode 100644 doc/user/builders-writing.xml delete mode 100644 doc/user/builders.sgml create mode 100644 doc/user/builders.xml delete mode 100644 doc/user/caching.sgml create mode 100644 doc/user/caching.xml delete mode 100644 doc/user/command-line.sgml create mode 100644 doc/user/command-line.xml delete mode 100644 doc/user/copyright.sgml create mode 100644 doc/user/copyright.xml delete mode 100644 doc/user/depends.sgml create mode 100644 doc/user/depends.xml delete mode 100644 doc/user/environments.sgml create mode 100644 doc/user/environments.xml delete mode 100644 doc/user/errors.sgml create mode 100644 doc/user/errors.xml delete mode 100644 doc/user/example.sgml create mode 100644 doc/user/example.xml delete mode 100644 doc/user/factories.sgml create mode 100644 doc/user/factories.xml delete mode 100644 doc/user/file-removal.sgml create mode 100644 doc/user/file-removal.xml delete mode 100644 doc/user/help.sgml create mode 100644 doc/user/help.xml delete mode 100644 doc/user/hierarchy.sgml create mode 100644 doc/user/hierarchy.xml delete mode 100644 doc/user/install.sgml create mode 100644 doc/user/install.xml delete mode 100644 doc/user/java.sgml create mode 100644 doc/user/java.xml delete mode 100644 doc/user/less-simple.sgml create mode 100644 doc/user/less-simple.xml delete mode 100644 doc/user/libraries.sgml create mode 100644 doc/user/libraries.xml delete mode 100644 doc/user/main.sgml create mode 100644 doc/user/main.xml delete mode 100644 doc/user/make.sgml create mode 100644 doc/user/make.xml delete mode 100644 doc/user/nodes.sgml create mode 100644 doc/user/nodes.xml delete mode 100644 doc/user/parseconfig.sgml create mode 100644 doc/user/parseconfig.xml delete mode 100644 doc/user/preface.sgml create mode 100644 doc/user/preface.xml delete mode 100644 doc/user/python.sgml create mode 100644 doc/user/python.xml delete mode 100644 doc/user/repositories.sgml create mode 100644 doc/user/repositories.xml delete mode 100644 doc/user/run.sgml create mode 100644 doc/user/run.xml delete mode 100644 doc/user/scanners.sgml create mode 100644 doc/user/scanners.xml delete mode 100644 doc/user/sconf.sgml create mode 100644 doc/user/sconf.xml delete mode 100644 doc/user/separate.sgml create mode 100644 doc/user/separate.xml delete mode 100644 doc/user/simple.sgml create mode 100644 doc/user/simple.xml delete mode 100644 doc/user/sourcecode.sgml create mode 100644 doc/user/sourcecode.xml delete mode 100644 doc/user/tasks.sgml create mode 100644 doc/user/tasks.xml delete mode 100644 doc/user/tools.sgml create mode 100644 doc/user/tools.xml delete mode 100644 doc/user/troubleshoot.sgml create mode 100644 doc/user/troubleshoot.xml delete mode 100644 doc/user/variables.sgml create mode 100644 doc/user/variables.xml delete mode 100644 doc/user/variants.sgml create mode 100644 doc/user/variants.xml create mode 100644 src/engine/SCons/CacheDir.py create mode 100644 src/engine/SCons/CacheDirTests.py create mode 100644 src/engine/SCons/Tool/install.xml create mode 100644 test/Actions/addpost-link.py create mode 100644 test/AddOption/optional-arg.py create mode 100644 test/CPPPATH/match-dir.py create mode 100644 test/CacheDir/environment.py create mode 100644 test/CacheDir/option--cd.py create mode 100644 test/CacheDir/option--cf.py create mode 100644 test/CacheDir/option--cs.py create mode 100644 test/Java/JAVACLASSPATH.py create mode 100644 test/Java/JAVASOURCEPATH.py create mode 100644 test/Java/multi-step.py create mode 100644 test/Java/source-files.py create mode 100644 test/Java/swig-dependencies.py create mode 100644 test/SWIG/SWIGFLAGS.py create mode 100644 test/SWIG/SWIGPATH.py create mode 100644 test/SWIG/build-dir.py delete mode 100644 test/option--cd.py delete mode 100644 test/option--cf.py delete mode 100644 test/option--cs.py diff --git a/.svnt/conf b/.svnt/conf index 255a4ac..dece324 100644 --- a/.svnt/conf +++ b/.svnt/conf @@ -3,6 +3,10 @@ import sys python = os.environ.get('PYTHON', sys.executable) +build = [ + '"%(python)s" bootstrap.py %%s' % locals() +] + cmd = '"%(python)s" runtest.py -q --noqmtest %%s' % locals() test_inputs = [ diff --git a/HOWTO/release.txt b/HOWTO/release.txt index ca46263..5fbbfc7 100644 --- a/HOWTO/release.txt +++ b/HOWTO/release.txt @@ -96,15 +96,9 @@ Things to do to release a new X.Y version of SCons: aecp SConstruct vi SConstruct - aecp rpm/scons.spec.in - vi rpm/scons.spec.in - aecp QMTest/TestSCons.py vi QMTest/TestSCons.py - aecp src/setup.py - vi src/setup.py - # Read through and update the README files if necessary [optional] aecp README [optional] vi README diff --git a/HOWTO/subrelease.txt b/HOWTO/subrelease.txt index 7b9254b..ecfaa1f 100644 --- a/HOWTO/subrelease.txt +++ b/HOWTO/subrelease.txt @@ -41,12 +41,6 @@ Things to do to release a new X.Y.Z version of SCons: aecp SConstruct vi SConstruct - aecp rpm/scons.spec.in - vi rpm/scons.spec.in - - aecp src/setup.py - vi src/setup.py - aecp QMTest/TestSCons.py vi QMTest/TestSCons.py diff --git a/QMTest/SConscript b/QMTest/SConscript index 3aa7d9c..e141a51 100644 --- a/QMTest/SConscript +++ b/QMTest/SConscript @@ -26,6 +26,7 @@ # import os.path +import string Import('build_dir', 'env') @@ -45,7 +46,12 @@ files = [ def copy(target, source, env): t = str(target[0]) s = str(source[0]) - open(t, 'wb').write(open(s, 'rb').read()) + c = open(s, 'rb').read() + # Note: We construct the __ VERSION __ substitution string at + # run-time so it doesn't get replaced when this file gets copied + # into the tree for packaging. + c = string.replace(c, '__' + 'VERSION' + '__', env['VERSION']) + open(t, 'wb').write(c) for file in files: # Guarantee that real copies of these files always exist in diff --git a/QMTest/TestCommon.py b/QMTest/TestCommon.py index b215a26..b8fb5cd 100644 --- a/QMTest/TestCommon.py +++ b/QMTest/TestCommon.py @@ -427,3 +427,35 @@ class TestCommon(TestCmd): print self.stdout() self.diff(stderr, self.stderr(), 'STDERR ') raise TestFailed + + def skip_test(self, message="Skipping test.\n"): + """Skips a test. + + Proper test-skipping behavior is dependent on the external + TESTCOMMON_PASS_SKIPS environment variable. If set, we treat + the skip as a PASS (exit 0), and otherwise treat it as NO RESULT. + In either case, we print the specified message as an indication + that the substance of the test was skipped. + + (This was originally added to support development under Aegis. + Technically, skipping a test is a NO RESULT, but Aegis would + treat that as a test failure and prevent the change from going to + the next step. Since we ddn't want to force anyone using Aegis + to have to install absolutely every tool used by the tests, we + would actually report to Aegis that a skipped test has PASSED + so that the workflow isn't held up.) + """ + if message: + sys.stdout.write(message) + sys.stdout.flush() + pass_skips = os.environ.get('TESTCOMMON_PASS_SKIPS') + if pass_skips in [None, 0, '0']: + # skip=1 means skip this function when showing where this + # result came from. They only care about the line where the + # script called test.skip_test(), not the line number where + # we call test.no_result(). + self.no_result(skip=1) + else: + # We're under the development directory for this change, + # so this is an Aegis invocation; pass the test (exit 0). + self.pass_test() diff --git a/QMTest/TestRuntest.py b/QMTest/TestRuntest.py index 9452746..ee33b25 100644 --- a/QMTest/TestRuntest.py +++ b/QMTest/TestRuntest.py @@ -158,36 +158,6 @@ class TestRuntest(TestCommon): os.environ['PYTHONPATH'] = '' os.environ['SCONS_SOURCE_PATH_EXECUTABLE'] = '' - def skip_test(self, message="Skipping test.\n"): - """Skips a test. - - Proper test-skipping behavior is dependent on whether we're being - executed as part of development of a change under Aegis. - - Technically, skipping a test is a NO RESULT, but Aegis will - treat that as a test failure and prevent the change from going - to the next step. We don't want to force anyone using Aegis - to have to install absolutely every tool used by the tests, - so we actually report to Aegis that a skipped test has PASSED - so that the workflow isn't held up. - """ - if message: - sys.stdout.write(message) - sys.stdout.flush() - devdir = os.popen("aesub '$dd' 2>/dev/null", "r").read()[:-1] - intdir = os.popen("aesub '$intd' 2>/dev/null", "r").read()[:-1] - if devdir and self._cwd[:len(devdir)] == devdir or \ - intdir and self._cwd[:len(intdir)] == intdir: - # We're under the development directory for this change, - # so this is an Aegis invocation; pass the test (exit 0). - self.pass_test() - else: - # skip=1 means skip this function when showing where this - # result came from. They only care about the line where the - # script called test.skip_test(), not the line number where - # we call test.no_result(). - self.no_result(skip=1) - def write_fake_scons_source_tree(self): os.mkdir('src') os.mkdir('src/script') diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py index 100d8ba..9c270fa 100644 --- a/QMTest/TestSCons.py +++ b/QMTest/TestSCons.py @@ -38,11 +38,14 @@ from TestCommon import __all__ # Some tests which verify that SCons has been packaged properly need to # look for specific version file names. Replicating the version number -# here provides independent verification that what we packaged conforms -# to what we expect. (If we derived the version number from the same -# data driving the build we might miss errors if the logic breaks.) +# here provides some independent verification that what we packaged +# conforms to what we expect. -SConsVersion = '0.97' +default_version = '0.97.0' + +SConsVersion = '__VERSION__' +if SConsVersion == '__' + 'VERSION' + '__': + SConsVersion = default_version __all__.extend([ 'TestSCons', 'machine', @@ -186,6 +189,10 @@ class TestSCons(TestCommon): kw['workdir'] = '' apply(TestCommon.__init__, [self], kw) + import SCons.Node.FS + if SCons.Node.FS.default_fs is None: + SCons.Node.FS.default_fs = SCons.Node.FS.FS() + def Environment(self, ENV=None, *args, **kw): """ Return a construction Environment that optionally overrides @@ -292,36 +299,6 @@ class TestSCons(TestCommon): kw['match'] = self.match_re_dotall apply(self.run, [], kw) - def skip_test(self, message="Skipping test.\n"): - """Skips a test. - - Proper test-skipping behavior is dependent on whether we're being - executed as part of development of a change under Aegis. - - Technically, skipping a test is a NO RESULT, but Aegis will - treat that as a test failure and prevent the change from going - to the next step. We don't want to force anyone using Aegis - to have to install absolutely every tool used by the tests, - so we actually report to Aegis that a skipped test has PASSED - so that the workflow isn't held up. - """ - if message: - sys.stdout.write(message) - sys.stdout.flush() - devdir = os.popen("aesub '$dd' 2>/dev/null", "r").read()[:-1] - intdir = os.popen("aesub '$intd' 2>/dev/null", "r").read()[:-1] - if devdir and self._cwd[:len(devdir)] == devdir or \ - intdir and self._cwd[:len(intdir)] == intdir: - # We're under the development directory for this change, - # so this is an Aegis invocation; pass the test (exit 0). - self.pass_test() - else: - # skip=1 means skip this function when showing where this - # result came from. They only care about the line where the - # script called test.skip_test(), not the line number where - # we call test.no_result(). - self.no_result(skip=1) - def diff_substr(self, expect, actual, prelen=20, postlen=40): i = 0 for x, y in zip(expect, actual): @@ -361,6 +338,44 @@ class TestSCons(TestCommon): r'/CreationDate (D:XXXX)', s) s = re.sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]', r'/ID [ ]', s) + s = re.sub(r'/(BaseFont|FontName) /[A-Z]{6}', + r'/\1 /XXXXXX', s) + s = re.sub(r'/Length \d+ *\n/Filter /FlateDecode\n', + r'/Length XXXX\n/Filter /FlateDecode\n', s) + + + try: + import zlib + except ImportError: + pass + else: + begin_marker = '/FlateDecode\n>>\nstream\n' + end_marker = 'endstream\nendobj' + + encoded = [] + b = string.find(s, begin_marker, 0) + while b != -1: + b = b + len(begin_marker) + e = string.find(s, end_marker, b) + encoded.append((b, e)) + b = string.find(s, begin_marker, e + len(end_marker)) + + x = 0 + r = [] + for b, e in encoded: + r.append(s[x:b]) + d = zlib.decompress(s[b:e]) + d = re.sub(r'%%CreationDate: [^\n]*\n', + r'%%CreationDate: 1970 Jan 01 00:00:00\n', d) + d = re.sub(r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d', + r'%DVIPSSource: TeX output 1970.01.01:0000', d) + d = re.sub(r'/(BaseFont|FontName) /[A-Z]{6}', + r'/\1 /XXXXXX', d) + r.append(d) + x = e + r.append(s[x:]) + s = string.join(r, '') + return s def java_ENV(self): @@ -744,6 +759,84 @@ print "self._msvs_versions =", str(env['MSVS']['VERSIONS']) print "-----------------------------------------------------" self.fail_test() + def get_python_version(self): + """ + Returns the Python version (just so everyone doesn't have to + hand-code slicing the right number of characters). + """ + # see also sys.prefix documentation + return sys.version[:3] + + def get_platform_python(self): + """ + Returns a path to a Python executable suitable for testing on + this platform. + + Mac OS X has no static libpython for SWIG to link against, + so we have to link against Apple's framwork version. However, + testing must use the executable version that corresponds to the + framework we link against, or else we get interpreter errors. + """ + if sys.platform == 'darwin': + return '/System/Library/Frameworks/Python.framework/Versions/Current/bin/python' + else: + global python + return python + + def get_quoted_platform_python(self): + """ + Returns a quoted path to a Python executable suitable for testing on + this platform. + + Mac OS X has no static libpython for SWIG to link against, + so we have to link against Apple's framwork version. However, + testing must use the executable version that corresponds to the + framework we link against, or else we get interpreter errors. + """ + if sys.platform == 'darwin': + return '"' + self.get_platform_python() + '"' + else: + global _python_ + return _python_ + + def get_platform_sys_prefix(self): + """ + Returns a "sys.prefix" value suitable for linking on this platform. + + Mac OS X has a built-in Python but no static libpython, + so we must link to it using Apple's 'framework' scheme. + """ + if sys.platform == 'darwin': + fmt = '/System/Library/Frameworks/Python.framework/Versions/%s/' + return fmt % self.get_python_version() + else: + return sys.prefix + + def get_python_frameworks_flags(self): + """ + Returns a FRAMEWORKSFLAGS value for linking with Python. + + Mac OS X has a built-in Python but no static libpython, + so we must link to it using Apple's 'framework' scheme. + """ + if sys.platform == 'darwin': + return '-framework Python' + else: + return '' + + def get_python_inc(self): + """ + Returns a path to the Python include directory. + """ + try: + import distutils.sysconfig + except ImportError: + return os.path.join(self.get_platform_sys_prefix(), + 'include', + 'python' + self.get_python_version()) + else: + return distutils.sysconfig.get_python_inc() + # In some environments, $AR will generate a warning message to stderr # if the library doesn't previously exist and is being created. One # way to fix this is to tell AR to be quiet (sometimes the 'c' flag), diff --git a/QMTest/TestSCons_time.py b/QMTest/TestSCons_time.py index 1ceb529..102181e 100644 --- a/QMTest/TestSCons_time.py +++ b/QMTest/TestSCons_time.py @@ -246,36 +246,6 @@ class TestSCons_time(TestCommon): self.write(python_name, profile_py % d) self.run(program = python_name, interpreter = sys.executable) - def skip_test(self, message="Skipping test.\n"): - """Skips a test. - - Proper test-skipping behavior is dependent on whether we're being - executed as part of development of a change under Aegis. - - Technically, skipping a test is a NO RESULT, but Aegis will - treat that as a test failure and prevent the change from going - to the next step. We don't want to force anyone using Aegis - to have to install absolutely every tool used by the tests, - so we actually report to Aegis that a skipped test has PASSED - so that the workflow isn't held up. - """ - if message: - sys.stdout.write(message) - sys.stdout.flush() - devdir = os.popen("aesub '$dd' 2>/dev/null", "r").read()[:-1] - intdir = os.popen("aesub '$intd' 2>/dev/null", "r").read()[:-1] - if devdir and self._cwd[:len(devdir)] == devdir or \ - intdir and self._cwd[:len(intdir)] == intdir: - # We're under the development directory for this change, - # so this is an Aegis invocation; pass the test (exit 0). - self.pass_test() - else: - # skip=1 means skip this function when showing where this - # result came from. They only care about the line where the - # script called test.skip_test(), not the line number where - # we call test.no_result(). - self.no_result(skip=1) - def write_fake_aegis_py(self, name): name = self.workpath(name) self.write(name, aegis_py) diff --git a/QMTest/scons_tdb.py b/QMTest/scons_tdb.py index e0d7683..954b8df 100644 --- a/QMTest/scons_tdb.py +++ b/QMTest/scons_tdb.py @@ -429,7 +429,7 @@ class Test(AegisTest): and fails otherwise. The program output is logged, but not validated.""" command = RedirectedExecutable() - args = [context.get('python', sys.executable), self.script] + args = [context.get('python', sys.executable), '-tt', self.script] status = command.Run(args, os.environ) if not check_exit_status(result, 'Test.', self.script, status): # In case of failure record exit code, stdout, and stderr. diff --git a/README b/README index 6eecb8c..36dbe23 100644 --- a/README +++ b/README @@ -606,7 +606,7 @@ bin/ of lines in each -- a script for synchronizing the Aegis tree to SourceForge -- a prototype script for capturing sample SCons output - in sgml files + in xml files -- a script that can profile and time a packaging build of SCons itself -- a copy of xml_export, which can retrieve project data diff --git a/SConstruct b/SConstruct index a81b37b..e00e109 100644 --- a/SConstruct +++ b/SConstruct @@ -35,17 +35,16 @@ month_year = 'January 2007' # import distutils.util +import fnmatch import os import os.path import re -import socket import stat import string import sys -import time project = 'scons' -default_version = '0.97' +default_version = '0.97.0' copyright = "Copyright (c) %s The SCons Foundation" % copyright_years SConsignFile() @@ -67,89 +66,80 @@ def whereis(file): return None # -# We let the presence or absence of various utilities determine -# whether or not we bother to build certain pieces of things. -# This should allow people to still do SCons work even if they -# don't have Aegis or RPM installed, for example. +# We let the presence or absence of various utilities determine whether +# or not we bother to build certain pieces of things. This should allow +# people to still do SCons packaging work even if they don't have all +# of the utilities installed (e.g. RPM). # -aegis = whereis('aegis') -aesub = whereis('aesub') dh_builddeb = whereis('dh_builddeb') fakeroot = whereis('fakeroot') gzip = whereis('gzip') rpmbuild = whereis('rpmbuild') or whereis('rpm') +svn = whereis('svn') unzip = whereis('unzip') zip = whereis('zip') # # Now grab the information that we "build" into the files. # -try: - date = ARGUMENTS['date'] -except: +date = ARGUMENTS.get('DATE') +if not date: + import time date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time())) -if ARGUMENTS.has_key('developer'): - developer = ARGUMENTS['developer'] -elif os.environ.has_key('USERNAME'): - developer = os.environ['USERNAME'] -elif os.environ.has_key('LOGNAME'): - developer = os.environ['LOGNAME'] -elif os.environ.has_key('USER'): - developer = os.environ['USER'] - -if ARGUMENTS.has_key('build_system'): - build_system = ARGUMENTS['build_system'] -else: +developer = ARGUMENTS.get('DEVELOPER') +if not developer: + for variable in ['USERNAME', 'LOGNAME', 'USER']: + developer = os.environ.get(variable) + if developer: + break + +build_system = ARGUMENTS.get('BUILD_SYSTEM') +if not build_system: + import socket build_system = string.split(socket.gethostname(), '.')[0] -if ARGUMENTS.has_key('version'): - revision = ARGUMENTS['version'] -elif aesub: - revision = os.popen(aesub + " \\$version", "r").read()[:-1] -else: - revision = default_version - -# This is old code that adds an initial "0" to revision numbers < 10. -#a = string.split(revision, '.') -#arr = [a[0]] -#for s in a[1:]: -# if len(s) == 1: -# s = '0' + s -# arr.append(s) -#revision = string.join(arr, '.') - -# Here's how we'd turn the calculated $revision into our package $version. -# This makes it difficult to coordinate with other files (debian/changelog -# and rpm/scons.spec) that hard-code the version number, so just go with -# the flow for now and hard code it here, too. -#if len(arr) >= 2: -# arr = arr[:-1] -#def xxx(str): -# if str[0] == 'C' or str[0] == 'D': -# str = str[1:] -# while len(str) > 2 and str[0] == '0': -# str = str[1:] -# return str -#arr = map(lambda x, xxx=xxx: xxx(x), arr) -#version = string.join(arr, '.') -version = default_version - -build_id = string.replace(revision, version + '.', '') - -if ARGUMENTS.has_key('change'): - change = ARGUMENTS['change'] -elif aesub: - change = os.popen(aesub + " \\$change", "r").read()[:-1] +version = ARGUMENTS.get('VERSION', '') +if not version: + version = default_version + +revision = ARGUMENTS.get('REVISION', '') +if not revision and svn: + svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read() + m = re.search('Revision: (\d+)', svn_info) + if m: + revision = m.group(1) + +checkpoint = ARGUMENTS.get('CHECKPOINT', '') +if checkpoint: + if checkpoint == 'd': + import time + checkpoint = time.strftime('d%Y%m%d', time.localtime(time.time())) + elif checkpoint == 'r': + checkpoint = 'r' + revision + version = version + checkpoint + +if svn: + svn_status = os.popen("%s status --verbose 2> /dev/null" % svn, "r").read() + svn_status_lines = svn_status[:-1].split('\n') else: - change = default_version + svn_status_lines = [] + +build_id = ARGUMENTS.get('BUILD_ID') +if build_id is None: + if revision: + build_id = 'r' + revision + if filter(lambda l: l[0] in 'ACDMR', svn_status_lines): + build_id = build_id + '[MODIFIED]' + else: + build_id = '' python_ver = sys.version[0:3] platform = distutils.util.get_platform() ENV = { 'PATH' : os.environ['PATH'] } -for key in ['AEGIS_PROJECT', 'LOGNAME', 'PYTHONPATH']: +for key in ['LOGNAME', 'PYTHONPATH']: if os.environ.has_key(key): ENV[key] = os.environ[key] @@ -157,17 +147,68 @@ 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)) +command_line_variables = [ + ("BUILDDIR=", "The directory in which to build the packages. " + + "The default is the './build' subdirectory."), + + ("BUILD_ID=", "An identifier for the specific build." + + "The default is the Subversion revision number."), + + ("BUILD_SYSTEM=", "The system on which the packages were built. " + + "The default is whatever hostname is returned " + + "by socket.gethostname()."), + + ("CHECKPOINT=", "The specific checkpoint release being packaged. " + + "This will be appended to the VERSION string. " + + "A value of CHECKPOINT=d will generate a string " + + "of 'd' plus today's date in the format YYYMMDD." + + "A value of CHECKPOINT=r will generate a " + + "string of 'r' plus the Subversion revision number. " + + "Any other CHECKPOINT= string will be used as is." + + "There is no default value."), + + ("DATE=", "The date string representing when the packaging " + + "build occurred. The default is the day and time " + + "the SConstruct file was invoked, in the format " + + "YYYY/MM/DD HH:MM:SS."), + + ("DEVELOPER=", "The developer who created the packages. " + + "The default is the first set environment " + + "variable from the list $USERNAME, $LOGNAME, $USER."), + + ("REVISION=", "The revision number of the source being built. " + + "The default is the Subversion revision returned " + + "'svn info', with an appended string of " + + "'[MODIFIED]' if there are any changes in the " + + "working copy."), + + ("VERSION=", "The SCons version being packaged. The default " + + "is the hard-coded value '%s' " % default_version + + "from this SConstruct file."), +] + Default('.', build_dir) packaging_flavors = [ - 'deb', - 'rpm', - 'tar-gz', - 'src-tar-gz', - 'local-tar-gz', - 'zip', - 'src-zip', - 'local-zip', + ('deb', "A .deb package. (This is currently not supported.)"), + + ('rpm', "A RedHat Package Manager file."), + + ('tar-gz', "The normal .tar.gz file for end-user installation."), + + ('src-tar-gz', "A .tar.gz file containing all the source " + + "(including tests and documentation)."), + + ('local-tar-gz', "A .tar.gz file for dropping into other software " + + "for local use."), + + ('zip', "The normal .zip file for end-user installation."), + + ('src-zip', "A .zip file containing all the source " + + "(including tests and documentation)."), + + ('local-zip', "A .zip file for dropping into other software " + + "for local use."), ] test_deb_dir = os.path.join(build_dir, "test-deb") @@ -192,6 +233,44 @@ else: project_script_subinst_dir = 'bin' + +import textwrap + +indent_fmt = ' %-26s ' + +Help(""" +The following aliases build packages of various types, and unpack the +contents into build/test-$PACKAGE subdirectories, which can be used by the +runtest.py -p option to run tests against what's been actually packaged: + +""") + +aliases = packaging_flavors + [('doc', 'The SCons documentation.')] +aliases.sort() + +for alias, help_text in aliases: + tw = textwrap.TextWrapper( + width = 78, + initial_indent = indent_fmt % alias, + subsequent_indent = indent_fmt % '' + ' ', + ) + Help(tw.fill(help_text) + '\n') + +Help(""" +The following command-line variables can be set: + +""") + +for variable, help_text in command_line_variables: + tw = textwrap.TextWrapper( + width = 78, + initial_indent = indent_fmt % variable, + subsequent_indent = indent_fmt % '' + ' ', + ) + Help(tw.fill(help_text) + '\n') + + + zcat = 'gzip -d -c' # @@ -312,6 +391,7 @@ env = Environment( COPYRIGHT = copyright, DATE = date, DEVELOPER = developer, + DISTDIR = os.path.join(build_dir, 'dist'), MONTH_YEAR = month_year, REVISION = revision, VERSION = version, @@ -342,7 +422,7 @@ env = Environment( BUILDERS = { 'SCons_revision' : revbuilder, 'SOElim' : soelimbuilder }, - PYTHON = sys.executable, + PYTHON = '"%s"' % sys.executable, PYTHONFLAGS = '-tt', ) @@ -393,11 +473,26 @@ python_scons = { 'buildermap' : {}, + 'extra_rpm_files' : [], + 'explicit_deps' : { 'SCons/__init__.py' : Version_values, }, } +# The RPM spec file we generate will just execute "python", not +# necessarily the one in sys.executable. If that version of python has +# a distutils that knows about Python eggs, then setup.py will generate +# a .egg-info file. Check for whether or not to add it to the expected +# RPM files by executing "python" in a subshell. + +cmd = "python -c 'import distutils.command.install_egg_info' > /dev/null 2>&1" +import_egg_error = os.system(cmd) + +if not import_egg_error: + egg_info_file = 'scons-' + version + '.egg-info' + python_scons['extra_rpm_files'].append(egg_info_file) + # # The original packaging scheme would have have required us to push # the Python version number into the package name (python1.5-scons, @@ -568,7 +663,7 @@ for p in [ scons ]: setup_py = os.path.join(build, 'setup.py') env.Replace(PKG = pkg, PKG_VERSION = pkg_version, - SETUP_PY = setup_py) + SETUP_PY = '"%s"' % setup_py) Local(setup_py) # @@ -670,7 +765,7 @@ for p in [ scons ]: distutils_targets = [ win32_exe ] - install_targets = distutils_targets[:] + Local(env.Install('$DISTDIR', distutils_targets)) if gzip: @@ -679,7 +774,10 @@ for p in [ scons ]: src_deps.append(tar_gz) distutils_targets.extend([ tar_gz, platform_tar_gz ]) - install_targets.extend([ tar_gz, platform_tar_gz ]) + + dist_tar_gz = env.Install('$DISTDIR', tar_gz) + dist_platform_tar_gz = env.Install('$DISTDIR', platform_tar_gz) + Local(dist_tar_gz, dist_platform_tar_gz) # # Unpack the tar.gz archive created by the distutils into @@ -695,7 +793,7 @@ for p in [ scons ]: unpack_tar_gz_files = map(lambda x, u=unpack_tar_gz_dir, pv=pkg_version: os.path.join(u, pv, x), src_files) - env.Command(unpack_tar_gz_files, tar_gz, [ + env.Command(unpack_tar_gz_files, dist_tar_gz, [ Delete(os.path.join(unpack_tar_gz_dir, pkg_version)), "$ZCAT $SOURCES > .temp", "tar xf .temp -C $UNPACK_TAR_GZ_DIR", @@ -760,7 +858,10 @@ for p in [ scons ]: src_deps.append(zip) distutils_targets.extend([ zip, platform_zip ]) - install_targets.extend([ zip, platform_zip ]) + + dist_zip = env.Install('$DISTDIR', zip) + dist_platform_zip = env.Install('$DISTDIR', platform_zip) + Local(dist_zip, dist_platform_zip) # # Unpack the zip archive created by the distutils into @@ -770,7 +871,7 @@ for p in [ scons ]: os.path.join(u, pv, x), src_files) - env.Command(unpack_zip_files, zip, [ + env.Command(unpack_zip_files, dist_zip, [ Delete(os.path.join(unpack_zip_dir, pkg_version)), unzipit, ]) @@ -796,7 +897,10 @@ for p in [ scons ]: os.path.join(unpack_zip_dir, pkg_version, 'setup.py'), ]) - if rpmbuild: + if not rpmbuild: + msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\"" + AlwaysBuild(Alias('rpm', [], msg)) + else: topdir = os.path.join(build, 'build', 'bdist.' + platform, 'rpm') @@ -822,7 +926,8 @@ for p in [ scons ]: maintain multiple lists. """ c = open(str(source[0]), 'rb').read() - c = string.replace(c, '__RPM_FILES__', env['RPM_FILES']) + c = string.replace(c, '__VERSION' + '__', env['VERSION']) + c = string.replace(c, '__RPM_FILES' + '__', env['RPM_FILES']) open(str(target[0]), 'wb').write(c) rpm_files.sort() @@ -838,20 +943,22 @@ for p in [ scons ]: cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot) if not os.path.isdir(BUILDdir): cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd - env.Command(targets, specfile, cmd) - env.Depends(targets, sourcefile) + t = env.Command(targets, specfile, cmd) + env.Depends(t, sourcefile) - install_targets.extend(targets) + dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm) + dist_src_rpm = env.Install('$DISTDIR', src_rpm) + Local(dist_noarch_rpm, dist_src_rpm) dfiles = map(lambda x, d=test_rpm_dir: os.path.join(d, 'usr', x), dst_files) env.Command(dfiles, - noarch_rpm, + dist_noarch_rpm, "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)") 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. + # so we don't need to Install() the .debs. 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) @@ -908,8 +1015,8 @@ for p in [ scons ]: 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_dir, 'dist', "%s.tar.gz" % s_l_v) - local_zip = os.path.join(build_dir, 'dist', "%s.zip" % s_l_v) + dist_local_tar_gz = os.path.join("$DISTDIR/%s.tar.gz" % s_l_v) + dist_local_zip = os.path.join("$DISTDIR/%s.zip" % s_l_v) commands = [ Delete(build_dir_local), @@ -941,7 +1048,7 @@ for p in [ scons ]: Local(l) if gzip: - env.Command(local_tar_gz, + env.Command(dist_local_tar_gz, local_targets, "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local) @@ -952,10 +1059,10 @@ for p in [ scons ]: Mkdir(test_local_tar_gz_dir), "cd %s && tar xzf $( ${SOURCE.abspath} $)" % test_local_tar_gz_dir] - env.Command(unpack_targets, local_tar_gz, commands) + env.Command(unpack_targets, dist_local_tar_gz, commands) if zipit: - env.Command(local_zip, local_targets, zipit, + env.Command(dist_local_zip, local_targets, zipit, CD = build_dir_local, PSV = '.') unpack_targets = map(lambda x, d=test_local_zip_dir: @@ -965,16 +1072,9 @@ for p in [ scons ]: Mkdir(test_local_zip_dir), unzipit] - env.Command(unpack_targets, local_zip, unzipit, + env.Command(unpack_targets, dist_local_zip, unzipit, UNPACK_ZIP_DIR = test_local_zip_dir) - # - # And, lastly, install the appropriate packages in the - # appropriate subdirectory. - # - b_d_files = env.Install(os.path.join(build_dir, 'dist'), install_targets) - Local(b_d_files) - # # # @@ -1014,35 +1114,24 @@ Export('build_dir', 'env', 'whereis') SConscript('doc/SConscript') # -# If we're running in the actual Aegis project, pack up a complete -# source archive from the project files and files in the change, -# so we can share it with helpful developers who don't use Aegis. +# If we're running in a Subversion working directory, pack up a complete +# source archive from the project files and files in the change. # -if change: - df = [] - cmd = "aegis -list -unf -c %s cf 2>/dev/null" % change - for line in map(lambda x: x[:-1], os.popen(cmd, "r").readlines()): - a = string.split(line) - if a[1] == "remove": - df.append(a[-1]) - - cmd = "aegis -list -terse pf 2>/dev/null" - pf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines()) - cmd = "aegis -list -terse -c %s cf 2>/dev/null" % change - cf = map(lambda x: x[:-1], os.popen(cmd, "r").readlines()) - u = {} - for f in pf + cf: - u[f] = 1 - for f in df: - try: - del u[f] - except KeyError: - pass - sfiles = filter(lambda x: x[-9:] != '.aeignore' and - x[-9:] != '.sconsign' and - x[-10:] != '.cvsignore', - u.keys()) +if svn_status: + slines = filter(lambda l: l[0] in ' MA', svn_status_lines) + sentries = map(lambda l: l.split()[-1], slines) + sfiles = filter(os.path.isfile, sentries) + + remove_patterns = [ + '.svnt/*', + '*.aeignore', + '*.cvsignore', + 'www/*', + ] + + for p in remove_patterns: + sfiles = filter(lambda s, p=p: not fnmatch.fnmatch(s, p), sfiles) if sfiles: ps = "%s-src" % project @@ -1123,7 +1212,7 @@ if change: 'scons', 'build')), Delete("$TEST_SRC_TAR_GZ_DIR"), - 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s"' % \ + 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \ (os.path.join(unpack_tar_gz_dir, psv), os.path.join('src', 'script', 'scons.py'), os.path.join('build', 'scons')), @@ -1179,7 +1268,7 @@ if change: 'scons', 'build')), Delete("$TEST_SRC_ZIP_DIR"), - 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s"' % \ + 'cd "%s" && $PYTHON $PYTHONFLAGS "%s" "%s" VERSION="$VERSION"' % \ (os.path.join(unpack_zip_dir, psv), os.path.join('src', 'script', 'scons.py'), os.path.join('build', 'scons')), @@ -1192,5 +1281,9 @@ if change: ], ENV = ENV) -for pf in packaging_flavors: - Alias(pf, ['build/test-'+pf, 'build/QMTest', 'build/runtest.py']) +for pf, help_text in packaging_flavors: + Alias(pf, [ + os.path.join(build_dir, 'test-'+pf), + os.path.join(build_dir, 'QMTest'), + os.path.join(build_dir, 'runtest.py'), + ]) diff --git a/bin/docdiff b/bin/docdiff index 59f9472..c565a04 100644 --- a/bin/docdiff +++ b/bin/docdiff @@ -2,15 +2,15 @@ if test $# -eq 0; then for f in doc/user/*.in; do - sgml=doc/user/`basename $f .in`.sgml + xml=doc/user/`basename $f .in`.xml echo $f: - python bin/sconsoutput.py $f | diff $DIFFFLAGS $sgml - + python bin/sconsoutput.py $f | diff $DIFFFLAGS $xml - done else for a in $*; do f=doc/user/$a.in - sgml=doc/user/$a.sgml + xml=doc/user/$a.xml echo $f: - python bin/sconsoutput.py $f | diff $DIFFFLAGS $sgml - + python bin/sconsoutput.py $f | diff $DIFFFLAGS $xml - done fi diff --git a/bin/docrun b/bin/docrun index 57ad202..5ba4215 100644 --- a/bin/docrun +++ b/bin/docrun @@ -2,14 +2,14 @@ if test $# -eq 0; then for f in doc/user/*.in; do - sgml=doc/user/`basename $f .in`.sgml + xml=doc/user/`basename $f .in`.xml echo $f: python bin/sconsoutput.py $f done else for a in $*; do f=doc/user/$a.in - sgml=doc/user/$a.sgml + xml=doc/user/$a.xml echo $f: python bin/sconsoutput.py $f done diff --git a/bin/docupdate b/bin/docupdate index c4eedb8..0e1631b 100644 --- a/bin/docupdate +++ b/bin/docupdate @@ -2,15 +2,15 @@ if test $# -eq 0; then for f in doc/user/*.in; do - sgml=doc/user/`basename $f .in`.sgml + xml=doc/user/`basename $f .in`.xml echo $f: - python bin/sconsoutput.py $f > $sgml + python bin/sconsoutput.py $f > $xml done else for a in $*; do f=doc/user/$a.in - sgml=doc/user/$a.sgml + xml=doc/user/$a.xml echo $f: - python bin/sconsoutput.py $f > $sgml + python bin/sconsoutput.py $f > $xml done fi diff --git a/bin/linecount b/bin/linecount index 765309e..7ab37f5 100644 --- a/bin/linecount +++ b/bin/linecount @@ -15,9 +15,7 @@ # # A source file is anything under the src/engine/ or src/script/ # directories that ends in '.py' but does NOT begin with 'test_' -# or end in 'Tests.py'. (We should probably ignore the stuff in -# src/engine/SCons/Optik, since it doesn't originate with SCons, but -# what the hell.) +# or end in 'Tests.py'. # # We report the number of tests and sources, the total number of lines # in each category, the number of non-blank lines, and the number of diff --git a/bin/scons-proc.py b/bin/scons-proc.py index fc60a9b..1de0caa 100644 --- a/bin/scons-proc.py +++ b/bin/scons-proc.py @@ -6,7 +6,7 @@ # construction variables documented in the specified XML files. # # Dependening on the options, the lists are output in either -# DocBook-formatted generated SGML files containing the summary text +# DocBook-formatted generated XML files containing the summary text # and/or .mod files contining the ENTITY definitions for each item, # or in man-page-formatted output. # @@ -23,7 +23,7 @@ import SConsDoc base_sys_path = [os.getcwd() + '/build/test-tar-gz/lib/scons'] + sys.path helpstr = """\ -Usage: scons-proc.py [--man|--sgml] +Usage: scons-proc.py [--man|--xml] [-b file(s)] [-t file(s)] [-v file(s)] [infile ...] Options: -b file(s) dump builder information to the specified file(s) @@ -31,17 +31,17 @@ Options: -v file(s) dump variable information to the specified file(s) --man print info in man page format, each -[btv] argument is a single file name - --sgml (default) print info in SGML format, each -[btv] argument + --xml (default) print info in SML format, each -[btv] argument is a pair of comma-separated .gen,.mod file names """ opts, args = getopt.getopt(sys.argv[1:], "b:ht:v:", ['builders=', 'help', - 'man', 'sgml', 'tools=', 'variables=']) + 'man', 'xml', 'tools=', 'variables=']) buildersfiles = None -output_type = '--sgml' +output_type = '--xml' toolsfiles = None variablesfiles = None @@ -51,7 +51,7 @@ for o, a in opts: elif o in ['-h', '--help']: sys.stdout.write(helpstr) sys.exit(0) - elif o in ['--man', '--sgml']: + elif o in ['--man', '--xml']: output_type = o elif o in ['-t', '--tools']: toolsfiles = a @@ -132,7 +132,7 @@ class SCons_XML: return sys.stdout return open(name, 'w') -class SCons_XML_to_SGML(SCons_XML): +class SCons_XML_to_XML(SCons_XML): def write(self, files): gen, mod = string.split(files, ',') g.write_gen(gen) @@ -232,8 +232,8 @@ class SCons_XML_to_man(SCons_XML): if output_type == '--man': processor_class = SCons_XML_to_man -elif output_type == '--sgml': - processor_class = SCons_XML_to_SGML +elif output_type == '--xml': + processor_class = SCons_XML_to_XML else: sys.stderr.write("Unknown output type '%s'\n" % output_type) sys.exit(1) diff --git a/bin/sconsoutput.py b/bin/sconsoutput.py index a9c502b..585d3c1 100644 --- a/bin/sconsoutput.py +++ b/bin/sconsoutput.py @@ -376,6 +376,8 @@ if filter_tools: toollist = map(lambda t: apply(ToolSurrogate, t), toollist) +toollist.append('install') + def surrogate_spawn(sh, escape, cmd, args, env): pass @@ -788,6 +790,10 @@ data = f.read() if f is not sys.stdin: f.close() +if data.startswith('', re. format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format="([^"]*)")?') # -# Find internal dependencies in .sgml files: +# Find internal dependencies in .xml files: # -# +# # # # @@ -76,7 +74,7 @@ format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format= # defined as a SYSTEM entity is, in fact, a file included # somewhere in the document. # -def scansgml(node, env, target): +def scanxml(node, env, target): includes = [] contents = node.get_contents() @@ -101,7 +99,7 @@ def scansgml(node, env, target): return includes -s = Scanner(name = 'sgml', function = scansgml, skeys = ['.sgml', '.mod']) +s = Scanner(name = 'xml', function = scanxml, skeys = ['.xml', '.mod']) orig_env = env env = orig_env.Clone(SCANNERS = [s], @@ -127,14 +125,14 @@ scons_doc_files = map(lambda x: File('#src/engine/'+x).rstr(), scons_doc_files) if jw: # - # Always create a version.sgml file containing the version information + # Always create a version.xml file containing the version information # for this run. Ignore it for dependency purposes so we don't # rebuild all the docs every time just because the date changes. # date, ver, rev = env.Dictionary('DATE', 'VERSION', 'REVISION') - version_sgml = File(os.path.join(build, "version.sgml")) - #version_sgml = File("version.sgml") - verfile = str(version_sgml) + version_xml = File(os.path.join(build, "version.xml")) + #version_xml = File("version.xml") + verfile = str(version_xml) try: os.unlink(verfile) except OSError: @@ -170,7 +168,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. variables_gen, variables_mod] b = env.Command(doc_output_files, scons_doc_files, - "$PYTHON $SCONS_PROC_PY --sgml -b ${TARGETS[0]},${TARGETS[1]} -t ${TARGETS[2]},${TARGETS[3]} -v ${TARGETS[4]},${TARGETS[5]} $( $SOURCES $)") + "$PYTHON $SCONS_PROC_PY --xml -b ${TARGETS[0]},${TARGETS[1]} -t ${TARGETS[2]},${TARGETS[3]} -v ${TARGETS[4]},${TARGETS[5]} $( $SOURCES $)") env.Depends(b, "$SCONS_PROC_PY") env.Local(b) @@ -237,8 +235,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. } # - # We have to tell SCons to scan the top-level SGML files which - # get included by the document SGML files in the subdirectories. + # We have to tell SCons to scan the top-level XML files which + # get included by the document XML files in the subdirectories. # manifest = File('MANIFEST').rstr() src_files = map(lambda x: x[:-1], open(manifest).readlines()) @@ -266,20 +264,20 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. if ext in ['.fig', '.jpg']: orig_env.InstallAs(build_s, doc_s) else: - if build_doc and ext == '.sgml': + if build_doc and ext == '.xml': env.Command(doc_s, base + '.in', "$PYTHON $SCONSOUTPUT_PY $SOURCE > $TARGET") orig_env.SCons_revision(build_s, doc_s) Local(build_s) - main = os.path.join(build, doc, 'main.sgml') + main = os.path.join(build, doc, 'main.xml') out = 'main.out' # 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_dir, 'scons-src', 'doc', main) - env.Ignore(scons_src_main, version_sgml) + env.Ignore(scons_src_main, version_xml) htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc) htmlindex = os.path.join(htmldir, docs[doc]['htmlindex']) @@ -289,12 +287,27 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc) if docs[doc].get('html') and jade: + def copy_index_html(target, source, env): + # Older versions of DocBook|jw|jade|whatever would + # create a book1.html file, while newer versions create + # an index.html file (logically enough). The scons.org + # web site links expect book1.html, so we're going to + # leave the target as is, and run this post-processing + # action function to check that the target really did + # get created, and if it didn't, copy it from index.html. + t = str(target[0]) + if not os.path.exists(t): + i = os.path.join(os.path.split(t)[0], 'index.html') + open(t, 'w').write(open(i, 'r').read()) + return None + cmds = [ Delete("${TARGET.dir}/*.html"), "jw -b html -o ${TARGET.dir} $SOURCES", ] if tidy: cmds.append("tidy -m -q $TARGET || true") + cmds.append(Action(copy_index_html)) env.Command(htmlindex, File(main), cmds) Local(htmlindex) @@ -308,7 +321,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. env.Command(html, File(main), cmds) Local(html) - env.Ignore([html, htmlindex], version_sgml) + env.Ignore([html, htmlindex], version_xml) tar_deps.extend([html, htmlindex]) tar_list.extend([html, htmldir]) @@ -339,7 +352,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. ]) Local(ps) - env.Ignore(ps, version_sgml) + env.Ignore(ps, version_xml) tar_deps.append(ps) tar_list.append(ps) @@ -369,7 +382,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. ]) Local(pdf) - env.Ignore(pdf, version_sgml) + env.Ignore(pdf, version_xml) tar_deps.append(pdf) tar_list.append(pdf) @@ -378,7 +391,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. env.Command(text, html, "lynx -dump ${SOURCE.abspath} > $TARGET") Local(text) - env.Ignore(text, version_sgml) + env.Ignore(text, version_xml) tar_deps.append(text) tar_list.append(text) @@ -505,6 +518,9 @@ if epydoc: if tar_deps: tar_list = string.join(map(lambda x, b=build+'/': string.replace(x, b, ''), tar_list)) - env.Command(doc_tar_gz, tar_deps, + t = env.Command(dist_doc_tar_gz, tar_deps, "tar cf${TAR_HFLAG} - -C %s %s | gzip > $TARGET" % (build, tar_list)) - Local(doc_tar_gz) + Local(t) + Alias('doc', t) +else: + Alias('doc', os.path.join(build_dir, 'doc')) diff --git a/doc/design/MANIFEST b/doc/design/MANIFEST index 3fb99f0..33ab8f0 100644 --- a/doc/design/MANIFEST +++ b/doc/design/MANIFEST @@ -1,14 +1,14 @@ -acks.sgml -bground.sgml -copyright.sgml +acks.xml +bground.xml +copyright.xml engine.fig engine.jpg -engine.sgml -goals.sgml -install.sgml -intro.sgml -issues.sgml -main.sgml -native.sgml -overview.sgml +engine.xml +goals.xml +install.xml +intro.xml +issues.xml +main.xml +native.xml +overview.xml scons.mod diff --git a/doc/design/acks.sgml b/doc/design/acks.sgml deleted file mode 100644 index b1a8a58..0000000 --- a/doc/design/acks.sgml +++ /dev/null @@ -1,179 +0,0 @@ - - - - - I'm grateful to the following people - for their influence, knowing or not, - on the design of &SCons;: - - - - - - Bob Sidebotham - - - - First, as the original author of &Cons;, Bob did the real heavy - lifting of creating the underlying model for dependency management - and software construction, as well as implementing it in Perl. - During the first years of &Cons;' existence, Bob did a skillful - job of integrating input and code from the first users, and - consequently is a source of practical wisdom and insight into the - problems of real-world software construction. His continuing - advice has been invaluable. - - - - - - - The &SCons; Development Team - - - - A big round of thanks go to those brave souls who have - gotten in on the ground floor: - David Abrahams, - Charles Crain, - Steven Leblanc. - Anthony Roach, - and - Steven Shaw. - Their contributions, - through their general knowledge of software build issues in general - Python in particular, - have made &SCons; what it is today. - - - - - - - The &Cons; Community - - - - The real-world build problems that the users of &Cons; - share on the cons-discuss mailing list - have informed much of the thinking that - has gone into the &SCons; design. - In particular, - Rajesh Vaidheeswarran, - the current maintainer of &Cons;, - has been a very steady influence. - I've also picked up valuable insight from - mailing-list participants - Johan Holmberg, - Damien Neil, - Gary Oberbrunner, - Wayne Scott, - and Greg Spencer. - - - - - - - Peter Miller - - - - - Peter has indirectly - influenced two aspects of the &SCons; design: - - - - - - Miller's influential paper - Recursive Make Considered Harmful - was what led me, indirectly, to my involvement with &Cons; - in the first place. - Experimenting with the single-Makefile approach he describes in - RMCH led me to conclude that while it worked - as advertised, it was not an extensible scheme. This solidified - my frustration with Make and led me to try &Cons;, which at its - core shares the single-process, universal-DAG model of the "RMCH" - single-Makefile technique. - - - - - - The testing framework that Miller created for his - Aegis change management system - changed the way I approach software development - by providing a framework for rigorous, repeatable - testing during development. - It was my success at using Aegis for personal projects - that led me to begin my involvement with &Cons; - by creating the cons-test regression suite. - - - - - - - Stuart Stanley - - - - An experienced Python programmer, - Stuart provided valuable advice and insight - into some of the more useful Python idioms at my disposal - during the original ScCons; design - for the Software Carpentry contest. - - - - - - - Gary Holt - - - - I don't know which came first, - the first-round Software Carpentry contest entry - or the tool itself, - but Gary's design for &Makepp; - showed me that it is possible to marry - the strengths of &Cons;-like dependency management - with backwards compatibility for &Makefile;s. - Striving to support both - &Makefile; compatibility and - a native Python interface - cleaned up the &SCons; design immeasurably - by factoring out the common elements - into the Build Engine. - - - - - - diff --git a/doc/design/acks.xml b/doc/design/acks.xml new file mode 100644 index 0000000..b1a8a58 --- /dev/null +++ b/doc/design/acks.xml @@ -0,0 +1,179 @@ + + + + + I'm grateful to the following people + for their influence, knowing or not, + on the design of &SCons;: + + + + + + Bob Sidebotham + + + + First, as the original author of &Cons;, Bob did the real heavy + lifting of creating the underlying model for dependency management + and software construction, as well as implementing it in Perl. + During the first years of &Cons;' existence, Bob did a skillful + job of integrating input and code from the first users, and + consequently is a source of practical wisdom and insight into the + problems of real-world software construction. His continuing + advice has been invaluable. + + + + + + + The &SCons; Development Team + + + + A big round of thanks go to those brave souls who have + gotten in on the ground floor: + David Abrahams, + Charles Crain, + Steven Leblanc. + Anthony Roach, + and + Steven Shaw. + Their contributions, + through their general knowledge of software build issues in general + Python in particular, + have made &SCons; what it is today. + + + + + + + The &Cons; Community + + + + The real-world build problems that the users of &Cons; + share on the cons-discuss mailing list + have informed much of the thinking that + has gone into the &SCons; design. + In particular, + Rajesh Vaidheeswarran, + the current maintainer of &Cons;, + has been a very steady influence. + I've also picked up valuable insight from + mailing-list participants + Johan Holmberg, + Damien Neil, + Gary Oberbrunner, + Wayne Scott, + and Greg Spencer. + + + + + + + Peter Miller + + + + + Peter has indirectly + influenced two aspects of the &SCons; design: + + + + + + Miller's influential paper + Recursive Make Considered Harmful + was what led me, indirectly, to my involvement with &Cons; + in the first place. + Experimenting with the single-Makefile approach he describes in + RMCH led me to conclude that while it worked + as advertised, it was not an extensible scheme. This solidified + my frustration with Make and led me to try &Cons;, which at its + core shares the single-process, universal-DAG model of the "RMCH" + single-Makefile technique. + + + + + + The testing framework that Miller created for his + Aegis change management system + changed the way I approach software development + by providing a framework for rigorous, repeatable + testing during development. + It was my success at using Aegis for personal projects + that led me to begin my involvement with &Cons; + by creating the cons-test regression suite. + + + + + + + Stuart Stanley + + + + An experienced Python programmer, + Stuart provided valuable advice and insight + into some of the more useful Python idioms at my disposal + during the original ScCons; design + for the Software Carpentry contest. + + + + + + + Gary Holt + + + + I don't know which came first, + the first-round Software Carpentry contest entry + or the tool itself, + but Gary's design for &Makepp; + showed me that it is possible to marry + the strengths of &Cons;-like dependency management + with backwards compatibility for &Makefile;s. + Striving to support both + &Makefile; compatibility and + a native Python interface + cleaned up the &SCons; design immeasurably + by factoring out the common elements + into the Build Engine. + + + + + + diff --git a/doc/design/bground.sgml b/doc/design/bground.sgml deleted file mode 100644 index c404e86..0000000 --- a/doc/design/bground.sgml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - Most of the ideas in &SCons; originate with &Cons;, a Perl-based - software construction utility that has been in use by a small but - growing community since its development by Bob Sidebotham at FORE - Systems in 1996. The &Cons; copyright was transferred in 2000 from - Marconi (who purchased FORE Systems) to the Free Software Foundation. - I've been a principal implementer and maintainer of &Cons; for several - years. - - - - - - &Cons; was originally designed to handle complicated software build - problems (multiple directories, variant builds) while keeping the - input files simple and maintainable. The general philosophy is that - the build tool should ``do the right thing'' with minimal input - from an unsophisticated user, while still providing a rich set of - underlying functionality for more complicated software construction - tasks needed by experts. - - - - - - In 2000, the Software Carpentry sought entries in a contest for a - new, Python-based build tool that would provide an improvement - over Make for physical scientists and other non-programmers - struggling to use their computers more effectively. Prior to that, - the idea of combining the superior build architecture of &Cons; - with the easier syntax of Python had come up several times on - the cons-discuss mailing list. The Software - Carpentry contest provided the right motivation to spend some - actual time working on a design document. - - - - - - After two rounds of competition, the submitted design, named - ScCons, won the competition. Software - Carpentry, however, did not immediately fund implementation of the - build tool, instead contracting for additional, more detailed draft(s) - of the design document. This proved to be not as strong motivation as - actual coding, and after several months of inactivity, I essentially - resigned from the Software Carpentry effort in early 2001 to start - working on the tool independently. - - - - - - After half a year of prototyping some of the important infrastructure, - I accumulated enough code to take the project public at SourceForge, - renaming it &SCons; to distinguish it slightly from the version of the - design that won the Software Carpentry contest while still honoring - its roots there and in the original &Cons; utility. And also because - it would be a teensy bit easier to type. - - diff --git a/doc/design/bground.xml b/doc/design/bground.xml new file mode 100644 index 0000000..c404e86 --- /dev/null +++ b/doc/design/bground.xml @@ -0,0 +1,86 @@ + + + + + Most of the ideas in &SCons; originate with &Cons;, a Perl-based + software construction utility that has been in use by a small but + growing community since its development by Bob Sidebotham at FORE + Systems in 1996. The &Cons; copyright was transferred in 2000 from + Marconi (who purchased FORE Systems) to the Free Software Foundation. + I've been a principal implementer and maintainer of &Cons; for several + years. + + + + + + &Cons; was originally designed to handle complicated software build + problems (multiple directories, variant builds) while keeping the + input files simple and maintainable. The general philosophy is that + the build tool should ``do the right thing'' with minimal input + from an unsophisticated user, while still providing a rich set of + underlying functionality for more complicated software construction + tasks needed by experts. + + + + + + In 2000, the Software Carpentry sought entries in a contest for a + new, Python-based build tool that would provide an improvement + over Make for physical scientists and other non-programmers + struggling to use their computers more effectively. Prior to that, + the idea of combining the superior build architecture of &Cons; + with the easier syntax of Python had come up several times on + the cons-discuss mailing list. The Software + Carpentry contest provided the right motivation to spend some + actual time working on a design document. + + + + + + After two rounds of competition, the submitted design, named + ScCons, won the competition. Software + Carpentry, however, did not immediately fund implementation of the + build tool, instead contracting for additional, more detailed draft(s) + of the design document. This proved to be not as strong motivation as + actual coding, and after several months of inactivity, I essentially + resigned from the Software Carpentry effort in early 2001 to start + working on the tool independently. + + + + + + After half a year of prototyping some of the important infrastructure, + I accumulated enough code to take the project public at SourceForge, + renaming it &SCons; to distinguish it slightly from the version of the + design that won the Software Carpentry contest while still honoring + its roots there and in the original &Cons; utility. And also because + it would be a teensy bit easier to type. + + diff --git a/doc/design/copyright.sgml b/doc/design/copyright.sgml deleted file mode 100644 index d73906e..0000000 --- a/doc/design/copyright.sgml +++ /dev/null @@ -1,39 +0,0 @@ - - -
- - - Copyright (c) 2001 Steven Knight - - Portions of this document, by the same author, were previously - published Copyright 2000 by CodeSourcery LLC, under the Software Carpentry - Open Publication License, the terms of which are available at - - http://www.software-carpentry.com/openpub-license.html - . - - -
diff --git a/doc/design/copyright.xml b/doc/design/copyright.xml new file mode 100644 index 0000000..d73906e --- /dev/null +++ b/doc/design/copyright.xml @@ -0,0 +1,39 @@ + + +
+ + + Copyright (c) 2001 Steven Knight + + Portions of this document, by the same author, were previously + published Copyright 2000 by CodeSourcery LLC, under the Software Carpentry + Open Publication License, the terms of which are available at + + http://www.software-carpentry.com/openpub-license.html + . + + +
diff --git a/doc/design/engine.sgml b/doc/design/engine.sgml deleted file mode 100644 index df78e3b..0000000 --- a/doc/design/engine.sgml +++ /dev/null @@ -1,1964 +0,0 @@ - - -
- General Principles - -
- Keyword arguments - - - - All methods and functions in this API will support the use of keyword - arguments in calls, for the sake of explicitness and readability. - For brevity in the hands of experts, most methods and functions - will also support positional arguments for their most-commonly-used - arguments. As an explicit example, the following two lines will each - arrange for an executable program named foo (or - foo.exe on a Win32 system) to be compiled from - the foo.c source file: - - - - - env.Program(target = 'foo', source = 'foo.c') - - env.Program('foo', 'foo.c') - - -
- -
- Internal object representation - - - - All methods and functions use internal (Python) objects that - represent the external objects (files, for example) for which they - perform dependency analysis. - - - - - - All methods and functions in this API that accept an external object - as an argument will accept either a string - description or an object reference. For example, the two following - two-line examples are equivalent: - - - - - env.Object(target = 'foo.o', source = 'foo.c') - env.Program(target = 'foo', 'foo.o') # builds foo from foo.o - - foo_obj = env.Object(target = 'foo.o', source = 'foo.c') - env.Program(target = 'foo', foo_obj) # builds foo from foo.o - - -
- -
- - - -
- &ConsEnvs - - - - A &consenv; is the basic means by which a software system interacts - with the &SCons; Python API to control a build process. - - - - - - A &consenv; is an object with associated methods for generating target - files of various types (&Builder; objects), other associated object - methods for automatically determining dependencies from the contents - of various types of source files (&Scanner; objects), and a dictionary - of values used by these methods. - - - - - - Passing no arguments to the &Environment; instantiation creates a - &consenv; with default values for the current platform: - - - - - env = Environment() - - -
- &Consvars; - - - - A &consenv; has an associated dictionary of &consvars; that control how - the build is performed. By default, the &Environment; method creates - a &consenv; with values that make most software build "out of the box" - on the host system. These default values will be generated at the - time &SCons; is installed using functionality similar to that provided - by GNU &Autoconf;. - - - It would be nice if we could avoid re-inventing the wheel here by - using some other Python-based tool &Autoconf replacement--like what - was supposed to come out of the Software Carpentry configuration - tool contest. It will probably be most efficient to roll our own - logic initially and convert if something better does come along. - - - At a minimum, there will be pre-configured sets of default values - that will provide reasonable defaults for UNIX and Windows NT. - - - - - - The default &consenv; values may be overridden when a new &consenv; is - created by specifying keyword arguments: - - - - - env = Environment(CC = 'gcc', - CCFLAGS = '-g', - CPPPATH = ['.', 'src', '/usr/include'], - LIBPATH = ['/usr/lib', '.']) - - -
- -
- Fetching &consvars; - - - - A copy of the dictionary of &consvars; can be returned using - the &Dictionary; method: - - - - - env = Environment() - dict = env.Dictionary() - - - - - If any arguments are supplied, then just the corresponding value(s) - are returned: - - - - - ccflags = env.Dictionary('CCFLAGS') - cc, ld = env.Dictionary('CC', 'LD') - - -
- -
- Copying a &consenv; - - - - A method exists to return a copy of an existing environment, with - any overridden values specified as keyword arguments to the method: - - - - - env = Environment() - debug = env.Copy(CCFLAGS = '-g') - - -
- -
- Multiple &consenvs; - - - - Different external objects often require different build - characteristics. Multiple &consenvs; may be defined, each with - different values: - - - - - env = Environment(CCFLAGS = '') - debug = Environment(CCFLAGS = '-g') - env.Make(target = 'hello', source = 'hello.c') - debug.Make(target = 'hello-debug', source = 'hello.c') - - - - - Dictionaries of values from multiple &consenvs; may be passed to the - &Environment; instantiation or the &Copy; method, in which case the - last-specified dictionary value wins: - - - - - env1 = Environment(CCFLAGS = '-O', LDFLAGS = '-d') - env2 = Environment(CCFLAGS = '-g') - new = Environment(env1.Dictionary(), env2.Dictionary()) - - - - - The new environment in the above example retains - LDFLAGS = '-d' from the env1 - environment, and CCFLAGS = '-g' from the - env2 environment. - - - - - -
- -
- Variable substitution - - - - Within a construction command, any variable from the &consenv; may - be interpolated by prefixing the name of the construction with - $: - - - - - MyBuilder = Builder(command = "$XX $XXFLAGS -c $_INPUTS -o $target") - - env.Command(targets = 'bar.out', sources = 'bar.in', - command = "sed '1d' < $source > $target") - - - - - Variable substitution is recursive: the command line is expanded - until no more substitutions can be made. - - - - - - Variable names following the $ may be enclosed in - braces. This can be used to concatenate an interpolated value with an - alphanumeric character: - - - - - VerboseBuilder = Builder(command = "$XX -${XXFLAGS}v > $target") - - - - - The variable within braces may contain a pair of parentheses - after a Python function name to be evaluated (for example, - ${map()}). &SCons; will interpolate the return - value from the function (presumably a string): - - - - - env = Environment(FUNC = myfunc) - env.Command(target = 'foo.out', source = 'foo.in', - command = "${FUNC($<)}") - - - - - If a referenced variable is not defined in the &consenv;, - the null string is interpolated. - - - - - - The following special variables can also be used: - - - - - - - $targets - - - - All target file names. If multiple targets are specified in an - array, $targets expands to the entire list of - targets, separated by a single space. - - - - - - Individual targets from a list may be extracted by enclosing - the targets keyword in braces and using the - appropriate Python array index or slice: - - - - - ${targets[0]} # expands to the first target - - ${targets[1:]} # expands to all but the first target - - ${targets[1:-1]} # expands to all but the first and last targets - - - - - - - $target - - - - A synonym for ${targets[0]}, the first target - specified. - - - - - - - $sources - - - - All input file names. Any input file names that - are used anywhere else on the current command - line (via ${sources[0]}, - ${sources{[1]}, etc.) are removed from the - expanded list. - - - - - - - - - - Any of the above special variables may be enclosed in braces and - followed immediately by one of the following attributes to select just - a portion of the expanded path name: - - - - - - - .base - - - - Basename: the directory plus the file name, minus any file suffix. - - - - - - - .dir - - - - The directory in which the file lives. This is a relative path, - where appropriate. - - - - - - - .file - - - - The file name, minus any directory portion. - - - - - - - .suffix - - - - The file name suffix (that is, the right-most dot in the file name, - and all characters to the right of that). - - - - - - - .filebase - - - - The file name (no directory portion), minus any file suffix. - - - - - - - .abspath - - - - The absolute path to the file. - - - - - - - -
- -
- - - -
- &Builder; Objects - - - - By default, &SCons; supplies (and uses) a number of pre-defined - &Builder; objects: - - - - - - - - - &Object; - compile or assemble an object file - - - - &Library; - archive files into a library - - - - &SharedLibrary; - archive files into a shared library - - - - &Program; - link objects and/or libraries into an executable - - - - &MakeBuilder; - build according to file suffixes; see below - - - - - - - -&Library; and &SharedLibrary; have nearly identical -semantics, just different -tools and &consenvs (paths, etc.) that they use. -In other words, you can construct a shared library -using just the &Library; &Builder; object -with a different environment. -I think that's a better way to do it. -Feedback? - - - - - A &consenv; can be explicitly initialized with associated &Builder; - objects that will be bound to the &consenv; object: - - - - - env = Environment(BUILDERS = ['Object', 'Program']) - - - - - &Builder; objects bound to a &consenv; can be called directly as - methods. When invoked, a &Builder; object returns a (list of) objects - that it will build: - - - - - obj = env.Object(target ='hello.o', source = 'hello.c') - lib = env.Library(target ='libfoo.a', - source = ['aaa.c', 'bbb.c']) - slib = env.SharedLibrary(target ='libbar.so', - source = ['xxx.c', 'yyy.c']) - prog = env.Program(target ='hello', - source = ['hello.o', 'libfoo.a', 'libbar.so']) - - -
- Specifying multiple inputs - - - - Multiple input files that go into creating a target file may be passed - in as a single string, with the individual file names separated by - white space: - - - - - env.Library(target = 'foo.a', source = 'aaa.c bbb.c ccc.c') - env.Object(target = 'yyy.o', source = 'yyy.c') - env.Program(target = 'bar', source = 'xxx.c yyy.o foo.a') - - - - - Alternatively, multiple input files that go into creating a target - file may be passed in as an array. This allows input files to be - specified using their object representation: - - - - - env.Library(target = 'foo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) - yyy_obj = env.Object(target = 'yyy.o', source = 'yyy.c') - env.Program(target = 'bar', source = ['xxx.c', yyy_obj, 'foo.a']) - - - - - Individual string elements within an array of input files are - not further split into white-space separated - file names. This allows file names that contain white space to - be specified by putting the value into an array: - - - env.Program(target = 'foo', source = ['an input file.c']) - - - - -
- -
- Specifying multiple targets - - - - Conversely, the generated target may be a string listing multiple - files separated by white space: - - - - - env.Object(target = 'grammar.o y.tab.h', source = 'grammar.y') - - - - - An array of multiple target files can be used to mix string and object - representations, or to accomodate file names that contain white space: - - - - - env.Program(target = ['my program'], source = 'input.c') - - -
- -
- File prefixes and suffixes - - - - For portability, if the target file name does not already have an - appropriate file prefix or suffix, the &Builder; objects will - append one appropriate for the file type on the current system: - - - - - # builds 'hello.o' on UNIX, 'hello.obj' on Windows NT: - obj = env.Object(target ='hello', source = 'hello.c') - - # builds 'libfoo.a' on UNIX, 'foo.lib' on Windows NT: - lib = env.Library(target ='foo', source = ['aaa.c', 'bbb.c']) - - # builds 'libbar.so' on UNIX, 'bar.dll' on Windows NT: - slib = env.SharedLibrary(target ='bar', source = ['xxx.c', 'yyy.c']) - - # builds 'hello' on UNIX, 'hello.exe' on Windows NT: - prog = env.Program(target ='hello', - source = ['hello.o', 'libfoo.a', 'libbar.so']) - - -
- -
- &Builder; object exceptions - - - - &Builder; objects raise the following exceptions on error: - - - LIST THESE ONCE WE FIGURE OUT WHAT THEY ARE FROM CODING THEM. - - - -
- -
- User-defined &Builder; objects - - - - Users can define additional &Builder; objects for specific external - object types unknown to &SCons;. A &Builder; object may build its - target by executing an external command: - - - - - WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target', - suffix = '.html', - src_suffix = '.in') - - - - - Alternatively, a &Builder; object may also build its target by - executing a Python function: - - - - - def update(dest): - # [code to update the object] - return 1 - - OtherBuilder1 = Builder(function = update, - src_suffix = ['.in', '.input']) - - - - - An optional argument to pass to the function may be specified: - - - - - def update_arg(dest, arg): - # [code to update the object] - return 1 - - OtherBuilder2 = Builder(function = update_arg, - function_arg = 'xyzzy', - src_suffix = ['.in', '.input']) - - - - - Both an external command and an internal function may be specified, - in which case the function will be called to build the object first, - followed by the command line. - - - - - NEED AN EXAMPLE HERE. - - - - - User-defined &Builder; objects can be used like the default &Builder; - objects to initialize &consenvs;. - - - - - WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target', - suffix = '.html', - src_suffix = '.in') - env = Environment(BUILDERS = ['WebPage']) - env.WebPage(target = 'foo.html', source = 'foo.in') - # Builds 'bar.html' on UNIX, 'bar.htm' on Windows NT: - env.WebPage(target = 'bar', source = 'bar.in') - - - - - The command-line specification can interpolate variables from the - &consenv;; see "Variable substitution," above. - - - - - - A &Builder; object may optionally be initialized with a list of: - - - - - - - - the prefix of the target file (e.g., 'lib' for libraries) - - - - - - - - the suffix of the target file (e.g., '.a' for libraries) - - - - - - - - the expected suffixes of the input files - (e.g., '.o' for object files) - - - - - - - - These arguments are used in automatic - dependency analysis and to generate output file names that don't - have suffixes supplied explicitly. - - -
- -
- Copying &Builder; Objects - - - - A &Copy; method exists to return a copy of an existing &Builder; - object, with any overridden values specified as keyword arguments to - the method: - - - - - build = Builder(function = my_build) - build_out = build.Copy(suffix = '.out') - - - - - Typically, &Builder; objects will be supplied by a tool-master or - administrator through a shared &consenv;. - - -
- -
- Special-purpose build rules - - - - A pre-defined &Command; builder exists to associate a target file with - a specific command or list of commands for building the file: - - - - - env.Command(target = 'foo.out', source = - command = 'foo.in', "foo.process $sources > $target") - - commands = [ "bar.process -o .tmpfile $sources", - "mv .tmpfile $target" ] - env.Command(target = 'bar.out', source = 'bar.in', command = commands) - - - - This is useful when it's too cumbersome to create a &Builder; - object just to build a single file in a special way. - - -
- -
- The &MakeBuilder; &Builder; - - - - A pre-defined &Builder; object named &MakeBuilder; exists to make - simple builds as easy as possible for users, at the expense of - sacrificing some build portability. - - - - - - The following minimal example builds the 'hello' program from the - 'hello.c' source file: - - - - - Environment().Make('hello', 'hello.c') - - - - - Users of the &MakeBuilder; &Builder; object are not required to - understand intermediate steps involved in generating a file--for - example, the distinction between compiling source code into an object - file, and then linking object files into an executable. The details - of intermediate steps are handled by the invoked method. Users that - need to, however, can specify intermediate steps explicitly: - - - - - env = Environment() - env.Make(target = 'hello.o', source = 'hello.c') - env.Make(target = 'hello', source = 'hello.o') - - - - - The &MakeBuilder; method understands the file suffixes specified and - "does the right thing" to generate the target object and program - files, respectively. It does this by examining the specified output - suffixes for the &Builder; objects bound to the environment. - - - - - - Because file name suffixes in the target and source file names - must be specified, the &MakeBuilder; method can't be used - portably across operating systems. In other words, for the - example above, the &MakeBuilder; builder will not generate - hello.exe on Windows NT. - - - -
- -
- &Builder; maps - - -Do we even need this anymore? -Now that the individual builders -have specified suffix -and src_suffix values, -all of the information we need to support -the &MakeBuilder; builder is right there in the environment. -I think this is a holdover from before I -added the suffix arguments. -If you want &MakeBuilder; to do something different, -you set it up with another environment... - - - - - The env.Make method "does the right thing" to - build different file types because it uses a dictionary from the - &consenv; that maps file suffixes to the appropriate &Builder; object. - This &BUILDERMAP; can be initialized at instantiation: - - - - - env = Environment(BUILDERMAP = { - '.o' : Object, - '.a' : Library, - '.html' : WebPage, - '' : Program, - }) - - - - - With the &BUILDERMAP; properly initialized, the - env.Make method can be used to build additional - file types: - - - - - env.Make(target = 'index.html', source = 'index.input') - - - - - &Builder; objects referenced in the &BUILDERMAP; do not need to be - listed separately in the &BUILDERS; variable. The &consenv; will - bind the union of the &Builder; objects listed in both variables. - - - - - -
- -
- - - -
- Dependencies - -
- Automatic dependencies - - - - By default, &SCons; assumes that a target file has automatic - dependencies on the: - - - -
- - - tool used to build the target file - - contents of the input files - - command line used to build the target file - - -
- - - - If any of these changes, the target file will be rebuilt. - - -
- -
- Implicit dependencies - - - - Additionally, &SCons; can scan the contents of files for - implicit dependencies on other files. For - example, &SCons; will scan the contents of a .c - file and determine that any object created from it is - dependent on any .h files specified via - #include. &SCons;, therefore, "does the right - thing" without needing to have these dependencies listed explicitly: - - - - - % cat Construct - env = Environment() - env.Program('hello', 'hello.c') - % cat hello.c - #include "hello_string.h" - main() - { - printf("%s\n", STRING); - } - % cat > hello_string.h - #define STRING "Hello, world!\n" - % scons . - gcc -c hello.c -o hello.o - gcc -o hello hello.c - % ./hello - Hello, world! - % cat > hello_string.h - #define STRING "Hello, world, hello!\n" - % scons . - gcc -c hello.c -o hello.o - gcc -o hello hello.c - % ./hello - Hello, world, hello! - % - - -
- -
- Ignoring dependencies - - - - Undesirable automatic dependencies or - implicit dependencies may be ignored: - - - - - env.Program(target = 'bar', source = 'bar.c') - env.Ignore('bar', '/usr/bin/gcc', 'version.h') - - - - - In the above example, the bar program will not - be rebuilt if the /usr/bin/gcc compiler or the - version.h file change. - - -
- -
- Explicit dependencies - - - - Dependencies that are unknown to &SCons; may be specified explicitly - in an &SCons; configuration file: - - - - - env.Dependency(target = 'output1', dependency = 'input_1 input_2') - env.Dependency(target = 'output2', dependency = ['input_1', 'input_2']) - env.Dependency(target = 'output3', dependency = ['white space input']) - - env.Dependency(target = 'output_a output_b', dependency = 'input_3') - env.Dependency(target = ['output_c', 'output_d'], dependency = 'input_4') - env.Dependency(target = ['white space output'], dependency = 'input_5') - - - - - Just like the target keyword argument, the - dependency keyword argument may be specified as a - string of white-space separated file names, or as an array. - - - - - - A dependency on an &SCons; configuration file itself may be specified - explicitly to force a rebuild whenever the configuration file changes: - - - - - env.Dependency(target = 'archive.tar.gz', dependency = 'SConstruct') - - -
- -
- - - -
- &Scanner; Objects - - - - Analagous to the previously-described &Builder; objects, &SCons; - supplies (and uses) &Scanner; objects to search the contents of - a file for implicit dependency files: - - - - - - - - - CScan - scan .{c,C,cc,cxx,cpp} files for #include dependencies - - - - - - - - - A &consenv; can be explicitly initialized with - associated &Scanner; objects: - - - - - env = Environment(SCANNERS = ['CScan', 'M4Scan']) - - - - - &Scanner; objects bound to a &consenv; can be - associated directly with specified files: - - - - - env.CScan('foo.c', 'bar.c') - env.M4Scan('input.m4') - - -
- User-defined &Scanner; objects - - - - A user may define a &Scanner; object to scan a type of file for - implicit dependencies: - - - - - def scanner1(file_contents): - # search for dependencies - return dependency_list - - FirstScan = Scanner(function = scanner1) - - - - - The scanner function must return a list of dependencies that its finds - based on analyzing the file contents it is passed as an argument. - - - - - - The scanner function, when invoked, will be passed the calling - environment. The scanner function can use &consenvs; from the passed - environment to affect how it performs its dependency scan--the - canonical example being to use some sort of search-path construction - variable to look for dependency files in other directories: - - - - - def scanner2(file_contents, env): - path = env.{'SCANNERPATH'} # XXX - # search for dependencies using 'path' - return dependency_list - - SecondScan = Scanner(function = scanner2) - - - - - The user may specify an additional argument when the &Scanner; object - is created. When the scanner is invoked, the additional argument - will be passed to the scanner funciton, which can be used in any way - the scanner function sees fit: - - - - - def scanner3(file_contents, env, arg): - # skip 'arg' lines, then search for dependencies - return dependency_list - - Skip_3_Lines_Scan = Scanner(function = scanner2, argument = 3) - Skip_6_Lines_Scan = Scanner(function = scanner2, argument = 6) - - -
- -
- Copying &Scanner; Objects - - - - A method exists to return a copy of an existing &Scanner; object, - with any overridden values specified as keyword arguments to the - method: - - - - - scan = Scanner(function = my_scan) - scan_path = scan.Copy(path = '%SCANNERPATH') - - - - - Typically, &Scanner; objects will be supplied by a tool-master or - administrator through a shared &consenv;. - - -
- -
- &Scanner; maps - - -If the &BUILDERMAP; proves unnecessary, -we could/should get rid of this one, too, -by adding a parallel src_suffix -argument to the &Scanner; factory... -Comments? - - - - - Each &consenv; has a &SCANNERMAP;, a dictionary that associates - different file suffixes with a scanner object that can be used to - generate a list of dependencies from the contents of that file. This - &SCANNERMAP; can be initialized at instantiation: - - - - - env = Environment(SCANNERMAP = { - '.c' : CScan, - '.cc' : CScan, - '.m4' : M4Scan, - }) - - - - - &Scanner; objects referenced in the &SCANNERMAP; do not need to - be listed separately in the &SCANNERS; variable. The &consenv; - will bind the union of the &Scanner; objects listed - in both variables. - - - -
- -
- - - -
- Targets - - - - The methods in the build engine API described so far merely - establish associations that describe file dependencies, how a - file should be scanned, etc. Since the real point is to actually - build files, &SCons; also has methods that - actually direct the build engine to build, or otherwise manipulate, - target files. - - - -
- Building targets - - - One or more targets may be built as follows: - - - - - env.Build(target = ['foo', 'bar']) - - - - - Note that specifying a directory (or other collective object) will - cause all subsidiary/dependent objects to be built as well: - - - - - env.Build(target = '.') - - env.Build(target = 'builddir') - - - - - By default, &SCons; explicitly removes a target file before - invoking the underlying function or command(s) to build it. - - -
- -
- Removing targets - - - - A "cleanup" operation of removing generated (target) files is - performed as follows: - - - - - env.Clean(target = ['foo', 'bar']) - - - - - Like the &Build; method, the &Clean; method may be passed a - directory or other collective object, in which case the subsidiary - target objects under the directory will be removed: - - - - - env.Clean(target = '.') - - env.Clean(target = 'builddir') - - - - - (The directories themselves are not removed.) - - -
- -
- Suppressing cleanup removal of build-targets - - - - By default, &SCons; explicitly removes all build-targets - when invoked to perform "cleanup". Files that should not be - removed during "cleanup" can be specified via the - &NoClean; method: - - - - - env.Library(target = 'libfoo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) - env.NoClean('libfoo.a') - - - - - The NoClean operation has precedence over the Clean operation. - A target that is specified as both Clean and NoClean, will not - be removed during a clean. - - In the following example, target 'foo' will not be removed - during "cleanup": - - - env.Clean(target = 'foo') - env.NoClean('foo') - - - - - -
- -
- Suppressing build-target removal - - - - As mentioned, by default, &SCons; explicitly removes a target - file before invoking the underlying function or command(s) to build - it. Files that should not be removed before rebuilding can be - specified via the &Precious; method: - - - - - env.Library(target = 'libfoo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) - env.Precious('libfoo.a') - - -
- -
- Default targets - - - - The user may specify default targets that will be built if there are no - targets supplied on the command line: - - - - - env.Default('install', 'src') - - - - - Multiple calls to the &Default; method (typically one per &SConscript; - file) append their arguments to the list of default targets. - - -
- -
- File installation - - - - Files may be installed in a destination directory: - - - - - env.Install('/usr/bin', 'program1', 'program2') - - - - - Files may be renamed on installation: - - - - - env.InstallAs('/usr/bin/xyzzy', 'xyzzy.in') - - - - - Multiple files may be renamed on installation by specifying - equal-length lists of target and source files: - - - - - env.InstallAs(['/usr/bin/foo', '/usr/bin/bar'], - ['foo.in', 'bar.in']) - - -
- -
- Target aliases - - - - In order to provide convenient "shortcut" target names that expand to - a specified list of targets, aliases may be established: - - - - - env.Alias(alias = 'install', - targets = ['/sbin', '/usr/lib', '/usr/share/man']) - - - - - In this example, specifying a target of install - will cause all the files in the associated directories to be built - (that is, installed). - - - - - - An &Alias; may include one or more other &Aliases; in its list: - - - - - env.Alias(alias = 'libraries', targets = ['lib']) - env.Alias(alias = 'programs', targets = ['libraries', 'src']) - - -
- -
- - - -
- Customizing output - - -Take this whole section with a grain of salt. -I whipped it up without a great deal of thought -to try to add a "competitive advantage" -for the second round of the Software Carpentry contest. -In particular, hard-coding the -analysis points and the keywords that specify them -feels inflexible, -but I can't think of another way it would be -done effectively. -I dunno, maybe this is fine as it is... - - - - - The &SCons; API supports the ability to customize, redirect, or - suppress its printed output through user-defined functions. - &SCons; has several pre-defined points in its build process at - which it calls a function to (potentially) print output. User-defined - functions can be specified for these call-back points when &Build; - or &Clean;is invoked: - - - - - env.Build(target = '.', - on_analysis = dump_dependency, - pre_update = my_print_command, - post_update = my_error_handler) - on_error = my_error_handler) - - - - - The specific call-back points are: - - - - - - - on_analysis - - - - Called for every object, immediately after the object has been - analyzed to see if it's out-of-date. Typically used to print a - trace of considered objects for debugging of unexpected dependencies. - - - - - - - pre_update - - - - Called for every object that has been determined to be out-of-date - before its update function or command is executed. Typically used - to print the command being called to update a target. - - - - - - - post_update - - - - Called for every object after its update function or command has - been executed. Typically used to report that a top-level specified - target is up-to-date or was not remade. - - - - - - - on_error - - - - Called for every error returned by an update function or command. - Typically used to report errors with some string that will be - identifiable to build-analysis tools. - - - - - - - - - - Functions for each of these call-back points all take the same - arguments: - - - - - my_dump_dependency(target, level, status, update, dependencies) - - - - - where the arguments are: - - - - - - - target - - - - The target object being considered. - - - - - - - level - - - - Specifies how many levels the dependency analysis has - recursed in order to consider the target. - A value of 0 specifies a top-level - target (that is, one passed to the - &Build; or &Clean; method). Objects which a top-level - target is directly dependent upon have a - level of <1>, their direct dependencies have a - level of <2>, etc. Typically used to indent - output to reflect the recursive levels. - - - - - - - status - - - - A string specifying the current status of the target - ("unknown", "built", - "error", "analyzed", etc.). A - complete list will be enumerated and described during implementation. - - - - - - - update - - - - The command line or function name that will be (or has been) executed - to update the target. - - - - - - - dependencies - - - - A list of direct dependencies of the target. - - - - - - - -
- - - -
- Separate source and build trees - - -I've never liked Cons' use of the name Link -for this functionality, -mainly because the term is overloaded -with linking object files into an executable. -Yet I've never come up with anything better. -Any suggestions? - - - -Also, I made this an &Environment; method because -it logically belongs in the API reference -(the build engine needs to know about it), -and I thought it was clean to have -everything in the build-engine API -be called through an &Environment; object. -But &Link isn't really -associated with a specific environment -(the &Cons; classic implementation just -leaves it as a bare function call), -so maybe we should just follow that example -and not call it through an environment... - - - - - &SCons; allows target files to be built completely separately from - the source files by "linking" a build directory to an underlying - source directory: - - - - - env.Link('build', 'src') - - SConscript('build/SConscript') - - - - - &SCons; will copy (or hard link) necessary files (including the - &SConscript; file) into the build directory hierarchy. This allows the - source directory to remain uncluttered by derived files. - - - -
- - - -
- Variant builds - - - - The &Link; method may be used in conjunction with multiple - &consenvs; to support variant builds. The following - &SConstruct; and &SConscript; files would build separate debug and - production versions of the same program side-by-side: - - - - - % cat SConstruct - env = Environment() - env.Link('build/debug', 'src') - env.Link('build/production', 'src') - flags = '-g' - SConscript('build/debug/SConscript', Export(env)) - flags = '-O' - SConscript('build/production/SConscript', Export(env)) - % cat src/SConscript - env = Environment(CCFLAGS = flags) - env.Program('hello', 'hello.c') - - - - - The following example would build the appropriate program for the current - compilation platform, without having to clean any directories of object - or executable files for other architectures: - - - - - % cat SConstruct - build_platform = os.path.join('build', sys.platform) - Link(build_platform, 'src') - SConscript(os.path.join(build_platform, 'SConscript')) - % cat src/SConscript - env = Environment - env.Program('hello', 'hello.c') - - -
- - - -
- Code repositories - - -Like &Link;, &Repository; and &Local; are part of the -API reference, but not really tied to any specific environment. -Is it better to be consistent about calling -everything in the API through an environment, -or to leave these independent so as -not to complicate their calling interface? - - - - - &SCons; may use files from one or more shared code repositories in order - to build local copies of changed target files. A repository would - typically be a central directory tree, maintained by an integrator, - with known good libraries and executables. - - - - - Repository('/home/source/1.1', '/home/source/1.0') - - - - - Specified repositories will be searched in-order for any file - (configuration file, input file, target file) that does not exist - in the local directory tree. When building a local target file, - &SCons; will rewrite path names in the build command to use the - necessary repository files. This includes modifying lists of - or flags to specify an - appropriate set of include paths for dependency analysis. - - - - - &SCons; will modify the Python sys.path variable to - reflect the addition of repositories to the search path, so that any - imported modules or packages necessary for the build can be found in a - repository, as well. - - - - - If an up-to-date target file is found in a code repository, the file - will not be rebuilt or copied locally. Files that must exist locally - (for example, to run tests) may be specified: - - - - - Local('program', 'libfoo.a') - - - - - in which case &SCons; will copy or link an up-to-date copy of the - file from the appropriate repository. - - - -
- - - -
- Derived-file caching - - -There should be extensions to this part of the API for -auxiliary functions like cleaning the cache. - - - - - &SCons; can maintain a cache directory of target files which may be - shared among multiple builds. This reduces build times by allowing - developers working on a project together to share common target - files: - - - - - Cache('/var/tmp/build.cache/i386') - - - - - When a target file is generated, a copy is added to the cache. - When generating a target file, if &SCons; determines that a file - that has been built with the exact same dependencies already exists - in the specified cache, &SCons; will copy the cached file rather - than re-building the target. - - - - - Command-line options exist to modify the &SCons; caching behavior - for a specific build, including disabling caching, building - dependencies in random order, and displaying commands as if cached - files were built. - - - -
- - - -
- Job management - - -This has been completely superseded by -the more sophisticated &Task; manager -that Anthony Roach has contributed. -I need to write that up... - - - - - A simple API exists to inform the Build Engine how many jobs may - be run simultaneously: - - - - - Jobs(limit = 4) - - -
diff --git a/doc/design/engine.xml b/doc/design/engine.xml new file mode 100644 index 0000000..1a1e335 --- /dev/null +++ b/doc/design/engine.xml @@ -0,0 +1,1964 @@ + + +
+ General Principles + +
+ Keyword arguments + + + + All methods and functions in this API will support the use of keyword + arguments in calls, for the sake of explicitness and readability. + For brevity in the hands of experts, most methods and functions + will also support positional arguments for their most-commonly-used + arguments. As an explicit example, the following two lines will each + arrange for an executable program named foo (or + foo.exe on a Win32 system) to be compiled from + the foo.c source file: + + + + + env.Program(target = 'foo', source = 'foo.c') + + env.Program('foo', 'foo.c') + + +
+ +
+ Internal object representation + + + + All methods and functions use internal (Python) objects that + represent the external objects (files, for example) for which they + perform dependency analysis. + + + + + + All methods and functions in this API that accept an external object + as an argument will accept either a string + description or an object reference. For example, the two following + two-line examples are equivalent: + + + + + env.Object(target = 'foo.o', source = 'foo.c') + env.Program(target = 'foo', 'foo.o') # builds foo from foo.o + + foo_obj = env.Object(target = 'foo.o', source = 'foo.c') + env.Program(target = 'foo', foo_obj) # builds foo from foo.o + + +
+ +
+ + + +
+ &ConsEnvs + + + + A &consenv; is the basic means by which a software system interacts + with the &SCons; Python API to control a build process. + + + + + + A &consenv; is an object with associated methods for generating target + files of various types (&Builder; objects), other associated object + methods for automatically determining dependencies from the contents + of various types of source files (&Scanner; objects), and a dictionary + of values used by these methods. + + + + + + Passing no arguments to the &Environment; instantiation creates a + &consenv; with default values for the current platform: + + + + + env = Environment() + + +
+ &Consvars; + + + + A &consenv; has an associated dictionary of &consvars; that control how + the build is performed. By default, the &Environment; method creates + a &consenv; with values that make most software build "out of the box" + on the host system. These default values will be generated at the + time &SCons; is installed using functionality similar to that provided + by GNU &Autoconf;. + + + It would be nice if we could avoid re-inventing the wheel here by + using some other Python-based tool &Autoconf replacement--like what + was supposed to come out of the Software Carpentry configuration + tool contest. It will probably be most efficient to roll our own + logic initially and convert if something better does come along. + + + At a minimum, there will be pre-configured sets of default values + that will provide reasonable defaults for UNIX and Windows NT. + + + + + + The default &consenv; values may be overridden when a new &consenv; is + created by specifying keyword arguments: + + + + + env = Environment(CC = 'gcc', + CCFLAGS = '-g', + CPPPATH = ['.', 'src', '/usr/include'], + LIBPATH = ['/usr/lib', '.']) + + +
+ +
+ Fetching &consvars; + + + + A copy of the dictionary of &consvars; can be returned using + the &Dictionary; method: + + + + + env = Environment() + dict = env.Dictionary() + + + + + If any arguments are supplied, then just the corresponding value(s) + are returned: + + + + + ccflags = env.Dictionary('CCFLAGS') + cc, ld = env.Dictionary('CC', 'LD') + + +
+ +
+ Copying a &consenv; + + + + A method exists to return a copy of an existing environment, with + any overridden values specified as keyword arguments to the method: + + + + + env = Environment() + debug = env.Copy(CCFLAGS = '-g') + + +
+ +
+ Multiple &consenvs; + + + + Different external objects often require different build + characteristics. Multiple &consenvs; may be defined, each with + different values: + + + + + env = Environment(CCFLAGS = '') + debug = Environment(CCFLAGS = '-g') + env.Make(target = 'hello', source = 'hello.c') + debug.Make(target = 'hello-debug', source = 'hello.c') + + + + + Dictionaries of values from multiple &consenvs; may be passed to the + &Environment; instantiation or the &Copy; method, in which case the + last-specified dictionary value wins: + + + + + env1 = Environment(CCFLAGS = '-O', LDFLAGS = '-d') + env2 = Environment(CCFLAGS = '-g') + new = Environment(env1.Dictionary(), env2.Dictionary()) + + + + + The new environment in the above example retains + LDFLAGS = '-d' from the env1 + environment, and CCFLAGS = '-g' from the + env2 environment. + + + + + +
+ +
+ Variable substitution + + + + Within a construction command, any variable from the &consenv; may + be interpolated by prefixing the name of the construction with + $: + + + + + MyBuilder = Builder(command = "$XX $XXFLAGS -c $_INPUTS -o $target") + + env.Command(targets = 'bar.out', sources = 'bar.in', + command = "sed '1d' < $source > $target") + + + + + Variable substitution is recursive: the command line is expanded + until no more substitutions can be made. + + + + + + Variable names following the $ may be enclosed in + braces. This can be used to concatenate an interpolated value with an + alphanumeric character: + + + + + VerboseBuilder = Builder(command = "$XX -${XXFLAGS}v > $target") + + + + + The variable within braces may contain a pair of parentheses + after a Python function name to be evaluated (for example, + ${map()}). &SCons; will interpolate the return + value from the function (presumably a string): + + + + + env = Environment(FUNC = myfunc) + env.Command(target = 'foo.out', source = 'foo.in', + command = "${FUNC($<)}") + + + + + If a referenced variable is not defined in the &consenv;, + the null string is interpolated. + + + + + + The following special variables can also be used: + + + + + + + $targets + + + + All target file names. If multiple targets are specified in an + array, $targets expands to the entire list of + targets, separated by a single space. + + + + + + Individual targets from a list may be extracted by enclosing + the targets keyword in braces and using the + appropriate Python array index or slice: + + + + + ${targets[0]} # expands to the first target + + ${targets[1:]} # expands to all but the first target + + ${targets[1:-1]} # expands to all but the first and last targets + + + + + + + $target + + + + A synonym for ${targets[0]}, the first target + specified. + + + + + + + $sources + + + + All input file names. Any input file names that + are used anywhere else on the current command + line (via ${sources[0]}, + ${sources{[1]}, etc.) are removed from the + expanded list. + + + + + + + + + + Any of the above special variables may be enclosed in braces and + followed immediately by one of the following attributes to select just + a portion of the expanded path name: + + + + + + + .base + + + + Basename: the directory plus the file name, minus any file suffix. + + + + + + + .dir + + + + The directory in which the file lives. This is a relative path, + where appropriate. + + + + + + + .file + + + + The file name, minus any directory portion. + + + + + + + .suffix + + + + The file name suffix (that is, the right-most dot in the file name, + and all characters to the right of that). + + + + + + + .filebase + + + + The file name (no directory portion), minus any file suffix. + + + + + + + .abspath + + + + The absolute path to the file. + + + + + + + +
+ +
+ + + +
+ &Builder; Objects + + + + By default, &SCons; supplies (and uses) a number of pre-defined + &Builder; objects: + + + + + + + + + &Object; + compile or assemble an object file + + + + &Library; + archive files into a library + + + + &SharedLibrary; + archive files into a shared library + + + + &Program; + link objects and/or libraries into an executable + + + + &MakeBuilder; + build according to file suffixes; see below + + + + + + + + + + + A &consenv; can be explicitly initialized with associated &Builder; + objects that will be bound to the &consenv; object: + + + + + env = Environment(BUILDERS = ['Object', 'Program']) + + + + + &Builder; objects bound to a &consenv; can be called directly as + methods. When invoked, a &Builder; object returns a (list of) objects + that it will build: + + + + + obj = env.Object(target ='hello.o', source = 'hello.c') + lib = env.Library(target ='libfoo.a', + source = ['aaa.c', 'bbb.c']) + slib = env.SharedLibrary(target ='libbar.so', + source = ['xxx.c', 'yyy.c']) + prog = env.Program(target ='hello', + source = ['hello.o', 'libfoo.a', 'libbar.so']) + + +
+ Specifying multiple inputs + + + + Multiple input files that go into creating a target file may be passed + in as a single string, with the individual file names separated by + white space: + + + + + env.Library(target = 'foo.a', source = 'aaa.c bbb.c ccc.c') + env.Object(target = 'yyy.o', source = 'yyy.c') + env.Program(target = 'bar', source = 'xxx.c yyy.o foo.a') + + + + + Alternatively, multiple input files that go into creating a target + file may be passed in as an array. This allows input files to be + specified using their object representation: + + + + + env.Library(target = 'foo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) + yyy_obj = env.Object(target = 'yyy.o', source = 'yyy.c') + env.Program(target = 'bar', source = ['xxx.c', yyy_obj, 'foo.a']) + + + + + Individual string elements within an array of input files are + not further split into white-space separated + file names. This allows file names that contain white space to + be specified by putting the value into an array: + + + env.Program(target = 'foo', source = ['an input file.c']) + + + + +
+ +
+ Specifying multiple targets + + + + Conversely, the generated target may be a string listing multiple + files separated by white space: + + + + + env.Object(target = 'grammar.o y.tab.h', source = 'grammar.y') + + + + + An array of multiple target files can be used to mix string and object + representations, or to accomodate file names that contain white space: + + + + + env.Program(target = ['my program'], source = 'input.c') + + +
+ +
+ File prefixes and suffixes + + + + For portability, if the target file name does not already have an + appropriate file prefix or suffix, the &Builder; objects will + append one appropriate for the file type on the current system: + + + + + # builds 'hello.o' on UNIX, 'hello.obj' on Windows NT: + obj = env.Object(target ='hello', source = 'hello.c') + + # builds 'libfoo.a' on UNIX, 'foo.lib' on Windows NT: + lib = env.Library(target ='foo', source = ['aaa.c', 'bbb.c']) + + # builds 'libbar.so' on UNIX, 'bar.dll' on Windows NT: + slib = env.SharedLibrary(target ='bar', source = ['xxx.c', 'yyy.c']) + + # builds 'hello' on UNIX, 'hello.exe' on Windows NT: + prog = env.Program(target ='hello', + source = ['hello.o', 'libfoo.a', 'libbar.so']) + + +
+ +
+ &Builder; object exceptions + + + + &Builder; objects raise the following exceptions on error: + + + + +
+ +
+ User-defined &Builder; objects + + + + Users can define additional &Builder; objects for specific external + object types unknown to &SCons;. A &Builder; object may build its + target by executing an external command: + + + + + WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target', + suffix = '.html', + src_suffix = '.in') + + + + + Alternatively, a &Builder; object may also build its target by + executing a Python function: + + + + + def update(dest): + # [code to update the object] + return 1 + + OtherBuilder1 = Builder(function = update, + src_suffix = ['.in', '.input']) + + + + + An optional argument to pass to the function may be specified: + + + + + def update_arg(dest, arg): + # [code to update the object] + return 1 + + OtherBuilder2 = Builder(function = update_arg, + function_arg = 'xyzzy', + src_suffix = ['.in', '.input']) + + + + + Both an external command and an internal function may be specified, + in which case the function will be called to build the object first, + followed by the command line. + + + + + + + + User-defined &Builder; objects can be used like the default &Builder; + objects to initialize &consenvs;. + + + + + WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target', + suffix = '.html', + src_suffix = '.in') + env = Environment(BUILDERS = ['WebPage']) + env.WebPage(target = 'foo.html', source = 'foo.in') + # Builds 'bar.html' on UNIX, 'bar.htm' on Windows NT: + env.WebPage(target = 'bar', source = 'bar.in') + + + + + The command-line specification can interpolate variables from the + &consenv;; see "Variable substitution," above. + + + + + + A &Builder; object may optionally be initialized with a list of: + + + + + + + + the prefix of the target file (e.g., 'lib' for libraries) + + + + + + + + the suffix of the target file (e.g., '.a' for libraries) + + + + + + + + the expected suffixes of the input files + (e.g., '.o' for object files) + + + + + + + + These arguments are used in automatic + dependency analysis and to generate output file names that don't + have suffixes supplied explicitly. + + +
+ +
+ Copying &Builder; Objects + + + + A &Copy; method exists to return a copy of an existing &Builder; + object, with any overridden values specified as keyword arguments to + the method: + + + + + build = Builder(function = my_build) + build_out = build.Copy(suffix = '.out') + + + + + Typically, &Builder; objects will be supplied by a tool-master or + administrator through a shared &consenv;. + + +
+ +
+ Special-purpose build rules + + + + A pre-defined &Command; builder exists to associate a target file with + a specific command or list of commands for building the file: + + + + + env.Command(target = 'foo.out', source = + command = 'foo.in', "foo.process $sources > $target") + + commands = [ "bar.process -o .tmpfile $sources", + "mv .tmpfile $target" ] + env.Command(target = 'bar.out', source = 'bar.in', command = commands) + + + + This is useful when it's too cumbersome to create a &Builder; + object just to build a single file in a special way. + + +
+ +
+ The &MakeBuilder; &Builder; + + + + A pre-defined &Builder; object named &MakeBuilder; exists to make + simple builds as easy as possible for users, at the expense of + sacrificing some build portability. + + + + + + The following minimal example builds the 'hello' program from the + 'hello.c' source file: + + + + + Environment().Make('hello', 'hello.c') + + + + + Users of the &MakeBuilder; &Builder; object are not required to + understand intermediate steps involved in generating a file--for + example, the distinction between compiling source code into an object + file, and then linking object files into an executable. The details + of intermediate steps are handled by the invoked method. Users that + need to, however, can specify intermediate steps explicitly: + + + + + env = Environment() + env.Make(target = 'hello.o', source = 'hello.c') + env.Make(target = 'hello', source = 'hello.o') + + + + + The &MakeBuilder; method understands the file suffixes specified and + "does the right thing" to generate the target object and program + files, respectively. It does this by examining the specified output + suffixes for the &Builder; objects bound to the environment. + + + + + + Because file name suffixes in the target and source file names + must be specified, the &MakeBuilder; method can't be used + portably across operating systems. In other words, for the + example above, the &MakeBuilder; builder will not generate + hello.exe on Windows NT. + + + +
+ +
+ &Builder; maps + + + + + + The env.Make method "does the right thing" to + build different file types because it uses a dictionary from the + &consenv; that maps file suffixes to the appropriate &Builder; object. + This &BUILDERMAP; can be initialized at instantiation: + + + + + env = Environment(BUILDERMAP = { + '.o' : Object, + '.a' : Library, + '.html' : WebPage, + '' : Program, + }) + + + + + With the &BUILDERMAP; properly initialized, the + env.Make method can be used to build additional + file types: + + + + + env.Make(target = 'index.html', source = 'index.input') + + + + + &Builder; objects referenced in the &BUILDERMAP; do not need to be + listed separately in the &BUILDERS; variable. The &consenv; will + bind the union of the &Builder; objects listed in both variables. + + + + + +
+ +
+ + + +
+ Dependencies + +
+ Automatic dependencies + + + + By default, &SCons; assumes that a target file has automatic + dependencies on the: + + + +
+ + + tool used to build the target file + + contents of the input files + + command line used to build the target file + + +
+ + + + If any of these changes, the target file will be rebuilt. + + +
+ +
+ Implicit dependencies + + + + Additionally, &SCons; can scan the contents of files for + implicit dependencies on other files. For + example, &SCons; will scan the contents of a .c + file and determine that any object created from it is + dependent on any .h files specified via + #include. &SCons;, therefore, "does the right + thing" without needing to have these dependencies listed explicitly: + + + + + % cat Construct + env = Environment() + env.Program('hello', 'hello.c') + % cat hello.c + #include "hello_string.h" + main() + { + printf("%s\n", STRING); + } + % cat > hello_string.h + #define STRING "Hello, world!\n" + % scons . + gcc -c hello.c -o hello.o + gcc -o hello hello.c + % ./hello + Hello, world! + % cat > hello_string.h + #define STRING "Hello, world, hello!\n" + % scons . + gcc -c hello.c -o hello.o + gcc -o hello hello.c + % ./hello + Hello, world, hello! + % + + +
+ +
+ Ignoring dependencies + + + + Undesirable automatic dependencies or + implicit dependencies may be ignored: + + + + + env.Program(target = 'bar', source = 'bar.c') + env.Ignore('bar', '/usr/bin/gcc', 'version.h') + + + + + In the above example, the bar program will not + be rebuilt if the /usr/bin/gcc compiler or the + version.h file change. + + +
+ +
+ Explicit dependencies + + + + Dependencies that are unknown to &SCons; may be specified explicitly + in an &SCons; configuration file: + + + + + env.Dependency(target = 'output1', dependency = 'input_1 input_2') + env.Dependency(target = 'output2', dependency = ['input_1', 'input_2']) + env.Dependency(target = 'output3', dependency = ['white space input']) + + env.Dependency(target = 'output_a output_b', dependency = 'input_3') + env.Dependency(target = ['output_c', 'output_d'], dependency = 'input_4') + env.Dependency(target = ['white space output'], dependency = 'input_5') + + + + + Just like the target keyword argument, the + dependency keyword argument may be specified as a + string of white-space separated file names, or as an array. + + + + + + A dependency on an &SCons; configuration file itself may be specified + explicitly to force a rebuild whenever the configuration file changes: + + + + + env.Dependency(target = 'archive.tar.gz', dependency = 'SConstruct') + + +
+ +
+ + + +
+ &Scanner; Objects + + + + Analagous to the previously-described &Builder; objects, &SCons; + supplies (and uses) &Scanner; objects to search the contents of + a file for implicit dependency files: + + + + + + + + + CScan + scan .{c,C,cc,cxx,cpp} files for #include dependencies + + + + + + + + + A &consenv; can be explicitly initialized with + associated &Scanner; objects: + + + + + env = Environment(SCANNERS = ['CScan', 'M4Scan']) + + + + + &Scanner; objects bound to a &consenv; can be + associated directly with specified files: + + + + + env.CScan('foo.c', 'bar.c') + env.M4Scan('input.m4') + + +
+ User-defined &Scanner; objects + + + + A user may define a &Scanner; object to scan a type of file for + implicit dependencies: + + + + + def scanner1(file_contents): + # search for dependencies + return dependency_list + + FirstScan = Scanner(function = scanner1) + + + + + The scanner function must return a list of dependencies that its finds + based on analyzing the file contents it is passed as an argument. + + + + + + The scanner function, when invoked, will be passed the calling + environment. The scanner function can use &consenvs; from the passed + environment to affect how it performs its dependency scan--the + canonical example being to use some sort of search-path construction + variable to look for dependency files in other directories: + + + + + def scanner2(file_contents, env): + path = env.{'SCANNERPATH'} # XXX + # search for dependencies using 'path' + return dependency_list + + SecondScan = Scanner(function = scanner2) + + + + + The user may specify an additional argument when the &Scanner; object + is created. When the scanner is invoked, the additional argument + will be passed to the scanner funciton, which can be used in any way + the scanner function sees fit: + + + + + def scanner3(file_contents, env, arg): + # skip 'arg' lines, then search for dependencies + return dependency_list + + Skip_3_Lines_Scan = Scanner(function = scanner2, argument = 3) + Skip_6_Lines_Scan = Scanner(function = scanner2, argument = 6) + + +
+ +
+ Copying &Scanner; Objects + + + + A method exists to return a copy of an existing &Scanner; object, + with any overridden values specified as keyword arguments to the + method: + + + + + scan = Scanner(function = my_scan) + scan_path = scan.Copy(path = '%SCANNERPATH') + + + + + Typically, &Scanner; objects will be supplied by a tool-master or + administrator through a shared &consenv;. + + +
+ +
+ &Scanner; maps + + + + + + Each &consenv; has a &SCANNERMAP;, a dictionary that associates + different file suffixes with a scanner object that can be used to + generate a list of dependencies from the contents of that file. This + &SCANNERMAP; can be initialized at instantiation: + + + + + env = Environment(SCANNERMAP = { + '.c' : CScan, + '.cc' : CScan, + '.m4' : M4Scan, + }) + + + + + &Scanner; objects referenced in the &SCANNERMAP; do not need to + be listed separately in the &SCANNERS; variable. The &consenv; + will bind the union of the &Scanner; objects listed + in both variables. + + + +
+ +
+ + + +
+ Targets + + + + The methods in the build engine API described so far merely + establish associations that describe file dependencies, how a + file should be scanned, etc. Since the real point is to actually + build files, &SCons; also has methods that + actually direct the build engine to build, or otherwise manipulate, + target files. + + + +
+ Building targets + + + One or more targets may be built as follows: + + + + + env.Build(target = ['foo', 'bar']) + + + + + Note that specifying a directory (or other collective object) will + cause all subsidiary/dependent objects to be built as well: + + + + + env.Build(target = '.') + + env.Build(target = 'builddir') + + + + + By default, &SCons; explicitly removes a target file before + invoking the underlying function or command(s) to build it. + + +
+ +
+ Removing targets + + + + A "cleanup" operation of removing generated (target) files is + performed as follows: + + + + + env.Clean(target = ['foo', 'bar']) + + + + + Like the &Build; method, the &Clean; method may be passed a + directory or other collective object, in which case the subsidiary + target objects under the directory will be removed: + + + + + env.Clean(target = '.') + + env.Clean(target = 'builddir') + + + + + (The directories themselves are not removed.) + + +
+ +
+ Suppressing cleanup removal of build-targets + + + + By default, &SCons; explicitly removes all build-targets + when invoked to perform "cleanup". Files that should not be + removed during "cleanup" can be specified via the + &NoClean; method: + + + + + env.Library(target = 'libfoo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) + env.NoClean('libfoo.a') + + + + + The NoClean operation has precedence over the Clean operation. + A target that is specified as both Clean and NoClean, will not + be removed during a clean. + + In the following example, target 'foo' will not be removed + during "cleanup": + + + env.Clean(target = 'foo') + env.NoClean('foo') + + + + + +
+ +
+ Suppressing build-target removal + + + + As mentioned, by default, &SCons; explicitly removes a target + file before invoking the underlying function or command(s) to build + it. Files that should not be removed before rebuilding can be + specified via the &Precious; method: + + + + + env.Library(target = 'libfoo.a', source = ['aaa.c', 'bbb.c', 'ccc.c']) + env.Precious('libfoo.a') + + +
+ +
+ Default targets + + + + The user may specify default targets that will be built if there are no + targets supplied on the command line: + + + + + env.Default('install', 'src') + + + + + Multiple calls to the &Default; method (typically one per &SConscript; + file) append their arguments to the list of default targets. + + +
+ +
+ File installation + + + + Files may be installed in a destination directory: + + + + + env.Install('/usr/bin', 'program1', 'program2') + + + + + Files may be renamed on installation: + + + + + env.InstallAs('/usr/bin/xyzzy', 'xyzzy.in') + + + + + Multiple files may be renamed on installation by specifying + equal-length lists of target and source files: + + + + + env.InstallAs(['/usr/bin/foo', '/usr/bin/bar'], + ['foo.in', 'bar.in']) + + +
+ +
+ Target aliases + + + + In order to provide convenient "shortcut" target names that expand to + a specified list of targets, aliases may be established: + + + + + env.Alias(alias = 'install', + targets = ['/sbin', '/usr/lib', '/usr/share/man']) + + + + + In this example, specifying a target of install + will cause all the files in the associated directories to be built + (that is, installed). + + + + + + An &Alias; may include one or more other &Aliases; in its list: + + + + + env.Alias(alias = 'libraries', targets = ['lib']) + env.Alias(alias = 'programs', targets = ['libraries', 'src']) + + +
+ +
+ + + +
+ Customizing output + + + + + + The &SCons; API supports the ability to customize, redirect, or + suppress its printed output through user-defined functions. + &SCons; has several pre-defined points in its build process at + which it calls a function to (potentially) print output. User-defined + functions can be specified for these call-back points when &Build; + or &Clean;is invoked: + + + + + env.Build(target = '.', + on_analysis = dump_dependency, + pre_update = my_print_command, + post_update = my_error_handler) + on_error = my_error_handler) + + + + + The specific call-back points are: + + + + + + + on_analysis + + + + Called for every object, immediately after the object has been + analyzed to see if it's out-of-date. Typically used to print a + trace of considered objects for debugging of unexpected dependencies. + + + + + + + pre_update + + + + Called for every object that has been determined to be out-of-date + before its update function or command is executed. Typically used + to print the command being called to update a target. + + + + + + + post_update + + + + Called for every object after its update function or command has + been executed. Typically used to report that a top-level specified + target is up-to-date or was not remade. + + + + + + + on_error + + + + Called for every error returned by an update function or command. + Typically used to report errors with some string that will be + identifiable to build-analysis tools. + + + + + + + + + + Functions for each of these call-back points all take the same + arguments: + + + + + my_dump_dependency(target, level, status, update, dependencies) + + + + + where the arguments are: + + + + + + + target + + + + The target object being considered. + + + + + + + level + + + + Specifies how many levels the dependency analysis has + recursed in order to consider the target. + A value of 0 specifies a top-level + target (that is, one passed to the + &Build; or &Clean; method). Objects which a top-level + target is directly dependent upon have a + level of <1>, their direct dependencies have a + level of <2>, etc. Typically used to indent + output to reflect the recursive levels. + + + + + + + status + + + + A string specifying the current status of the target + ("unknown", "built", + "error", "analyzed", etc.). A + complete list will be enumerated and described during implementation. + + + + + + + update + + + + The command line or function name that will be (or has been) executed + to update the target. + + + + + + + dependencies + + + + A list of direct dependencies of the target. + + + + + + + +
+ + + +
+ Separate source and build trees + + + + + + + + &SCons; allows target files to be built completely separately from + the source files by "linking" a build directory to an underlying + source directory: + + + + + env.Link('build', 'src') + + SConscript('build/SConscript') + + + + + &SCons; will copy (or hard link) necessary files (including the + &SConscript; file) into the build directory hierarchy. This allows the + source directory to remain uncluttered by derived files. + + + +
+ + + +
+ Variant builds + + + + The &Link; method may be used in conjunction with multiple + &consenvs; to support variant builds. The following + &SConstruct; and &SConscript; files would build separate debug and + production versions of the same program side-by-side: + + + + + % cat SConstruct + env = Environment() + env.Link('build/debug', 'src') + env.Link('build/production', 'src') + flags = '-g' + SConscript('build/debug/SConscript', Export(env)) + flags = '-O' + SConscript('build/production/SConscript', Export(env)) + % cat src/SConscript + env = Environment(CCFLAGS = flags) + env.Program('hello', 'hello.c') + + + + + The following example would build the appropriate program for the current + compilation platform, without having to clean any directories of object + or executable files for other architectures: + + + + + % cat SConstruct + build_platform = os.path.join('build', sys.platform) + Link(build_platform, 'src') + SConscript(os.path.join(build_platform, 'SConscript')) + % cat src/SConscript + env = Environment + env.Program('hello', 'hello.c') + + +
+ + + +
+ Code repositories + + + + + + &SCons; may use files from one or more shared code repositories in order + to build local copies of changed target files. A repository would + typically be a central directory tree, maintained by an integrator, + with known good libraries and executables. + + + + + Repository('/home/source/1.1', '/home/source/1.0') + + + + + Specified repositories will be searched in-order for any file + (configuration file, input file, target file) that does not exist + in the local directory tree. When building a local target file, + &SCons; will rewrite path names in the build command to use the + necessary repository files. This includes modifying lists of + or flags to specify an + appropriate set of include paths for dependency analysis. + + + + + &SCons; will modify the Python sys.path variable to + reflect the addition of repositories to the search path, so that any + imported modules or packages necessary for the build can be found in a + repository, as well. + + + + + If an up-to-date target file is found in a code repository, the file + will not be rebuilt or copied locally. Files that must exist locally + (for example, to run tests) may be specified: + + + + + Local('program', 'libfoo.a') + + + + + in which case &SCons; will copy or link an up-to-date copy of the + file from the appropriate repository. + + + +
+ + + +
+ Derived-file caching + + + + + + &SCons; can maintain a cache directory of target files which may be + shared among multiple builds. This reduces build times by allowing + developers working on a project together to share common target + files: + + + + + Cache('/var/tmp/build.cache/i386') + + + + + When a target file is generated, a copy is added to the cache. + When generating a target file, if &SCons; determines that a file + that has been built with the exact same dependencies already exists + in the specified cache, &SCons; will copy the cached file rather + than re-building the target. + + + + + Command-line options exist to modify the &SCons; caching behavior + for a specific build, including disabling caching, building + dependencies in random order, and displaying commands as if cached + files were built. + + + +
+ + + +
+ Job management + + + + + + A simple API exists to inform the Build Engine how many jobs may + be run simultaneously: + + + + + Jobs(limit = 4) + + +
diff --git a/doc/design/goals.sgml b/doc/design/goals.sgml deleted file mode 100644 index 774e04d..0000000 --- a/doc/design/goals.sgml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - As a next-generation build tool, - &SCons should fundamentally - improve on its predecessors. - Rather than simply being driven by trying to - not be like previous tools, - &SCons; aims to satisfy the following goals: - - - - - - - Practicality - - - - The &SCons; design emphasizes - an implementable feature set - that lets users get practical, useful work done. - &SCons; is helped in this regard by its roots in &Cons;, - which has had its feature set honed by - several years of input - from a dedicated band of users. - - - - - - - Portability - - - - &SCons; is intended as a portable build tool, - able to handle software construction tasks - on a variety of operating systems. - It should be possible (although not mandatory) - to use &SCons; so that the same configuration file - builds the same software correctly on, - for example, both Linux and Windows NT. - Consequently, &SCons; should hide from users - operating-system-dependent details - such as filename extensions - (for example, .o - vs. .obj). - - - - - - - - - - - Usability - - - - Novice users should be able to grasp quickly - the rudiments of using &SCons; to build their software. - This extends to installing &SCons;, too. - Installation should be painless, - and the installed &SCons; - should work "out of the box" - to build most software. - - - - - - This goal should be kept in mind during implementation, - when there is always a tendency to try to optimize too early. - Speed is nice, but not as important as clarity - and ease of use. - - - - - - - Utility - - - - &SCons; should also provide a rich enough set of features - to accommodate building more complicated software projects. - However, the features required for - building complicated software projects - should not get in the way of novice users. - (See the previous goal.) - In other words, complexity should be available - when it's needed - but not required to get work done. - Practically, this implies that &SCons; - shouldn't be dumbed down to the point it - excludes complicated software builds. - - - - - - - Sharability - - - - As a key element in balancing the conflicting - needs of Usability and Utility, - &SCons; should provide mechanisms to - allow &SCons; users to share build rules, - dependency scanners, and other objects and recipes - for constructing software. - A good sharing mechanism should support - the model wherein most developers on a project - use rules and templates - that are created - and maintained by a local integrator or build-master, - - - - - - - Extensibility - - - - &SCons; should provide mechanisms for - easily extending its capabilities, - including building new types of files, - adding new types of dependency scanning, - being able to accomodate dependencies - between objects other than files, - etc. - - - - - - - Flexibility - - - - In addition to providing a useful command-line interface, - &SCons; should provide the right architectural - framework for embedding its dependency management - in other interfaces. - &SCons; would help strengthen other GUIs or IDEs - and the additional requirements of the - other interfaces would help broaden and solidify - the core &SCons; dependency management. - - - - - - - -
- Fixing &Make;'s problems - - -To be written. - - -
- -
- Fixing &Cons;'s problems - - -To be written. - - -
diff --git a/doc/design/goals.xml b/doc/design/goals.xml new file mode 100644 index 0000000..2a7b69b --- /dev/null +++ b/doc/design/goals.xml @@ -0,0 +1,216 @@ + + + + + As a next-generation build tool, + &SCons should fundamentally + improve on its predecessors. + Rather than simply being driven by trying to + not be like previous tools, + &SCons; aims to satisfy the following goals: + + + + + + + Practicality + + + + The &SCons; design emphasizes + an implementable feature set + that lets users get practical, useful work done. + &SCons; is helped in this regard by its roots in &Cons;, + which has had its feature set honed by + several years of input + from a dedicated band of users. + + + + + + + Portability + + + + &SCons; is intended as a portable build tool, + able to handle software construction tasks + on a variety of operating systems. + It should be possible (although not mandatory) + to use &SCons; so that the same configuration file + builds the same software correctly on, + for example, both Linux and Windows NT. + Consequently, &SCons; should hide from users + operating-system-dependent details + such as filename extensions + (for example, .o + vs. .obj). + + + + + + + + + + + Usability + + + + Novice users should be able to grasp quickly + the rudiments of using &SCons; to build their software. + This extends to installing &SCons;, too. + Installation should be painless, + and the installed &SCons; + should work "out of the box" + to build most software. + + + + + + This goal should be kept in mind during implementation, + when there is always a tendency to try to optimize too early. + Speed is nice, but not as important as clarity + and ease of use. + + + + + + + Utility + + + + &SCons; should also provide a rich enough set of features + to accommodate building more complicated software projects. + However, the features required for + building complicated software projects + should not get in the way of novice users. + (See the previous goal.) + In other words, complexity should be available + when it's needed + but not required to get work done. + Practically, this implies that &SCons; + shouldn't be dumbed down to the point it + excludes complicated software builds. + + + + + + + Sharability + + + + As a key element in balancing the conflicting + needs of Usability and Utility, + &SCons; should provide mechanisms to + allow &SCons; users to share build rules, + dependency scanners, and other objects and recipes + for constructing software. + A good sharing mechanism should support + the model wherein most developers on a project + use rules and templates + that are created + and maintained by a local integrator or build-master, + + + + + + + Extensibility + + + + &SCons; should provide mechanisms for + easily extending its capabilities, + including building new types of files, + adding new types of dependency scanning, + being able to accomodate dependencies + between objects other than files, + etc. + + + + + + + Flexibility + + + + In addition to providing a useful command-line interface, + &SCons; should provide the right architectural + framework for embedding its dependency management + in other interfaces. + &SCons; would help strengthen other GUIs or IDEs + and the additional requirements of the + other interfaces would help broaden and solidify + the core &SCons; dependency management. + + + + + + + +
+ Fixing &Make;'s problems + + + + + + + +
+ +
+ Fixing &Cons;'s problems + + + + + + + +
diff --git a/doc/design/install.sgml b/doc/design/install.sgml deleted file mode 100644 index 918fd64..0000000 --- a/doc/design/install.sgml +++ /dev/null @@ -1,28 +0,0 @@ - - - -THIS CHAPTER NEEDS TO BE DISCUSSED AND WRITTEN. - diff --git a/doc/design/install.xml b/doc/design/install.xml new file mode 100644 index 0000000..e670e83 --- /dev/null +++ b/doc/design/install.xml @@ -0,0 +1,28 @@ + + + diff --git a/doc/design/intro.sgml b/doc/design/intro.sgml deleted file mode 100644 index 3ab8e3f..0000000 --- a/doc/design/intro.sgml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - The &SCons; tool provides an easy-to-use, feature-rich interface - for constructing software. Architecturally, &SCons; separates - its dependency analysis and external object management into an - interface-independent Build Engine that could be embedded in any - software system that can run Python. - - - - - - At the command line, &SCons; presents an easily-grasped tool - where configuration files are Python scripts, reducing the need - to learn new build-tool syntax. Inexperienced users can use - intelligent methods that ``do the right thing'' to build software - with a minimum of fuss. Sophisticated users can use a rich set - of underlying features for finer control of the build process, - including mechanisms for easily extending the build process to new - file types. - - - - - - Dependencies are tracked using digital signatures, - which provide more robust dependency analysis than file time - stamps. Implicit dependencies are determined automatically by - scanning the contents of source files, avoiding the need for - laborious and fragile maintenance of static lists of dependencies in - configuration files. - - - - - - The &SCons; tool supports use of files from one or more central code - repositories, a mechanism for caching derived files, and parallel - builds. The tool also includes a framework for sharing build - environments, which allows system administrators or integrators to - define appropriate build parameters for use by other users. - - - -
- About This Document - - - - This document is an ongoing work-in-progress to write down the ideas - and tradeoffs that have gone, and will go into, the &SCons; design. - As such, this is intended primarily for use by developers and others - working on &SCons;, although it is also intended to serve as a - detailed overview of &SCons; for other interested parties. It will - be continually updated and evolve, and will likely overlap with other - documentation produced by the project. Sections of this document - that deal with syntax, for example, may move or be copied into a user - guide or reference manual. - - - - - - So please don't assume that everything mentioned here has been - decided and carved in stone. If you have ideas for improvements, or - questions about things that don't seem to make any sense, please help - improve the design by speaking up about them. - - - - -Sections marked like this -(prefixed with RATIONALE: in the HTML, -surrounded by BEGIN RATIONALE: -and END RATIONALE: -in the printed documentatio) -are DocBook REMARKs, -comments about the document -rather than actual document. -I've used these to mark sections that need work, -but also to cite some open design issues. -If you have input on any of these marked issues, -I'm especially eager to hear it. - - -
diff --git a/doc/design/intro.xml b/doc/design/intro.xml new file mode 100644 index 0000000..561baa4 --- /dev/null +++ b/doc/design/intro.xml @@ -0,0 +1,111 @@ + + + + + The &SCons; tool provides an easy-to-use, feature-rich interface + for constructing software. Architecturally, &SCons; separates + its dependency analysis and external object management into an + interface-independent Build Engine that could be embedded in any + software system that can run Python. + + + + + + At the command line, &SCons; presents an easily-grasped tool + where configuration files are Python scripts, reducing the need + to learn new build-tool syntax. Inexperienced users can use + intelligent methods that ``do the right thing'' to build software + with a minimum of fuss. Sophisticated users can use a rich set + of underlying features for finer control of the build process, + including mechanisms for easily extending the build process to new + file types. + + + + + + Dependencies are tracked using digital signatures, + which provide more robust dependency analysis than file time + stamps. Implicit dependencies are determined automatically by + scanning the contents of source files, avoiding the need for + laborious and fragile maintenance of static lists of dependencies in + configuration files. + + + + + + The &SCons; tool supports use of files from one or more central code + repositories, a mechanism for caching derived files, and parallel + builds. The tool also includes a framework for sharing build + environments, which allows system administrators or integrators to + define appropriate build parameters for use by other users. + + + +
+ About This Document + + + + This document is an ongoing work-in-progress to write down the ideas + and tradeoffs that have gone, and will go into, the &SCons; design. + As such, this is intended primarily for use by developers and others + working on &SCons;, although it is also intended to serve as a + detailed overview of &SCons; for other interested parties. It will + be continually updated and evolve, and will likely overlap with other + documentation produced by the project. Sections of this document + that deal with syntax, for example, may move or be copied into a user + guide or reference manual. + + + + + + So please don't assume that everything mentioned here has been + decided and carved in stone. If you have ideas for improvements, or + questions about things that don't seem to make any sense, please help + improve the design by speaking up about them. + + + + + +
diff --git a/doc/design/issues.sgml b/doc/design/issues.sgml deleted file mode 100644 index 1f9a78c..0000000 --- a/doc/design/issues.sgml +++ /dev/null @@ -1,195 +0,0 @@ - - - - No build tools is perfect. - Here are some &SCons; issues that - do not yet have solutions. - - - -
- Interaction with SC-config - - - - The SC-config tool will be used in the &SCons; installation - process to generate an appropriate default construction environment - so that building most software works "out of the box" on the - installed platform. The SC-config tool will find reasonable default - compilers (C, C++, Fortran), linkers/loaders, library archive tools, - etc. for specification in the default &SCons; construction - environment. - - - -
- -
- Interaction with test infrastructures - - - - &SCons; can be configured to use SC-test (or some other test tool) - to provide controlled, automated testing of software. The &Link; - method could link a test subdirectory to a build - subdirectory: - - - - - Link('test', 'build') - SConscript('test/SConscript') - - - - Any test cases checked in with the source code will be linked - into the test subdirectory and executed. If - &SConscript; files and test cases are written with this in mind, then - invoking: - - - - - % sccons test - - - - Would run all the automated test cases that depend on any changed - software. - - - - - - -
- -
- Java dependencies - - - - Java dependencies are difficult for an external dependency-based - construction tool to accomodate. Determining Java class dependencies - is more complicated than the simple pattern-matching of C or C++ - #include files. From the point of view of an - external build tool, the Java compiler behaves "unpredictably" - because it may create or update multiple output class files and - directories as a result of its internal class dependencies. - - - - - - An obvious &SCons; implementation would be to have the &Scanner; - object parse output from Java -depend -verbose to - calculate dependencies, but this has the distinct disadvantage of - requiring two separate compiler invocations, thereby slowing down - builds. - - - -
- -
- Limitations of digital signature calculation - - - - In practice, calculating digital signatures of a file's contents is a - more robust mechanism than time stamps for determining what needs - building. However: - - - - - - - - - Developers used to the time stamp model of &Make; can initially - find digital signatures counter-intuitive. The assumption that: - - - % touch file.c - - will cause a rebuild of file is strong... - - - - - - - - Abstracting dependency calculation into a single digital signature - loses a little information: It is no longer possible to tell - (without laborious additional calculation) which input file dependency - caused a rebuild of a given target file. A feature that could - report, "I'm rebuilding file X because it's out-of-date with respect - to file Y," would be good, but an digital-signature implementation of - such a feature is non-obvious. - - - - - - -
- -
- Remote execution - - - - The ability to use multiple build systems through remote execution - of tools would be good. This should be implementable through the - &Job; class. Construction environments would need modification - to specify build systems. - - - -
- -
- Conditional builds - - - - The ability to check run-time conditions as suggested on the - sc-discuss mailing list ("build X only if: the machine is idle / - the file system has Y megabytes free space") would also be good, - but is not part of the current design. - - - -
diff --git a/doc/design/issues.xml b/doc/design/issues.xml new file mode 100644 index 0000000..1f9a78c --- /dev/null +++ b/doc/design/issues.xml @@ -0,0 +1,195 @@ + + + + No build tools is perfect. + Here are some &SCons; issues that + do not yet have solutions. + + + +
+ Interaction with SC-config + + + + The SC-config tool will be used in the &SCons; installation + process to generate an appropriate default construction environment + so that building most software works "out of the box" on the + installed platform. The SC-config tool will find reasonable default + compilers (C, C++, Fortran), linkers/loaders, library archive tools, + etc. for specification in the default &SCons; construction + environment. + + + +
+ +
+ Interaction with test infrastructures + + + + &SCons; can be configured to use SC-test (or some other test tool) + to provide controlled, automated testing of software. The &Link; + method could link a test subdirectory to a build + subdirectory: + + + + + Link('test', 'build') + SConscript('test/SConscript') + + + + Any test cases checked in with the source code will be linked + into the test subdirectory and executed. If + &SConscript; files and test cases are written with this in mind, then + invoking: + + + + + % sccons test + + + + Would run all the automated test cases that depend on any changed + software. + + + + + + +
+ +
+ Java dependencies + + + + Java dependencies are difficult for an external dependency-based + construction tool to accomodate. Determining Java class dependencies + is more complicated than the simple pattern-matching of C or C++ + #include files. From the point of view of an + external build tool, the Java compiler behaves "unpredictably" + because it may create or update multiple output class files and + directories as a result of its internal class dependencies. + + + + + + An obvious &SCons; implementation would be to have the &Scanner; + object parse output from Java -depend -verbose to + calculate dependencies, but this has the distinct disadvantage of + requiring two separate compiler invocations, thereby slowing down + builds. + + + +
+ +
+ Limitations of digital signature calculation + + + + In practice, calculating digital signatures of a file's contents is a + more robust mechanism than time stamps for determining what needs + building. However: + + + + + + + + + Developers used to the time stamp model of &Make; can initially + find digital signatures counter-intuitive. The assumption that: + + + % touch file.c + + will cause a rebuild of file is strong... + + + + + + + + Abstracting dependency calculation into a single digital signature + loses a little information: It is no longer possible to tell + (without laborious additional calculation) which input file dependency + caused a rebuild of a given target file. A feature that could + report, "I'm rebuilding file X because it's out-of-date with respect + to file Y," would be good, but an digital-signature implementation of + such a feature is non-obvious. + + + + + + +
+ +
+ Remote execution + + + + The ability to use multiple build systems through remote execution + of tools would be good. This should be implementable through the + &Job; class. Construction environments would need modification + to specify build systems. + + + +
+ +
+ Conditional builds + + + + The ability to check run-time conditions as suggested on the + sc-discuss mailing list ("build X only if: the machine is idle / + the file system has Y megabytes free space") would also be good, + but is not part of the current design. + + + +
diff --git a/doc/design/main.sgml b/doc/design/main.sgml deleted file mode 100644 index 6246a73..0000000 --- a/doc/design/main.sgml +++ /dev/null @@ -1,151 +0,0 @@ - - - - %version; - --> - - - - - - - %scons; - - - - - - - - - - - - -]> - - - - SCons Design version &buildversion; - - - Steven - Knight - - - Revision &buildrevision; (&builddate;) - - 2001 - - - 2001 - Steven Knight - - - - ©right; - - - version &buildversion; - - - - - Introduction - &intro; - - - - Goals - &goals; - - - - Overview - &overview; - - - - Build Engine API - &engine; - - - - Native Python Interface - &native; - - - - Installation - &install; - - - - Other Issues - &issues; - - - - Background - &bground; - - - - Summary - - - &SCons; offers a robust and feature-rich design for an SC-build - tool. With a Build Engine based on the proven design of - the &Cons; utility, it offers increased simplification of the - user interface for unsophisticated users with the addition - of the "do-the-right-thing" env.Make - method, increased flexibility for sophisticated users with the - addition of &Builder; and &Scanner; objects, a mechanism to - allow tool-masters (and users) to share working construction - environments, and embeddability to provide reliable dependency - management in a variety of environments and interfaces. - - - - - - Acknowledgements - &acks; - - - diff --git a/doc/design/main.xml b/doc/design/main.xml new file mode 100644 index 0000000..e991b36 --- /dev/null +++ b/doc/design/main.xml @@ -0,0 +1,158 @@ + + + + + + %version; + --> + + + + + + + %scons; + + + + + + + + + + + + +]> + + + + SCons Design version &buildversion; + + + Steven + Knight + + + Revision &buildrevision; (&builddate;) + + 2001 + + + 2001 + Steven Knight + + + + ©right; + + + version &buildversion; + + + + + Introduction + &intro; + + + + Goals + &goals; + + + + Overview + &overview; + + + + Build Engine API + &engine; + + + + Native Python Interface + &native; + + + + + + Other Issues + &issues; + + + + Background + &bground; + + + + Summary + + + &SCons; offers a robust and feature-rich design for an SC-build + tool. With a Build Engine based on the proven design of + the &Cons; utility, it offers increased simplification of the + user interface for unsophisticated users with the addition + of the "do-the-right-thing" env.Make + method, increased flexibility for sophisticated users with the + addition of &Builder; and &Scanner; objects, a mechanism to + allow tool-masters (and users) to share working construction + environments, and embeddability to provide reliable dependency + management in a variety of environments and interfaces. + + + + + + Acknowledgements + &acks; + + + diff --git a/doc/design/native.sgml b/doc/design/native.sgml deleted file mode 100644 index 5a791a7..0000000 --- a/doc/design/native.sgml +++ /dev/null @@ -1,364 +0,0 @@ - - - - - The "Native Python" interface is the interface - that the actual &SCons; utility will present to users. - Because it exposes the Python Build Engine API, - &SCons; users will have direct access to the complete - functionality of the Build Engine. - In contrast, a different user interface such as a GUI - may choose to only use, and present to the end-user, - a subset of the Build Engine functionality. - - - -
- Configuration files - - - - &SCons; configuration files are simply Python scripts that invoke - methods to specify target files to be built, rules for building the - target files, and dependencies. Common build rules are available by - default and need not be explicitly specified in the configuration - files. - - - - - - By default, the &SCons; utility searches for a file named - &SConstruct;, &Sconstruct; or &sconstruct (in that order) in the - current directory, and reads its configuration from the first file - found. A command-line option exists to read a - different file name. - - - -
- - - -
- Python syntax - - - - Because &SCons; configuration files are Python scripts, normal Python - syntax can be used to generate or manipulate lists of targets or - dependencies: - - - - - sources = ['aaa.c', 'bbb.c', 'ccc.c'] - env.Make('bar', sources) - - - - - Python flow-control can be used to iterate through invocations of - build rules: - - - - - objects = ['aaa.o', 'bbb.o', 'ccc.o'] - for obj in objects: - src = replace(obj, '.o', '.c') - env.Make(obj, src) - - - - - or to handle more complicated conditional invocations: - - - - - # only build 'foo' on Linux systems - if sys.platform == 'linux1': - env.Make('foo', 'foo.c') - - - - - Because &SCons; configuration files are Python scripts, syntax errors - will be caught by the Python parser. Target-building does not begin - until after all configuration files are read, so a syntax error will - not cause a build to fail half-way. - - - -
- - - -
- Subsidiary configuration Files - - - - A configuration file can instruct &SCons; to read up subsidiary - configuration files. Subsidiary files are specified explicitly in a - configuration file via the &SConscript; method. As usual, multiple - file names may be specified with white space separation, or in an - array: - - - - - SConscript('other_file') - SConscript('file1 file2') - SConscript(['file3', 'file4']) - SConscript(['file name with white space']) - - - - - An explicit sconscript keyword may be used: - - - - - SConscript(sconscript = 'other_file') - - - - - Including subsidiary configuration files is recursive: a configuration - file included via &SConscript; may in turn &SConscript; other - configuration files. - - - -
- - - -
- Variable scoping in subsidiary files - - - - When a subsidiary configuration file is read, it is given its own - namespace; it does not have automatic access to variables from the parent - configuration file. - - - - - - Any variables (not just &SCons; objects) that are to be shared between configuration files must be - explicitly passed in the &SConscript; call - using the &Export method: - - - - - env = Environment() - debug = Environment(CCFLAGS = '-g') - installdir = '/usr/bin' - SConscript('src/SConscript', Export(env=env, debug=debug, installdir=installdir)) - - - -The env=env stuff bugs me -because it imposes extra work on the normal -case where you don't rename -the variables. -Can we simplify the &Export; method -so that a string -without a keyword assignment -is split into variables that are passed -through transparently? -Equivalent to the above example: -SConscript('src/SConscript', Export('env debug installdir')) - - - - - Which may be specified explicitly using a keyword argument: - - - - - env = Environment() - debug = Environment(CCFLAGS = '-g') - installdir = '/usr/bin' - SConscript(sconscript = 'src/SConscript', - export = Export(env=env, debug=debug, installdir=installdir)) - - - - - Explicit variable-passing provides control over exactly what is available - to a subsidiary file, and avoids unintended side effects of changes in - one configuration file affecting other far-removed configuration files - (a very hard-to-debug class of build problem). - - - -
- - - -
- Hierarchical builds - - - - The &SConscript; method is so named because, by convention, subsidiary - configuration files in subdirectories are named &SConscript;: - - - - - SConscript('src/SConscript') - SConscript('lib/build_me') - - - - - When a subsidiary configuration file is read from a subdirectory, all - of that configuration file's targets and build rules are interpreted - relative to that directory (as if &SCons; had changed its working - directory to that subdirectory). This allows for easy support of - hierarchical builds of directory trees for large projects. - - - -
- - - -
- Sharing &consenvs; - - - - &SCons; will allow users to share &consenvs, as well as other &SCons; - objects and Python variables, by importing them from a central, shared - repository using normal Python syntax: - - - - - from LocalEnvironments import optimized, debug - - optimized.Make('foo', 'foo.c') - debug.Make('foo-d', 'foo.c') - - - - - The expectation is that some local tool-master, integrator or - administrator will be responsible for assembling environments (creating - the &Builder; objects that specify the tools, options, etc.) and make - these available for sharing by all users. - - - - - - The modules containing shared &consenvs; - (LocalEnvironments in the above example) can be - checked in and controlled with the rest of the source files. This - allows a project to track the combinations of tools and command-line - options that work on different platforms, at different times, and with - different tool versions, by using already-familiar revision control - tools. - - - -
- - - -
- Help - - - - The &SCons; utility provides a &Help; function to allow the writer - of a &SConstruct; file to provide help text that is specific to - the local build tree: - - - - - Help(""" - Type: - scons . build and test everything - scons test build the software - scons src run the tests - scons web build the web pages - """) - - - - - This help text is displayed in response to the - command-line option. Calling the &Help; function more than once is an - error. - - - -
- - - -
- Debug - - - - &SCons; supports several command-line options for printing extra - information with which to debug build problems. - - - - -These need to be specified and explained -beyond what the man page will have. - - - - - - - See the -d, -p, -pa, and -pw options - in the , below. - All of these options make use of call-back functions to - - printed by the Build Engine. - - - - - -
diff --git a/doc/design/native.xml b/doc/design/native.xml new file mode 100644 index 0000000..8cdd867 --- /dev/null +++ b/doc/design/native.xml @@ -0,0 +1,364 @@ + + + + + The "Native Python" interface is the interface + that the actual &SCons; utility will present to users. + Because it exposes the Python Build Engine API, + &SCons; users will have direct access to the complete + functionality of the Build Engine. + In contrast, a different user interface such as a GUI + may choose to only use, and present to the end-user, + a subset of the Build Engine functionality. + + + +
+ Configuration files + + + + &SCons; configuration files are simply Python scripts that invoke + methods to specify target files to be built, rules for building the + target files, and dependencies. Common build rules are available by + default and need not be explicitly specified in the configuration + files. + + + + + + By default, the &SCons; utility searches for a file named + &SConstruct;, &Sconstruct; or &sconstruct (in that order) in the + current directory, and reads its configuration from the first file + found. A command-line option exists to read a + different file name. + + + +
+ + + +
+ Python syntax + + + + Because &SCons; configuration files are Python scripts, normal Python + syntax can be used to generate or manipulate lists of targets or + dependencies: + + + + + sources = ['aaa.c', 'bbb.c', 'ccc.c'] + env.Make('bar', sources) + + + + + Python flow-control can be used to iterate through invocations of + build rules: + + + + + objects = ['aaa.o', 'bbb.o', 'ccc.o'] + for obj in objects: + src = replace(obj, '.o', '.c') + env.Make(obj, src) + + + + + or to handle more complicated conditional invocations: + + + + + # only build 'foo' on Linux systems + if sys.platform == 'linux1': + env.Make('foo', 'foo.c') + + + + + Because &SCons; configuration files are Python scripts, syntax errors + will be caught by the Python parser. Target-building does not begin + until after all configuration files are read, so a syntax error will + not cause a build to fail half-way. + + + +
+ + + +
+ Subsidiary configuration Files + + + + A configuration file can instruct &SCons; to read up subsidiary + configuration files. Subsidiary files are specified explicitly in a + configuration file via the &SConscript; method. As usual, multiple + file names may be specified with white space separation, or in an + array: + + + + + SConscript('other_file') + SConscript('file1 file2') + SConscript(['file3', 'file4']) + SConscript(['file name with white space']) + + + + + An explicit sconscript keyword may be used: + + + + + SConscript(sconscript = 'other_file') + + + + + Including subsidiary configuration files is recursive: a configuration + file included via &SConscript; may in turn &SConscript; other + configuration files. + + + +
+ + + +
+ Variable scoping in subsidiary files + + + + When a subsidiary configuration file is read, it is given its own + namespace; it does not have automatic access to variables from the parent + configuration file. + + + + + + Any variables (not just &SCons; objects) that are to be shared between configuration files must be + explicitly passed in the &SConscript; call + using the &Export method: + + + + + env = Environment() + debug = Environment(CCFLAGS = '-g') + installdir = '/usr/bin' + SConscript('src/SConscript', Export(env=env, debug=debug, installdir=installdir)) + + + + + + + Which may be specified explicitly using a keyword argument: + + + + + env = Environment() + debug = Environment(CCFLAGS = '-g') + installdir = '/usr/bin' + SConscript(sconscript = 'src/SConscript', + export = Export(env=env, debug=debug, installdir=installdir)) + + + + + Explicit variable-passing provides control over exactly what is available + to a subsidiary file, and avoids unintended side effects of changes in + one configuration file affecting other far-removed configuration files + (a very hard-to-debug class of build problem). + + + +
+ + + +
+ Hierarchical builds + + + + The &SConscript; method is so named because, by convention, subsidiary + configuration files in subdirectories are named &SConscript;: + + + + + SConscript('src/SConscript') + SConscript('lib/build_me') + + + + + When a subsidiary configuration file is read from a subdirectory, all + of that configuration file's targets and build rules are interpreted + relative to that directory (as if &SCons; had changed its working + directory to that subdirectory). This allows for easy support of + hierarchical builds of directory trees for large projects. + + + +
+ + + +
+ Sharing &consenvs; + + + + &SCons; will allow users to share &consenvs, as well as other &SCons; + objects and Python variables, by importing them from a central, shared + repository using normal Python syntax: + + + + + from LocalEnvironments import optimized, debug + + optimized.Make('foo', 'foo.c') + debug.Make('foo-d', 'foo.c') + + + + + The expectation is that some local tool-master, integrator or + administrator will be responsible for assembling environments (creating + the &Builder; objects that specify the tools, options, etc.) and make + these available for sharing by all users. + + + + + + The modules containing shared &consenvs; + (LocalEnvironments in the above example) can be + checked in and controlled with the rest of the source files. This + allows a project to track the combinations of tools and command-line + options that work on different platforms, at different times, and with + different tool versions, by using already-familiar revision control + tools. + + + +
+ + + +
+ Help + + + + The &SCons; utility provides a &Help; function to allow the writer + of a &SConstruct; file to provide help text that is specific to + the local build tree: + + + + + Help(""" + Type: + scons . build and test everything + scons test build the software + scons src run the tests + scons web build the web pages + """) + + + + + This help text is displayed in response to the + command-line option. Calling the &Help; function more than once is an + error. + + + +
+ + + +
+ Debug + + + + &SCons; supports several command-line options for printing extra + information with which to debug build problems. + + + + + + + + + + See the -d, -p, -pa, and -pw options + in the , below. + All of these options make use of call-back functions to + + printed by the Build Engine. + + + + + +
diff --git a/doc/design/overview.sgml b/doc/design/overview.sgml deleted file mode 100644 index 8d6f060..0000000 --- a/doc/design/overview.sgml +++ /dev/null @@ -1,498 +0,0 @@ - - -
- Architecture - - - - The heart of &SCons; is its Build Engine. - The &SCons; Build Engine is a Python module - that manages dependencies between - external objects - such as files or database records. - The Build Engine is designed to - be interface-neutral - and easily embeddable in any - software system that needs dependency - analysis between updatable objects. - - - - - - The key parts of the Build Engine architecture - are captured in the following quasi-UML diagram: - - - - -Including this figure makes our PDF build blow up. -The figure, however, -is left over from the Software Carpentry contest -and is therefore old, out-of-date, and needs to be redone anyway. -This is where it will go, anyway... - - - - - - - The point of &SCons; is to manage - dependencies between arbitrary external objects. - Consequently, the Build Engine does not restrict or specify - the nature of the external objects it manages, - but instead relies on subclass of the &Node; - class to interact with the external system or systems - (file systems, database management systems) - that maintain the objects being examined or updated. - - - - - - The Build Engine presents to the software system in - which it is embedded - a Python API for specifying source (input) and target (output) objects, - rules for building/updating objects, - rules for scanning objects for dependencies, etc. - Above its Python API, - the Build Engine is completely - interface-independent, - and can be encapsulated by any other software - that supports embedded Python. - - - - - - Software that chooses to use the Build Engine - for dependency management - interacts with it - through Construction Environments. - A Construction Environment consists - of a dictionary of environment variables, - and one or more associated - &Scanner; objects - and &Builder; objects. - The Python API is used to - form these associations. - - - - - - A &Scanner; object specifies - how to examine a type of source object - (C source file, database record) - for dependency information. - A &Scanner; object may use - variables from the associated - Construction Environment - to modify how it scans an object: - specifying a search path for included files, - which field in a database record to consult, - etc. - - - - - - A &Builder; object specifies - how to update a type of target object: - executable program, object file, database field, etc. - Like a &Scanner; object, - a &Builder; object may use - variables from the associated - Construction Environment - to modify how it builds an object: - specifying flags to a compiler, - using a different update function, - etc. - - - - - - &Scanner; and &Builder; objects will return one or more - &Node; objects that represent external objects. - &Node; objects are the means by which the - Build Engine tracks dependencies: - A &Node; may represent a source (input) object that - should already exist, - or a target (output) object which may be built, - or both. - The &Node; class is sub-classed to - represent external objects of specific type: - files, directories, database fields or records, etc. - Because dependency information, however, - is tracked by the top-level &Node; methods and attributes, - dependencies can exist - between nodes representing different external object types. - For example, - building a file could be made - dependent on the value of a given - field in a database record, - or a database table could depend - on the contents of an external file. - - - - - - The Build Engine uses a &Job; class (not displayed) - to manage the actual work of updating external target objects: - spawning commands to build files, - submitting the necessary commands to update a database record, - etc. - The &Job; class has sub-classes - to handle differences between spawning - jobs in parallel and serially. - - - - - - The Build Engine also uses a - &Signature; class (not displayed) - to maintain information about whether - an external object is up-to-date. - Target objects with out-of-date signatures - are updated using the appropriate - &Builder; object. - - - - - - - - - -
- - - -
- Build Engine - - - - More detailed discussion of some of the - Build Engine's characteristics: - - - -
- Python API - - - - The Build Engine can be embedded in any other software - that supports embedding Python: - in a GUI, - in a wrapper script that - interprets classic Makefile syntax, - or in any other software that - can translate its dependency representation - into the appropriate calls to the Build Engine API. - describes in detail - the specification for a "Native Python" interface - that will drive the &SCons; implementation effort. - - - -
- -
- Single-image execution - - - - When building/updating the objects, - the Build Engine operates as a single executable - with a complete Directed Acyclic Graph (DAG) - of the dependencies in the entire build tree. - This is in stark contrast to the - commonplace recursive use of Make - to handle hierarchical directory-tree builds. - - - -
- -
- Dependency analysis - - - - Dependency analysis is carried out via digital signatures - (a.k.a. "fingerprints"). - Contents of object are examined and reduced - to a number that can be stored and compared to - see if the object has changed. - Additionally, &SCons; uses the same - signature technique on the command-lines that - are executed to update an object. - If the command-line has changed since the last time, - then the object must be rebuilt. - - - -
- -
- Customized output - - - - The output of Build Engine is customizable - through user-defined functions. - This could be used to print additional desired - information about what &SCons; is doing, - or tailor output to a specific build analyzer, - GUI, or IDE. - - - -
- -
- Build failures - - - - &SCons; detects build failures via the exit status from the tools - used to build the target files. By default, a failed exit status - (non-zero on UNIX systems) terminates the build with an appropriate - error message. An appropriate class from the Python library will - interpret build-tool failures via an OS-independent API. - - - - - - If multiple tasks are executing in a parallel build, and one tool - returns failure, &SCons; will not initiate any further build tasks, - but allow the other build tasks to complete before terminating. - - - - - - A command-line option may be used to ignore - errors and continue building other targets. In no case will a target - that depends on a failed build be rebuilt. - - - -
- -
- - - -
- Interfaces - - - - As previously described, - the &SCons; Build Engine - is interface-independent above its Python API, - and can be embedded in any software system - that can translate its dependency requirements - into the necessary Python calls. - - - - - - The "main" &SCons; interface - for implementation purposes, - uses Python scripts as configuration files. - Because this exposes the Build Engine's Python API to the user, - it is current called the "Native Python" interface. - - - - - - This section will also discuss - how &SCons; will function in the context - of two other interfaces: - the &Makefile; interface of the classic &Make; utility, - and a hypothetical graphical user interface (GUI). - - - -
- Native Python interface - - - - The Native Python interface is intended to be the primary interface - by which users will know &SCons;--that is, - it is the interface they will use - if they actually type &SCons; at a command-line prompt. - - - - - - In the Native Python interface, &SCons; configuration files are simply - Python scripts that directly invoke methods from the Build Engine's - Python API to specify target files to be built, rules for building - the target files, and dependencies. Additional methods, specific to - this interface, are added to handle functionality that is specific to - the Native Python interface: reading a subsidiary configuration file; - copying target files to an installation directory; etc. - - - - - - Because configuration files are Python scripts, Python flow control - can be used to provide very flexible manipulation of objects and - dependencies. For example, a function could be used to invoke a common - set of methods on a file, and called iteratively over an array of - files. - - - - - - As an additional advantage, syntax errors in &SCons; Native Python - configuration files will be caught by the Python parser. Target-building - does not begin until after all configuration files are read, so a syntax - error will not cause a build to fail half-way. - - - -
- -
- Makefile interface - - - - An alternate &SCons; interface would provide backwards - compatibility with the classic &Make utility. - This would be done by embedding the &SCons; Build Engine - in a Python script that can translate existing - &Makefile;s into the underlying calls to the - Build Engine's Python API - for building and tracking dependencies. - Here are approaches to solving some of the issues - that arise from marrying these two pieces: - - - - - - - - &Makefile; suffix rules can be translated - into an appropriate &Builder; object - with suffix maps from the Construction Environment. - - - - - - Long lists of static dependences - appended to a &Makefile; by - various "make depend" schemes - can be preserved - but supplemented by - the more accurate dependency information - provided by &Scanner; objects. - - - - - - Recursive invocations of &Make; - can be avoided by reading up - the subsidiary &Makefile; instead. - - - - - - - - Lest this seem like too outlandish an undertaking, - there is a working example of this approach: - Gary Holt's &Makepp; utility - is a Perl script that provides - admirably complete parsing of complicated &Makefile;s - around an internal build engine inspired, - in part, by the classic Cons utility. - - - -
- -
- Graphical interfaces - - - - The &SCons; Build Engine - is designed from the ground up to be embedded - into multiple interfaces. - Consequently, embedding the dependency capabilities - of &SCons; into graphical interface - would be a matter of mapping the - GUI's dependency representation - (either implicit or explicit) - into corresponding calls to the Python API - of the &SCons; Build Engine. - - - - - - Note, however, that this proposal leaves the problem of - designed a good graphical interface - for representing software build dependencies - to people with actual GUI design experience... - - - -
- -
diff --git a/doc/design/overview.xml b/doc/design/overview.xml new file mode 100644 index 0000000..38e4258 --- /dev/null +++ b/doc/design/overview.xml @@ -0,0 +1,498 @@ + + +
+ Architecture + + + + The heart of &SCons; is its Build Engine. + The &SCons; Build Engine is a Python module + that manages dependencies between + external objects + such as files or database records. + The Build Engine is designed to + be interface-neutral + and easily embeddable in any + software system that needs dependency + analysis between updatable objects. + + + + + + The key parts of the Build Engine architecture + are captured in the following quasi-UML diagram: + + + + + + + + + + The point of &SCons; is to manage + dependencies between arbitrary external objects. + Consequently, the Build Engine does not restrict or specify + the nature of the external objects it manages, + but instead relies on subclass of the &Node; + class to interact with the external system or systems + (file systems, database management systems) + that maintain the objects being examined or updated. + + + + + + The Build Engine presents to the software system in + which it is embedded + a Python API for specifying source (input) and target (output) objects, + rules for building/updating objects, + rules for scanning objects for dependencies, etc. + Above its Python API, + the Build Engine is completely + interface-independent, + and can be encapsulated by any other software + that supports embedded Python. + + + + + + Software that chooses to use the Build Engine + for dependency management + interacts with it + through Construction Environments. + A Construction Environment consists + of a dictionary of environment variables, + and one or more associated + &Scanner; objects + and &Builder; objects. + The Python API is used to + form these associations. + + + + + + A &Scanner; object specifies + how to examine a type of source object + (C source file, database record) + for dependency information. + A &Scanner; object may use + variables from the associated + Construction Environment + to modify how it scans an object: + specifying a search path for included files, + which field in a database record to consult, + etc. + + + + + + A &Builder; object specifies + how to update a type of target object: + executable program, object file, database field, etc. + Like a &Scanner; object, + a &Builder; object may use + variables from the associated + Construction Environment + to modify how it builds an object: + specifying flags to a compiler, + using a different update function, + etc. + + + + + + &Scanner; and &Builder; objects will return one or more + &Node; objects that represent external objects. + &Node; objects are the means by which the + Build Engine tracks dependencies: + A &Node; may represent a source (input) object that + should already exist, + or a target (output) object which may be built, + or both. + The &Node; class is sub-classed to + represent external objects of specific type: + files, directories, database fields or records, etc. + Because dependency information, however, + is tracked by the top-level &Node; methods and attributes, + dependencies can exist + between nodes representing different external object types. + For example, + building a file could be made + dependent on the value of a given + field in a database record, + or a database table could depend + on the contents of an external file. + + + + + + The Build Engine uses a &Job; class (not displayed) + to manage the actual work of updating external target objects: + spawning commands to build files, + submitting the necessary commands to update a database record, + etc. + The &Job; class has sub-classes + to handle differences between spawning + jobs in parallel and serially. + + + + + + The Build Engine also uses a + &Signature; class (not displayed) + to maintain information about whether + an external object is up-to-date. + Target objects with out-of-date signatures + are updated using the appropriate + &Builder; object. + + + + + + + + + +
+ + + +
+ Build Engine + + + + More detailed discussion of some of the + Build Engine's characteristics: + + + +
+ Python API + + + + The Build Engine can be embedded in any other software + that supports embedding Python: + in a GUI, + in a wrapper script that + interprets classic Makefile syntax, + or in any other software that + can translate its dependency representation + into the appropriate calls to the Build Engine API. + describes in detail + the specification for a "Native Python" interface + that will drive the &SCons; implementation effort. + + + +
+ +
+ Single-image execution + + + + When building/updating the objects, + the Build Engine operates as a single executable + with a complete Directed Acyclic Graph (DAG) + of the dependencies in the entire build tree. + This is in stark contrast to the + commonplace recursive use of Make + to handle hierarchical directory-tree builds. + + + +
+ +
+ Dependency analysis + + + + Dependency analysis is carried out via digital signatures + (a.k.a. "fingerprints"). + Contents of object are examined and reduced + to a number that can be stored and compared to + see if the object has changed. + Additionally, &SCons; uses the same + signature technique on the command-lines that + are executed to update an object. + If the command-line has changed since the last time, + then the object must be rebuilt. + + + +
+ +
+ Customized output + + + + The output of Build Engine is customizable + through user-defined functions. + This could be used to print additional desired + information about what &SCons; is doing, + or tailor output to a specific build analyzer, + GUI, or IDE. + + + +
+ +
+ Build failures + + + + &SCons; detects build failures via the exit status from the tools + used to build the target files. By default, a failed exit status + (non-zero on UNIX systems) terminates the build with an appropriate + error message. An appropriate class from the Python library will + interpret build-tool failures via an OS-independent API. + + + + + + If multiple tasks are executing in a parallel build, and one tool + returns failure, &SCons; will not initiate any further build tasks, + but allow the other build tasks to complete before terminating. + + + + + + A command-line option may be used to ignore + errors and continue building other targets. In no case will a target + that depends on a failed build be rebuilt. + + + +
+ +
+ + + +
+ Interfaces + + + + As previously described, + the &SCons; Build Engine + is interface-independent above its Python API, + and can be embedded in any software system + that can translate its dependency requirements + into the necessary Python calls. + + + + + + The "main" &SCons; interface + for implementation purposes, + uses Python scripts as configuration files. + Because this exposes the Build Engine's Python API to the user, + it is current called the "Native Python" interface. + + + + + + This section will also discuss + how &SCons; will function in the context + of two other interfaces: + the &Makefile; interface of the classic &Make; utility, + and a hypothetical graphical user interface (GUI). + + + +
+ Native Python interface + + + + The Native Python interface is intended to be the primary interface + by which users will know &SCons;--that is, + it is the interface they will use + if they actually type &SCons; at a command-line prompt. + + + + + + In the Native Python interface, &SCons; configuration files are simply + Python scripts that directly invoke methods from the Build Engine's + Python API to specify target files to be built, rules for building + the target files, and dependencies. Additional methods, specific to + this interface, are added to handle functionality that is specific to + the Native Python interface: reading a subsidiary configuration file; + copying target files to an installation directory; etc. + + + + + + Because configuration files are Python scripts, Python flow control + can be used to provide very flexible manipulation of objects and + dependencies. For example, a function could be used to invoke a common + set of methods on a file, and called iteratively over an array of + files. + + + + + + As an additional advantage, syntax errors in &SCons; Native Python + configuration files will be caught by the Python parser. Target-building + does not begin until after all configuration files are read, so a syntax + error will not cause a build to fail half-way. + + + +
+ +
+ Makefile interface + + + + An alternate &SCons; interface would provide backwards + compatibility with the classic &Make utility. + This would be done by embedding the &SCons; Build Engine + in a Python script that can translate existing + &Makefile;s into the underlying calls to the + Build Engine's Python API + for building and tracking dependencies. + Here are approaches to solving some of the issues + that arise from marrying these two pieces: + + + + + + + + &Makefile; suffix rules can be translated + into an appropriate &Builder; object + with suffix maps from the Construction Environment. + + + + + + Long lists of static dependences + appended to a &Makefile; by + various "make depend" schemes + can be preserved + but supplemented by + the more accurate dependency information + provided by &Scanner; objects. + + + + + + Recursive invocations of &Make; + can be avoided by reading up + the subsidiary &Makefile; instead. + + + + + + + + Lest this seem like too outlandish an undertaking, + there is a working example of this approach: + Gary Holt's &Makepp; utility + is a Perl script that provides + admirably complete parsing of complicated &Makefile;s + around an internal build engine inspired, + in part, by the classic Cons utility. + + + +
+ +
+ Graphical interfaces + + + + The &SCons; Build Engine + is designed from the ground up to be embedded + into multiple interfaces. + Consequently, embedding the dependency capabilities + of &SCons; into graphical interface + would be a matter of mapping the + GUI's dependency representation + (either implicit or explicit) + into corresponding calls to the Python API + of the &SCons; Build Engine. + + + + + + Note, however, that this proposal leaves the problem of + designed a good graphical interface + for representing software build dependencies + to people with actual GUI design experience... + + + +
+ +
diff --git a/doc/developer/MANIFEST b/doc/developer/MANIFEST index 12a4de0..eece338 100644 --- a/doc/developer/MANIFEST +++ b/doc/developer/MANIFEST @@ -1,9 +1,9 @@ -architecture.sgml -branches.sgml -copyright.sgml -cycle.sgml -main.sgml -packaging.sgml -preface.sgml -sourcetree.sgml -testing.sgml +architecture.xml +branches.xml +copyright.xml +cycle.xml +main.xml +packaging.xml +preface.xml +sourcetree.xml +testing.xml diff --git a/doc/developer/architecture.sgml b/doc/developer/architecture.sgml deleted file mode 100644 index 0fc357f..0000000 --- a/doc/developer/architecture.sgml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - XXX - - - -
- Architecture - - - - XXX - - -
diff --git a/doc/developer/architecture.xml b/doc/developer/architecture.xml new file mode 100644 index 0000000..0fc357f --- /dev/null +++ b/doc/developer/architecture.xml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Architecture + + + + XXX + + +
diff --git a/doc/developer/branches.sgml b/doc/developer/branches.sgml deleted file mode 100644 index abba398..0000000 --- a/doc/developer/branches.sgml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - XXX - - - -
- &SCons; Branches - - - - XXX - - -
diff --git a/doc/developer/branches.xml b/doc/developer/branches.xml new file mode 100644 index 0000000..abba398 --- /dev/null +++ b/doc/developer/branches.xml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ &SCons; Branches + + + + XXX + + +
diff --git a/doc/developer/copyright.sgml b/doc/developer/copyright.sgml deleted file mode 100644 index d926f5b..0000000 --- a/doc/developer/copyright.sgml +++ /dev/null @@ -1,32 +0,0 @@ - - -
- - - SCons Developer's Guide Copyright (c) 2007 Steven Knight - - -
diff --git a/doc/developer/copyright.xml b/doc/developer/copyright.xml new file mode 100644 index 0000000..d926f5b --- /dev/null +++ b/doc/developer/copyright.xml @@ -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 deleted file mode 100644 index 629a1a8..0000000 --- a/doc/developer/cycle.sgml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - XXX - - - -
- Development Cycle - - - - XXX - - -
diff --git a/doc/developer/cycle.xml b/doc/developer/cycle.xml new file mode 100644 index 0000000..629a1a8 --- /dev/null +++ b/doc/developer/cycle.xml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Development Cycle + + + + XXX + + +
diff --git a/doc/developer/main.sgml b/doc/developer/main.sgml deleted file mode 100644 index 949287a..0000000 --- a/doc/developer/main.sgml +++ /dev/null @@ -1,107 +0,0 @@ - - - - %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/main.xml b/doc/developer/main.xml new file mode 100644 index 0000000..e2e414c --- /dev/null +++ b/doc/developer/main.xml @@ -0,0 +1,110 @@ + + + + + + %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 deleted file mode 100644 index 3860ee7..0000000 --- a/doc/developer/packaging.sgml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - XXX - - - -
- Packaging - - - - XXX - - -
diff --git a/doc/developer/packaging.xml b/doc/developer/packaging.xml new file mode 100644 index 0000000..3860ee7 --- /dev/null +++ b/doc/developer/packaging.xml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Packaging + + + + XXX + + +
diff --git a/doc/developer/preface.sgml b/doc/developer/preface.sgml deleted file mode 100644 index 5784cee..0000000 --- a/doc/developer/preface.sgml +++ /dev/null @@ -1,175 +0,0 @@ - - - - - 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/preface.xml b/doc/developer/preface.xml new file mode 100644 index 0000000..5784cee --- /dev/null +++ b/doc/developer/preface.xml @@ -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 deleted file mode 100644 index be1c45a..0000000 --- a/doc/developer/sourcetree.sgml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - XXX - - - -
- Source Tree - - - - XXX - - -
diff --git a/doc/developer/sourcetree.xml b/doc/developer/sourcetree.xml new file mode 100644 index 0000000..be1c45a --- /dev/null +++ b/doc/developer/sourcetree.xml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Source Tree + + + + XXX + + +
diff --git a/doc/developer/testing.sgml b/doc/developer/testing.sgml deleted file mode 100644 index c577c5c..0000000 --- a/doc/developer/testing.sgml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - XXX - - - -
- Testing - - - - XXX - - -
diff --git a/doc/developer/testing.xml b/doc/developer/testing.xml new file mode 100644 index 0000000..c577c5c --- /dev/null +++ b/doc/developer/testing.xml @@ -0,0 +1,40 @@ + + + + + XXX + + + +
+ Testing + + + + XXX + + +
diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 769cfc2..a20a0f6 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -2041,8 +2041,8 @@ This function adds a new command-line option to be recognized. The specified .I arguments are the same as supported by the standard Python -.B optparse.add_option -method; +.BR optparse.add_option () +method (with a few additional capabilities noted below); see the documentation for .B optparse for a thorough discussion of its option-processing capabities. @@ -2054,6 +2054,31 @@ contains a compatible version of the module that is used to provide identical functionality when run by earlier Python versions.) +In addition to the arguments and values supported by the +.B optparse.add_option () +method, +the SCons +.BR AddOption () +function allows you to set the +.B nargs +keyword value to +.B '?' +(a string with just the question mark) +to indicate that the specified long option(s) take(s) an +.I optional +argument. +When +.B "nargs = '?'" +is passed to the +.BR AddOption () +function, the +.B const +keyword argument +may be used to supply the "default" +value that should be used when the +option is specified on the command line +without an explicit argument. + If no .B default= keyword argument is supplied when calling @@ -2459,6 +2484,26 @@ The derived files in the cache will be shared among all the builds using the same .BR CacheDir () call. +Specifying a +.I cache_dir +of +.B None +disables derived file caching. + +Calling +.BR env.CacheDir () +will only affect targets built +through the specified construction environment. +Calling +.BR CacheDir () +sets a global default +that will be used by all targets built +through construction environments +that do +.I not +have an +.BR env.CacheDir () +specified. When a .BR CacheDir () @@ -3280,43 +3325,6 @@ Import("*") '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP -.RI Install( dir ", " source ) -.TP -.RI env.Install( dir ", " source ) -Installs one or more source files or directories -in a destination directory -.IR dir . -The names of the specified source files or directories -remain the same within the destination directory. - -.ES -env.Install(dir = '/usr/local/bin', source = ['foo', 'bar']) -.EE - -'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP -.RI InstallAs( target ", " source ) -.TP -.RI env.InstallAs( target ", " source ) -Installs one or more source files or directories -to specific names, -allowing changing a file or directory name -as part of the installation. -It is an error if the -.I target -and -.I source -arguments list different numbers of files or directories. - -.ES -env.InstallAs(target = '/usr/local/bin/foo', - source = 'foo_debug') -env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], - source = ['libFOO.a', 'libBAR.a']) -.EE - -'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP .RI Literal( string ) .TP .RI env.Literal( string ) @@ -4494,6 +4502,26 @@ files = Split(""" '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP +.RI Tag( node ", " tags ) +Annotates file or directory Nodes with +information about how the +.BR Package () +Builder should package those files or directories. +All tags are optional. + +.ES +# makes sure the built library will be installed with 0644 file +# access mode +Tag( Library( 'lib.c' ), UNIX_ATTR="0644" ) + +# marks file2.txt to be a documentation file +Tag( 'file2.txt', DOC ) +.EE + + + +'\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.TP .RI TargetSignatures( type ) .TP .RI env.TargetSignatures( type ) @@ -7026,7 +7054,7 @@ class foo: self.arg = arg def __call__(self, target, source, env, for_signature): - return arg + " bar" + return self.arg + " bar" # Will expand $BAR to "my argument bar baz" env=Environment(FOO=foo, BAR="${FOO('my argument')} baz") diff --git a/doc/python10/MANIFEST b/doc/python10/MANIFEST index e962e6a..c9484d8 100644 --- a/doc/python10/MANIFEST +++ b/doc/python10/MANIFEST @@ -1,16 +1,16 @@ -abstract.sgml -acks.sgml +abstract.xml +acks.xml arch.fig builder.fig -copyright.sgml -design.sgml -future.sgml -install.sgml -intro.sgml +copyright.xml +design.xml +future.xml +install.xml +intro.xml job-task.fig -main.sgml +main.xml node.fig -process.sgml +process.xml scanner.fig scons.mod sig.fig diff --git a/doc/python10/abstract.sgml b/doc/python10/abstract.sgml deleted file mode 100644 index 294180b..0000000 --- a/doc/python10/abstract.sgml +++ /dev/null @@ -1,32 +0,0 @@ - - - &SCons; is a software construction tool (build tool, or make tool) - implemented in Python, which uses Python scripts as "configuration - files" for software builds. Based on the design which won the - Software Carpentry build tool competition, &SCons solves a number of - problems associated with other build tools, especially including the - classic and ubiquitous &Make; itself. - - - - - - Distinctive features of &SCons; include: a modular design that - lends itself to being embedded in other applications; a global - view of all dependencies in the source tree; an improved model for - parallel () builds; automatic scanning of files for - dependencies; use of MD5 signatures for deciding whether a file - is up-to-date; use of traditional file timestamps instead of - MD5 signatures available as an option; - use of Python functions or objects to build target files; easy user - extensibility. - - - - - - This paper discusses the goals of the &SCons; project, gives an overview - of the design of &SCons; itself, describes the development process used, - and discusses future plans and directions for the tool. - - diff --git a/doc/python10/abstract.xml b/doc/python10/abstract.xml new file mode 100644 index 0000000..294180b --- /dev/null +++ b/doc/python10/abstract.xml @@ -0,0 +1,32 @@ + + + &SCons; is a software construction tool (build tool, or make tool) + implemented in Python, which uses Python scripts as "configuration + files" for software builds. Based on the design which won the + Software Carpentry build tool competition, &SCons solves a number of + problems associated with other build tools, especially including the + classic and ubiquitous &Make; itself. + + + + + + Distinctive features of &SCons; include: a modular design that + lends itself to being embedded in other applications; a global + view of all dependencies in the source tree; an improved model for + parallel () builds; automatic scanning of files for + dependencies; use of MD5 signatures for deciding whether a file + is up-to-date; use of traditional file timestamps instead of + MD5 signatures available as an option; + use of Python functions or objects to build target files; easy user + extensibility. + + + + + + This paper discusses the goals of the &SCons; project, gives an overview + of the design of &SCons; itself, describes the development process used, + and discusses future plans and directions for the tool. + + diff --git a/doc/python10/acks.sgml b/doc/python10/acks.sgml deleted file mode 100644 index 895bad7..0000000 --- a/doc/python10/acks.sgml +++ /dev/null @@ -1,27 +0,0 @@ - - - First, many thanks to the great group of developers who dove in right - from the beginning and have contributed the code and ideas to make - &SCons; a success: Chad Austin, Charles Crain, Steve Leblanc, and - Anthony Roach. Thanks also to those on the scons-devel mailing list - who have contributed greatly to the discussion, notably including - David Abrahams, Trent Mick, and Steven Shaw. - - - - - - &SCons; would not exist today without the pioneering work of Bob - Sidebotham on the original &Cons; tool, and without Greg Wilson's - having started the Software Carpentry contest. - - - - - - Thanks also to Peter Miller for: Aegis; the testing discipline that it - enforces, without which creating a stable but flexible tool would be - impossible; the "Recursive Make Considered Harmful" paper which led me - to experiment with &Cons; in the first place. - - diff --git a/doc/python10/acks.xml b/doc/python10/acks.xml new file mode 100644 index 0000000..895bad7 --- /dev/null +++ b/doc/python10/acks.xml @@ -0,0 +1,27 @@ + + + First, many thanks to the great group of developers who dove in right + from the beginning and have contributed the code and ideas to make + &SCons; a success: Chad Austin, Charles Crain, Steve Leblanc, and + Anthony Roach. Thanks also to those on the scons-devel mailing list + who have contributed greatly to the discussion, notably including + David Abrahams, Trent Mick, and Steven Shaw. + + + + + + &SCons; would not exist today without the pioneering work of Bob + Sidebotham on the original &Cons; tool, and without Greg Wilson's + having started the Software Carpentry contest. + + + + + + Thanks also to Peter Miller for: Aegis; the testing discipline that it + enforces, without which creating a stable but flexible tool would be + impossible; the "Recursive Make Considered Harmful" paper which led me + to experiment with &Cons; in the first place. + + diff --git a/doc/python10/copyright.sgml b/doc/python10/copyright.sgml deleted file mode 100644 index d141fc6..0000000 --- a/doc/python10/copyright.sgml +++ /dev/null @@ -1,32 +0,0 @@ - - -
- - - Copyright (c) 2001, 2002 Steven Knight - - -
diff --git a/doc/python10/copyright.xml b/doc/python10/copyright.xml new file mode 100644 index 0000000..d141fc6 --- /dev/null +++ b/doc/python10/copyright.xml @@ -0,0 +1,32 @@ + + +
+ + + Copyright (c) 2001, 2002 Steven Knight + + +
diff --git a/doc/python10/design.sgml b/doc/python10/design.sgml deleted file mode 100644 index cb58af9..0000000 --- a/doc/python10/design.sgml +++ /dev/null @@ -1,898 +0,0 @@ - - - The &SCons; architecture consists of three layers: - - - - - - - - - - - - - - - - - - - The &SCons; Build Engine, a package of Python - modules that handle dependency management and updating out-of-date - objects. - - - - - - - - The &SCons; API (applications programming - interface) between the Build Engine - and the user interface. - - - - - - - - The &scons; script itself (note lower case - sc), which is the pre-provided interface to - the Build Engine. - - - - - - - - - Notice that this architecture separates the internal workings of - &SCons; (the Build Engine) from the - external user interface. The benefit is that the &SCons; Build Engine - can be imported into any other software package written in Python - to support a variety of user interfaces—or, to look at it - in reverse, other software interfaces can use the &SCons; Build - Engine to manage dependencies between their objects. - - - - - - Because the - &SCons; package itself is modular, only those parts of the package - relevant to the embedding interface need be imported; for example, - a utility that wants to use only file timestamps for checking - whether a file is up-to-date - need not import the MD5 signature module. - - - -
- The &SCons; Build Engine - - - - The Build Engine is a package of Python modules that - form the heart of &SCons;. - - The Build Engine can be broadly divided into five - architectural subsystems, each responsible - for a crucial part of &SCons; functionality: - - - - - - - - - A node subsystem, responsible for managing - the files (or other objects) to be built, and the dependency - relationships between them. - - - - - - - - A scanner subsystem, responsible for - scanning various file types for implicit dependencies. - - - - - - - - A signature subsystem, responsible for - deciding whether a given file (or other object) requires - rebuilding. - - - - - - - - A builder subsystem, responsible for - actually executing the necessary command or function to - build a file (or other object). - - - - - - - - A job/task subsystem, responsible for - handling parallelization of builds. - - - - - - - - - The rest of this section will provide a high-level overview of the - class structure of each of these Build Engine subsystems. - - - -
- Node Subsystem - - - - The node subsystem of the Build Engine is - responsible for managing the knowledge in &SCons; of - the relationships among the external objects - (files) it is responsible for updating. - The most important of these relationships is - the dependency relationship between various &Node; objects, - which &SCons; uses to determine the order - in which builds should be performed. - - - - - - - - - - - - - - - - The &scons; script (or other - user interface) - tells the Build Engine - about dependencies - through its &consenv; API. - The Build Engine also discovers - dependencies automatically through the use of &Scanner; objects. - - - - - - Subclasses of the &Node; class maintain additional - relationships that reflect the real-world - existence of these objects. - For example, the &Node_FS; subclass - is responsible for managing a - representation of the directory hierarchy - of a file system. - - - - - - A &Walker; class is used by other subsystems - to walk the dependency tree maintained by the &Node; class. - The &Walker; class maintains a stack of &Node; objects - visited during its depth-first traversal of the - dependency tree, - and uses an intermediate node &Wrapper; class - to maintain state information about a - &Node; object's dependencies. - - - -
- -
- Scanner Subsystem - - - - The scanner subsystem is responsible for maintaining - objects that can scan the contents of a &Node;'s - for implicit dependencies. - - - - - - - - - - - - - - - - In practice, a given &Scanner; subclass object - functions as a prototype, - returning clones of itself - depending on the &consenv; - values governing how the &Node; - should be scanned. - - - -
- -
- Signature Subsystem - - - - The signature subsystem is responsible for computing - signature information for &Node; objects. - The signature subsystem in &SCons; - supports multiple ways to - determine whether a &Node is up-to-date - by using an abstract &Sig; class - as a strategy wrapper: - - - - - - - - - - - - - - - - By default, &SCons; tracks dependencies by computing and - maintaining MD5 signatures for the contents of each source file - (or other object). The signature of a derived - file consists of the aggregate of the signatures of all the source - files plus the command-line string used to - build the file. These signatures are stored in a &sconsign; file - in each directory. - - - - - - If the contents of any of the source files changes, the change to its - MD5 signature is propogated to the signature of the derived file(s). The - simple fact that the new signature does not match the stored signature - indicates that the derived file is not up to date and must be rebuilt. - - - - - - A separate &TimeStamp; subclass of the &Sig; class supports - the use of traditional file timestamps for - deciding whether files are up-to-date. - - - -
- -
- Builder Subsystem - - - - The &SCons; Build Engine records how out-of-date files - (or other objects) should be rebuilt in &Builder; objects, - maintained by the builder subsystem: - - - - - - - - - - - - - - - - The actual underlying class name is &BuilderBase;, - and there are subclasses that can encapsulate - multiple &Builder; objects for special purposes. - One subclass - (&CompositeBuilder;) - selects an appropriate encapsulated &Builder; - based on the file suffix of the target object. - The other - (&MultiStepBuilder;). - can chain together multiple - &Builder; objects, - for example, - to build an executable program from a source file - through an implicit intermediate object file. - - - - - - A &BuilderBase; object has an associated - &ActionBase; object - responsible for actually executing - the appropriate steps - to update the target file. - There are three subclasses, - one for externally executable commands - (&CommandAction;), - one for Python functions - (&FunctionAction;), - and one for lists of - multiple &Action; objects - (&ListAction;). - - - -
- -
- Job/Task Subsystem - - - - &SCons; supports parallel builds with a thread-based tasking - model, managed by the job/task subsystem. - - - - - - - - - - - - - - - - Instead of performing an outer-loop recursive descent - of the dependency tree and then forking a task when it finds a - file that needs updating, &SCons; starts as many threads as are - requested, each thread managed by the &Jobs; class. - As a performance optimization, - the &Jobs; class maintains an internal - distinction between - &Serial; and &Parallel; - build jobs, - so that serial builds - don't pay any performance penalty - by using a multi-threaded implementation - written for &Parallel; builds. - - - - - - Each &Jobs; object, running in its own thread, - then requests a &Task; from a central &Taskmaster;, - which is responsible - for handing out available &Task; objects for (re-)building - out-of-date nodes. A condition variable - makes sure that the &Jobs; objects - query the &Taskmaster; one at a time. - - - - - - The &Taskmaster uses the node subsystem's - &Walker; class to walk the dependency tree, - and the &Sig; class to use the - appropriate method - of deciding if a &Node; is up-to-date. - - - - - - This scheme has many advantages over the standard &Make; - implementation of . - Effective use of is difficult - with the usual recursive use of Make, - because the number of jobs started by multiply - at each level of the source tree. - This makes the actual number of jobs - executed at any moment very dependent on the size and layout of - the tree. &SCons;, in contrast, starts only as many jobs as are - requested, and keeps them constantly busy (excepting jobs that - block waiting for their dependency files to finish building). - - - -
- -
- -
- The &SCons; API - - - - This section provides an overview of the &SCons; interface. The - complete interface specification is both more detailed and flexible - than this overview. - - - -
- &ConsVars; - - - - In &SCons;, a &consenv; is an object through which an external - interface (such as the &scons; script) communicates dependency - information to the &SCons; Build Engine. - - - - - - A construction environment is implemented as a dictionary - containing: - - - - - - - - - construction variables, string values that are substituted - into command lines or used by builder functions; - - - - - - - - one or more &Builder; objects that can be invoked to update a - file or other object; - - - - - - - - one or more &Scanner; objects that can be used to - scan a file automatically for dependencies (such as - files specified on #include lines). - - - - - - - - - &Consenvs; are instantiated as follows: - - - - - env = Environment() - env_debug = Environment(CCFLAGS = '-g') - - -
- -
- &Builder; Objects - - - - An &SCons; &Builder; object encapsulates information about how to - build a specific type of file: an executable program, an object - file, a library, etc. A &Builder; object is associated with a - file through an associated &consenv; method and later invoked to - actually build the file. The &Builder; object will typically use - construction variables (such as &CCFLAGS;, &LIBPATH;) to influence - the specific build execution. - - - - - - &Builder; objects are instantiated as follows: - - - - - bld = Builder(name = 'Program', action = "$CC -o $TARGET $SOURCES") - - - - - In the above example, the action is a - command-line string in which the Build Engine will - interpolate the values of construction - variables before execution. The actual - action specified, though, - may be a function: - - - - - def update(dest): - # [code to update the object] - return 0 - - bld = Builder(name = 'Program', function = update) - - - - - Or a callable Python object (or class): - - - - - class class_a: - def __call__(self, kw): - # build the desired object - return 0 - - builder = SCons.Builder.Builder(action = class_a()) - - - - - A &Builder; object may have the prefix and - suffix of its target file type specified - as keyword arguments at instantiation. Additionally, the - suffix of the source files used by this - &Builder; to build its target files may be specified using the - src_suffix keyword argument: - - - - - bld_lib = Builder(name = 'Library', action = "$AR r $TARGET $SOURCES", - prefix = 'lib', suffix = '.a', src_suffix = '.o') - - - - - The specified prefix and - suffix will be appended to the name of any - target file built by this &Builder; object, if they are not - already part of the file name. The src_suffix - is used by the &SCons; Build Engine to chain together - multiple &Builder; objects to create, - for example, a library from the original source - files without having to specify the - intermediate .o files. - - - - - - &Builder; objects are associated with a &consenv; through a - &consvar; named &BUILDERS;, a list of the &Builder objects that - will be available for execution through the &consenv: - - - - - env = Environment(BUILDERS = [ Object, Library, WebPage, Program ]) - - -
- -
- &Scanner; Objects - - - - &Scanner; objects perform automatic checking for dependencies - by scanning the contents of files. The canonical - example is scanning a C source file or header file for - files specified on #include lines. - - - - - - A &Scanner; object is instantiated as follows: - - - - - def c_scan(contents): - # scan contents of file - return # list of files found - - c_scanner = Scanner(name = 'CScan', function = c_scan, - argument = None, - skeys = ['.c', '.C', '.h', '.H') - - - - - The skeys argument specifies a list of file - suffixes for file types that this &Scanner; knows how to scan. - - - - - - &Scanner; objects are associated with a &consenv; through a - &consvar; named &SCANNERS;, a list of the &Scanner; objects that - will be available through the &consenv: - - - - - env = Environment(SCANNERS = [ CScan, M4Scan ]) - - - - - For utilities that will build files with a variety of file - suffixes, or which require unusual scanning rules, a &Scanner; - object may be associated explicitly with a &Builder; object as - follows: - - - - - def tool_scan(contents): - # scan contents of file - return # list of files found - - tool_scanner = Scanner(name = 'TScan', function = tool_scan) - - bld = Builder(name = 'Tool', scanner = tool_scanner) - - -
- -
- &BuildDir; - - - - &SCons; supports a flexible mechanism for building target - files in a separate build directory from the source files. - The &BuildDir; syntax is straightforward: - - - - - BuildDir(source = 'src', build = 'bld') - - - - - By - default, source files are linked or copied into the build - directory, because exactly replicating the source directory - is sometimes necessary for certain combinations of use of - #include "..." and search - paths. - - An option exists to specify that only output files should be placed in - the build directory: - - - - - BuildDir(source = 'src', build = 'bld', no_sources = 1) - - -
- -
- &Repository; - - - - &SCons; supports the ability to search a list of code repositories - for source files and derived files. This works much like - &Make;'s VPATH feature, as implemented in - recent versions of GNU &Make;. - (The POSIX standard for &Make; specifies slightly - different behavior for VPATH.) - The syntax is: - - - - - Repository('/home/source/1.1', '/home/source/1.0') - - - - - A command-line option exists to allow - repositories to be specified on the command line, or in the - &SCONSFLAGS; environment variable (not construction variable!). - This avoids a chicken-and-egg situation and allows the top-level - &SConstruct; file to be found in a repository as well. - - - -
- -
- &Cache; - - - - &SCons; supports a way for developers to share derived files. Again, the - syntax is straightforward: - - - - - Cache('/var/build.cache/i386') - - - - - Copies of any derived files built will be placed in the specified - directory with their MD5 signature. If another build results in an - out-of-date derived file with the same signature, the derived file - will be copied from the cache instead of being rebuilt. - - - -
- -
- -
- The &scons; Script - - - - The &scons; script provides an interface - that looks roughly equivalent to the - classic &Make; utility—that is, execution from the command - line, and dependency information read from configuration files. - - - - - - The most noticeable difference between &scons; and &Make;, or most - other build tools, is that the configuration files are actually - Python scripts, generically called "SConscripts" (although the - top-level "Makefile" is named &SConstruct). Users do not have to - learn a new language syntax, but instead configure dependency - information by making direct calls to the Python API of the - &SCons; Build Engine. Here is an example &SConstruct file which - builds a program in side-by-side normal and debug versions: - - - - - env = Environment() - debug = env.Copy(CCFLAGS = '-g') - - source_files = ['f1.c', 'f2.c', 'f3.c'] - - env.Program(target = 'foo', sources = source_files) - debug.Program(target = 'foo-debug', sources = source_files) - - - - - Notice the fact that this file is a Python script, which allows us - to define and re-use an array that lists the source files. - - - - - - Because quoting individul strings in long - lists of files can get tedious and error-prone, the &SCons; - methods support a short-cut of listing multiple files in a single - string, separated by white space. - This would change - the assignment in the above example to a more easily-readable: - - - - - source_files = 'f1.c f2.c f3.c' - - - - - The mechanism to establish hierarchical builds is to "include" any - subsidiary configuration files in the build by listing them explicitly - in a call to the &SConscript; function: - - - - - SConscript('src/SConscript', 'lib/SConscript') - - - - - By convention, configuration files in subdirectories are named - &SConscript;. - - - - - - The &scons; script has intentionally been made to look, from - the outside, as much like &Make; as is practical. To this - end, the &scons; script supports all of the same command-line - options supported by GNU &Make;: FILE, - , , , - etc. For compatibility, &scons; ignores those GNU &Make; options - that don't make sense for the &SCons; architecture, such as - , , , - and . The - intention is that, given an equivalent &SConstruct; file for a - &Makefile;, a user could use &SCons; as a drop-in replacement for - &Make;. Additional command-line options are, where possible, taken - from the Perl &Cons; utility on which the &SCons; design is based. - - - -
diff --git a/doc/python10/design.xml b/doc/python10/design.xml new file mode 100644 index 0000000..cb58af9 --- /dev/null +++ b/doc/python10/design.xml @@ -0,0 +1,898 @@ + + + The &SCons; architecture consists of three layers: + + + + + + + + + + + + + + + + + + + The &SCons; Build Engine, a package of Python + modules that handle dependency management and updating out-of-date + objects. + + + + + + + + The &SCons; API (applications programming + interface) between the Build Engine + and the user interface. + + + + + + + + The &scons; script itself (note lower case + sc), which is the pre-provided interface to + the Build Engine. + + + + + + + + + Notice that this architecture separates the internal workings of + &SCons; (the Build Engine) from the + external user interface. The benefit is that the &SCons; Build Engine + can be imported into any other software package written in Python + to support a variety of user interfaces—or, to look at it + in reverse, other software interfaces can use the &SCons; Build + Engine to manage dependencies between their objects. + + + + + + Because the + &SCons; package itself is modular, only those parts of the package + relevant to the embedding interface need be imported; for example, + a utility that wants to use only file timestamps for checking + whether a file is up-to-date + need not import the MD5 signature module. + + + +
+ The &SCons; Build Engine + + + + The Build Engine is a package of Python modules that + form the heart of &SCons;. + + The Build Engine can be broadly divided into five + architectural subsystems, each responsible + for a crucial part of &SCons; functionality: + + + + + + + + + A node subsystem, responsible for managing + the files (or other objects) to be built, and the dependency + relationships between them. + + + + + + + + A scanner subsystem, responsible for + scanning various file types for implicit dependencies. + + + + + + + + A signature subsystem, responsible for + deciding whether a given file (or other object) requires + rebuilding. + + + + + + + + A builder subsystem, responsible for + actually executing the necessary command or function to + build a file (or other object). + + + + + + + + A job/task subsystem, responsible for + handling parallelization of builds. + + + + + + + + + The rest of this section will provide a high-level overview of the + class structure of each of these Build Engine subsystems. + + + +
+ Node Subsystem + + + + The node subsystem of the Build Engine is + responsible for managing the knowledge in &SCons; of + the relationships among the external objects + (files) it is responsible for updating. + The most important of these relationships is + the dependency relationship between various &Node; objects, + which &SCons; uses to determine the order + in which builds should be performed. + + + + + + + + + + + + + + + + The &scons; script (or other + user interface) + tells the Build Engine + about dependencies + through its &consenv; API. + The Build Engine also discovers + dependencies automatically through the use of &Scanner; objects. + + + + + + Subclasses of the &Node; class maintain additional + relationships that reflect the real-world + existence of these objects. + For example, the &Node_FS; subclass + is responsible for managing a + representation of the directory hierarchy + of a file system. + + + + + + A &Walker; class is used by other subsystems + to walk the dependency tree maintained by the &Node; class. + The &Walker; class maintains a stack of &Node; objects + visited during its depth-first traversal of the + dependency tree, + and uses an intermediate node &Wrapper; class + to maintain state information about a + &Node; object's dependencies. + + + +
+ +
+ Scanner Subsystem + + + + The scanner subsystem is responsible for maintaining + objects that can scan the contents of a &Node;'s + for implicit dependencies. + + + + + + + + + + + + + + + + In practice, a given &Scanner; subclass object + functions as a prototype, + returning clones of itself + depending on the &consenv; + values governing how the &Node; + should be scanned. + + + +
+ +
+ Signature Subsystem + + + + The signature subsystem is responsible for computing + signature information for &Node; objects. + The signature subsystem in &SCons; + supports multiple ways to + determine whether a &Node is up-to-date + by using an abstract &Sig; class + as a strategy wrapper: + + + + + + + + + + + + + + + + By default, &SCons; tracks dependencies by computing and + maintaining MD5 signatures for the contents of each source file + (or other object). The signature of a derived + file consists of the aggregate of the signatures of all the source + files plus the command-line string used to + build the file. These signatures are stored in a &sconsign; file + in each directory. + + + + + + If the contents of any of the source files changes, the change to its + MD5 signature is propogated to the signature of the derived file(s). The + simple fact that the new signature does not match the stored signature + indicates that the derived file is not up to date and must be rebuilt. + + + + + + A separate &TimeStamp; subclass of the &Sig; class supports + the use of traditional file timestamps for + deciding whether files are up-to-date. + + + +
+ +
+ Builder Subsystem + + + + The &SCons; Build Engine records how out-of-date files + (or other objects) should be rebuilt in &Builder; objects, + maintained by the builder subsystem: + + + + + + + + + + + + + + + + The actual underlying class name is &BuilderBase;, + and there are subclasses that can encapsulate + multiple &Builder; objects for special purposes. + One subclass + (&CompositeBuilder;) + selects an appropriate encapsulated &Builder; + based on the file suffix of the target object. + The other + (&MultiStepBuilder;). + can chain together multiple + &Builder; objects, + for example, + to build an executable program from a source file + through an implicit intermediate object file. + + + + + + A &BuilderBase; object has an associated + &ActionBase; object + responsible for actually executing + the appropriate steps + to update the target file. + There are three subclasses, + one for externally executable commands + (&CommandAction;), + one for Python functions + (&FunctionAction;), + and one for lists of + multiple &Action; objects + (&ListAction;). + + + +
+ +
+ Job/Task Subsystem + + + + &SCons; supports parallel builds with a thread-based tasking + model, managed by the job/task subsystem. + + + + + + + + + + + + + + + + Instead of performing an outer-loop recursive descent + of the dependency tree and then forking a task when it finds a + file that needs updating, &SCons; starts as many threads as are + requested, each thread managed by the &Jobs; class. + As a performance optimization, + the &Jobs; class maintains an internal + distinction between + &Serial; and &Parallel; + build jobs, + so that serial builds + don't pay any performance penalty + by using a multi-threaded implementation + written for &Parallel; builds. + + + + + + Each &Jobs; object, running in its own thread, + then requests a &Task; from a central &Taskmaster;, + which is responsible + for handing out available &Task; objects for (re-)building + out-of-date nodes. A condition variable + makes sure that the &Jobs; objects + query the &Taskmaster; one at a time. + + + + + + The &Taskmaster uses the node subsystem's + &Walker; class to walk the dependency tree, + and the &Sig; class to use the + appropriate method + of deciding if a &Node; is up-to-date. + + + + + + This scheme has many advantages over the standard &Make; + implementation of . + Effective use of is difficult + with the usual recursive use of Make, + because the number of jobs started by multiply + at each level of the source tree. + This makes the actual number of jobs + executed at any moment very dependent on the size and layout of + the tree. &SCons;, in contrast, starts only as many jobs as are + requested, and keeps them constantly busy (excepting jobs that + block waiting for their dependency files to finish building). + + + +
+ +
+ +
+ The &SCons; API + + + + This section provides an overview of the &SCons; interface. The + complete interface specification is both more detailed and flexible + than this overview. + + + +
+ &ConsVars; + + + + In &SCons;, a &consenv; is an object through which an external + interface (such as the &scons; script) communicates dependency + information to the &SCons; Build Engine. + + + + + + A construction environment is implemented as a dictionary + containing: + + + + + + + + + construction variables, string values that are substituted + into command lines or used by builder functions; + + + + + + + + one or more &Builder; objects that can be invoked to update a + file or other object; + + + + + + + + one or more &Scanner; objects that can be used to + scan a file automatically for dependencies (such as + files specified on #include lines). + + + + + + + + + &Consenvs; are instantiated as follows: + + + + + env = Environment() + env_debug = Environment(CCFLAGS = '-g') + + +
+ +
+ &Builder; Objects + + + + An &SCons; &Builder; object encapsulates information about how to + build a specific type of file: an executable program, an object + file, a library, etc. A &Builder; object is associated with a + file through an associated &consenv; method and later invoked to + actually build the file. The &Builder; object will typically use + construction variables (such as &CCFLAGS;, &LIBPATH;) to influence + the specific build execution. + + + + + + &Builder; objects are instantiated as follows: + + + + + bld = Builder(name = 'Program', action = "$CC -o $TARGET $SOURCES") + + + + + In the above example, the action is a + command-line string in which the Build Engine will + interpolate the values of construction + variables before execution. The actual + action specified, though, + may be a function: + + + + + def update(dest): + # [code to update the object] + return 0 + + bld = Builder(name = 'Program', function = update) + + + + + Or a callable Python object (or class): + + + + + class class_a: + def __call__(self, kw): + # build the desired object + return 0 + + builder = SCons.Builder.Builder(action = class_a()) + + + + + A &Builder; object may have the prefix and + suffix of its target file type specified + as keyword arguments at instantiation. Additionally, the + suffix of the source files used by this + &Builder; to build its target files may be specified using the + src_suffix keyword argument: + + + + + bld_lib = Builder(name = 'Library', action = "$AR r $TARGET $SOURCES", + prefix = 'lib', suffix = '.a', src_suffix = '.o') + + + + + The specified prefix and + suffix will be appended to the name of any + target file built by this &Builder; object, if they are not + already part of the file name. The src_suffix + is used by the &SCons; Build Engine to chain together + multiple &Builder; objects to create, + for example, a library from the original source + files without having to specify the + intermediate .o files. + + + + + + &Builder; objects are associated with a &consenv; through a + &consvar; named &BUILDERS;, a list of the &Builder objects that + will be available for execution through the &consenv: + + + + + env = Environment(BUILDERS = [ Object, Library, WebPage, Program ]) + + +
+ +
+ &Scanner; Objects + + + + &Scanner; objects perform automatic checking for dependencies + by scanning the contents of files. The canonical + example is scanning a C source file or header file for + files specified on #include lines. + + + + + + A &Scanner; object is instantiated as follows: + + + + + def c_scan(contents): + # scan contents of file + return # list of files found + + c_scanner = Scanner(name = 'CScan', function = c_scan, + argument = None, + skeys = ['.c', '.C', '.h', '.H') + + + + + The skeys argument specifies a list of file + suffixes for file types that this &Scanner; knows how to scan. + + + + + + &Scanner; objects are associated with a &consenv; through a + &consvar; named &SCANNERS;, a list of the &Scanner; objects that + will be available through the &consenv: + + + + + env = Environment(SCANNERS = [ CScan, M4Scan ]) + + + + + For utilities that will build files with a variety of file + suffixes, or which require unusual scanning rules, a &Scanner; + object may be associated explicitly with a &Builder; object as + follows: + + + + + def tool_scan(contents): + # scan contents of file + return # list of files found + + tool_scanner = Scanner(name = 'TScan', function = tool_scan) + + bld = Builder(name = 'Tool', scanner = tool_scanner) + + +
+ +
+ &BuildDir; + + + + &SCons; supports a flexible mechanism for building target + files in a separate build directory from the source files. + The &BuildDir; syntax is straightforward: + + + + + BuildDir(source = 'src', build = 'bld') + + + + + By + default, source files are linked or copied into the build + directory, because exactly replicating the source directory + is sometimes necessary for certain combinations of use of + #include "..." and search + paths. + + An option exists to specify that only output files should be placed in + the build directory: + + + + + BuildDir(source = 'src', build = 'bld', no_sources = 1) + + +
+ +
+ &Repository; + + + + &SCons; supports the ability to search a list of code repositories + for source files and derived files. This works much like + &Make;'s VPATH feature, as implemented in + recent versions of GNU &Make;. + (The POSIX standard for &Make; specifies slightly + different behavior for VPATH.) + The syntax is: + + + + + Repository('/home/source/1.1', '/home/source/1.0') + + + + + A command-line option exists to allow + repositories to be specified on the command line, or in the + &SCONSFLAGS; environment variable (not construction variable!). + This avoids a chicken-and-egg situation and allows the top-level + &SConstruct; file to be found in a repository as well. + + + +
+ +
+ &Cache; + + + + &SCons; supports a way for developers to share derived files. Again, the + syntax is straightforward: + + + + + Cache('/var/build.cache/i386') + + + + + Copies of any derived files built will be placed in the specified + directory with their MD5 signature. If another build results in an + out-of-date derived file with the same signature, the derived file + will be copied from the cache instead of being rebuilt. + + + +
+ +
+ +
+ The &scons; Script + + + + The &scons; script provides an interface + that looks roughly equivalent to the + classic &Make; utility—that is, execution from the command + line, and dependency information read from configuration files. + + + + + + The most noticeable difference between &scons; and &Make;, or most + other build tools, is that the configuration files are actually + Python scripts, generically called "SConscripts" (although the + top-level "Makefile" is named &SConstruct). Users do not have to + learn a new language syntax, but instead configure dependency + information by making direct calls to the Python API of the + &SCons; Build Engine. Here is an example &SConstruct file which + builds a program in side-by-side normal and debug versions: + + + + + env = Environment() + debug = env.Copy(CCFLAGS = '-g') + + source_files = ['f1.c', 'f2.c', 'f3.c'] + + env.Program(target = 'foo', sources = source_files) + debug.Program(target = 'foo-debug', sources = source_files) + + + + + Notice the fact that this file is a Python script, which allows us + to define and re-use an array that lists the source files. + + + + + + Because quoting individul strings in long + lists of files can get tedious and error-prone, the &SCons; + methods support a short-cut of listing multiple files in a single + string, separated by white space. + This would change + the assignment in the above example to a more easily-readable: + + + + + source_files = 'f1.c f2.c f3.c' + + + + + The mechanism to establish hierarchical builds is to "include" any + subsidiary configuration files in the build by listing them explicitly + in a call to the &SConscript; function: + + + + + SConscript('src/SConscript', 'lib/SConscript') + + + + + By convention, configuration files in subdirectories are named + &SConscript;. + + + + + + The &scons; script has intentionally been made to look, from + the outside, as much like &Make; as is practical. To this + end, the &scons; script supports all of the same command-line + options supported by GNU &Make;: FILE, + , , , + etc. For compatibility, &scons; ignores those GNU &Make; options + that don't make sense for the &SCons; architecture, such as + , , , + and . The + intention is that, given an equivalent &SConstruct; file for a + &Makefile;, a user could use &SCons; as a drop-in replacement for + &Make;. Additional command-line options are, where possible, taken + from the Perl &Cons; utility on which the &SCons; design is based. + + + +
diff --git a/doc/python10/future.sgml b/doc/python10/future.sgml deleted file mode 100644 index 272d508..0000000 --- a/doc/python10/future.sgml +++ /dev/null @@ -1,170 +0,0 @@ - - - There are a number of things we would like to do to continue to - improve &SCons; in the future. - - - -
- Distutils Cooperation - - - - There is a certain amount of overlap between what &SCons; does - to search out and make use of various compilers on a system, and - the impressively complete job that the Distutils do of describing - much the same thing. Collaborating to provide some sort of common - interface between the two tools would benefit both tools. - - - -
- -
- Additional Builder Support - - - - Adding additional builders would broaden the - potential user base. In rough order of importance: - - - - - - - Java - - - - Given the popularity of Java, support for it would greatly - increase the appeal of &SCons; in the large community of Java - users. - - - - - - Good support for Java is, however, a tricky - proposition. Because the Java compiler can make decisions - about compiling other files based on what classes it finds - in a file, it behaves "unpredictably" from the point of - view of an outside build tool like &SCons; or &Make;. Some - sort of sophisticated scanning of Java source code to - identify what other classes are likely to be compiled - would be an obvious first step, but notice that here - &SCons; would be scanning the file to find additional - targets to be built. This is the inverse of the sort of - #include scanning performed - for C files, in which &SCons; is looking for additional - dependencies. - - - - - - - Documentation toolchains - - - - A number of early adopters - are using &SCons; to - build documents - from TeX or DocBook source files. - Built-in support for - various documentation toolchains - would be an obvious boon - for many people. - - - - - - - C# - - - - The reality is that anything that Microsoft does will doubtless - have a wide audience. Turning &SCons;' back on that would be - cutting off its nose to spite its face. - - - - - - - Fortran - - - - Despite the fact that &SCons; is no longer directly - associated with Software Carpentry, it still shares the - same goal: to make programming easier for more than just - programmers. To that end, good Fortran support would - help a great many physical scientists and other computer - users out there who still rely on Fortran - for a great deal of their work. - - - - - - - -
- -
- Database Interface - - - - The Nodes in an &SCons; dependency graph aren't only restricted to - files. Creating an interface to mSQL or MySQL databases would allow - the possibility of updating external files in response to changes in - database fields, or vice versa. This could be handy, for example, - for generating a cache of static web pages from a database that only - need re-generating when the appropriate database objects change. - - - -
- -
- Tool Integration - - - - &SCons; should work well with as many popular Integrated Development - Environments (IDEs) and tool chains as possible: Komodo, Microsoft - Visual Studio, ClearCase, etc. Suggestions for additional tools are - welcome. - - - -
- -
- Makefile Interface - - - - Because the &SCons; Build Engine can be embedded in any Python - interface, there isn't any technical reason why a &Makefile; - interpreter couldn't be written in Python and use the &SCons; Build - Engine for its dependency analysis. - - - - - - Proof-of-concept for the idea already exists. Gary Holt's - make++ (also known as makepp) - is a Perl implementation of just such a &Makefile; interpreter. It - could possible serve as a model for a Python version, in much the - same way the &Cons; design served as the prototype for &SCons;. - - - -
diff --git a/doc/python10/future.xml b/doc/python10/future.xml new file mode 100644 index 0000000..272d508 --- /dev/null +++ b/doc/python10/future.xml @@ -0,0 +1,170 @@ + + + There are a number of things we would like to do to continue to + improve &SCons; in the future. + + + +
+ Distutils Cooperation + + + + There is a certain amount of overlap between what &SCons; does + to search out and make use of various compilers on a system, and + the impressively complete job that the Distutils do of describing + much the same thing. Collaborating to provide some sort of common + interface between the two tools would benefit both tools. + + + +
+ +
+ Additional Builder Support + + + + Adding additional builders would broaden the + potential user base. In rough order of importance: + + + + + + + Java + + + + Given the popularity of Java, support for it would greatly + increase the appeal of &SCons; in the large community of Java + users. + + + + + + Good support for Java is, however, a tricky + proposition. Because the Java compiler can make decisions + about compiling other files based on what classes it finds + in a file, it behaves "unpredictably" from the point of + view of an outside build tool like &SCons; or &Make;. Some + sort of sophisticated scanning of Java source code to + identify what other classes are likely to be compiled + would be an obvious first step, but notice that here + &SCons; would be scanning the file to find additional + targets to be built. This is the inverse of the sort of + #include scanning performed + for C files, in which &SCons; is looking for additional + dependencies. + + + + + + + Documentation toolchains + + + + A number of early adopters + are using &SCons; to + build documents + from TeX or DocBook source files. + Built-in support for + various documentation toolchains + would be an obvious boon + for many people. + + + + + + + C# + + + + The reality is that anything that Microsoft does will doubtless + have a wide audience. Turning &SCons;' back on that would be + cutting off its nose to spite its face. + + + + + + + Fortran + + + + Despite the fact that &SCons; is no longer directly + associated with Software Carpentry, it still shares the + same goal: to make programming easier for more than just + programmers. To that end, good Fortran support would + help a great many physical scientists and other computer + users out there who still rely on Fortran + for a great deal of their work. + + + + + + + +
+ +
+ Database Interface + + + + The Nodes in an &SCons; dependency graph aren't only restricted to + files. Creating an interface to mSQL or MySQL databases would allow + the possibility of updating external files in response to changes in + database fields, or vice versa. This could be handy, for example, + for generating a cache of static web pages from a database that only + need re-generating when the appropriate database objects change. + + + +
+ +
+ Tool Integration + + + + &SCons; should work well with as many popular Integrated Development + Environments (IDEs) and tool chains as possible: Komodo, Microsoft + Visual Studio, ClearCase, etc. Suggestions for additional tools are + welcome. + + + +
+ +
+ Makefile Interface + + + + Because the &SCons; Build Engine can be embedded in any Python + interface, there isn't any technical reason why a &Makefile; + interpreter couldn't be written in Python and use the &SCons; Build + Engine for its dependency analysis. + + + + + + Proof-of-concept for the idea already exists. Gary Holt's + make++ (also known as makepp) + is a Perl implementation of just such a &Makefile; interpreter. It + could possible serve as a model for a Python version, in much the + same way the &Cons; design served as the prototype for &SCons;. + + + +
diff --git a/doc/python10/install.sgml b/doc/python10/install.sgml deleted file mode 100644 index d150beb..0000000 --- a/doc/python10/install.sgml +++ /dev/null @@ -1,179 +0,0 @@ - - - Initial installation of a new utility provides the first, lasting - impression of how well the software is likely to perform. From the - start, &SCons; has made clean installation a priority. - - - -
- Version Control - - - - Distributing an application like &SCons; that depends - on a package normally found in a library poses a - problem. If the &scons; script and the &SCons; Build Engine - are installed separately, it could be easy - to introduce a version mismatch between the Build Engine - installed in - /usr/lib/python*/site-packages - and the &scons; script installed in - /usr/bin. - Such a mismatch - could possible mean exceptions that prevent builds, or even worse, - silently unreliable builds. - - - - - - To reduce the possibility of a version mismatch, - the &scons; script looks first for its - imported modules in /usr/lib/scons-{version}/, - then in /usr/lib/scons/, - and then in the normal &PYTHONPATH; locations, - including /usr/lib/python*/site-packages). - Searching in a version-specific library directory first - makes it convenient to install and use multiple - side-by-side versions of &SCons;, - which is sometimes important - when verifying that a new version does not introduce any - errors into the local build process. - Searching next in an &SCons;-specific library directory - makes it convenient for other software to find - the &SCons; Build Engine without having to worry about - installing separate copies for - multiple versions of Python. - - - -
- -
- Packages - - - - &SCons; is currently distributed in the following packages: - - - - - - - - - - - scons-version.tar.gz - - - - The traditional .tar.gz file, - installable by running setup.py. - - - - - - - scons-version.noarch.rpm - - - - An RPM file for typical installation. - - - - - - - scons-version_all.deb - - - - A Debian package. - - - - - - - scons-version.win32.exe - - - - A Windows installer. - - - - - - - scons-version.src.rpm - - - - A source RPM file. - - - - - - - scons-src-version.tar.gz - - - - A tarball of the &SCons; source tree, - including the full set of regression tests. - - - - - - -
- - - - Like other software written in Python, &SCons; benefits greatly from - the tremendous effort put into the distutils by - Greg Ward and others. These take care of 90% of the work by making - it almost trivial to generate the appropriate RPM files, Debian - packages, and Windows installer. - - - -
- -
- Default Builder Objects - - - - As part of the installation process, &SCons; runs a set of scripts - that look for popular compilers and other tools and set up - appropriate default &Builder; objects for the tools found. These - &Builder; objects are then used to initialize the default &consenv; - values. - - - -
- -
- Default Scanner Objects - - - - Additionally, &SCons; comes with a stock set of &Scanner; objects - for the various file types that it supports out of the box. Any - unusal &Scanner; objects required for a specific tool will be - detected at installation time and associated with the appropriate - &Builder; object for the tool. - - - -
diff --git a/doc/python10/install.xml b/doc/python10/install.xml new file mode 100644 index 0000000..d150beb --- /dev/null +++ b/doc/python10/install.xml @@ -0,0 +1,179 @@ + + + Initial installation of a new utility provides the first, lasting + impression of how well the software is likely to perform. From the + start, &SCons; has made clean installation a priority. + + + +
+ Version Control + + + + Distributing an application like &SCons; that depends + on a package normally found in a library poses a + problem. If the &scons; script and the &SCons; Build Engine + are installed separately, it could be easy + to introduce a version mismatch between the Build Engine + installed in + /usr/lib/python*/site-packages + and the &scons; script installed in + /usr/bin. + Such a mismatch + could possible mean exceptions that prevent builds, or even worse, + silently unreliable builds. + + + + + + To reduce the possibility of a version mismatch, + the &scons; script looks first for its + imported modules in /usr/lib/scons-{version}/, + then in /usr/lib/scons/, + and then in the normal &PYTHONPATH; locations, + including /usr/lib/python*/site-packages). + Searching in a version-specific library directory first + makes it convenient to install and use multiple + side-by-side versions of &SCons;, + which is sometimes important + when verifying that a new version does not introduce any + errors into the local build process. + Searching next in an &SCons;-specific library directory + makes it convenient for other software to find + the &SCons; Build Engine without having to worry about + installing separate copies for + multiple versions of Python. + + + +
+ +
+ Packages + + + + &SCons; is currently distributed in the following packages: + + + + + + + + + + + scons-version.tar.gz + + + + The traditional .tar.gz file, + installable by running setup.py. + + + + + + + scons-version.noarch.rpm + + + + An RPM file for typical installation. + + + + + + + scons-version_all.deb + + + + A Debian package. + + + + + + + scons-version.win32.exe + + + + A Windows installer. + + + + + + + scons-version.src.rpm + + + + A source RPM file. + + + + + + + scons-src-version.tar.gz + + + + A tarball of the &SCons; source tree, + including the full set of regression tests. + + + + + + +
+ + + + Like other software written in Python, &SCons; benefits greatly from + the tremendous effort put into the distutils by + Greg Ward and others. These take care of 90% of the work by making + it almost trivial to generate the appropriate RPM files, Debian + packages, and Windows installer. + + + +
+ +
+ Default Builder Objects + + + + As part of the installation process, &SCons; runs a set of scripts + that look for popular compilers and other tools and set up + appropriate default &Builder; objects for the tools found. These + &Builder; objects are then used to initialize the default &consenv; + values. + + + +
+ +
+ Default Scanner Objects + + + + Additionally, &SCons; comes with a stock set of &Scanner; objects + for the various file types that it supports out of the box. Any + unusal &Scanner; objects required for a specific tool will be + detected at installation time and associated with the appropriate + &Builder; object for the tool. + + + +
diff --git a/doc/python10/intro.sgml b/doc/python10/intro.sgml deleted file mode 100644 index d3057be..0000000 --- a/doc/python10/intro.sgml +++ /dev/null @@ -1,212 +0,0 @@ - - - More than twenty years after its creation, the classic UNIX &Make; - utility and its descendants are still the dominant way in which - software is built. &Make; has maintained this position despite the - fact that the intervening years have revealed many - shortcomings of the &Make; model for building software: - - - - - - - - - The use of timestamps to decide when a file has been updated is - imprecise and prone to error, especially across distributed file - systems such as NFS. - - - - - - - - Builds of typical large software systems still take hours, if not - days, despite the tremendous advances in CPU and disk speeds over - recent years. - - - - - - - - &Make; maintains static definitions of dependencies in its - &Makefiles;. Much effort has been put into - utilities (mkdepend, gcc - -M) and schemes (Makefile.d - files) to try to keep &Makefile; dependencies up-to-date, - but these only confirm that &Make;'s static dependencies are - inherently fragile. - - - - - - - - The standard recursive use of &Make; for build hierarchies leads - to incomplete dependency graphs, which must be overcome by - manually changing the order in which directories are built, or - through the use of multiple build passes. - - - - - - - - - One need only look at the plethora of helper and wrapper utilities - (automake, easymake, imake, jmake, makeLib, maketool, mkmed, shake, - SMake, TMAKE) and complete alternatives to &Make; (Ant, bake, bau, - bras, Cake, Cons, Cook, Jam, jmk, jus, makeme, mash, MK, nmake, Odin, - VMake) that have been created over the years to realize that vanilla - &Make; is not satisfying everyone's build requirements. So why Yet - Another build tool? - - - -
- Enter Software Carpentry - - - - Most of the build tools just mentioned - were written by programmers and for - programmers. The fact that most programmer-friendly - utilities do a poor job of fulfilling the needs - of non-programmers prompted Greg Wilson to - organize the Software Carpentry competition in January 2000. - Software Carpentry was an - open design contest with the express goal of producing a set of - next-generation utilities, including a build tool, that would be - accessible - not only to - programmers - but also to computer users - such as physical scientists. - - - - - - The key to this usability would be that all of - these utilities, including the build tool, would be - written in Python. - This provided the catalyst for actually - pursuing an idea - that had been floating around one of the more - intriguing &Make; alternatives, - a Perl utility called &Cons;. - What if the friendlier syntax of Python - could be married to the - architectural advantages of &Cons;? - - - - - - The resulting merged design, at that time named &ScCons;, - won the Software Carpentry build tool competition. CodeSourcery (by - then the administrators of the competition) ultimately decided not to - fund development of the build tool, but the seed had been planted and the - design had taken root. - - - -
- -
- Cons - - - - It helps to know something about &Cons;. - &Cons; was first released in 1996 by Bob Sidebotham, - then an employee of Fore Systems, - and it has a number of - distinctive features that set it apart from most &Make;-alikes: - - - - - - - - - &Cons; "configuration files" are not Yet Another - invented mini-language, but are actually Perl - scripts, which means the full power and flexibility of - a real scripting language can be applied to build problems. - - - - - - - - &Cons; builds everything from a single process at the top of the - source tree, with a global view of the dependencies. - - - - - - - - &Cons; scans files automatically for dependencies such as - files specified on #include lines. - - - - - - - - &Cons; decides if a file was out-of-date by using MD5 checksums of - the contents of files, not timestamps. - - - - - - - - - Despite all of these intriguing architectural features, the great - strength of &Cons;—being written in Perl—was also one of - its weaknesses, turning away many potential users due to the - (real or perceived) steep learning curve of Perl. - - - -
- -
- &SCons; - - - - Through the &ScCons; contest entry, - &SCons; is the direct descendant of the &Cons; architecture, - and is currently - under active, supported development with a growing body of - users. Its first release was 13 December 2001, under the simple and - non-restrictive MIT license, and from the outset, the goal of the - members of the &SCons; project has been to deliver a stable, reliable - tool that can be used for industrial-strength software builds. - - - - - - The rest of this paper will give an overview of the &SCons; design - (including its architecture and interface), describe the development - methodology used, and discuss future directions for &SCons;. - - - -
diff --git a/doc/python10/intro.xml b/doc/python10/intro.xml new file mode 100644 index 0000000..d3057be --- /dev/null +++ b/doc/python10/intro.xml @@ -0,0 +1,212 @@ + + + More than twenty years after its creation, the classic UNIX &Make; + utility and its descendants are still the dominant way in which + software is built. &Make; has maintained this position despite the + fact that the intervening years have revealed many + shortcomings of the &Make; model for building software: + + + + + + + + + The use of timestamps to decide when a file has been updated is + imprecise and prone to error, especially across distributed file + systems such as NFS. + + + + + + + + Builds of typical large software systems still take hours, if not + days, despite the tremendous advances in CPU and disk speeds over + recent years. + + + + + + + + &Make; maintains static definitions of dependencies in its + &Makefiles;. Much effort has been put into + utilities (mkdepend, gcc + -M) and schemes (Makefile.d + files) to try to keep &Makefile; dependencies up-to-date, + but these only confirm that &Make;'s static dependencies are + inherently fragile. + + + + + + + + The standard recursive use of &Make; for build hierarchies leads + to incomplete dependency graphs, which must be overcome by + manually changing the order in which directories are built, or + through the use of multiple build passes. + + + + + + + + + One need only look at the plethora of helper and wrapper utilities + (automake, easymake, imake, jmake, makeLib, maketool, mkmed, shake, + SMake, TMAKE) and complete alternatives to &Make; (Ant, bake, bau, + bras, Cake, Cons, Cook, Jam, jmk, jus, makeme, mash, MK, nmake, Odin, + VMake) that have been created over the years to realize that vanilla + &Make; is not satisfying everyone's build requirements. So why Yet + Another build tool? + + + +
+ Enter Software Carpentry + + + + Most of the build tools just mentioned + were written by programmers and for + programmers. The fact that most programmer-friendly + utilities do a poor job of fulfilling the needs + of non-programmers prompted Greg Wilson to + organize the Software Carpentry competition in January 2000. + Software Carpentry was an + open design contest with the express goal of producing a set of + next-generation utilities, including a build tool, that would be + accessible + not only to + programmers + but also to computer users + such as physical scientists. + + + + + + The key to this usability would be that all of + these utilities, including the build tool, would be + written in Python. + This provided the catalyst for actually + pursuing an idea + that had been floating around one of the more + intriguing &Make; alternatives, + a Perl utility called &Cons;. + What if the friendlier syntax of Python + could be married to the + architectural advantages of &Cons;? + + + + + + The resulting merged design, at that time named &ScCons;, + won the Software Carpentry build tool competition. CodeSourcery (by + then the administrators of the competition) ultimately decided not to + fund development of the build tool, but the seed had been planted and the + design had taken root. + + + +
+ +
+ Cons + + + + It helps to know something about &Cons;. + &Cons; was first released in 1996 by Bob Sidebotham, + then an employee of Fore Systems, + and it has a number of + distinctive features that set it apart from most &Make;-alikes: + + + + + + + + + &Cons; "configuration files" are not Yet Another + invented mini-language, but are actually Perl + scripts, which means the full power and flexibility of + a real scripting language can be applied to build problems. + + + + + + + + &Cons; builds everything from a single process at the top of the + source tree, with a global view of the dependencies. + + + + + + + + &Cons; scans files automatically for dependencies such as + files specified on #include lines. + + + + + + + + &Cons; decides if a file was out-of-date by using MD5 checksums of + the contents of files, not timestamps. + + + + + + + + + Despite all of these intriguing architectural features, the great + strength of &Cons;—being written in Perl—was also one of + its weaknesses, turning away many potential users due to the + (real or perceived) steep learning curve of Perl. + + + +
+ +
+ &SCons; + + + + Through the &ScCons; contest entry, + &SCons; is the direct descendant of the &Cons; architecture, + and is currently + under active, supported development with a growing body of + users. Its first release was 13 December 2001, under the simple and + non-restrictive MIT license, and from the outset, the goal of the + members of the &SCons; project has been to deliver a stable, reliable + tool that can be used for industrial-strength software builds. + + + + + + The rest of this paper will give an overview of the &SCons; design + (including its architecture and interface), describe the development + methodology used, and discuss future directions for &SCons;. + + + +
diff --git a/doc/python10/main.sgml b/doc/python10/main.sgml deleted file mode 100644 index ff1e317..0000000 --- a/doc/python10/main.sgml +++ /dev/null @@ -1,218 +0,0 @@ - - - - %scons; - - - - - - - - - - -]> - -
- - SCons Design and Implementation - - - Steven - Knight - - - - 2001 - 2002 - Steven Knight - - - 2002 - - - 4-7 February 2002 - The Tenth International Python Conference -
Alexandria, Virginia
-
- - - - 0.2 - 16 December 2001 - Internal re-review. - - - 0.1 - 8 October 2001 - Submitted for Python10 conference. - - - -
- - - &abstract; - - -
- Introduction - &intro; -
- -
- Architecture - &design; -
- -
- Installation - &install; -
- -
- Development Process - &process; -
- -
- Future Directions - &future; -
- -
- Summary - - - This paper has introduced &SCons;, a next-generation build tool - with a modular, embeddable architecture and a direct Python - interface. &SCons; has a global view of the dependencies in a source - tree, uses MD5 signatures to decide if derived files are out of date, - and automatically scans files for dependencies, all of which make &SCons; - builds exceptionally reliable. The &SCons; development methodology has - been described, notable for its emphasis on automated regression - testing to ensure a robust and reliable tool from day one. Several - future directions for &SCons; have also been discussed. - - -
- -
- Acknowledgements - &acks; -
- - - - - References - - - 1 - - Stuart I.Feldman - - - Aug 1978 - - - Bell Laboratories - - Make - A Program for Maintaining Computer Programs - - - - 2 - - PeterMiller - - - 1997 - Peter Miller - - Recursive Make Considered Harmful - - - - - 3 - - AndrewOram - SteveTalbott - - - 1986 - 1991 - O'Reilly & Associates, Inc. - - - O'Reilly & Associates, Inc. - - Managing Projects with Make, 2nd Ed. - - - - 4 - - Richard M.Stallman - RolandMcGrath - - - 1988 - '89 - '90 - '91 - '92 - '93 - '94 - '95 - '96 - '97 - '98 - '99 - 2000 - Free Software Foundation, Inc. - - - Free Software Foundation, Inc. - - GNU Make - A Program for Directing Recompilation - - - - -
diff --git a/doc/python10/main.xml b/doc/python10/main.xml new file mode 100644 index 0000000..42bc4af --- /dev/null +++ b/doc/python10/main.xml @@ -0,0 +1,221 @@ + + + + + + %scons; + + + + + + + + + + +]> + +
+ + SCons Design and Implementation + + + Steven + Knight + + + + 2001 + 2002 + Steven Knight + + + 2002 + + + 4-7 February 2002 + The Tenth International Python Conference +
Alexandria, Virginia
+
+ + + + 0.2 + 16 December 2001 + Internal re-review. + + + 0.1 + 8 October 2001 + Submitted for Python10 conference. + + + +
+ + + &abstract; + + +
+ Introduction + &intro; +
+ +
+ Architecture + &design; +
+ +
+ Installation + &install; +
+ +
+ Development Process + &process; +
+ +
+ Future Directions + &future; +
+ +
+ Summary + + + This paper has introduced &SCons;, a next-generation build tool + with a modular, embeddable architecture and a direct Python + interface. &SCons; has a global view of the dependencies in a source + tree, uses MD5 signatures to decide if derived files are out of date, + and automatically scans files for dependencies, all of which make &SCons; + builds exceptionally reliable. The &SCons; development methodology has + been described, notable for its emphasis on automated regression + testing to ensure a robust and reliable tool from day one. Several + future directions for &SCons; have also been discussed. + + +
+ +
+ Acknowledgements + &acks; +
+ + + + + References + + + 1 + + Stuart I.Feldman + + + Aug 1978 + + + Bell Laboratories + + Make - A Program for Maintaining Computer Programs + + + + 2 + + PeterMiller + + + 1997 + Peter Miller + + Recursive Make Considered Harmful + + + + + 3 + + AndrewOram + SteveTalbott + + + 1986 + 1991 + O'Reilly & Associates, Inc. + + + O'Reilly & Associates, Inc. + + Managing Projects with Make, 2nd Ed. + + + + 4 + + Richard M.Stallman + RolandMcGrath + + + 1988 + '89 + '90 + '91 + '92 + '93 + '94 + '95 + '96 + '97 + '98 + '99 + 2000 + Free Software Foundation, Inc. + + + Free Software Foundation, Inc. + + GNU Make + A Program for Directing Recompilation + + + + +
diff --git a/doc/python10/process.sgml b/doc/python10/process.sgml deleted file mode 100644 index f1b2479..0000000 --- a/doc/python10/process.sgml +++ /dev/null @@ -1,353 +0,0 @@ - - - The &SCons; project has paid particular attention from day one to the - development process. One of the first internal documents produced was - a set of Developer's Guidelines to provide a loose framework for what - we were trying to accomplish and how we would go about accomplishing - it. These Guidelines cover things like: - - - - - - - - - &SCons; will be written to Python version 1.5.2 (to ensure - usability by a wide install base). - - - - - - - - How &SCons; is be tested: which infrastructure modules to use, - what platforms to test on, etc. - - - - - - - - Expectations for developers (subscribe to the mailing list, - encouraged to register at SourceForge). - - - - - - - - Brief outline of how to use the change management systems (Aegis and - CVS) for &SCons; development;. - - - - - - - - - Establishing these guidelines up front had two purposes: 1) - Demonstrate the seriousness of the project to anyone wondering about - joining the effort; 2) Give potential developers an idea up front as - to whether their development style would mesh with the rest of the - project. - - - -
- Aegis - - - - One of the most important aspects of the &SCons; development process - is the use of Peter Miller's Aegis change management system. I - had been using Aegis for personal projects for several years, and - found its development methodology vastly improved the quality of my - programming. I was consequently committed to using it for &SCons; - development. - - - - - - Aegis provides a number of things, including: - - - - - - - - - A flexible source code control and branching model. - - - - - - - - A defined process with separate development, review and - integration steps. - - - - - - - - A distributed development model based on distribution of atomic - change sets. - - - - - - - - - The single most important reason for using Aegis, however, is its - management of automated tests as part of the development process. - - - -
- -
- Testing, Testing, Testing - - - - The &SCons; project has made extensive use of automated tests from day - one, taking inspiration mostly from Aegis, partly from the eXtreme - Programming model, and with a little home-brew scripting for glue. - - - -
- Testing Criteria - - - - The underlying criteria for testing changes to the &SCons; code - are taken from Aegis: - - - - - - - - - Every change must have one or more new or modified tests - checked in along with the code. - - - - - - - - The new code being checked in must pass all of the new and/or - modified tests. - - - - - - - - The old, already checked-in code in must - fail all of the new and/or modified - tests. - - - - - - - - The new code being checked in must pass all unmodified, - already checked-in tests. - - - - - - - - - In practice, these restrictions can be overridden as necessary­for - example, when changing comments or documentation. - - - - - - The criterion that surprises many people is having the old code - fail the tests in the change. This makes sure that the new tests - or modified tests really do exercise the bug fix or feature being - added by the change. - - - - - - Together, these criteria ensure that every newly checked-in - version &SCons; conforms to defined behavior, as defined by - the tests. Whenever a bug is found, its fix is checked in with - a new or modified test that guarantees the bug will not recur - in the future. We have already built up a regression test base - of almost 90 tests that cover the vast majority of &SCons;' - functionality. - - - -
- -
- Testing Infrastructure - - - - Testing standards are no good if they're too much of a burden for - developers, who will at best work around or ignore the testing - requirements, or at worst stop contributing code and go join a - project that's more fun. To this end, good testing infrastructure - that makes it easy to write tests is crucial. - - - - - - &SCons; development uses two development methodologies, one for - the individual modules in the build engine, and the other for - end-to-end tests of the &SCons; script. - - - - - - For the build engine modules, we use PyUnit. Every change to a - build engine module must have a change to its corresponding unit - tests, which live side-by-side in a separate file that imports - module. As we build up a large body of unit tests, this ensures - that the build engine will perform correctly whenever someone uses - it in some application other than the &SCons; script itself. - - - - - - For end-to-end script tests, we have developed two modules to make - writing tests easy. The first, TestCmd.py, - is a generic module for - testing commands or scripts (in any language, not just Python). - - The second module, TestScons.py, - is a subclass of the generic - TestCmd.py module. - TestScons.py - takes care of initialization and - displaying error conditions - specific to testing &SCons;. - - - - - - In practice, simple tests only - need to initialize a test object, use the object to write some - input files, run &SCons;, and then check whatever criteria - determine whether the test passed or failed. A complete test of - the &Program; method, for example, looks like this: - - - - - test = TestSCons.TestSCons() - - test.write('SConstruct', - """env = Environment() - env.Program(target = 'foo', source = 'foo.c') - """) - - test.write('foo.c', - """ - int - main(int argc, char *argv[]) - { - argv[argc++] = "-"; /* dummy use of args */ - printf("foo.c successfully compiled\\n"); - exit (0); - } - """) - - test.run(arguments = 'foo') # runs SCons - - test.run(program = test.workpath('foo')) - - test.fail_test(test.stdout() != "foo.c successfully compiled\n") - - test.pass_test() - - -
- -
- -
- SourceForge - - - - Registration of the &SCons; project was approved at SourceForge on - 29 June 2001. Within a week, the initial code base was checked in, - mailing lists were created, and the web site was set up. We started - making use of the task-list manager to track what we had to finish - for initial release. - - - - - - The obvious complication was how to use - structured testing methodology of Aegis when SourceForge uses - CVS for source control. Not using the SourceForge CVS tree would - have had two significant disadvantages: one, missing out on the - archiving and central location in the event of disaster; two, people - coming to the SourceForge project page wouldn't be able to browse - the source. The latter was particularly important in - the early stages of development, in order to avoid any impression - that this was Yet Another Project that starts with a bang and then - dwindles as the initial enthusiasm starts to wear off. - - - - - - The solution was to use the SourceForge CVS repository for read-only - access to the source. &SCons; developers are welcome to use CVS for - their development, but the changes are not - committed to the SourceForge repository. Instead, patches are sent - to the integrator for processing through Aegis. When the change - has been integrated into the Aegis repository, a home-brew - script translates the Aegis change into a virtual shell script - of commands that copy the necessary files from Aegis and check them - in to CVS at SourceForge. - - - - - - (In practice, write access is not actually disabled for registered - developers, but if they do make any changes directly at SourceForge, - they can be overwritten at the next Aegis update.) - - - -
diff --git a/doc/python10/process.xml b/doc/python10/process.xml new file mode 100644 index 0000000..f1b2479 --- /dev/null +++ b/doc/python10/process.xml @@ -0,0 +1,353 @@ + + + The &SCons; project has paid particular attention from day one to the + development process. One of the first internal documents produced was + a set of Developer's Guidelines to provide a loose framework for what + we were trying to accomplish and how we would go about accomplishing + it. These Guidelines cover things like: + + + + + + + + + &SCons; will be written to Python version 1.5.2 (to ensure + usability by a wide install base). + + + + + + + + How &SCons; is be tested: which infrastructure modules to use, + what platforms to test on, etc. + + + + + + + + Expectations for developers (subscribe to the mailing list, + encouraged to register at SourceForge). + + + + + + + + Brief outline of how to use the change management systems (Aegis and + CVS) for &SCons; development;. + + + + + + + + + Establishing these guidelines up front had two purposes: 1) + Demonstrate the seriousness of the project to anyone wondering about + joining the effort; 2) Give potential developers an idea up front as + to whether their development style would mesh with the rest of the + project. + + + +
+ Aegis + + + + One of the most important aspects of the &SCons; development process + is the use of Peter Miller's Aegis change management system. I + had been using Aegis for personal projects for several years, and + found its development methodology vastly improved the quality of my + programming. I was consequently committed to using it for &SCons; + development. + + + + + + Aegis provides a number of things, including: + + + + + + + + + A flexible source code control and branching model. + + + + + + + + A defined process with separate development, review and + integration steps. + + + + + + + + A distributed development model based on distribution of atomic + change sets. + + + + + + + + + The single most important reason for using Aegis, however, is its + management of automated tests as part of the development process. + + + +
+ +
+ Testing, Testing, Testing + + + + The &SCons; project has made extensive use of automated tests from day + one, taking inspiration mostly from Aegis, partly from the eXtreme + Programming model, and with a little home-brew scripting for glue. + + + +
+ Testing Criteria + + + + The underlying criteria for testing changes to the &SCons; code + are taken from Aegis: + + + + + + + + + Every change must have one or more new or modified tests + checked in along with the code. + + + + + + + + The new code being checked in must pass all of the new and/or + modified tests. + + + + + + + + The old, already checked-in code in must + fail all of the new and/or modified + tests. + + + + + + + + The new code being checked in must pass all unmodified, + already checked-in tests. + + + + + + + + + In practice, these restrictions can be overridden as necessary­for + example, when changing comments or documentation. + + + + + + The criterion that surprises many people is having the old code + fail the tests in the change. This makes sure that the new tests + or modified tests really do exercise the bug fix or feature being + added by the change. + + + + + + Together, these criteria ensure that every newly checked-in + version &SCons; conforms to defined behavior, as defined by + the tests. Whenever a bug is found, its fix is checked in with + a new or modified test that guarantees the bug will not recur + in the future. We have already built up a regression test base + of almost 90 tests that cover the vast majority of &SCons;' + functionality. + + + +
+ +
+ Testing Infrastructure + + + + Testing standards are no good if they're too much of a burden for + developers, who will at best work around or ignore the testing + requirements, or at worst stop contributing code and go join a + project that's more fun. To this end, good testing infrastructure + that makes it easy to write tests is crucial. + + + + + + &SCons; development uses two development methodologies, one for + the individual modules in the build engine, and the other for + end-to-end tests of the &SCons; script. + + + + + + For the build engine modules, we use PyUnit. Every change to a + build engine module must have a change to its corresponding unit + tests, which live side-by-side in a separate file that imports + module. As we build up a large body of unit tests, this ensures + that the build engine will perform correctly whenever someone uses + it in some application other than the &SCons; script itself. + + + + + + For end-to-end script tests, we have developed two modules to make + writing tests easy. The first, TestCmd.py, + is a generic module for + testing commands or scripts (in any language, not just Python). + + The second module, TestScons.py, + is a subclass of the generic + TestCmd.py module. + TestScons.py + takes care of initialization and + displaying error conditions + specific to testing &SCons;. + + + + + + In practice, simple tests only + need to initialize a test object, use the object to write some + input files, run &SCons;, and then check whatever criteria + determine whether the test passed or failed. A complete test of + the &Program; method, for example, looks like this: + + + + + test = TestSCons.TestSCons() + + test.write('SConstruct', + """env = Environment() + env.Program(target = 'foo', source = 'foo.c') + """) + + test.write('foo.c', + """ + int + main(int argc, char *argv[]) + { + argv[argc++] = "-"; /* dummy use of args */ + printf("foo.c successfully compiled\\n"); + exit (0); + } + """) + + test.run(arguments = 'foo') # runs SCons + + test.run(program = test.workpath('foo')) + + test.fail_test(test.stdout() != "foo.c successfully compiled\n") + + test.pass_test() + + +
+ +
+ +
+ SourceForge + + + + Registration of the &SCons; project was approved at SourceForge on + 29 June 2001. Within a week, the initial code base was checked in, + mailing lists were created, and the web site was set up. We started + making use of the task-list manager to track what we had to finish + for initial release. + + + + + + The obvious complication was how to use + structured testing methodology of Aegis when SourceForge uses + CVS for source control. Not using the SourceForge CVS tree would + have had two significant disadvantages: one, missing out on the + archiving and central location in the event of disaster; two, people + coming to the SourceForge project page wouldn't be able to browse + the source. The latter was particularly important in + the early stages of development, in order to avoid any impression + that this was Yet Another Project that starts with a bang and then + dwindles as the initial enthusiasm starts to wear off. + + + + + + The solution was to use the SourceForge CVS repository for read-only + access to the source. &SCons; developers are welcome to use CVS for + their development, but the changes are not + committed to the SourceForge repository. Instead, patches are sent + to the integrator for processing through Aegis. When the change + has been integrated into the Aegis repository, a home-brew + script translates the Aegis change into a virtual shell script + of commands that copy the necessary files from Aegis and check them + in to CVS at SourceForge. + + + + + + (In practice, write access is not actually disabled for registered + developers, but if they do make any changes directly at SourceForge, + they can be overwritten at the next Aegis update.) + + + +
diff --git a/doc/reference/Alias.sgml b/doc/reference/Alias.sgml deleted file mode 100644 index b87967d..0000000 --- a/doc/reference/Alias.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &Alias; Method - - - - X - - - -
diff --git a/doc/reference/Alias.xml b/doc/reference/Alias.xml new file mode 100644 index 0000000..b87967d --- /dev/null +++ b/doc/reference/Alias.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &Alias; Method + + + + X + + + +
diff --git a/doc/reference/CFile.sgml b/doc/reference/CFile.sgml deleted file mode 100644 index f76c390..0000000 --- a/doc/reference/CFile.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &CFile; Method - - - - X - - - -
diff --git a/doc/reference/CFile.xml b/doc/reference/CFile.xml new file mode 100644 index 0000000..f76c390 --- /dev/null +++ b/doc/reference/CFile.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &CFile; Method + + + + X + + + +
diff --git a/doc/reference/CXXFile.sgml b/doc/reference/CXXFile.sgml deleted file mode 100644 index c1c038e..0000000 --- a/doc/reference/CXXFile.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &CXXFile; Method - - - - X - - - -
diff --git a/doc/reference/CXXFile.xml b/doc/reference/CXXFile.xml new file mode 100644 index 0000000..c1c038e --- /dev/null +++ b/doc/reference/CXXFile.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &CXXFile; Method + + + + X + + + +
diff --git a/doc/reference/Command.sgml b/doc/reference/Command.sgml deleted file mode 100644 index abb3a58..0000000 --- a/doc/reference/Command.sgml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - X - - - -
- The &Command; Method - - - - X - - - -
diff --git a/doc/reference/Command.xml b/doc/reference/Command.xml new file mode 100644 index 0000000..abb3a58 --- /dev/null +++ b/doc/reference/Command.xml @@ -0,0 +1,73 @@ + + + + + + + X + + + +
+ The &Command; Method + + + + X + + + +
diff --git a/doc/reference/Install.sgml b/doc/reference/Install.sgml deleted file mode 100644 index 2d06e3b..0000000 --- a/doc/reference/Install.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &Install; Method - - - - X - - - -
diff --git a/doc/reference/Install.xml b/doc/reference/Install.xml new file mode 100644 index 0000000..2d06e3b --- /dev/null +++ b/doc/reference/Install.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &Install; Method + + + + X + + + +
diff --git a/doc/reference/InstallAs.sgml b/doc/reference/InstallAs.sgml deleted file mode 100644 index ed8cb78..0000000 --- a/doc/reference/InstallAs.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &InstallAs; Method - - - - X - - - -
diff --git a/doc/reference/InstallAs.xml b/doc/reference/InstallAs.xml new file mode 100644 index 0000000..ed8cb78 --- /dev/null +++ b/doc/reference/InstallAs.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &InstallAs; Method + + + + X + + + +
diff --git a/doc/reference/Library.sgml b/doc/reference/Library.sgml deleted file mode 100644 index 19a3e96..0000000 --- a/doc/reference/Library.sgml +++ /dev/null @@ -1,152 +0,0 @@ - - - - -
- Linking With a Library - - - env = Environment(CC = 'gcc', - LIBS = 'world') - env.Program('hello.c') - - - - % scons - gcc -c hello.c -o hello.o - gcc -c world.c -o world.o - gcc -o hello hello.o -lworld - - -
- -
- Creating a Library - - - env = Environment(CC = 'gcc', - LIBS = 'world') - env.Program('hello.c') - env.Library('world.c') - - - - % scons - gcc -c hello.c -o hello.o - gcc -c world.c -o world.o - ar r libworld.a world.o - ar: creating libworld.a - ranlib libworld.a - gcc -o hello hello.o libworld.a - - -
- - - -
- The &Library; Builder - - - - X - - - -
diff --git a/doc/reference/Library.xml b/doc/reference/Library.xml new file mode 100644 index 0000000..19a3e96 --- /dev/null +++ b/doc/reference/Library.xml @@ -0,0 +1,152 @@ + + + + +
+ Linking With a Library + + + env = Environment(CC = 'gcc', + LIBS = 'world') + env.Program('hello.c') + + + + % scons + gcc -c hello.c -o hello.o + gcc -c world.c -o world.o + gcc -o hello hello.o -lworld + + +
+ +
+ Creating a Library + + + env = Environment(CC = 'gcc', + LIBS = 'world') + env.Program('hello.c') + env.Library('world.c') + + + + % scons + gcc -c hello.c -o hello.o + gcc -c world.c -o world.o + ar r libworld.a world.o + ar: creating libworld.a + ranlib libworld.a + gcc -o hello hello.o libworld.a + + +
+ + + +
+ The &Library; Builder + + + + X + + + +
diff --git a/doc/reference/MANIFEST b/doc/reference/MANIFEST index 1ea958e..438aada 100644 --- a/doc/reference/MANIFEST +++ b/doc/reference/MANIFEST @@ -1,21 +1,21 @@ -Alias.sgml -CFile.sgml -CXXFile.sgml -Command.sgml -Install.sgml -InstallAs.sgml -Library.sgml -Object.sgml -PCH.sgml -PDF.sgml -PostScript.sgml -Program.sgml -RES.sgml -SharedLibrary.sgml -SharedObject.sgml -StaticLibrary.sgml -StaticObject.sgml -copyright.sgml -errors.sgml -main.sgml -preface.sgml +Alias.xml +CFile.xml +CXXFile.xml +Command.xml +Install.xml +InstallAs.xml +Library.xml +Object.xml +PCH.xml +PDF.xml +PostScript.xml +Program.xml +RES.xml +SharedLibrary.xml +SharedObject.xml +StaticLibrary.xml +StaticObject.xml +copyright.xml +errors.xml +main.xml +preface.xml diff --git a/doc/reference/Object.sgml b/doc/reference/Object.sgml deleted file mode 100644 index 9e887d8..0000000 --- a/doc/reference/Object.sgml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - X - - - -
- The &Object; Method - - - - X - - - -
diff --git a/doc/reference/Object.xml b/doc/reference/Object.xml new file mode 100644 index 0000000..9e887d8 --- /dev/null +++ b/doc/reference/Object.xml @@ -0,0 +1,71 @@ + + + + + + + X + + + +
+ The &Object; Method + + + + X + + + +
diff --git a/doc/reference/PCH.sgml b/doc/reference/PCH.sgml deleted file mode 100644 index b2a4d75..0000000 --- a/doc/reference/PCH.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &PCH; Method - - - - X - - - -
diff --git a/doc/reference/PCH.xml b/doc/reference/PCH.xml new file mode 100644 index 0000000..b2a4d75 --- /dev/null +++ b/doc/reference/PCH.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &PCH; Method + + + + X + + + +
diff --git a/doc/reference/PDF.sgml b/doc/reference/PDF.sgml deleted file mode 100644 index b3a25dc..0000000 --- a/doc/reference/PDF.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &PDF; Method - - - - X - - - -
diff --git a/doc/reference/PDF.xml b/doc/reference/PDF.xml new file mode 100644 index 0000000..b3a25dc --- /dev/null +++ b/doc/reference/PDF.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &PDF; Method + + + + X + + + +
diff --git a/doc/reference/PostScript.sgml b/doc/reference/PostScript.sgml deleted file mode 100644 index f5a6579..0000000 --- a/doc/reference/PostScript.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &PostScript; Method - - - - X - - - -
diff --git a/doc/reference/PostScript.xml b/doc/reference/PostScript.xml new file mode 100644 index 0000000..f5a6579 --- /dev/null +++ b/doc/reference/PostScript.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &PostScript; Method + + + + X + + + +
diff --git a/doc/reference/Program.sgml b/doc/reference/Program.sgml deleted file mode 100644 index 30f90d2..0000000 --- a/doc/reference/Program.sgml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - X - - - -
- The &Program; Builder - - - - X - - - -
diff --git a/doc/reference/Program.xml b/doc/reference/Program.xml new file mode 100644 index 0000000..30f90d2 --- /dev/null +++ b/doc/reference/Program.xml @@ -0,0 +1,77 @@ + + + + + + + X + + + +
+ The &Program; Builder + + + + X + + + +
diff --git a/doc/reference/RES.sgml b/doc/reference/RES.sgml deleted file mode 100644 index 15c0aea..0000000 --- a/doc/reference/RES.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &RES; Method - - - - X - - - -
diff --git a/doc/reference/RES.xml b/doc/reference/RES.xml new file mode 100644 index 0000000..15c0aea --- /dev/null +++ b/doc/reference/RES.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &RES; Method + + + + X + + + +
diff --git a/doc/reference/SharedLibrary.sgml b/doc/reference/SharedLibrary.sgml deleted file mode 100644 index 603dab1..0000000 --- a/doc/reference/SharedLibrary.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &SharedLibrary; Method - - - - X - - - -
diff --git a/doc/reference/SharedLibrary.xml b/doc/reference/SharedLibrary.xml new file mode 100644 index 0000000..603dab1 --- /dev/null +++ b/doc/reference/SharedLibrary.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &SharedLibrary; Method + + + + X + + + +
diff --git a/doc/reference/SharedObject.sgml b/doc/reference/SharedObject.sgml deleted file mode 100644 index 0860769..0000000 --- a/doc/reference/SharedObject.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &SharedObject; Method - - - - X - - - -
diff --git a/doc/reference/SharedObject.xml b/doc/reference/SharedObject.xml new file mode 100644 index 0000000..0860769 --- /dev/null +++ b/doc/reference/SharedObject.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &SharedObject; Method + + + + X + + + +
diff --git a/doc/reference/StaticLibrary.sgml b/doc/reference/StaticLibrary.sgml deleted file mode 100644 index ea7ae5b..0000000 --- a/doc/reference/StaticLibrary.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &StaticLibrary; Method - - - - X - - - -
diff --git a/doc/reference/StaticLibrary.xml b/doc/reference/StaticLibrary.xml new file mode 100644 index 0000000..ea7ae5b --- /dev/null +++ b/doc/reference/StaticLibrary.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &StaticLibrary; Method + + + + X + + + +
diff --git a/doc/reference/StaticObject.sgml b/doc/reference/StaticObject.sgml deleted file mode 100644 index ff8dae8..0000000 --- a/doc/reference/StaticObject.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- The &StaticObject; Method - - - - X - - - -
diff --git a/doc/reference/StaticObject.xml b/doc/reference/StaticObject.xml new file mode 100644 index 0000000..ff8dae8 --- /dev/null +++ b/doc/reference/StaticObject.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ The &StaticObject; Method + + + + X + + + +
diff --git a/doc/reference/copyright.sgml b/doc/reference/copyright.sgml deleted file mode 100644 index 7f6059c..0000000 --- a/doc/reference/copyright.sgml +++ /dev/null @@ -1,32 +0,0 @@ - - -
- - - SCons User's Guide Copyright (c) 2003 Steven Knight - - -
diff --git a/doc/reference/copyright.xml b/doc/reference/copyright.xml new file mode 100644 index 0000000..7f6059c --- /dev/null +++ b/doc/reference/copyright.xml @@ -0,0 +1,32 @@ + + +
+ + + SCons User's Guide Copyright (c) 2003 Steven Knight + + +
diff --git a/doc/reference/errors.sgml b/doc/reference/errors.sgml deleted file mode 100644 index 448777f..0000000 --- a/doc/reference/errors.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - X - - - -
- X - - - - X - - - -
diff --git a/doc/reference/errors.xml b/doc/reference/errors.xml new file mode 100644 index 0000000..448777f --- /dev/null +++ b/doc/reference/errors.xml @@ -0,0 +1,41 @@ + + + + + X + + + +
+ X + + + + X + + + +
diff --git a/doc/reference/main.sgml b/doc/reference/main.sgml deleted file mode 100644 index 87b4a84..0000000 --- a/doc/reference/main.sgml +++ /dev/null @@ -1,204 +0,0 @@ - - - - %version; - - - %scons; - - - - - - - - - - - - - - - - - - - - - - - - - - -]> - - - - SCons Reference Manual &buildversion; - - - Steven - Knight - - - Revision &buildrevision; (&builddate;) - - 2003 - - - 2003 - Steven Knight - - - - ©right; - - - version &buildversion; - - - - - Preface - &preface; - - - - Builder Reference - -
- The Alias Builder - &Alias_file; -
- -
- The CFile Builder - &CFile_file; -
- -
- The Command Builder - &Command_file; -
- -
- The CXXFile Builder - &CXXFile_file; -
- -
- The Install Builder - &Install_file; -
- -
- The InstallAs Builder - &InstallAs_file; -
- -
- The Library Builder - &Library_file; -
- -
- The Object Builder - &Object_file; -
- -
- The PCH Builder - &PCH_file; -
- -
- The PDF Builder - &PDF_file; -
- -
- The PDF Builder - &PostScript_file; -
- -
- The Program Builder - &Program_file; -
- -
- The RES Builder - &RES_file; -
- -
- The SharedLibrary Builder - &SharedLibrary_file; -
- -
- The SharedObject Builder - &SharedObject_file; -
- -
- The StaticLibrary Builder - &StaticLibrary_file; -
- -
- The StaticObject Builder - &StaticObject_file; -
- -
- - - &ConsVar; Reference - -
- AR - - - - X - - - -
- -
- - - Errors Generated by &SCons; - &errors; - - -
diff --git a/doc/reference/main.xml b/doc/reference/main.xml new file mode 100644 index 0000000..ed122f6 --- /dev/null +++ b/doc/reference/main.xml @@ -0,0 +1,207 @@ + + + + + + %version; + + + %scons; + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + SCons Reference Manual &buildversion; + + + Steven + Knight + + + Revision &buildrevision; (&builddate;) + + 2003 + + + 2003 + Steven Knight + + + + ©right; + + + version &buildversion; + + + + + Preface + &preface; + + + + Builder Reference + +
+ The Alias Builder + &Alias_file; +
+ +
+ The CFile Builder + &CFile_file; +
+ +
+ The Command Builder + &Command_file; +
+ +
+ The CXXFile Builder + &CXXFile_file; +
+ +
+ The Install Builder + &Install_file; +
+ +
+ The InstallAs Builder + &InstallAs_file; +
+ +
+ The Library Builder + &Library_file; +
+ +
+ The Object Builder + &Object_file; +
+ +
+ The PCH Builder + &PCH_file; +
+ +
+ The PDF Builder + &PDF_file; +
+ +
+ The PDF Builder + &PostScript_file; +
+ +
+ The Program Builder + &Program_file; +
+ +
+ The RES Builder + &RES_file; +
+ +
+ The SharedLibrary Builder + &SharedLibrary_file; +
+ +
+ The SharedObject Builder + &SharedObject_file; +
+ +
+ The StaticLibrary Builder + &StaticLibrary_file; +
+ +
+ The StaticObject Builder + &StaticObject_file; +
+ +
+ + + &ConsVar; Reference + +
+ AR + + + + X + + + +
+ +
+ + + Errors Generated by &SCons; + &errors; + + +
diff --git a/doc/reference/preface.sgml b/doc/reference/preface.sgml deleted file mode 100644 index 82ea44a..0000000 --- a/doc/reference/preface.sgml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - X - - - -
- Why &SCons;? - - - - X - - - -
- -
- History - - - - X - - - -
- -
- Conventions - - - - X - - - -
- -
- Acknowledgements - - - - X - - - -
- -
- Contact - - - - X - - - -
diff --git a/doc/reference/preface.xml b/doc/reference/preface.xml new file mode 100644 index 0000000..82ea44a --- /dev/null +++ b/doc/reference/preface.xml @@ -0,0 +1,85 @@ + + + + + X + + + +
+ Why &SCons;? + + + + X + + + +
+ +
+ History + + + + X + + + +
+ +
+ Conventions + + + + X + + + +
+ +
+ Acknowledgements + + + + X + + + +
+ +
+ Contact + + + + X + + + +
diff --git a/doc/scons.mod b/doc/scons.mod index 652787a..f3c1a86 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -175,6 +175,7 @@ Export"> File"> FindFile"> +FindInstalledFiles"> Finish"> GenerateHelpText"> GetOption"> @@ -212,10 +213,12 @@ Salt"> SetBuildSignatureType"> SetContentSignatureType"> +SetOption"> SideEffect"> SourceSignature"> SourceSignatures"> Split"> +Tag"> TargetSignatures"> Task"> Touch"> diff --git a/doc/user/ENV.sgml b/doc/user/ENV.sgml deleted file mode 100644 index d843276..0000000 --- a/doc/user/ENV.sgml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - When &SCons; builds a target file, - it does not execute the commands with - the same external environment - that you used to execute &SCons;. - Instead, it uses the dictionary - stored in the &cv-link-ENV; construction variable - as the external environment - for executing commands. - - - - - - The most important ramification of this behavior - is that the &PATH; environment variable, - which controls where the operating system - will look for commands and utilities, - is not the same as in the external environment - from which you called &SCons;. - This means that &SCons; will not, by default, - necessarily find all of the tools - that you can execute from the command line. - - - - - - The default value of the &PATH; environment variable - on a POSIX system - is /usr/local/bin:/bin:/usr/bin. - The default value of the &PATH; environment variable - 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, - you need to set the &PATH; value - in the &cv-ENV; dictionary - in your construction environment. - - - - - - The simplest way to do this is to initialize explicitly - the value when you create the construction environment; - this is one way to do that: - - - - - path = ['/usr/local/bin', '/bin', '/usr/bin'] - env = Environment(ENV = {'PATH' : path}) - - - - - Assign a dictionary to the &cv-ENV; - construction variable in this way - completely resets the external environment - so that the only variable that will be - set when external commands are executed - will be the &PATH; value. - If you want to use the rest of - the values in &cv-ENV; and only - set the value of &PATH;, - the most straightforward way is probably: - - - - - env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] - - - - - Note that &SCons; does allow you to define - the directories in the &PATH; in a string, - separated by the pathname-separator character - for your system (':' on POSIX systems, ';' on Windows): - - - - - env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' - - - - - But doing so makes your &SConscript; file less portable, - (although in this case that may not be a huge concern - since the directories you list are likley system-specific, anyway). - - - - - -
- Propagating &PATH; From the External Environment - - - - You may want to propagate the external &PATH; - to the execution environment for commands. - You do this by initializing the &PATH; - variable with the &PATH; value from - the os.environ - dictionary, - which is Python's way of letting you - get at the external environment: - - - - - import os - env = Environment(ENV = {'PATH' : os.environ['PATH']}) - - - - - Alternatively, you may find it easier - to just propagate the entire external - environment to the execution environment - for commands. - This is simpler to code than explicity - selecting the &PATH; value: - - - - - import os - env = Environment(ENV = os.environ) - - - - - Either of these will guarantee that - &SCons; will be able to execute - any command that you can execute from the command line. - The drawback is that the build can behave - differently if it's run by people with - different &PATH; values in their environment--for example, - both the /bin and - /usr/local/bin directories - have different &cc; commands, - then which one will be used to compile programs - will depend on which directory is listed - first in the user's &PATH; variable. - - - -
diff --git a/doc/user/ENV.xml b/doc/user/ENV.xml new file mode 100644 index 0000000..d843276 --- /dev/null +++ b/doc/user/ENV.xml @@ -0,0 +1,208 @@ + + + + + + When &SCons; builds a target file, + it does not execute the commands with + the same external environment + that you used to execute &SCons;. + Instead, it uses the dictionary + stored in the &cv-link-ENV; construction variable + as the external environment + for executing commands. + + + + + + The most important ramification of this behavior + is that the &PATH; environment variable, + which controls where the operating system + will look for commands and utilities, + is not the same as in the external environment + from which you called &SCons;. + This means that &SCons; will not, by default, + necessarily find all of the tools + that you can execute from the command line. + + + + + + The default value of the &PATH; environment variable + on a POSIX system + is /usr/local/bin:/bin:/usr/bin. + The default value of the &PATH; environment variable + 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, + you need to set the &PATH; value + in the &cv-ENV; dictionary + in your construction environment. + + + + + + The simplest way to do this is to initialize explicitly + the value when you create the construction environment; + this is one way to do that: + + + + + path = ['/usr/local/bin', '/bin', '/usr/bin'] + env = Environment(ENV = {'PATH' : path}) + + + + + Assign a dictionary to the &cv-ENV; + construction variable in this way + completely resets the external environment + so that the only variable that will be + set when external commands are executed + will be the &PATH; value. + If you want to use the rest of + the values in &cv-ENV; and only + set the value of &PATH;, + the most straightforward way is probably: + + + + + env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] + + + + + Note that &SCons; does allow you to define + the directories in the &PATH; in a string, + separated by the pathname-separator character + for your system (':' on POSIX systems, ';' on Windows): + + + + + env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' + + + + + But doing so makes your &SConscript; file less portable, + (although in this case that may not be a huge concern + since the directories you list are likley system-specific, anyway). + + + + + +
+ Propagating &PATH; From the External Environment + + + + You may want to propagate the external &PATH; + to the execution environment for commands. + You do this by initializing the &PATH; + variable with the &PATH; value from + the os.environ + dictionary, + which is Python's way of letting you + get at the external environment: + + + + + import os + env = Environment(ENV = {'PATH' : os.environ['PATH']}) + + + + + Alternatively, you may find it easier + to just propagate the entire external + environment to the execution environment + for commands. + This is simpler to code than explicity + selecting the &PATH; value: + + + + + import os + env = Environment(ENV = os.environ) + + + + + Either of these will guarantee that + &SCons; will be able to execute + any command that you can execute from the command line. + The drawback is that the build can behave + differently if it's run by people with + different &PATH; values in their environment--for example, + both the /bin and + /usr/local/bin directories + have different &cc; commands, + then which one will be used to compile programs + will depend on which directory is listed + first in the user's &PATH; variable. + + + +
diff --git a/doc/user/MANIFEST b/doc/user/MANIFEST index cc7dc1e..565298f 100644 --- a/doc/user/MANIFEST +++ b/doc/user/MANIFEST @@ -1,46 +1,46 @@ -actions.sgml -alias.sgml -ant.sgml -builders.sgml -builders-built-in.sgml -builders-commands.sgml -builders-writing.sgml -build-install.sgml -caching.sgml -command-line.sgml +actions.xml +alias.xml +ant.xml +builders.xml +builders-built-in.xml +builders-commands.xml +builders-writing.xml +build-install.xml +caching.xml +command-line.xml cons.pl -copyright.sgml -depends.sgml -ENV.sgml -environments.sgml -errors.sgml -example.sgml -factories.sgml -file-removal.sgml -help.sgml -hierarchy.sgml -install.sgml -java.sgml -libraries.sgml -less-simple.sgml -main.sgml -make.sgml -nodes.sgml -parseconfig.sgml -preface.sgml -python.sgml -repositories.sgml -run.sgml -scanners.sgml -sconf.sgml -separate.sgml -simple.sgml -sourcecode.sgml -tasks.sgml -tools.sgml -troubleshoot.sgml -variants.sgml -variables.sgml +copyright.xml +depends.xml +ENV.xml +environments.xml +errors.xml +example.xml +factories.xml +file-removal.xml +help.xml +hierarchy.xml +install.xml +java.xml +libraries.xml +less-simple.xml +main.xml +make.xml +nodes.xml +parseconfig.xml +preface.xml +python.xml +repositories.xml +run.xml +scanners.xml +sconf.xml +separate.xml +simple.xml +sourcecode.xml +tasks.xml +tools.xml +troubleshoot.xml +variants.xml +variables.xml SCons-win32-install-1.jpg SCons-win32-install-2.jpg SCons-win32-install-3.jpg diff --git a/doc/user/actions.sgml b/doc/user/actions.sgml deleted file mode 100644 index 928b7ea..0000000 --- a/doc/user/actions.sgml +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - XXX - - - -
- XXX - - - - XXX - - - -
diff --git a/doc/user/actions.xml b/doc/user/actions.xml new file mode 100644 index 0000000..928b7ea --- /dev/null +++ b/doc/user/actions.xml @@ -0,0 +1,240 @@ + + + + + + + XXX + + + +
+ XXX + + + + XXX + + + +
diff --git a/doc/user/alias.sgml b/doc/user/alias.sgml deleted file mode 100644 index f285d70..0000000 --- a/doc/user/alias.sgml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - We've already seen how you can use the &Alias; - function to create a target named install: - - - - - env = Environment() - hello = env.Program('hello.c') - env.Install('/usr/bin', hello) - env.Alias('install', '/usr/bin') - - - - - You can then use this alias on the command line - to tell &SCons; more naturally that you want to install files: - - - - - % scons -Q install - cc -o hello.o -c hello.c - cc -o hello hello.o - Install file: "hello" as "/usr/bin/hello" - - - - - Like other &Builder; methods, though, - the &Alias; method returns an object - representing the alias being built. - You can then use this object as input to anothother &Builder;. - This is especially useful if you use such an object - as input to another call to the &Alias; &Builder;, - allowing you to create a hierarchy - of nested aliases: - - - - - env = Environment() - p = env.Program('foo.c') - l = env.Library('bar.c') - env.Install('/usr/bin', p) - env.Install('/usr/lib', l) - ib = env.Alias('install-bin', '/usr/bin') - il = env.Alias('install-lib', '/usr/lib') - env.Alias('install', [ib, il]) - - - - - This example defines separate install, - install-bin, - and install-lib aliases, - allowing you finer control over what gets installed: - - - - - % scons -Q install-bin - cc -o foo.o -c foo.c - cc -o foo foo.o - Install file: "foo" as "/usr/bin/foo" - % scons -Q install-lib - 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 / - Removed foo.o - Removed foo - Removed /usr/bin/foo - Removed bar.o - Removed libbar.a - Removed /usr/lib/libbar.a - % scons -Q install - cc -o foo.o -c foo.c - cc -o foo foo.o - Install file: "foo" as "/usr/bin/foo" - 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/alias.xml b/doc/user/alias.xml new file mode 100644 index 0000000..f285d70 --- /dev/null +++ b/doc/user/alias.xml @@ -0,0 +1,112 @@ + + + + + We've already seen how you can use the &Alias; + function to create a target named install: + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('/usr/bin', hello) + env.Alias('install', '/usr/bin') + + + + + You can then use this alias on the command line + to tell &SCons; more naturally that you want to install files: + + + + + % scons -Q install + cc -o hello.o -c hello.c + cc -o hello hello.o + Install file: "hello" as "/usr/bin/hello" + + + + + Like other &Builder; methods, though, + the &Alias; method returns an object + representing the alias being built. + You can then use this object as input to anothother &Builder;. + This is especially useful if you use such an object + as input to another call to the &Alias; &Builder;, + allowing you to create a hierarchy + of nested aliases: + + + + + env = Environment() + p = env.Program('foo.c') + l = env.Library('bar.c') + env.Install('/usr/bin', p) + env.Install('/usr/lib', l) + ib = env.Alias('install-bin', '/usr/bin') + il = env.Alias('install-lib', '/usr/lib') + env.Alias('install', [ib, il]) + + + + + This example defines separate install, + install-bin, + and install-lib aliases, + allowing you finer control over what gets installed: + + + + + % scons -Q install-bin + cc -o foo.o -c foo.c + cc -o foo foo.o + Install file: "foo" as "/usr/bin/foo" + % scons -Q install-lib + 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 / + Removed foo.o + Removed foo + Removed /usr/bin/foo + Removed bar.o + Removed libbar.a + Removed /usr/lib/libbar.a + % scons -Q install + cc -o foo.o -c foo.c + cc -o foo foo.o + Install file: "foo" as "/usr/bin/foo" + 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/ant.sgml b/doc/user/ant.sgml deleted file mode 100644 index 0df1027..0000000 --- a/doc/user/ant.sgml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - XXX - - - -
- Differences Between &Ant; and &SCons; - - - - XXX - - - -
- -
- Advantages of &SCons; Over &Ant; - - - - XXX - - - -
diff --git a/doc/user/ant.xml b/doc/user/ant.xml new file mode 100644 index 0000000..0df1027 --- /dev/null +++ b/doc/user/ant.xml @@ -0,0 +1,52 @@ + + + + + XXX + + + +
+ Differences Between &Ant; and &SCons; + + + + XXX + + + +
+ +
+ Advantages of &SCons; Over &Ant; + + + + XXX + + + +
diff --git a/doc/user/build-install.sgml b/doc/user/build-install.sgml deleted file mode 100644 index 763c13e..0000000 --- a/doc/user/build-install.sgml +++ /dev/null @@ -1,693 +0,0 @@ - - - - - This chapter will take you through the basic steps - of installing &SCons; on your system, - and building &SCons; if you don't have a - pre-built package available - (or simply prefer the flexibility of building it yourself). - Before that, however, this chapter will also describe the basic steps - involved in installing Python on your system, - in case that is necessary. - Fortunately, both &SCons; and Python - are very easy to install on almost any system, - and Python already comes installed on many systems. - - - - - -
- Installing Python - - - - Because &SCons; is written in Python, - you must obviously have Python installed on your system - to use &SCons; - Before you try to install Python, - you should check to see if Python is already - available on your system by typing - python - at your system's command-line prompt. - You should see something like the following - on a UNIX or Linux system that has Python installed: - - - - - - - $ python - Python 2.2.2 (#1, Feb 24 2003, 19:13:11) - [GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-4)] on linux2 - Type "help", "copyright", "credits" or "license" for more information. - >>> ^D - - - - - And on a Windows system with Python installed: - - - - - C:\>python - Python 2.2.2 (#34, Apr 9 2002, 19:34:33) [MSC 32 bit (Intel)] on win32 - Type "help", "copyright", "credits" or "license" for more information. - >>> ^Z - - - - - The >>> is the input prompt - for the Python interpreter. - The ^D and ^Z - represent the CTRL-D and CTRL-Z characters - that you will need to type to get out of the interpreter - before proceeding to installing &SCons;. - - - - - - If Python is not installed on your system, - you will see an error message - stating something like "command not found" - (on UNIX or Linux) - or "'python' is not recognized - as an internal or external command, operable progam or batch file" - (on Windows). - In that case, you need to install Python - before you can install &SCons;. - - - - - - The standard location for information - about downloading and installing Python is - http://www.python.org/download/. - See that page for information about - how to download and install Python on your system. - - - -
- -
- Installing &SCons; From Pre-Built Packages - - - - &SCons; comes pre-packaged for installation on a number of systems, - including Linux and Windows systems. - You do not need to read this entire section, - you should only need to read the section - appropriate to the type of system you're running on. - - - -
- Installing &SCons; on Red Hat (and Other RPM-based) Linux Systems - - - - &SCons; comes in RPM (Red Hat Package Manager) format, - pre-built and ready to install on Red Hat Linux, - Fedora Core, - or any other Linux distribution that uses RPM. - Your distribution may - already have an &SCons; RPM built specifically for it; - many do, including SuSe, Mandrake and Fedora. - You can check for the availability of an &SCons; RPM - on your distribution's download servers, - or by consulting an RPM search site like - http://www.rpmfind.net/ or - http://rpm.pbone.net/. - - - - - - If your Linux distribution does not already have - a specific &SCons; RPM file, - you can download and install from the - generic RPM provided by the &SCons; project. - This will install the - SCons script(s) in /usr/bin, - and the SCons library modules in - /usr/lib/scons. - - - - - - To install from the command line, simply download the - appropriate .rpm file, - and then run: - - - - - # rpm -Uvh scons-0.96-1.noarch.rpm - - - - - Or, you can use a graphical RPM package manager - like gnorpm. - See your package manager application's documention - for specific instructions about - how to use it to install a downloaded RPM. - - - -
- -
- Installing &SCons; on Debian Linux Systems - - - - Debian Linux systems use a different package management - format that also makes it very easy to install &SCons;. - - - - - - If your system is connected to the Internet, - you can install the latest official Debian package - by running: - - - - - # apt-get install scons - - - - -
- -
- Installing &SCons; on Windows Systems - - - - &SCons; provides a Windows installer - that makes installation extremely easy. - Download the scons-0.95.win32.exe - file from the &SCons; download page at - http://www.scons.org/download.html. - Then all you need to do is execute the file - (usually by clicking on its icon in Windows Explorer). - These will take you through a small - sequence of windows that will install - &SCons; on your system. - - - - - - - - - -
- -
- -
- Building and Installing &SCons; on Any System - - - - If a pre-built &SCons; package is not available for your system, - then you can still easily build and install &SCons; using the native - Python distutils package. - - - - - - The first step is to download either the - scons-__VERSION__.tar.gz - or scons-__VERSION__.zip, - which are available from the SCons download page at - http://www.scons.org/download.html. - - - - - - Unpack the archive you downloaded, - using a utility like tar - on Linux or UNIX, - or WinZip on Windows. - This will create a directory called - scons-__VERSION__, - usually in your local directory. - Then change your working directory to that directory - and install &SCons; by executing the following commands: - - - - - # cd scons-__VERSION__ - # python setup.py install - - - - - This will build &SCons;, - install the scons script - in the default system scripts directory - (/usr/local/bin or - C:\Python2.2\Scripts), - and will install the &SCons; build engine - in an appropriate stand-alone library directory - (/usr/local/lib/scons or - C:\Python2.2\scons). - Because these are system directories, - you may need root (on Linux or UNIX) or Administrator (on Windows) - privileges to install &SCons; like this. - - - - - -
- Building and Installing Multiple Versions of &SCons; Side-by-Side - - - - The &SCons; setup.py script - has some extensions that support - easy installation of multiple versions of &SCons; - in side-by-side locations. - This makes it easier to download and - experiment with different versions of &SCons; - before moving your official build process to a new version, - for example. - - - - - - To install &SCons; in a version-specific location, - add the option - when you call setup.py: - - - - - # python setup.py install --version-lib - - - - - This will install the &SCons; build engine - in the - /usr/lib/scons-__VERSION__ - or - C:\Python2.2\scons-__VERSION__ - directory, for example. - - - - - - If you use the option - the first time you install &SCons;, - you do not need to specify it each time you install - a new version. - The &SCons; setup.py script - will detect the version-specific directory name(s) - and assume you want to install all versions - in version-specific directories. - You can override that assumption in the future - by explicitly specifying the option. - - - -
- -
- Installing &SCons; in Other Locations - - - - You can install &SCons; in locations other than - the default by specifying the option: - - - - - # python setup.py install --prefix=/opt/scons - - - - - This would - install the scons script in - /opt/scons/bin - and the build engine in - /opt/scons/lib/scons, - - - - - - Note that you can specify both the - and the options - at the same type, - in which case setup.py - will install the build engine - in a version-specific directory - relative to the specified prefix. - Adding to the - above example would install the build engine in - /opt/scons/lib/scons-__VERSION__. - - - -
- -
- Building and Installing &SCons; Without Administrative Privileges - - - - If you don't have the right privileges to install &SCons; - in a system location, - simply use the --prefix= option - to install it in a location of your choosing. - For example, - to install &SCons; in appropriate locations - relative to the user's $HOME directory, - the scons script in - $HOME/bin - and the build engine in - $HOME/lib/scons, - simply type: - - - - - $ python setup.py install --prefix=$HOME - - - - - You may, of course, specify any other location you prefer, - and may use the option - if you would like to install version-specific directories - relative to the specified prefix. - - - -
- -
- - diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml new file mode 100644 index 0000000..763c13e --- /dev/null +++ b/doc/user/build-install.xml @@ -0,0 +1,693 @@ + + + + + This chapter will take you through the basic steps + of installing &SCons; on your system, + and building &SCons; if you don't have a + pre-built package available + (or simply prefer the flexibility of building it yourself). + Before that, however, this chapter will also describe the basic steps + involved in installing Python on your system, + in case that is necessary. + Fortunately, both &SCons; and Python + are very easy to install on almost any system, + and Python already comes installed on many systems. + + + + + +
+ Installing Python + + + + Because &SCons; is written in Python, + you must obviously have Python installed on your system + to use &SCons; + Before you try to install Python, + you should check to see if Python is already + available on your system by typing + python + at your system's command-line prompt. + You should see something like the following + on a UNIX or Linux system that has Python installed: + + + + + + + $ python + Python 2.2.2 (#1, Feb 24 2003, 19:13:11) + [GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-4)] on linux2 + Type "help", "copyright", "credits" or "license" for more information. + >>> ^D + + + + + And on a Windows system with Python installed: + + + + + C:\>python + Python 2.2.2 (#34, Apr 9 2002, 19:34:33) [MSC 32 bit (Intel)] on win32 + Type "help", "copyright", "credits" or "license" for more information. + >>> ^Z + + + + + The >>> is the input prompt + for the Python interpreter. + The ^D and ^Z + represent the CTRL-D and CTRL-Z characters + that you will need to type to get out of the interpreter + before proceeding to installing &SCons;. + + + + + + If Python is not installed on your system, + you will see an error message + stating something like "command not found" + (on UNIX or Linux) + or "'python' is not recognized + as an internal or external command, operable progam or batch file" + (on Windows). + In that case, you need to install Python + before you can install &SCons;. + + + + + + The standard location for information + about downloading and installing Python is + http://www.python.org/download/. + See that page for information about + how to download and install Python on your system. + + + +
+ +
+ Installing &SCons; From Pre-Built Packages + + + + &SCons; comes pre-packaged for installation on a number of systems, + including Linux and Windows systems. + You do not need to read this entire section, + you should only need to read the section + appropriate to the type of system you're running on. + + + +
+ Installing &SCons; on Red Hat (and Other RPM-based) Linux Systems + + + + &SCons; comes in RPM (Red Hat Package Manager) format, + pre-built and ready to install on Red Hat Linux, + Fedora Core, + or any other Linux distribution that uses RPM. + Your distribution may + already have an &SCons; RPM built specifically for it; + many do, including SuSe, Mandrake and Fedora. + You can check for the availability of an &SCons; RPM + on your distribution's download servers, + or by consulting an RPM search site like + http://www.rpmfind.net/ or + http://rpm.pbone.net/. + + + + + + If your Linux distribution does not already have + a specific &SCons; RPM file, + you can download and install from the + generic RPM provided by the &SCons; project. + This will install the + SCons script(s) in /usr/bin, + and the SCons library modules in + /usr/lib/scons. + + + + + + To install from the command line, simply download the + appropriate .rpm file, + and then run: + + + + + # rpm -Uvh scons-0.96-1.noarch.rpm + + + + + Or, you can use a graphical RPM package manager + like gnorpm. + See your package manager application's documention + for specific instructions about + how to use it to install a downloaded RPM. + + + +
+ +
+ Installing &SCons; on Debian Linux Systems + + + + Debian Linux systems use a different package management + format that also makes it very easy to install &SCons;. + + + + + + If your system is connected to the Internet, + you can install the latest official Debian package + by running: + + + + + # apt-get install scons + + + + +
+ +
+ Installing &SCons; on Windows Systems + + + + &SCons; provides a Windows installer + that makes installation extremely easy. + Download the scons-0.95.win32.exe + file from the &SCons; download page at + http://www.scons.org/download.html. + Then all you need to do is execute the file + (usually by clicking on its icon in Windows Explorer). + These will take you through a small + sequence of windows that will install + &SCons; on your system. + + + + + + + + + +
+ +
+ +
+ Building and Installing &SCons; on Any System + + + + If a pre-built &SCons; package is not available for your system, + then you can still easily build and install &SCons; using the native + Python distutils package. + + + + + + The first step is to download either the + scons-__VERSION__.tar.gz + or scons-__VERSION__.zip, + which are available from the SCons download page at + http://www.scons.org/download.html. + + + + + + Unpack the archive you downloaded, + using a utility like tar + on Linux or UNIX, + or WinZip on Windows. + This will create a directory called + scons-__VERSION__, + usually in your local directory. + Then change your working directory to that directory + and install &SCons; by executing the following commands: + + + + + # cd scons-__VERSION__ + # python setup.py install + + + + + This will build &SCons;, + install the scons script + in the default system scripts directory + (/usr/local/bin or + C:\Python2.2\Scripts), + and will install the &SCons; build engine + in an appropriate stand-alone library directory + (/usr/local/lib/scons or + C:\Python2.2\scons). + Because these are system directories, + you may need root (on Linux or UNIX) or Administrator (on Windows) + privileges to install &SCons; like this. + + + + + +
+ Building and Installing Multiple Versions of &SCons; Side-by-Side + + + + The &SCons; setup.py script + has some extensions that support + easy installation of multiple versions of &SCons; + in side-by-side locations. + This makes it easier to download and + experiment with different versions of &SCons; + before moving your official build process to a new version, + for example. + + + + + + To install &SCons; in a version-specific location, + add the option + when you call setup.py: + + + + + # python setup.py install --version-lib + + + + + This will install the &SCons; build engine + in the + /usr/lib/scons-__VERSION__ + or + C:\Python2.2\scons-__VERSION__ + directory, for example. + + + + + + If you use the option + the first time you install &SCons;, + you do not need to specify it each time you install + a new version. + The &SCons; setup.py script + will detect the version-specific directory name(s) + and assume you want to install all versions + in version-specific directories. + You can override that assumption in the future + by explicitly specifying the option. + + + +
+ +
+ Installing &SCons; in Other Locations + + + + You can install &SCons; in locations other than + the default by specifying the option: + + + + + # python setup.py install --prefix=/opt/scons + + + + + This would + install the scons script in + /opt/scons/bin + and the build engine in + /opt/scons/lib/scons, + + + + + + Note that you can specify both the + and the options + at the same type, + in which case setup.py + will install the build engine + in a version-specific directory + relative to the specified prefix. + Adding to the + above example would install the build engine in + /opt/scons/lib/scons-__VERSION__. + + + +
+ +
+ Building and Installing &SCons; Without Administrative Privileges + + + + If you don't have the right privileges to install &SCons; + in a system location, + simply use the --prefix= option + to install it in a location of your choosing. + For example, + to install &SCons; in appropriate locations + relative to the user's $HOME directory, + the scons script in + $HOME/bin + and the build engine in + $HOME/lib/scons, + simply type: + + + + + $ python setup.py install --prefix=$HOME + + + + + You may, of course, specify any other location you prefer, + and may use the option + if you would like to install version-specific directories + relative to the specified prefix. + + + +
+ +
+ + diff --git a/doc/user/builders-built-in.sgml b/doc/user/builders-built-in.sgml deleted file mode 100644 index cf09fd5..0000000 --- a/doc/user/builders-built-in.sgml +++ /dev/null @@ -1,937 +0,0 @@ - - - - - &SCons; provides the ability to build a lot of different - types of files right "out of the box." - So far, we've been using &SCons;' ability to build - programs, objects and libraries to - illustrate much of the underlying functionality of &SCons; - This section will describe all of the different - types of files that you can build with &SCons;, - and the built-in &Builder; objects used to build them. - By default, all of the &Builder; objects in this section - can be built either with or without an explicit - construction environment. - - - -
- Programs: the &Program; Builder - - - - As we've seen, the &b-link-Program; Builder - is used to build an executable program. - The &source; argument is one or more - source-code files or object files, - and the ⌖ argument is the - name of the executable program name to be created. - For example: - - - - - Program('prog', 'file1.o') - - - - - Will create the &prog; - executable on a POSIX system, - the &prog_exe; executable on a Windows system. - - - - - - The target file's prefix and suffix may be omitted, - and the values from the - &cv-link-PROGPREFIX; - and - &cv-link-PROGSUFFIX; - construction variables - will be appended appropriately. - For example: - - - - - env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx') - env.Program('prog', ['file1.o', 'file2.o']) - - - - - Will create a program named - myprog.xxx - regardless of the system on which it is run. - - - - - - If you omit the ⌖, - the base of the first input - file name specified - becomes the base of the target - program created. - For example: - - - - - Program(['hello.c', 'goodbye.c']) - - - - - Will create the &hello; - executable on a POSIX system, - the &hello_exe; executable on a Windows system. - - - - - - Two construction variables control what libraries - will be linked with the resulting program. - The &cv-link-LIBS; variable is a list of the names of - libraries that will be linked into any programs, - and the &cv-link-LIBPATH; variables is a list of - directories that will be searched for - the specified libraries. - &SCons; will construct the right command-line - options for the running system. - For example: - - - - - env = Environment(LIBS = ['foo1', 'foo2'], - LIBPATH = ['/usr/dir1', 'dir2']) - env.Program(['hello.c', 'goodbye.c']) - - - - - Will execute as follows on a POSIX system: - - - - - % scons -Q - 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 - - - - - And execute as follows on a Windows system: - - - - - C:\>scons -Q - cl /nologo /c goodbye.c /Fogoodbye.obj - cl /nologo /c hello.c /Fohello.obj - link /nologo /OUT:hello.exe /LIBPATH:\usr\dir1 /LIBPATH:dir2 foo1.lib foo2.lib hello.obj goodbye.obj - - - - - The &cv-LIBS; construction variable - is turned into command line options - by appending the &cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; - construction variables to the beginning and end, - respectively, of each specified library. - - - - - - The &cv-LIBPATH; construction variable - is turned into command line options - by appending the &cv-link-LIBDIRPREFIX; and &cv-link-LIBDIRSUFFIX; - construction variables to the beginning and end, - respectively, of each specified library. - - - - - - Other relevant construction variables - include those used by the &b-link-Object; - builders to affect how the - source files specified as input to the &t-Program; - builders are turned into object files; - see the next section. - - - - - - The command line used to control how a program is linked - is specified by the &cv-link-LINKCOM; construction variable. - By default, it uses the - &cv-link-LINK; construction variable - and the &cv-link-LINKFLAGS; construction variable. - - - -
- -
- Object-File Builders - - - - &SCons; provides separate Builder objects - to create static and shared object files. - The distinction becomes especially important when - archiving object files into different types of libraries. - - - -
- The &StaticObject; Builder - - - - The &b-link-StaticObject; Builder - is used to build an object file - suitable for static linking into a program, - or for inclusion in a static library. - The &source; argument is a single source-code file, - and the ⌖ argument is the - name of the static object file to be created. - For example: - - - - - StaticObject('file', 'file.c') - - - - - Will create the &file_o; - object file on a POSIX system, - the &file_obj; executable on a Windows system. - - - - - - The target file's prefix and suffix may be omitted, - and the values from the - &cv-link-OBJPREFIX; - and - &cv-link-OBJSUFFIX; - construction variables - will be appended appropriately. - For example: - - - - - env = Environment(OBJPREFIX='my', OBJSUFFIX='.xxx') - env.StaticObject('file', 'file.c') - - - - - Will create an object file named - myfile.xxx - regardless of the system on which it is run. - - - - - - If you omit the ⌖, - the base of the first input - file name specified - beomces the base of the name - of the static object file to be created. - For example: - - - - - StaticObject('file.c') - - - - - Will create the &file_o; - executable on a POSIX system, - the &file_obj; executable on a Windows system. - - - -
- -
- The &SharedObject; Builder - - - - The &b-link-SharedObject; Builder - is used to build an object file - suitable for shared linking into a program, - or for inclusion in a shared library. - The &source; argument is a single source-code file, - and the ⌖ argument is the - name of the shared object file to be created. - For example: - - - - - SharedObject('file', 'file.c') - - - - - Will create the &file_o; - object file on a POSIX system, - the &file_obj; executable on a Windows system. - - - - - - The target file's prefix and suffix may be omitted, - and the values from the - &cv-link-SHOBJPREFIX; - and - &cv-link-SHOBJSUFFIX; - construction variables - will be appended appropriately. - For example: - - - - - env = Environment(SHOBJPREFIX='my', SHOBJSUFFIX='.xxx') - env.SharedObject('file', 'file.c') - - - - - Will create an object file named - myfile.xxx - regardless of the system on which it is run. - - - - - - If you omit the ⌖, - the base of the first input - file name specified - becomes the base of the name - of the shared object file to be created. - For example: - - - - - SharedObject('file.c') - - - - - Will create the &file_o; - executable on a POSIX system, - the &file_obj; executable on a Windows system. - - - -
- -
- The &Object; Builder - - - - The &b-link-Object; Builder is a synonym for &b-link-StaticObject; - and is completely equivalent. - - - -
- -
- -
- Library Builders - - - - &SCons; provides separate Builder objects - to create static and shared libraries. - - - -
- The &StaticLibrary; Builder - - - - The &b-link-StaticLibrary; Builder - is used to create a library - suitable for static linking into a program. - The &source; argument is one or more - source-code files or object files, - and the ⌖ argument is the - name of the static library to be created. - For example: - - - - - StaticLibrary('foo', ['file1.c', 'file2.c']) - - - - - The target file's prefix and suffix may be omitted, - and the values from the - &cv-link-LIBPREFIX; - and - &cv-link-LIBSUFFIX; - construction variables - will be appended appropriately. - For example: - - - - - env = Environment(LIBPREFIX='my', LIBSUFFIX='.xxx') - env.StaticLibrary('lib', ['file1.o', 'file2.o']) - - - - - Will create an object file named - mylib.xxx - regardless of the system on which it is run. - - - - - StaticLibrary('foo', ['file1.c', 'file2.c']) - - - - - If you omit the ⌖, - the base of the first input - file name specified - becomes the base of the name of the static object file to be created. - For example: - - - - - StaticLibrary(['file.c', 'another.c']) - - - - - Will create the &libfile_a; - library on a POSIX system, - the &file_lib; library on a Windows system. - - - -
- -
- The &SharedLibrary; Builder - - - - The &b-link-SharedLibrary; Builder - is used to create a shared library - suitable for linking with a program. - The &source; argument is one or more - source-code files or object files, - and the ⌖ argument is the - name of the shared library to be created. - For example: - - - - - SharedLibrary('foo', ['file1.c', 'file2.c']) - - - - - The target file's prefix and suffix may be omitted, - and the values from the - &cv-link-SHLIBPREFIX; - and - &cv-link-SHLIBSUFFIX; - construction variables - will be appended appropriately. - For example: - - - - - env = Environment(SHLIBPREFIX='my', SHLIBSUFFIX='.xxx') - env.SharedLibrary('shared', ['file1.o', 'file2.o']) - - - - - Will create an object file named - myshared.xxx - regardless of the system on which it is run. - - - - - SharedLibrary('foo', ['file1.c', 'file2.c']) - - - - - If you omit the ⌖, - the base of the first input - file name specified - becomes the base of the name of the shared library to be created. - For example: - - - - - SharedLibrary(['file.c', 'another.c']) - - - - - Will create the &libfile_so; - library on a POSIX system, - the &file_dll; library on a Windows system. - - - -
- -
- The &Library; Builder - - - - The &b-link-Library; Builder is a synonym for &b-link-StaticLibrary; - and is completely equivalent. - - - -
- -
- -
- Pre-Compiled Headers: the &PCH; Builder - - - - XXX PCH() - - - -
- -
- Microsoft Visual C++ Resource Files: the &RES; Builder - - - - XXX RES() - - - -
- -
- Source Files - - - - By default - &SCons; supports two Builder objects - that know how to build source files - from other input files. - These are typically invoked "internally" - to turn files that need preprocessing into other source files. - - - -
- The &CFile; Builder - - - - XXX CFile() - - - - - XXX CFile() programlisting - - - - XXX CFile() screen - - -
- -
- The &CXXFile; Builder - - - - XXX CXXFILE() - - - - - XXX CXXFILE() programlisting - - - - XXX CXXFILE() screen - - -
- -
- -
- Documents - - - - &SCons; provides a number of Builder objects - for creating different types of documents. - - - -
- The &DVI; Builder - - - - XXX DVI() para - - - - - XXX DVI() programlisting - - - - XXX DVI() screen - - -
- -
- The &PDF; Builder - - - - XXX PDF() para - - - -
- -
- The &PostScript; Builder - - - - XXX PostScript() para - - - - - XXX PostScript() programlisting - - - - XXX PostScript() screen - - -
- -
- -
- Archives - - - - &SCons; provides Builder objects - for creating two different types of archive files. - - - -
- The &Tar; Builder - - - - The &b-link-Tar; Builder object uses the &tar; - utility to create archives of files - and/or directory trees: - - - - - env = Environment() - env.Tar('out1.tar', ['file1', 'file2']) - env.Tar('out2', 'directory') - - - - % scons -Q . - tar -c -f out1.tar file1 file2 - tar -c -f out2.tar directory - - - - - One common requirement when creating a &tar; archive - is to create a compressed archive using the - option. - This is easily handled by specifying - the value of the &cv-link-TARFLAGS; variable - when you create the construction environment. - Note, however, that the used to - to instruct &tar; to create the archive - is part of the default value of &cv-TARFLAGS;, - so you need to set it both options: - - - - - env = Environment(TARFLAGS = '-c -z') - env.Tar('out.tar.gz', 'directory') - - - - % scons -Q . - tar -c -z -f out.tar.gz directory - - - - - you may also wish to set the value of the - &cv-link-TARSUFFIX; construction variable - to your desired suffix for compress &tar; archives, - so that &SCons; can append it to the target file name - without your having to specify it explicitly: - - - - - env = Environment(TARFLAGS = '-c -z', - TARSUFFIX = '.tgz') - env.Tar('out', 'directory') - - - - % scons -Q . - tar -c -z -f out.tgz directory - - -
- -
- The &Zip; Builder - - - - The &b-link-Zip; Builder object creates archives of files - and/or directory trees in the ZIP file format. - Python versions 1.6 or later - contain an internal &zipfile; module - that &SCons; will use. - In this case, given the following - &SConstruct; file: - - - - - env = Environment() - env.Zip('out', ['file1', 'file2']) - - - - - Your output will reflect the fact - that an internal Python function - is being used to create the output ZIP archive: - - - - - % scons -Q . - zip(["out.zip"], ["file1", "file2"]) - - - - - If you're using Python version 1.5.2 to run &SCons;, - then &SCons; will try to use an external - &zip; program as follows: - - - - - % scons -Q . - zip /home/my/project/zip.out file1 file2 - - -
- -
- -
- Java - - - - &SCons; provides Builder objects - for creating various types of Java output files. - - - -
- Building Class Files: the &Java; Builder - - - - The &b-link-Java; builder takes one or more input - .java files - and turns them into one or more - .class files - Unlike most builders, however, - the &Java; builder takes - target and source directories, - not files, as input. - - - - - env = Environment() - env.Java(target = 'classes', source = 'src') - - - - - The &Java; builder will then - search the specified source directory - tree for all .java files, - and pass any out-of-date - - - - - XXX Java() screen - - -
- -
- The &Jar; Builder - - - - XXX The &Jar; builder object - - - - - env = Environment() - env.Java(target = 'classes', source = 'src') - env.Jar(target = '', source = 'classes') - - - - XXX Jar() screen - - -
- -
- Building C header and stub files: the &JavaH; Builder - - - - XXX JavaH() para - - - - - XXX JavaH() programlisting - - - - XXX JavaH() screen - - -
- -
- Building RMI stub and skeleton class files: the &RMIC; Builder - - - - XXX RMIC() para - - - - - XXX RMIC() programlisting - - - - XXX RMIC() screen - - -
- -
diff --git a/doc/user/builders-built-in.xml b/doc/user/builders-built-in.xml new file mode 100644 index 0000000..cf09fd5 --- /dev/null +++ b/doc/user/builders-built-in.xml @@ -0,0 +1,937 @@ + + + + + &SCons; provides the ability to build a lot of different + types of files right "out of the box." + So far, we've been using &SCons;' ability to build + programs, objects and libraries to + illustrate much of the underlying functionality of &SCons; + This section will describe all of the different + types of files that you can build with &SCons;, + and the built-in &Builder; objects used to build them. + By default, all of the &Builder; objects in this section + can be built either with or without an explicit + construction environment. + + + +
+ Programs: the &Program; Builder + + + + As we've seen, the &b-link-Program; Builder + is used to build an executable program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the executable program name to be created. + For example: + + + + + Program('prog', 'file1.o') + + + + + Will create the &prog; + executable on a POSIX system, + the &prog_exe; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-PROGPREFIX; + and + &cv-link-PROGSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx') + env.Program('prog', ['file1.o', 'file2.o']) + + + + + Will create a program named + myprog.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the target + program created. + For example: + + + + + Program(['hello.c', 'goodbye.c']) + + + + + Will create the &hello; + executable on a POSIX system, + the &hello_exe; executable on a Windows system. + + + + + + Two construction variables control what libraries + will be linked with the resulting program. + The &cv-link-LIBS; variable is a list of the names of + libraries that will be linked into any programs, + and the &cv-link-LIBPATH; variables is a list of + directories that will be searched for + the specified libraries. + &SCons; will construct the right command-line + options for the running system. + For example: + + + + + env = Environment(LIBS = ['foo1', 'foo2'], + LIBPATH = ['/usr/dir1', 'dir2']) + env.Program(['hello.c', 'goodbye.c']) + + + + + Will execute as follows on a POSIX system: + + + + + % scons -Q + 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 + + + + + And execute as follows on a Windows system: + + + + + C:\>scons -Q + cl /nologo /c goodbye.c /Fogoodbye.obj + cl /nologo /c hello.c /Fohello.obj + link /nologo /OUT:hello.exe /LIBPATH:\usr\dir1 /LIBPATH:dir2 foo1.lib foo2.lib hello.obj goodbye.obj + + + + + The &cv-LIBS; construction variable + is turned into command line options + by appending the &cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; + construction variables to the beginning and end, + respectively, of each specified library. + + + + + + The &cv-LIBPATH; construction variable + is turned into command line options + by appending the &cv-link-LIBDIRPREFIX; and &cv-link-LIBDIRSUFFIX; + construction variables to the beginning and end, + respectively, of each specified library. + + + + + + Other relevant construction variables + include those used by the &b-link-Object; + builders to affect how the + source files specified as input to the &t-Program; + builders are turned into object files; + see the next section. + + + + + + The command line used to control how a program is linked + is specified by the &cv-link-LINKCOM; construction variable. + By default, it uses the + &cv-link-LINK; construction variable + and the &cv-link-LINKFLAGS; construction variable. + + + +
+ +
+ Object-File Builders + + + + &SCons; provides separate Builder objects + to create static and shared object files. + The distinction becomes especially important when + archiving object files into different types of libraries. + + + +
+ The &StaticObject; Builder + + + + The &b-link-StaticObject; Builder + is used to build an object file + suitable for static linking into a program, + or for inclusion in a static library. + The &source; argument is a single source-code file, + and the ⌖ argument is the + name of the static object file to be created. + For example: + + + + + StaticObject('file', 'file.c') + + + + + Will create the &file_o; + object file on a POSIX system, + the &file_obj; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-OBJPREFIX; + and + &cv-link-OBJSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(OBJPREFIX='my', OBJSUFFIX='.xxx') + env.StaticObject('file', 'file.c') + + + + + Will create an object file named + myfile.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + beomces the base of the name + of the static object file to be created. + For example: + + + + + StaticObject('file.c') + + + + + Will create the &file_o; + executable on a POSIX system, + the &file_obj; executable on a Windows system. + + + +
+ +
+ The &SharedObject; Builder + + + + The &b-link-SharedObject; Builder + is used to build an object file + suitable for shared linking into a program, + or for inclusion in a shared library. + The &source; argument is a single source-code file, + and the ⌖ argument is the + name of the shared object file to be created. + For example: + + + + + SharedObject('file', 'file.c') + + + + + Will create the &file_o; + object file on a POSIX system, + the &file_obj; executable on a Windows system. + + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-SHOBJPREFIX; + and + &cv-link-SHOBJSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(SHOBJPREFIX='my', SHOBJSUFFIX='.xxx') + env.SharedObject('file', 'file.c') + + + + + Will create an object file named + myfile.xxx + regardless of the system on which it is run. + + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name + of the shared object file to be created. + For example: + + + + + SharedObject('file.c') + + + + + Will create the &file_o; + executable on a POSIX system, + the &file_obj; executable on a Windows system. + + + +
+ +
+ The &Object; Builder + + + + The &b-link-Object; Builder is a synonym for &b-link-StaticObject; + and is completely equivalent. + + + +
+ +
+ +
+ Library Builders + + + + &SCons; provides separate Builder objects + to create static and shared libraries. + + + +
+ The &StaticLibrary; Builder + + + + The &b-link-StaticLibrary; Builder + is used to create a library + suitable for static linking into a program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the static library to be created. + For example: + + + + + StaticLibrary('foo', ['file1.c', 'file2.c']) + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-LIBPREFIX; + and + &cv-link-LIBSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(LIBPREFIX='my', LIBSUFFIX='.xxx') + env.StaticLibrary('lib', ['file1.o', 'file2.o']) + + + + + Will create an object file named + mylib.xxx + regardless of the system on which it is run. + + + + + StaticLibrary('foo', ['file1.c', 'file2.c']) + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name of the static object file to be created. + For example: + + + + + StaticLibrary(['file.c', 'another.c']) + + + + + Will create the &libfile_a; + library on a POSIX system, + the &file_lib; library on a Windows system. + + + +
+ +
+ The &SharedLibrary; Builder + + + + The &b-link-SharedLibrary; Builder + is used to create a shared library + suitable for linking with a program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the shared library to be created. + For example: + + + + + SharedLibrary('foo', ['file1.c', 'file2.c']) + + + + + The target file's prefix and suffix may be omitted, + and the values from the + &cv-link-SHLIBPREFIX; + and + &cv-link-SHLIBSUFFIX; + construction variables + will be appended appropriately. + For example: + + + + + env = Environment(SHLIBPREFIX='my', SHLIBSUFFIX='.xxx') + env.SharedLibrary('shared', ['file1.o', 'file2.o']) + + + + + Will create an object file named + myshared.xxx + regardless of the system on which it is run. + + + + + SharedLibrary('foo', ['file1.c', 'file2.c']) + + + + + If you omit the ⌖, + the base of the first input + file name specified + becomes the base of the name of the shared library to be created. + For example: + + + + + SharedLibrary(['file.c', 'another.c']) + + + + + Will create the &libfile_so; + library on a POSIX system, + the &file_dll; library on a Windows system. + + + +
+ +
+ The &Library; Builder + + + + The &b-link-Library; Builder is a synonym for &b-link-StaticLibrary; + and is completely equivalent. + + + +
+ +
+ +
+ Pre-Compiled Headers: the &PCH; Builder + + + + XXX PCH() + + + +
+ +
+ Microsoft Visual C++ Resource Files: the &RES; Builder + + + + XXX RES() + + + +
+ +
+ Source Files + + + + By default + &SCons; supports two Builder objects + that know how to build source files + from other input files. + These are typically invoked "internally" + to turn files that need preprocessing into other source files. + + + +
+ The &CFile; Builder + + + + XXX CFile() + + + + + XXX CFile() programlisting + + + + XXX CFile() screen + + +
+ +
+ The &CXXFile; Builder + + + + XXX CXXFILE() + + + + + XXX CXXFILE() programlisting + + + + XXX CXXFILE() screen + + +
+ +
+ +
+ Documents + + + + &SCons; provides a number of Builder objects + for creating different types of documents. + + + +
+ The &DVI; Builder + + + + XXX DVI() para + + + + + XXX DVI() programlisting + + + + XXX DVI() screen + + +
+ +
+ The &PDF; Builder + + + + XXX PDF() para + + + +
+ +
+ The &PostScript; Builder + + + + XXX PostScript() para + + + + + XXX PostScript() programlisting + + + + XXX PostScript() screen + + +
+ +
+ +
+ Archives + + + + &SCons; provides Builder objects + for creating two different types of archive files. + + + +
+ The &Tar; Builder + + + + The &b-link-Tar; Builder object uses the &tar; + utility to create archives of files + and/or directory trees: + + + + + env = Environment() + env.Tar('out1.tar', ['file1', 'file2']) + env.Tar('out2', 'directory') + + + + % scons -Q . + tar -c -f out1.tar file1 file2 + tar -c -f out2.tar directory + + + + + One common requirement when creating a &tar; archive + is to create a compressed archive using the + option. + This is easily handled by specifying + the value of the &cv-link-TARFLAGS; variable + when you create the construction environment. + Note, however, that the used to + to instruct &tar; to create the archive + is part of the default value of &cv-TARFLAGS;, + so you need to set it both options: + + + + + env = Environment(TARFLAGS = '-c -z') + env.Tar('out.tar.gz', 'directory') + + + + % scons -Q . + tar -c -z -f out.tar.gz directory + + + + + you may also wish to set the value of the + &cv-link-TARSUFFIX; construction variable + to your desired suffix for compress &tar; archives, + so that &SCons; can append it to the target file name + without your having to specify it explicitly: + + + + + env = Environment(TARFLAGS = '-c -z', + TARSUFFIX = '.tgz') + env.Tar('out', 'directory') + + + + % scons -Q . + tar -c -z -f out.tgz directory + + +
+ +
+ The &Zip; Builder + + + + The &b-link-Zip; Builder object creates archives of files + and/or directory trees in the ZIP file format. + Python versions 1.6 or later + contain an internal &zipfile; module + that &SCons; will use. + In this case, given the following + &SConstruct; file: + + + + + env = Environment() + env.Zip('out', ['file1', 'file2']) + + + + + Your output will reflect the fact + that an internal Python function + is being used to create the output ZIP archive: + + + + + % scons -Q . + zip(["out.zip"], ["file1", "file2"]) + + + + + If you're using Python version 1.5.2 to run &SCons;, + then &SCons; will try to use an external + &zip; program as follows: + + + + + % scons -Q . + zip /home/my/project/zip.out file1 file2 + + +
+ +
+ +
+ Java + + + + &SCons; provides Builder objects + for creating various types of Java output files. + + + +
+ Building Class Files: the &Java; Builder + + + + The &b-link-Java; builder takes one or more input + .java files + and turns them into one or more + .class files + Unlike most builders, however, + the &Java; builder takes + target and source directories, + not files, as input. + + + + + env = Environment() + env.Java(target = 'classes', source = 'src') + + + + + The &Java; builder will then + search the specified source directory + tree for all .java files, + and pass any out-of-date + + + + + XXX Java() screen + + +
+ +
+ The &Jar; Builder + + + + XXX The &Jar; builder object + + + + + env = Environment() + env.Java(target = 'classes', source = 'src') + env.Jar(target = '', source = 'classes') + + + + XXX Jar() screen + + +
+ +
+ Building C header and stub files: the &JavaH; Builder + + + + XXX JavaH() para + + + + + XXX JavaH() programlisting + + + + XXX JavaH() screen + + +
+ +
+ Building RMI stub and skeleton class files: the &RMIC; Builder + + + + XXX RMIC() para + + + + + XXX RMIC() programlisting + + + + XXX RMIC() screen + + +
+ +
diff --git a/doc/user/builders-commands.in b/doc/user/builders-commands.in index 13a5c56..7265a23 100644 --- a/doc/user/builders-commands.in +++ b/doc/user/builders-commands.in @@ -79,6 +79,15 @@ + + + When executed, + &SCons; runs the specified command, + substituting &cv-link-SOURCE; and &cv-link-TARGET; + as expected: + + + scons -Q @@ -94,7 +103,9 @@ - Note that the action you + Note that the action you specify to the + &Command; &Builder; can be any legal &SCons; &Action;, + such as a Python function: @@ -111,6 +122,12 @@ + + + Which executes as follows: + + + scons -Q diff --git a/doc/user/builders-commands.sgml b/doc/user/builders-commands.sgml deleted file mode 100644 index d2b990a..0000000 --- a/doc/user/builders-commands.sgml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - Creating a &Builder; and attaching it to a &consenv; - allows for a lot of flexibility when you - want to re-use actions - to build multiple files of the same type. - This can, however, be cumbersome - if you only need to execute one specific command - to build a single file (or group of files). - For these situations, &SCons; supports a - &Command; &Builder; that arranges - for a specific action to be executed - to build a specific file or files. - This looks a lot like the other builders - (like &b-link-Program;, &b-link-Object;, etc.), - but takes as an additional argument - the command to be executed to build the file: - - - - - env = Environment() - env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET") - - - - % scons -Q - sed 's/x/y/' < foo.in > foo.out - - - - - This is often more convenient than - creating a &Builder; object - and adding it to the &cv-link-BUILDERS; variable - of a &consenv; - - - - - - Note that the action you - - - - - env = Environment() - def build(target, source, env): - # Whatever it takes to build - return None - env.Command('foo.out', 'foo.in', build) - - - - % scons -Q - build(["foo.out"], ["foo.in"]) - diff --git a/doc/user/builders-commands.xml b/doc/user/builders-commands.xml new file mode 100644 index 0000000..15a6015 --- /dev/null +++ b/doc/user/builders-commands.xml @@ -0,0 +1,125 @@ + + + + + + + Creating a &Builder; and attaching it to a &consenv; + allows for a lot of flexibility when you + want to re-use actions + to build multiple files of the same type. + This can, however, be cumbersome + if you only need to execute one specific command + to build a single file (or group of files). + For these situations, &SCons; supports a + &Command; &Builder; that arranges + for a specific action to be executed + to build a specific file or files. + This looks a lot like the other builders + (like &b-link-Program;, &b-link-Object;, etc.), + but takes as an additional argument + the command to be executed to build the file: + + + + + env = Environment() + env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET") + + + + + When executed, + &SCons; runs the specified command, + substituting &cv-link-SOURCE; and &cv-link-TARGET; + as expected: + + + + + % scons -Q + sed 's/x/y/' < foo.in > foo.out + + + + + This is often more convenient than + creating a &Builder; object + and adding it to the &cv-link-BUILDERS; variable + of a &consenv; + + + + + + Note that the action you specify to the + &Command; &Builder; can be any legal &SCons; &Action;, + such as a Python function: + + + + + env = Environment() + def build(target, source, env): + # Whatever it takes to build + return None + env.Command('foo.out', 'foo.in', build) + + + + + Which executes as follows: + + + + + % scons -Q + build(["foo.out"], ["foo.in"]) + diff --git a/doc/user/builders-writing.in b/doc/user/builders-writing.in index 62717aa..7497277 100644 --- a/doc/user/builders-writing.in +++ b/doc/user/builders-writing.in @@ -219,8 +219,31 @@ This functionality could be invoked as in the following example: + - + + import SCons.Defaults; SCons.Defaults.ConstructionEnvironment['TOOLS'] = {}; bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') env = Environment(BUILDERS = {'Foo' : bld}) env.Foo('file.foo', 'file.input') diff --git a/doc/user/builders-writing.sgml b/doc/user/builders-writing.sgml deleted file mode 100644 index 412d431..0000000 --- a/doc/user/builders-writing.sgml +++ /dev/null @@ -1,673 +0,0 @@ - - - - - - - Although &SCons; provides many useful methods - for building common software products: - programs, libraries, documents. - you frequently want to be - able to build some other type of file - not supported directly by &SCons; - Fortunately, &SCons; makes it very easy - to define your own &Builder; objects - for any custom file types you want to build. - (In fact, the &SCons; interfaces for creating - &Builder; objects are flexible enough and easy enough to use - that all of the the &SCons; built-in &Builder; objects - are created the mechanisms described in this section.) - - - -
- Writing Builders That Execute External Commands - - - - The simplest &Builder; to create is - one that executes an external command. - For example, if we want to build - an output file by running the contents - of the input file through a command named - foobuild, - creating that &Builder; might look like: - - - - - bld = Builder(action = 'foobuild < $SOURCE > $TARGET') - - - - - All the above line does is create a free-standing - &Builder; object. - The next section will show us how to actually use it. - - - -
- -
- Attaching a Builder to a &ConsEnv; - - - - A &Builder; object isn't useful - until it's attached to a &consenv; - so that we can call it to arrange - for files to be built. - This is done through the &cv-link-BUILDERS; - &consvar; in an environment. - The &cv-BUILDERS; variable is a Python dictionary - that maps the names by which you want to call - various &Builder; objects to the objects themselves. - For example, if we want to call the - &Builder; we just defined by the name - Foo, - our &SConstruct; file might look like: - - - - - - - bld = Builder(action = 'foobuild < $SOURCE > $TARGET') - env = Environment(BUILDERS = {'Foo' : bld}) - - - - - With the &Builder; so attached to our &consenv; - we can now actually call it like so: - - - - - env.Foo('file.foo', 'file.input') - - - - - Then when we run &SCons; it looks like: - - - - - % scons -Q - foobuild < file.input > file.foo - - - - - Note, however, that the default &cv-BUILDERS; - variable in a &consenv; - comes with a default set of &Builder; objects - already defined: - &b-link-Program;, &b-link-Library;, etc. - And when we explicitly set the &cv-BUILDERS; variable - when we create the &consenv;, - the default &Builder;s are no longer part of - the environment: - - - - - bld = Builder(action = 'foobuild < $SOURCE > $TARGET') - env = Environment(BUILDERS = {'Foo' : bld}) - env.Foo('file.foo', 'file.input') - env.Program('hello.c') - - - - % scons -Q - AttributeError: 'SConsEnvironment' object has no attribute 'Program': - File "SConstruct", line 4: - env.Program('hello.c') - - - - - To be able use both our own defined &Builder; objects - and the default &Builder; objects in the same &consenv;, - you can either add to the &cv-BUILDERS; variable - using the &Append; function: - - - - - - - env = Environment() - bld = Builder(action = 'foobuild < $SOURCE > $TARGET') - env.Append(BUILDERS = {'Foo' : bld}) - env.Foo('file.foo', 'file.input') - env.Program('hello.c') - - - - - Or you can explicitly set the appropriately-named - key in the &cv-BUILDERS; dictionary: - - - - - env = Environment() - bld = Builder(action = 'foobuild < $SOURCE > $TARGET') - env['BUILDERS']['Foo'] = bld - env.Foo('file.foo', 'file.input') - env.Program('hello.c') - - - - - Either way, the same &consenv; - can then use both the newly-defined - Foo &Builder; - and the default &b-link-Program; &Builder;: - - - - - % scons -Q - foobuild < file.input > file.foo - cc -o hello.o -c hello.c - cc -o hello hello.o - - -
- -
- Letting &SCons; Handle The File Suffixes - - - - By supplying additional information - when you create a &Builder;, - you can let &SCons; add appropriate file - suffixes to the target and/or the source file. - For example, rather than having to specify - explicitly that you want the Foo - &Builder; to build the file.foo - target file from the file.input source file, - you can give the .foo - and .input suffixes to the &Builder;, - making for more compact and readable calls to - the Foo &Builder;: - - - - - - - bld = Builder(action = 'foobuild < $SOURCE > $TARGET', - suffix = '.foo', - src_suffix = '.input') - env = Environment(BUILDERS = {'Foo' : bld}) - env.Foo('file1') - env.Foo('file2') - - - - % scons -Q - foobuild < file1.input > file1.foo - foobuild < file2.input > file2.foo - - - - - You can also supply a prefix keyword argument - if it's appropriate to have &SCons; append a prefix - to the beginning of target file names. - - - -
- -
- Builders That Execute Python Functions - - - - In &SCons;, you don't have to call an external command - to build a file. - You can, instead, define a Python function - that a &Builder; object can invoke - to build your target file (or files). - Such a &buildfunc; definition looks like: - - - - - def build_function(target, source, env): - # Code to build "target" from "source" - return None - - - - - The arguments of a &buildfunc; are: - - - - - - - target - - - - - A list of Node objects representing - the target or targets to be - built by this builder function. - The file names of these target(s) - may be extracted using the Python &str; function. - - - - - - - source - - - - - A list of Node objects representing - the sources to be - used by this builder function to build the targets. - The file names of these source(s) - may be extracted using the Python &str; function. - - - - - - - env - - - - - The &consenv; used for building the target(s). - The builder function may use any of the - environment's construction variables - in any way to affect how it builds the targets. - - - - - - - - - - The builder function must - return a 0 or None value - if the target(s) are built successfully. - The builder function - may raise an exception - or return any non-zero value - to indicate that the build is unsuccessful, - - - - - - Once you've defined the Python function - that will build your target file, - defining a &Builder; object for it is as - simple as specifying the name of the function, - instead of an external command, - as the &Builder;'s - action - argument: - - - - - def build_function(target, source, env): - # Code to build "target" from "source" - return None - bld = Builder(action = build_function, - suffix = '.foo', - src_suffix = '.input') - env = Environment(BUILDERS = {'Foo' : bld}) - env.Foo('file') - - - - - And notice that the output changes slightly, - reflecting the fact that a Python function, - not an external command, - is now called to build the target file: - - - - - % scons -Q - build_function(["file.foo"], ["file.input"]) - - -
- -
- Builders That Create Actions Using a &Generator; - - - - &SCons; Builder objects can create an action "on the fly" - by using a function called a &generator;. - This provides a great deal of flexibility to - construct just the right list of commands - to build your target. - A &generator; looks like: - - - - - def generate_actions(source, target, env, for_signature): - return 'foobuild < %s > %s' % (target[0], source[0]) - - - - - The arguments of a &generator; are: - - - - - - - source - - - - - A list of Node objects representing - the sources to be built - by the command or other action - generated by this function. - The file names of these source(s) - may be extracted using the Python &str; function. - - - - - - - - target - - - - - A list of Node objects representing - the target or targets to be built - by the command or other action - generated by this function. - The file names of these target(s) - may be extracted using the Python &str; function. - - - - - - - - env - - - - - The &consenv; used for building the target(s). - The generator may use any of the - environment's construction variables - in any way to determine what command - or other action to return. - - - - - - - - for_signature - - - - - A flag that specifies whether the - generator is being called to contribute to a build signature, - as opposed to actually executing the command. - - - - - - - - - - - - - The &generator; must return a - command string or other action that will be used to - build the specified target(s) from the specified source(s). - - - - - - Once you've defined a &generator;, - you create a &Builder; to use it - by specifying the generator keyword argument - instead of action. - - - - - - - def generate_actions(source, target, env, for_signature): - return 'foobuild < %s > %s' % (source[0], target[0]) - bld = Builder(generator = generate_actions, - suffix = '.foo', - src_suffix = '.input') - env = Environment(BUILDERS = {'Foo' : bld}) - env.Foo('file') - - - - % scons -Q - foobuild < file.input > file.foo - - - - - Note that it's illegal to specify both an - action - and a - generator - for a &Builder;. - - - -
- -
- Builders That Modify the Target or Source Lists Using an &Emitter; - - - - &SCons; supports the ability for a Builder to modify the - lists of target(s) from the specified source(s). - - - - - - - def modify_targets(target, source, env): - target.append('new_target') - source.append('new_source') - return target, source - bld = Builder(action = 'foobuild $TARGETS - $SOURCES', - suffix = '.foo', - src_suffix = '.input', - emitter = modify_targets) - env = Environment(BUILDERS = {'Foo' : bld}) - env.Foo('file') - - - - % scons -Q - foobuild file.foo new_target - file.input new_source - - - - bld = Builder(action = 'my_command', - suffix = '.foo', - src_suffix = '.input', - emitter = 'MY_EMITTER') - def modify1(target, source, env): - return target, source - def modify2(target, source, env): - return target, source - env1 = Environment(BUILDERS = {'Foo' : bld}, - MY_EMITTER = modify1) - env2 = Environment(BUILDERS = {'Foo' : bld}, - MY_EMITTER = modify2) - env1.Foo('file1') - env2.Foo('file2') - - -
- - diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml new file mode 100644 index 0000000..50f6556 --- /dev/null +++ b/doc/user/builders-writing.xml @@ -0,0 +1,690 @@ + + + + + + + Although &SCons; provides many useful methods + for building common software products: + programs, libraries, documents. + you frequently want to be + able to build some other type of file + not supported directly by &SCons; + Fortunately, &SCons; makes it very easy + to define your own &Builder; objects + for any custom file types you want to build. + (In fact, the &SCons; interfaces for creating + &Builder; objects are flexible enough and easy enough to use + that all of the the &SCons; built-in &Builder; objects + are created the mechanisms described in this section.) + + + +
+ Writing Builders That Execute External Commands + + + + The simplest &Builder; to create is + one that executes an external command. + For example, if we want to build + an output file by running the contents + of the input file through a command named + foobuild, + creating that &Builder; might look like: + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + + + + + All the above line does is create a free-standing + &Builder; object. + The next section will show us how to actually use it. + + + +
+ +
+ Attaching a Builder to a &ConsEnv; + + + + A &Builder; object isn't useful + until it's attached to a &consenv; + so that we can call it to arrange + for files to be built. + This is done through the &cv-link-BUILDERS; + &consvar; in an environment. + The &cv-BUILDERS; variable is a Python dictionary + that maps the names by which you want to call + various &Builder; objects to the objects themselves. + For example, if we want to call the + &Builder; we just defined by the name + Foo, + our &SConstruct; file might look like: + + + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + + + + + With the &Builder; so attached to our &consenv; + we can now actually call it like so: + + + + + env.Foo('file.foo', 'file.input') + + + + + Then when we run &SCons; it looks like: + + + + + % scons -Q + foobuild < file.input > file.foo + + + + + Note, however, that the default &cv-BUILDERS; + variable in a &consenv; + comes with a default set of &Builder; objects + already defined: + &b-link-Program;, &b-link-Library;, etc. + And when we explicitly set the &cv-BUILDERS; variable + when we create the &consenv;, + the default &Builder;s are no longer part of + the environment: + + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + + % scons -Q + AttributeError: SConsEnvironment instance has no attribute 'Program': + File "/home/my/project/SConstruct", line 4: + env.Program('hello.c') + + + + + To be able use both our own defined &Builder; objects + and the default &Builder; objects in the same &consenv;, + you can either add to the &cv-BUILDERS; variable + using the &Append; function: + + + + + + + env = Environment() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env.Append(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + + + Or you can explicitly set the appropriately-named + key in the &cv-BUILDERS; dictionary: + + + + + env = Environment() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env['BUILDERS']['Foo'] = bld + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + + + + + Either way, the same &consenv; + can then use both the newly-defined + Foo &Builder; + and the default &b-link-Program; &Builder;: + + + + + % scons -Q + foobuild < file.input > file.foo + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Letting &SCons; Handle The File Suffixes + + + + By supplying additional information + when you create a &Builder;, + you can let &SCons; add appropriate file + suffixes to the target and/or the source file. + For example, rather than having to specify + explicitly that you want the Foo + &Builder; to build the file.foo + target file from the file.input source file, + you can give the .foo + and .input suffixes to the &Builder;, + making for more compact and readable calls to + the Foo &Builder;: + + + + + + + bld = Builder(action = 'foobuild < $SOURCE > $TARGET', + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file1') + env.Foo('file2') + + + + % scons -Q + foobuild < file1.input > file1.foo + foobuild < file2.input > file2.foo + + + + + You can also supply a prefix keyword argument + if it's appropriate to have &SCons; append a prefix + to the beginning of target file names. + + + +
+ +
+ Builders That Execute Python Functions + + + + In &SCons;, you don't have to call an external command + to build a file. + You can, instead, define a Python function + that a &Builder; object can invoke + to build your target file (or files). + Such a &buildfunc; definition looks like: + + + + + def build_function(target, source, env): + # Code to build "target" from "source" + return None + + + + + The arguments of a &buildfunc; are: + + + + + + + target + + + + + A list of Node objects representing + the target or targets to be + built by this builder function. + The file names of these target(s) + may be extracted using the Python &str; function. + + + + + + + source + + + + + A list of Node objects representing + the sources to be + used by this builder function to build the targets. + The file names of these source(s) + may be extracted using the Python &str; function. + + + + + + + env + + + + + The &consenv; used for building the target(s). + The builder function may use any of the + environment's construction variables + in any way to affect how it builds the targets. + + + + + + + + + + The builder function must + return a 0 or None value + if the target(s) are built successfully. + The builder function + may raise an exception + or return any non-zero value + to indicate that the build is unsuccessful, + + + + + + Once you've defined the Python function + that will build your target file, + defining a &Builder; object for it is as + simple as specifying the name of the function, + instead of an external command, + as the &Builder;'s + action + argument: + + + + + def build_function(target, source, env): + # Code to build "target" from "source" + return None + bld = Builder(action = build_function, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + + + And notice that the output changes slightly, + reflecting the fact that a Python function, + not an external command, + is now called to build the target file: + + + + + % scons -Q + build_function(["file.foo"], ["file.input"]) + + +
+ +
+ Builders That Create Actions Using a &Generator; + + + + &SCons; Builder objects can create an action "on the fly" + by using a function called a &generator;. + This provides a great deal of flexibility to + construct just the right list of commands + to build your target. + A &generator; looks like: + + + + + def generate_actions(source, target, env, for_signature): + return 'foobuild < %s > %s' % (target[0], source[0]) + + + + + The arguments of a &generator; are: + + + + + + + source + + + + + A list of Node objects representing + the sources to be built + by the command or other action + generated by this function. + The file names of these source(s) + may be extracted using the Python &str; function. + + + + + + + + target + + + + + A list of Node objects representing + the target or targets to be built + by the command or other action + generated by this function. + The file names of these target(s) + may be extracted using the Python &str; function. + + + + + + + + env + + + + + The &consenv; used for building the target(s). + The generator may use any of the + environment's construction variables + in any way to determine what command + or other action to return. + + + + + + + + for_signature + + + + + A flag that specifies whether the + generator is being called to contribute to a build signature, + as opposed to actually executing the command. + + + + + + + + + + + + + The &generator; must return a + command string or other action that will be used to + build the specified target(s) from the specified source(s). + + + + + + Once you've defined a &generator;, + you create a &Builder; to use it + by specifying the generator keyword argument + instead of action. + + + + + + + def generate_actions(source, target, env, for_signature): + return 'foobuild < %s > %s' % (source[0], target[0]) + bld = Builder(generator = generate_actions, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + + % scons -Q + foobuild < file.input > file.foo + + + + + Note that it's illegal to specify both an + action + and a + generator + for a &Builder;. + + + +
+ +
+ Builders That Modify the Target or Source Lists Using an &Emitter; + + + + &SCons; supports the ability for a Builder to modify the + lists of target(s) from the specified source(s). + + + + + + + def modify_targets(target, source, env): + target.append('new_target') + source.append('new_source') + return target, source + bld = Builder(action = 'foobuild $TARGETS - $SOURCES', + suffix = '.foo', + src_suffix = '.input', + emitter = modify_targets) + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + + + + % scons -Q + foobuild file.foo new_target - file.input new_source + + + + bld = Builder(action = 'my_command', + suffix = '.foo', + src_suffix = '.input', + emitter = 'MY_EMITTER') + def modify1(target, source, env): + return target, source + def modify2(target, source, env): + return target, source + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + + +
+ + diff --git a/doc/user/builders.in b/doc/user/builders.in index 2bc139d..f3989ef 100644 --- a/doc/user/builders.in +++ b/doc/user/builders.in @@ -24,7 +24,8 @@ --> - - - - - -This appendix contains descriptions of all of the -Builders that are potentially -available "out of the box" in this version of SCons. - - - - - -&builders-gen; - - diff --git a/doc/user/builders.xml b/doc/user/builders.xml new file mode 100644 index 0000000..f3989ef --- /dev/null +++ b/doc/user/builders.xml @@ -0,0 +1,57 @@ + + + + + + +This appendix contains descriptions of all of the +Builders that are potentially +available "out of the box" in this version of SCons. + + + + + +&builders-gen; + + diff --git a/doc/user/caching.in b/doc/user/caching.in index 8dfa731..186ece6 100644 --- a/doc/user/caching.in +++ b/doc/user/caching.in @@ -436,7 +436,7 @@ in a random order without having to specify the --random on very command line, you can use the &SetOption; function to - set the random option + set the random option within any &SConscript; file: diff --git a/doc/user/caching.sgml b/doc/user/caching.sgml deleted file mode 100644 index 02c3597..0000000 --- a/doc/user/caching.sgml +++ /dev/null @@ -1,472 +0,0 @@ - - - - - On multi-developer software projects, - you can sometimes speed up every developer's builds a lot by - allowing them to share the derived files that they build. - &SCons; makes this easy, as well as reliable. - - - -
- Specifying the Shared Cache Directory - - - - To enable sharing of derived files, - use the &CacheDir; function - in any &SConscript; file: - - - - - CacheDir('/usr/local/build_cache') - - - - - Note that the directory you specify must already exist - and be readable and writable by all developers - who will be sharing derived files. - It should also be in some central location - that all builds will be able to access. - In environments where developers are using separate systems - (like individual workstations) for builds, - this directory would typically be - on a shared or NFS-mounted file system. - - - - - - Here's what happens: - When a build has a &CacheDir; specified, - every time a file is built, - it is stored in the shared cache directory - along with its MD5 build signature. - - - Actually, the MD5 signature is used as the name of the file - in the shared cache directory in which the contents are stored. - - - On subsequent builds, - before an action is invoked to build a file, - &SCons; will check the shared cache directory - to see if a file with the exact same build - signature already exists. - If so, the derived file will not be built locally, - but will be copied into the local build directory - from the shared cache directory, - like so: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q -c - Removed hello.o - Removed hello - % scons -Q - Retrieved `hello.o' from cache - Retrieved `hello' from cache - - -
- -
- Keeping Build Output Consistent - - - - One potential drawback to using a shared cache - is that the output printed by &SCons; - can be inconsistent from invocation to invocation, - because any given file may be rebuilt one time - and retrieved from the shared cache the next time. - This can make analyzing build output more difficult, - especially for automated scripts that - expect consistent output each time. - - - - - - If, however, you use the --cache-show option, - &SCons; will print the command line that it - would have executed - to build the file, - even when it is retrieving the file from the shared cache. - This makes the build output consistent - every time the build is run: - - - - - % scons -Q - 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 -o hello.o -c hello.c - cc -o hello hello.o - - - - - The trade-off, of course, is that you no longer - know whether or not &SCons; - has retrieved a derived file from cache - or has rebuilt it locally. - - - -
- -
- Not Using the Shared Cache for Specific Files - - - - You may want to disable caching for certain - specific files in your configuration. - For example, if you only want to put - executable files in a central cache, - but not the intermediate object files, - you can use the &NoCache; - function to specify that the - object files should not be cached: - - - - - env = Environment() - obj = env.Object('hello.c') - env.Program('hello.c') - CacheDir('cache') - NoCache('hello.o') - - - - - Then when you run &scons; after cleaning - the built targets, - it will recompile the object file locally - (since it doesn't exist in the shared cache directory), - but still realize that the shared cache directory - contains an up-to-date executable program - that can be retrieved instead of re-linking: - - - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q -c - Removed hello.o - Removed hello - % scons -Q - cc -o hello.o -c hello.c - Retrieved `hello' from cache - - -
- -
- Disabling the Shared Cache - - - - Retrieving an already-built file - from the shared cache - is usually a significant time-savings - over rebuilding the file, - but how much of a savings - (or even whether it saves time at all) - can depend a great deal on your - system or network configuration. - For example, retrieving cached files - from a busy server over a busy network - might end up being slower than - rebuilding the files locally. - - - - - - In these cases, you can specify - the --cache-disable - command-line option to tell &SCons; - to not retrieve already-built files from the - shared cache directory: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q -c - Removed hello.o - Removed hello - % scons -Q - Retrieved `hello.o' from cache - Retrieved `hello' from cache - % scons -Q -c - Removed hello.o - Removed hello - % scons -Q --cache-disable - cc -o hello.o -c hello.c - cc -o hello hello.o - - -
- -
- Populating a Shared Cache With Already-Built Files - - - - Sometimes, you may have one or more derived files - already built in your local build tree - that you wish to make available to other people doing builds. - For example, you may find it more effective to perform - integration builds with the cache disabled - (per the previous section) - and only populate the shared cache directory - with the built files after the integration build - has completed successfully. - This way, the cache will only get filled up - with derived files that are part of a complete, successful build - not with files that might be later overwritten - while you debug integration problems. - - - - - - In this case, you can use the - the --cache-force option - to tell &SCons; to put all derived files in the cache, - even if the files already exist in your local tree - from having been built by a previous invocation: - - - - - % scons -Q --cache-disable - 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 -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q --cache-force - scons: `.' is up to date. - % scons -Q - scons: `.' is up to date. - - - - - Notice how the above sample run - demonstrates that the --cache-disable - option avoids putting the built - hello.o - and - hello files in the cache, - but after using the --cache-force option, - the files have been put in the cache - for the next invocation to retrieve. - - - -
- -
- Minimizing Cache Contention: the <literal>--random</literal> Option - - - - If you allow multiple builds to update the - shared cache directory simultaneously, - two builds that occur at the same time - can sometimes start "racing" - with one another to build the same files - in the same order. - If, for example, - you are linking multiple files into an executable program: - - - - - Program('prog', - ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) - - - - - &SCons; will normally build the input object files - on which the program depends in their normal, sorted order: - - - - - % scons -Q - cc -o f1.o -c f1.c - cc -o f2.o -c f2.c - cc -o f3.o -c f3.c - cc -o f4.o -c f4.c - cc -o f5.o -c f5.c - cc -o prog f1.o f2.o f3.o f4.o f5.o - - - - - But if two such builds take place simultaneously, - they may each look in the cache at nearly the same - time and both decide that f1.o - must be rebuilt and pushed into the shared cache directory, - then both decide that f2.o - must be rebuilt (and pushed into the shared cache directory), - then both decide that f3.o - must be rebuilt... - This won't cause any actual build problems--both - builds will succeed, - generate correct output files, - and populate the cache--but - it does represent wasted effort. - - - - - - To alleviate such contention for the cache, - you can use the --random command-line option - to tell &SCons; to build dependencies - in a random order: - - - - - - - % scons -Q --random - cc -o f3.o -c f3.c - cc -o f1.o -c f1.c - cc -o f5.o -c f5.c - cc -o f2.o -c f2.c - cc -o f4.o -c f4.c - cc -o prog f1.o f2.o f3.o f4.o f5.o - - - - - Multiple builds using the --random option - will usually build their dependencies in different, - random orders, - which minimizes the chances for a lot of - contention for same-named files - in the shared cache directory. - Multiple simultaneous builds might still race to try to build - the same target file on occasion, - but long sequences of inefficient contention - should be rare. - - - - - - Note, of course, - the --random option - will cause the output that &SCons; prints - to be inconsistent from invocation to invocation, - which may be an issue when - trying to compare output from different build runs. - - - -
- - - - diff --git a/doc/user/caching.xml b/doc/user/caching.xml new file mode 100644 index 0000000..51b30ae --- /dev/null +++ b/doc/user/caching.xml @@ -0,0 +1,492 @@ + + + + + On multi-developer software projects, + you can sometimes speed up every developer's builds a lot by + allowing them to share the derived files that they build. + &SCons; makes this easy, as well as reliable. + + + +
+ Specifying the Shared Cache Directory + + + + To enable sharing of derived files, + use the &CacheDir; function + in any &SConscript; file: + + + + + CacheDir('/usr/local/build_cache') + + + + + Note that the directory you specify must already exist + and be readable and writable by all developers + who will be sharing derived files. + It should also be in some central location + that all builds will be able to access. + In environments where developers are using separate systems + (like individual workstations) for builds, + this directory would typically be + on a shared or NFS-mounted file system. + + + + + + Here's what happens: + When a build has a &CacheDir; specified, + every time a file is built, + it is stored in the shared cache directory + along with its MD5 build signature. + + + Actually, the MD5 signature is used as the name of the file + in the shared cache directory in which the contents are stored. + + + On subsequent builds, + before an action is invoked to build a file, + &SCons; will check the shared cache directory + to see if a file with the exact same build + signature already exists. + If so, the derived file will not be built locally, + but will be copied into the local build directory + from the shared cache directory, + like so: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + Retrieved `hello.o' from cache + Retrieved `hello' from cache + + +
+ +
+ Keeping Build Output Consistent + + + + One potential drawback to using a shared cache + is that the output printed by &SCons; + can be inconsistent from invocation to invocation, + because any given file may be rebuilt one time + and retrieved from the shared cache the next time. + This can make analyzing build output more difficult, + especially for automated scripts that + expect consistent output each time. + + + + + + If, however, you use the --cache-show option, + &SCons; will print the command line that it + would have executed + to build the file, + even when it is retrieving the file from the shared cache. + This makes the build output consistent + every time the build is run: + + + + + % scons -Q + 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 -o hello.o -c hello.c + cc -o hello hello.o + + + + + The trade-off, of course, is that you no longer + know whether or not &SCons; + has retrieved a derived file from cache + or has rebuilt it locally. + + + +
+ +
+ Not Using the Shared Cache for Specific Files + + + + You may want to disable caching for certain + specific files in your configuration. + For example, if you only want to put + executable files in a central cache, + but not the intermediate object files, + you can use the &NoCache; + function to specify that the + object files should not be cached: + + + + + env = Environment() + obj = env.Object('hello.c') + env.Program('hello.c') + CacheDir('cache') + NoCache('hello.o') + + + + + Then when you run &scons; after cleaning + the built targets, + it will recompile the object file locally + (since it doesn't exist in the shared cache directory), + but still realize that the shared cache directory + contains an up-to-date executable program + that can be retrieved instead of re-linking: + + + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + cc -o hello.o -c hello.c + Retrieved `hello' from cache + + +
+ +
+ Disabling the Shared Cache + + + + Retrieving an already-built file + from the shared cache + is usually a significant time-savings + over rebuilding the file, + but how much of a savings + (or even whether it saves time at all) + can depend a great deal on your + system or network configuration. + For example, retrieving cached files + from a busy server over a busy network + might end up being slower than + rebuilding the files locally. + + + + + + In these cases, you can specify + the --cache-disable + command-line option to tell &SCons; + to not retrieve already-built files from the + shared cache directory: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q + Retrieved `hello.o' from cache + Retrieved `hello' from cache + % scons -Q -c + Removed hello.o + Removed hello + % scons -Q --cache-disable + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Populating a Shared Cache With Already-Built Files + + + + Sometimes, you may have one or more derived files + already built in your local build tree + that you wish to make available to other people doing builds. + For example, you may find it more effective to perform + integration builds with the cache disabled + (per the previous section) + and only populate the shared cache directory + with the built files after the integration build + has completed successfully. + This way, the cache will only get filled up + with derived files that are part of a complete, successful build + not with files that might be later overwritten + while you debug integration problems. + + + + + + In this case, you can use the + the --cache-force option + to tell &SCons; to put all derived files in the cache, + even if the files already exist in your local tree + from having been built by a previous invocation: + + + + + % scons -Q --cache-disable + 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 -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q --cache-force + scons: `.' is up to date. + % scons -Q + scons: `.' is up to date. + + + + + Notice how the above sample run + demonstrates that the --cache-disable + option avoids putting the built + hello.o + and + hello files in the cache, + but after using the --cache-force option, + the files have been put in the cache + for the next invocation to retrieve. + + + +
+ +
+ Minimizing Cache Contention: the <literal>--random</literal> Option + + + + If you allow multiple builds to update the + shared cache directory simultaneously, + two builds that occur at the same time + can sometimes start "racing" + with one another to build the same files + in the same order. + If, for example, + you are linking multiple files into an executable program: + + + + + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + + + + &SCons; will normally build the input object files + on which the program depends in their normal, sorted order: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + cc -o f4.o -c f4.c + cc -o f5.o -c f5.c + cc -o prog f1.o f2.o f3.o f4.o f5.o + + + + + But if two such builds take place simultaneously, + they may each look in the cache at nearly the same + time and both decide that f1.o + must be rebuilt and pushed into the shared cache directory, + then both decide that f2.o + must be rebuilt (and pushed into the shared cache directory), + then both decide that f3.o + must be rebuilt... + This won't cause any actual build problems--both + builds will succeed, + generate correct output files, + and populate the cache--but + it does represent wasted effort. + + + + + + To alleviate such contention for the cache, + you can use the --random command-line option + to tell &SCons; to build dependencies + in a random order: + + + + + + + % scons -Q --random + cc -o f3.o -c f3.c + cc -o f1.o -c f1.c + cc -o f5.o -c f5.c + cc -o f2.o -c f2.c + cc -o f4.o -c f4.c + cc -o prog f1.o f2.o f3.o f4.o f5.o + + + + + Multiple builds using the --random option + will usually build their dependencies in different, + random orders, + which minimizes the chances for a lot of + contention for same-named files + in the shared cache directory. + Multiple simultaneous builds might still race to try to build + the same target file on occasion, + but long sequences of inefficient contention + should be rare. + + + + + + Note, of course, + the --random option + will cause the output that &SCons; prints + to be inconsistent from invocation to invocation, + which may be an issue when + trying to compare output from different build runs. + + + + + + If you want to make sure dependencies will be built + in a random order without having to specify + the --random on very command line, + you can use the &SetOption; function to + set the random option + within any &SConscript; file: + + + + + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + SetOption('random', 1) + Program('prog', + ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c']) + + +
+ + + + diff --git a/doc/user/command-line.sgml b/doc/user/command-line.sgml deleted file mode 100644 index 1bc94d7..0000000 --- a/doc/user/command-line.sgml +++ /dev/null @@ -1,1486 +0,0 @@ - - - - - &SCons; provides a number of ways that - allow the writer of the &SConscript; files - to give users a great deal of control over how to run the builds. - - - -
- Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable - - - - Users may find themselves supplying - the same command-line options every time - they run &SCons;. - For example, a user might find that it saves time - to specify a value of -j 2 - to run the builds in parallel. - To avoid having to type -j 2 by hand - every time, - you can set the external environment variable - &SCONSFLAGS; to a string containing - command-line options that you want &SCons; to use. - - - - - - If, for example, - and you're using a POSIX shell that's - compatible with the Bourne shell, - and you always want &SCons; to use the - -Q option, - you can set the &SCONSFLAGS; - environment as follows: - - - - - - - % scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - ... [build output] ... - scons: done building targets. - % export SCONSFLAGS="-Q" - % scons - ... [build output] ... - - - - - Users of &csh;-style shells on POSIX systems - can set the &SCONSFLAGS; environment as follows: - - - - - $ setenv SCONSFLAGS "-Q" - - - - - Windows users may typically want to set this - &SCONSFLAGS; in the appropriate tab of the - System Properties window. - - - -
- -
- Getting at Command-Line Targets - - - - &SCons; supports a &COMMAND_LINE_TARGETS; variable - that lets you get at the list of targets that the - user specified on the command line. - You can use the targets to manipulate the - build in any way you wish. - As a simple example, - suppose that you want to print a reminder - to the user whenever a specific program is built. - You can do this by checking for the - target in the &COMMAND_LINE_TARGETS; list: - - - - - if 'bar' in COMMAND_LINE_TARGETS: - print "Don't forget to copy `bar' to the archive!" - Default(Program('foo.c')) - Program('bar.c') - - - - - Then, running &SCons; with the default target - works as it always does, - but explicity specifying the &bar; target - on the command line generates the warning message: - - - - - % scons -Q - 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 -o bar.o -c bar.c - cc -o bar bar.o - - - - - Another practical use for the &COMMAND_LINE_TARGETS; variable - might be to speed up a build - by only reading certain subsidiary &SConscript; - files if a specific target is requested. - - - -
- -
- Controlling the Default Targets - - - - One of the most basic things you can control - is which targets &SCons; will build by default--that is, - when there are no targets specified on the command line. - As mentioned previously, - &SCons; will normally build every target - in or below the current directory - by default--that is, when you don't - explicitly specify one or more targets - on the command line. - Sometimes, however, you may want - to specify explicitly that only - certain programs, or programs in certain directories, - should be built by default. - You do this with the &Default; function: - - - - - env = Environment() - hello = env.Program('hello.c') - env.Program('goodbye.c') - Default(hello) - - - - - This &SConstruct; file knows how to build two programs, - &hello; and &goodbye;, - but only builds the - &hello; program by default: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q - scons: `hello' is up to date. - % scons -Q goodbye - cc -o goodbye.o -c goodbye.c - cc -o goodbye goodbye.o - - - - - Note that, even when you use the &Default; - function in your &SConstruct; file, - you can still explicitly specify the current directory - (.) on the command line - to tell &SCons; to build - everything in (or below) the current directory: - - - - - % scons -Q . - cc -o goodbye.o -c goodbye.c - cc -o goodbye goodbye.o - cc -o hello.o -c hello.c - cc -o hello hello.o - - - - - You can also call the &Default; - function more than once, - in which case each call - adds to the list of targets to be built by default: - - - - - env = Environment() - prog1 = env.Program('prog1.c') - Default(prog1) - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog3) - - - - - Or you can specify more than one target - in a single call to the &Default; function: - - - - - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog1, prog3) - - - - - Either of these last two examples - will build only the - prog1 - and - prog3 - programs by default: - - - - - % scons -Q - cc -o prog1.o -c prog1.c - cc -o prog1 prog1.o - cc -o prog3.o -c prog3.c - cc -o prog3 prog3.o - % scons -Q . - cc -o prog2.o -c prog2.c - cc -o prog2 prog2.o - - - - - You can list a directory as - an argument to &Default;: - - - - - env = Environment() - env.Program(['prog1/main.c', 'prog1/foo.c']) - env.Program(['prog2/main.c', 'prog2/bar.c']) - Default('prog1') - - - - - In which case only the target(s) in that - directory will be built by default: - - - - - % scons -Q - 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 -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 - - - - - Lastly, if for some reason you don't want - any targets built by default, - you can use the Python None - variable: - - - - - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - Default(None) - - - - - Which would produce build output like: - - - - - % scons -Q - scons: *** No targets specified and no Default() targets found. Stop. - % scons -Q . - cc -o prog1.o -c prog1.c - cc -o prog1 prog1.o - cc -o prog2.o -c prog2.c - cc -o prog2 prog2.o - - -
- Getting at the List of Default Targets - - - - &SCons; supports a &DEFAULT_TARGETS; variable - that lets you get at the current list of default targets. - The &DEFAULT_TARGETS variable has - two important differences from the &COMMAND_LINE_TARGETS; variable. - First, the &DEFAULT_TARGETS; variable is a list of - internal &SCons; nodes, - so you need to convert the list elements to strings - if you want to print them or look for a specific target name. - Fortunately, you can do this easily - by using the Python map function - to run the list through str: - - - - - prog1 = Program('prog1.c') - Default(prog1) - print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) - - - - - (Keep in mind that all of the manipulation of the - &DEFAULT_TARGETS; list takes place during the - first phase when &SCons; is reading up the &SConscript; files, - which is obvious if - we leave off the -Q flag when we run &SCons;:) - - - - - % scons - scons: Reading SConscript files ... - DEFAULT_TARGETS is ['prog1'] - scons: done reading SConscript files. - scons: Building targets ... - cc -o prog1.o -c prog1.c - cc -o prog1 prog1.o - scons: done building targets. - - - - - Second, - the contents of the &DEFAULT_TARGETS; list change - in response to calls to the &Default;: function, - as you can see from the following &SConstruct; file: - - - - - prog1 = Program('prog1.c') - Default(prog1) - print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) - prog2 = Program('prog2.c') - Default(prog2) - print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) - - - - - Which yields the output: - - - - - % scons - scons: Reading SConscript files ... - DEFAULT_TARGETS is now ['prog1'] - DEFAULT_TARGETS is now ['prog1', 'prog2'] - scons: done reading SConscript files. - scons: Building targets ... - cc -o prog1.o -c prog1.c - cc -o prog1 prog1.o - cc -o prog2.o -c prog2.c - cc -o prog2 prog2.o - scons: done building targets. - - - - - In practice, this simply means that you - need to pay attention to the order in - which you call the &Default; function - and refer to the &DEFAULT_TARGETS; list, - to make sure that you don't examine the - list before you've added the default targets - you expect to find in it. - - - -
- -
- -
- Getting at the List of Build Targets, Regardless of Origin - - - - We've already been introduced to the - &COMMAND_LINE_TARGETS; variable, - which contains a list of targets specified on the command line, - and the &DEFAULT_TARGETS; variable, - which contains a list of targets specified - via calls to the &Default; method or function. - Sometimes, however, - you want a list of whatever targets - &SCons; will try to build, - regardless of whether the targets came from the - command line or a &Default; call. - You could code this up by hand, as follows: - - - - - if COMMAND_LINE_TARGETS: - targets = COMMAND_LINE_TARGETS - else: - targets = DEFAULT_TARGETS - - - - - &SCons;, however, provides a convenient - &BUILD_TARGETS; variable - that eliminates the need for this by-hand manipulation. - Essentially, the &BUILD_TARGETS; variable - contains a list of the command-line targets, - if any were specified, - and if no command-line targets were specified, - it contains a list of the targets specified - via the &Default; method or function. - - - - - - Because &BUILD_TARGETS; may contain a list of &SCons; nodes, - you must convert the list elements to strings - if you want to print them or look for a specific target name, - just like the &DEFAULT_TARGETS; list: - - - - - prog1 = Program('prog1.c') - Program('prog2.c') - Default(prog1) - print "BUILD_TARGETS is", map(str, BUILD_TARGETS) - - - - - Notice how the value of &BUILD_TARGETS; - changes depending on whether a target is - specified on the command line: - - - - - % scons -Q - BUILD_TARGETS is ['prog1'] - cc -o prog1.o -c prog1.c - cc -o prog1 prog1.o - % scons -Q prog2 - BUILD_TARGETS is ['prog2'] - cc -o prog2.o -c prog2.c - cc -o prog2 prog2.o - % scons -Q -c . - BUILD_TARGETS is ['.'] - Removed prog1.o - Removed prog1 - Removed prog2.o - Removed prog2 - - -
- -
- Command-Line <varname>variable</varname>=<varname>value</varname> Build Options - - - - You may want to control various aspects - of your build by allowing the user - to specify variable=value - values on the command line. - For example, suppose you - want users to be able to - build a debug version of a program - by running &SCons; as follows: - - - - - % scons -Q debug=1 - - - - - &SCons; provides an &ARGUMENTS; dictionary - that stores all of the - variable=value - assignments from the command line. - This allows you to modify - aspects of your build in response - to specifications on the command line. - (Note that unless you want to require - that users always - specify an option, - you probably want to use - the Python - ARGUMENTS.get() function, - which allows you to specify a default value - to be used if there is no specification - on the command line.) - - - - - - The following code sets the &cv-link-CCFLAGS; construction - variable in response to the debug - flag being set in the &ARGUMENTS; dictionary: - - - - - env = Environment() - debug = ARGUMENTS.get('debug', 0) - if int(debug): - env.Append(CCFLAGS = '-g') - env.Program('prog.c') - - - - - This results in the -g - compiler option being used when - debug=1 - is used on the command line: - - - - - % scons -Q debug=0 - 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 -o prog.o -c -g prog.c - cc -o prog prog.o - % scons -Q debug=1 - scons: `.' is up to date. - - - - - Notice that &SCons; keeps track of - the last values used to build the object files, - and as a result correctly rebuilds - the object and executable files - only when the value of the debug - argument has changed. - - - -
- -
- Controlling Command-Line Build Options - - - - Being able to use a command-line build option like - debug=1 is handy, - but it can be a chore to write specific Python code - to recognize each such option - and apply the values to a construction variable. - To help with this, - &SCons; supports a class to - define such build options easily, - and a mechanism to apply the - build options to a construction environment. - This allows you to control how the build options affect - construction environments. - - - - - - For example, suppose that you want users to set - a &RELEASE; construction variable on the - command line whenever the time comes to build - a program for release, - and that the value of this variable - should be added to the command line - with the appropriate -D option - (or other command line option) - to pass the value to the C compiler. - Here's how you might do that by setting - the appropriate value in a dictionary for the - &cv-link-CPPDEFINES; construction variable: - - - - - opts = Options() - opts.Add('RELEASE', 'Set to 1 to build for release', 0) - env = Environment(options = opts, - CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) - env.Program(['foo.c', 'bar.c']) - - - - - This &SConstruct; file first creates an - &Options; object - (the opts = Options() call), - and then uses the object's &Add; - method to indicate that the &RELEASE; - option can be set on the command line, - and that it's default value will be 0 - (the third argument to the &Add; method). - The second argument is a line of help text; - we'll learn how to use it in the next section. - - - - - - We then pass the created &Options; - object as an &options; keyword argument - to the &Environment; call - used to create the construction environment. - This then allows a user to set the - &RELEASE; build option on the command line - and have the variable show up in - the command line used to build each object from - a C source file: - - - - - % scons -Q RELEASE=1 - 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 - - -
- -
- Providing Help for Command-Line Build Options - - - - To make command-line build options most useful, - you ideally want to provide - some help text that will describe - the available options - when the user runs scons -h. - You could write this text by hand, - but &SCons; provides an easier way. - &Options; objects support a - &GenerateHelpText; method - that will, as its name indicates, - generate text that describes - the various options that - have been added to it. - You then pass the output from this method to - the &Help; function: - - - - - opts = Options('custom.py') - opts.Add('RELEASE', 'Set to 1 to build for release', 0) - env = Environment(options = opts) - Help(opts.GenerateHelpText(env)) - - - - - &SCons; will now display some useful text - when the -h option is used: - - - - - % scons -Q -h - - RELEASE: Set to 1 to build for release - default: 0 - actual: 0 - - Use scons -H for help about command-line options. - - - - - Notice that the help output shows the default value, - and the current actual value of the build option. - - - -
- -
- Reading Build Options From a File - - - - Being able to use a command-line build option like - debug=1 is handy, - but it can be a chore to write specific Python code - to recognize each such option - and apply the values to a construction variable. - To help with this, - &SCons; supports a class to - define such build options easily - and to read build option values from a file. - This allows you to control how the build options affect - construction environments. - The way you do this is by specifying - a file name when you call &Options;, - like &custom_py; in the following example: - - - - - opts = Options('custom.py') - opts.Add('RELEASE', 'Set to 1 to build for release', 0) - env = Environment(options = opts, - CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) - env.Program(['foo.c', 'bar.c']) - Help(opts.GenerateHelpText(env)) - - - - - This then allows us to control the &RELEASE; - variable by setting it in the &custom_py; file: - - - - - RELEASE = 1 - - - - - Note that this file is actually executed - like a Python script. - Now when we run &SCons;: - - - - - % scons -Q - 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 - - - - - And if we change the contents of &custom_py; to: - - - - - RELEASE = 0 - - - - - The object files are rebuilt appropriately - with the new option: - - - - - % scons -Q - cc -o bar.o -c -DRELEASE_BUILD=0 bar.c - cc -o foo.o -c -DRELEASE_BUILD=0 foo.c - cc -o foo foo.o bar.o - - -
- -
- Canned Build Options - - - - &SCons; provides a number of functions - that provide ready-made behaviors - for various types of command-line build options. - - - -
- True/False Values: the &BoolOption; Build Option - - - - It's often handy to be able to specify an - option that controls a simple Boolean variable - with a &true; or &false; value. - It would be even more handy to accomodate - users who have different preferences for how to represent - &true; or &false; values. - The &BoolOption; function - makes it easy to accomodate a variety of - common values that represent - &true; or &false;. - - - - - - The &BoolOption; function takes three arguments: - the name of the build option, - the default value of the build option, - and the help string for the option. - It then returns appropriate information for - passing to the &Add; method of an &Options; object, like so: - - - - - opts = Options('custom.py') - opts.Add(BoolOption('RELEASE', 'Set to build for release', 0)) - env = Environment(options = opts, - CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) - env.Program('foo.c') - - - - - With this build option, - the &RELEASE; variable can now be enabled by - setting it to the value yes - or t: - - - - - % scons -Q RELEASE=yes foo.o - cc -o foo.o -c -DRELEASE_BUILD=True foo.c - - - - % scons -Q RELEASE=t foo.o - cc -o foo.o -c -DRELEASE_BUILD=True foo.c - - - - - Other values that equate to &true; include - y, - 1, - on - and - all. - - - - - - Conversely, &RELEASE; may now be given a &false; - value by setting it to - no - or - f: - - - - - % scons -Q RELEASE=no foo.o - cc -o foo.o -c -DRELEASE_BUILD=False foo.c - - - - % scons -Q RELEASE=f foo.o - cc -o foo.o -c -DRELEASE_BUILD=False foo.c - - - - - Other values that equate to &false; include - n, - 0, - off - and - none. - - - - - - Lastly, if a user tries to specify - any other value, - &SCons; supplies an appropriate error message: - - - - - % scons -Q RELEASE=bad_value foo.o - - scons: *** Error converting option: RELEASE - Invalid value for boolean option: bad_value - File "/home/my/project/SConstruct", line 4, in ? - - -
- -
- Single Value From a List: the &EnumOption; Build Option - - - - Suppose that we want a user to be able to - set a &COLOR; option - that selects a background color to be - displayed by an application, - but that we want to restrict the - choices to a specific set of allowed colors. - This can be set up quite easily - using the &EnumOption;, - which takes a list of &allowed_values - in addition to the variable name, - default value, - and help text arguments: - - - - - opts = Options('custom.py') - opts.Add(EnumOption('COLOR', 'Set background color', 'red', - allowed_values=('red', 'green', 'blue'))) - env = Environment(options = opts, - CPPDEFINES={'COLOR' : '"${COLOR}"'}) - env.Program('foo.c') - - - - - The user can now explicity set the &COLOR; build option - to any of the specified allowed values: - - - - - % scons -Q COLOR=red foo.o - cc -o foo.o -c -DCOLOR="red" foo.c - % scons -Q COLOR=blue foo.o - cc -o foo.o -c -DCOLOR="blue" foo.c - % scons -Q COLOR=green foo.o - cc -o foo.o -c -DCOLOR="green" foo.c - - - - - But, almost more importantly, - an attempt to set &COLOR; - to a value that's not in the list - generates an error message: - - - - - % scons -Q COLOR=magenta foo.o - - scons: *** Invalid value for option COLOR: magenta - File "/home/my/project/SConstruct", line 5, in ? - - - - - The &EnumOption; function also supports a way - to map alternate names to allowed values. - Suppose, for example, - that we want to allow the user - to use the word navy as a synonym for - blue. - We do this by adding a ↦ dictionary - that will map its key values - to the desired legal value: - - - - - opts = Options('custom.py') - opts.Add(EnumOption('COLOR', 'Set background color', 'red', - allowed_values=('red', 'green', 'blue'), - map={'navy':'blue'})) - env = Environment(options = opts, - CPPDEFINES={'COLOR' : '"${COLOR}"'}) - env.Program('foo.c') - - - - - As desired, the user can then use - navy on the command line, - and &SCons; will translate it into blue - when it comes time to use the &COLOR; - option to build a target: - - - - - % scons -Q COLOR=navy foo.o - cc -o foo.o -c -DCOLOR="blue" foo.c - - - - - By default, when using the &EnumOption; function, - arguments that differ - from the legal values - only in case - are treated as illegal values: - - - - - % scons -Q COLOR=Red foo.o - - scons: *** Invalid value for option COLOR: Red - File "/home/my/project/SConstruct", line 5, in ? - % scons -Q COLOR=BLUE foo.o - - scons: *** Invalid value for option COLOR: BLUE - File "/home/my/project/SConstruct", line 5, in ? - % scons -Q COLOR=nAvY foo.o - - scons: *** Invalid value for option COLOR: nAvY - File "/home/my/project/SConstruct", line 5, in ? - - - - - The &EnumOption; function can take an additional - &ignorecase; keyword argument that, - when set to 1, - tells &SCons; to allow case differences - when the values are specified: - - - - - opts = Options('custom.py') - opts.Add(EnumOption('COLOR', 'Set background color', 'red', - allowed_values=('red', 'green', 'blue'), - map={'navy':'blue'}, - ignorecase=1)) - env = Environment(options = opts, - CPPDEFINES={'COLOR' : '"${COLOR}"'}) - env.Program('foo.c') - - - - - Which yields the output: - - - - - % scons -Q COLOR=Red foo.o - cc -o foo.o -c -DCOLOR="Red" foo.c - % scons -Q COLOR=BLUE foo.o - cc -o foo.o -c -DCOLOR="BLUE" foo.c - % scons -Q COLOR=nAvY foo.o - cc -o foo.o -c -DCOLOR="blue" foo.c - % scons -Q COLOR=green foo.o - cc -o foo.o -c -DCOLOR="green" foo.c - - - - - Notice that an &ignorecase; value of 1 - preserves the case-spelling that the user supplied. - If you want &SCons; to translate the names - into lower-case, - regardless of the case used by the user, - specify an &ignorecase; value of 2: - - - - - opts = Options('custom.py') - opts.Add(EnumOption('COLOR', 'Set background color', 'red', - allowed_values=('red', 'green', 'blue'), - map={'navy':'blue'}, - ignorecase=2)) - env = Environment(options = opts, - CPPDEFINES={'COLOR' : '"${COLOR}"'}) - env.Program('foo.c') - - - - - Now &SCons; will use values of - red, - green or - blue - regardless of how the user spells - those values on the command line: - - - - - % scons -Q COLOR=Red foo.o - cc -o foo.o -c -DCOLOR="red" foo.c - % scons -Q COLOR=nAvY foo.o - cc -o foo.o -c -DCOLOR="blue" foo.c - % scons -Q COLOR=GREEN foo.o - cc -o foo.o -c -DCOLOR="green" foo.c - - -
- -
- Multiple Values From a List: the &ListOption; Build Option - - - - Another way in which you might want to allow users - to control build option is to - specify a list of one or more legal values. - &SCons; supports this through the &ListOption; function. - If, for example, we want a user to be able to set a - &COLORS; option to one or more of the legal list of values: - - - - - opts = Options('custom.py') - opts.Add(ListOption('COLORS', 'List of colors', 0, - ['red', 'green', 'blue'])) - env = Environment(options = opts, - CPPDEFINES={'COLORS' : '"${COLORS}"'}) - env.Program('foo.c') - - - - - A user can now specify a comma-separated list - of legal values, - which will get translated into a space-separated - list for passing to the any build commands: - - - - - % scons -Q COLORS=red,blue foo.o - cc -o foo.o -c -DCOLORS="red blue" foo.c - % scons -Q COLORS=blue,green,red foo.o - cc -o foo.o -c -DCOLORS="blue green red" foo.c - - - - - In addition, the &ListOption; function - allows the user to specify explicit keywords of - &all; or &none; - to select all of the legal values, - or none of them, respectively: - - - - - % scons -Q COLORS=all foo.o - cc -o foo.o -c -DCOLORS="red green blue" foo.c - % scons -Q COLORS=none foo.o - cc -o foo.o -c -DCOLORS="" foo.c - - - - - And, of course, an illegal value - still generates an error message: - - - - - % scons -Q COLORS=magenta foo.o - - scons: *** Error converting option: COLORS - Invalid value(s) for option: magenta - File "/home/my/project/SConstruct", line 5, in ? - - -
- -
- Path Names: the &PathOption; Build Option - - - - &SCons; supports a &PathOption; function - to make it easy to create a build option - to control an expected path name. - If, for example, you need to - define a variable in the preprocessor - that control the location of a - configuration file: - - - - - opts = Options('custom.py') - opts.Add(PathOption('CONFIG', - 'Path to configuration file', - '/etc/my_config')) - env = Environment(options = opts, - CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) - env.Program('foo.c') - - - - - This then allows the user to - override the &CONFIG; build option - on the command line as necessary: - - - - - % scons -Q foo.o - 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. - - - - - By default, &PathOption; checks to make sure - that the specified path exists and generates an error if it - doesn't: - - - - - % scons -Q CONFIG=/does/not/exist foo.o - - scons: *** Path for option CONFIG does not exist: /does/not/exist - File "/home/my/project/SConstruct", line 6, in ? - - - - - &PathOption; provides a number of methods - that you can use to change this behavior. - If you want to ensure that any specified paths are, - in fact, files and not directories, - use the &PathOption_PathIsFile; method: - - - - - opts = Options('custom.py') - opts.Add(PathOption('CONFIG', - 'Path to configuration file', - '/etc/my_config', - PathOption.PathIsFile)) - env = Environment(options = opts, - CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) - env.Program('foo.c') - - - - - Conversely, to ensure that any specified paths are - directories and not files, - use the &PathOption_PathIsDir; method: - - - - - opts = Options('custom.py') - opts.Add(PathOption('DBDIR', - 'Path to database directory', - '/var/my_dbdir', - PathOption.PathIsDir)) - env = Environment(options = opts, - CPPDEFINES={'DBDIR' : '"$DBDIR"'}) - env.Program('foo.c') - - - - - If you want to make sure that any specified paths - are directories, - and you would like the directory created - if it doesn't already exist, - use the &PathOption_PathIsDirCreate; method: - - - - - opts = Options('custom.py') - opts.Add(PathOption('DBDIR', - 'Path to database directory', - '/var/my_dbdir', - PathOption.PathIsDirCreate)) - env = Environment(options = opts, - CPPDEFINES={'DBDIR' : '"$DBDIR"'}) - env.Program('foo.c') - - - - - Lastly, if you don't care whether the path exists, - is a file, or a directory, - use the &PathOption_PathAccept; method - to accept any path that the user supplies: - - - - - opts = Options('custom.py') - opts.Add(PathOption('OUTPUT', - 'Path to output file or directory', - None, - PathOption.PathAccept)) - env = Environment(options = opts, - CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) - env.Program('foo.c') - - -
- -
- Enabled/Disabled Path Names: the &PackageOption; Build Option - - - - Sometimes you want to give users - even more control over a path name variable, - allowing them to explicitly enable or - disable the path name - by using yes or no keywords, - in addition to allow them - to supply an explicit path name. - &SCons; supports the &PackageOption; - function to support this: - - - - - opts = Options('custom.py') - opts.Add(PackageOption('PACKAGE', - 'Location package', - '/opt/location')) - env = Environment(options = opts, - CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) - env.Program('foo.c') - - - - - When the &SConscript; file uses the &PackageOption; funciton, - user can now still use the default - or supply an overriding path name, - but can now explicitly set the - specified variable to a value - that indicates the package should be enabled - (in which case the default should be used) - or disabled: - - - - - % scons -Q foo.o - cc -o foo.o -c -DPACKAGE="/opt/location" foo.c - % scons -Q PACKAGE=/usr/local/location foo.o - cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c - % scons -Q PACKAGE=yes foo.o - cc -o foo.o -c -DPACKAGE="True" foo.c - % scons -Q PACKAGE=no foo.o - cc -o foo.o -c -DPACKAGE="False" foo.c - - -
- -
- -
- Adding Multiple Command-Line Build Options at Once - - - - Lastly, &SCons; provides a way to add - multiple build options to an &Options; object at once. - Instead of having to call the &Add; method - multiple times, - you can call the &AddOptions; - method with a list of build options - to be added to the object. - Each build option is specified - as either a tuple of arguments, - just like you'd pass to the &Add; method itself, - or as a call to one of the canned - functions for pre-packaged command-line build options. - in any order: - - - - - opts = Options() - opts.AddOptions( - ('RELEASE', 'Set to 1 to build for release', 0), - ('CONFIG', 'Configuration file', '/etc/my_config'), - BoolOption('warnings', 'compilation with -Wall and similiar', 1), - EnumOption('debug', 'debug output and symbols', 'no', - allowed_values=('yes', 'no', 'full'), - map={}, ignorecase=0), # case sensitive - ListOption('shared', - 'libraries to build as shared libraries', - 'all', - names = list_of_libs), - PackageOption('x11', - 'use X11 installed here (yes = search some places)', - 'yes'), - PathOption('qtdir', 'where the root of Qt is installed', qtdir), - ) - - - - - -
diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml new file mode 100644 index 0000000..d7f3d7b --- /dev/null +++ b/doc/user/command-line.xml @@ -0,0 +1,1486 @@ + + + + + &SCons; provides a number of ways that + allow the writer of the &SConscript; files + to give users a great deal of control over how to run the builds. + + + +
+ Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable + + + + Users may find themselves supplying + the same command-line options every time + they run &SCons;. + For example, a user might find that it saves time + to specify a value of -j 2 + to run the builds in parallel. + To avoid having to type -j 2 by hand + every time, + you can set the external environment variable + &SCONSFLAGS; to a string containing + command-line options that you want &SCons; to use. + + + + + + If, for example, + and you're using a POSIX shell that's + compatible with the Bourne shell, + and you always want &SCons; to use the + -Q option, + you can set the &SCONSFLAGS; + environment as follows: + + + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + ... [build output] ... + scons: done building targets. + % export SCONSFLAGS="-Q" + % scons + ... [build output] ... + + + + + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: + + + + + $ setenv SCONSFLAGS "-Q" + + + + + Windows users may typically want to set this + &SCONSFLAGS; in the appropriate tab of the + System Properties window. + + + +
+ +
+ Getting at Command-Line Targets + + + + &SCons; supports a &COMMAND_LINE_TARGETS; variable + that lets you get at the list of targets that the + user specified on the command line. + You can use the targets to manipulate the + build in any way you wish. + As a simple example, + suppose that you want to print a reminder + to the user whenever a specific program is built. + You can do this by checking for the + target in the &COMMAND_LINE_TARGETS; list: + + + + + if 'bar' in COMMAND_LINE_TARGETS: + print "Don't forget to copy `bar' to the archive!" + Default(Program('foo.c')) + Program('bar.c') + + + + + Then, running &SCons; with the default target + works as it always does, + but explicity specifying the &bar; target + on the command line generates the warning message: + + + + + % scons -Q + 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 -o bar.o -c bar.c + cc -o bar bar.o + + + + + Another practical use for the &COMMAND_LINE_TARGETS; variable + might be to speed up a build + by only reading certain subsidiary &SConscript; + files if a specific target is requested. + + + +
+ +
+ Controlling the Default Targets + + + + One of the most basic things you can control + is which targets &SCons; will build by default--that is, + when there are no targets specified on the command line. + As mentioned previously, + &SCons; will normally build every target + in or below the current directory + by default--that is, when you don't + explicitly specify one or more targets + on the command line. + Sometimes, however, you may want + to specify explicitly that only + certain programs, or programs in certain directories, + should be built by default. + You do this with the &Default; function: + + + + + env = Environment() + hello = env.Program('hello.c') + env.Program('goodbye.c') + Default(hello) + + + + + This &SConstruct; file knows how to build two programs, + &hello; and &goodbye;, + but only builds the + &hello; program by default: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q + scons: `hello' is up to date. + % scons -Q goodbye + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + + + + + Note that, even when you use the &Default; + function in your &SConstruct; file, + you can still explicitly specify the current directory + (.) on the command line + to tell &SCons; to build + everything in (or below) the current directory: + + + + + % scons -Q . + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + You can also call the &Default; + function more than once, + in which case each call + adds to the list of targets to be built by default: + + + + + env = Environment() + prog1 = env.Program('prog1.c') + Default(prog1) + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog3) + + + + + Or you can specify more than one target + in a single call to the &Default; function: + + + + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog1, prog3) + + + + + Either of these last two examples + will build only the + prog1 + and + prog3 + programs by default: + + + + + % scons -Q + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + cc -o prog3.o -c prog3.c + cc -o prog3 prog3.o + % scons -Q . + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + + + + + You can list a directory as + an argument to &Default;: + + + + + env = Environment() + env.Program(['prog1/main.c', 'prog1/foo.c']) + env.Program(['prog2/main.c', 'prog2/bar.c']) + Default('prog1') + + + + + In which case only the target(s) in that + directory will be built by default: + + + + + % scons -Q + 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 -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 + + + + + Lastly, if for some reason you don't want + any targets built by default, + you can use the Python None + variable: + + + + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + Default(None) + + + + + Which would produce build output like: + + + + + % scons -Q + scons: *** No targets specified and no Default() targets found. Stop. + % scons -Q . + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + + +
+ Getting at the List of Default Targets + + + + &SCons; supports a &DEFAULT_TARGETS; variable + that lets you get at the current list of default targets. + The &DEFAULT_TARGETS variable has + two important differences from the &COMMAND_LINE_TARGETS; variable. + First, the &DEFAULT_TARGETS; variable is a list of + internal &SCons; nodes, + so you need to convert the list elements to strings + if you want to print them or look for a specific target name. + Fortunately, you can do this easily + by using the Python map function + to run the list through str: + + + + + prog1 = Program('prog1.c') + Default(prog1) + print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) + + + + + (Keep in mind that all of the manipulation of the + &DEFAULT_TARGETS; list takes place during the + first phase when &SCons; is reading up the &SConscript; files, + which is obvious if + we leave off the -Q flag when we run &SCons;:) + + + + + % scons + scons: Reading SConscript files ... + DEFAULT_TARGETS is ['prog1'] + scons: done reading SConscript files. + scons: Building targets ... + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + scons: done building targets. + + + + + Second, + the contents of the &DEFAULT_TARGETS; list change + in response to calls to the &Default;: function, + as you can see from the following &SConstruct; file: + + + + + prog1 = Program('prog1.c') + Default(prog1) + print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) + prog2 = Program('prog2.c') + Default(prog2) + print "DEFAULT_TARGETS is now", map(str, DEFAULT_TARGETS) + + + + + Which yields the output: + + + + + % scons + scons: Reading SConscript files ... + DEFAULT_TARGETS is now ['prog1'] + DEFAULT_TARGETS is now ['prog1', 'prog2'] + scons: done reading SConscript files. + scons: Building targets ... + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + scons: done building targets. + + + + + In practice, this simply means that you + need to pay attention to the order in + which you call the &Default; function + and refer to the &DEFAULT_TARGETS; list, + to make sure that you don't examine the + list before you've added the default targets + you expect to find in it. + + + +
+ +
+ +
+ Getting at the List of Build Targets, Regardless of Origin + + + + We've already been introduced to the + &COMMAND_LINE_TARGETS; variable, + which contains a list of targets specified on the command line, + and the &DEFAULT_TARGETS; variable, + which contains a list of targets specified + via calls to the &Default; method or function. + Sometimes, however, + you want a list of whatever targets + &SCons; will try to build, + regardless of whether the targets came from the + command line or a &Default; call. + You could code this up by hand, as follows: + + + + + if COMMAND_LINE_TARGETS: + targets = COMMAND_LINE_TARGETS + else: + targets = DEFAULT_TARGETS + + + + + &SCons;, however, provides a convenient + &BUILD_TARGETS; variable + that eliminates the need for this by-hand manipulation. + Essentially, the &BUILD_TARGETS; variable + contains a list of the command-line targets, + if any were specified, + and if no command-line targets were specified, + it contains a list of the targets specified + via the &Default; method or function. + + + + + + Because &BUILD_TARGETS; may contain a list of &SCons; nodes, + you must convert the list elements to strings + if you want to print them or look for a specific target name, + just like the &DEFAULT_TARGETS; list: + + + + + prog1 = Program('prog1.c') + Program('prog2.c') + Default(prog1) + print "BUILD_TARGETS is", map(str, BUILD_TARGETS) + + + + + Notice how the value of &BUILD_TARGETS; + changes depending on whether a target is + specified on the command line: + + + + + % scons -Q + BUILD_TARGETS is ['prog1'] + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + % scons -Q prog2 + BUILD_TARGETS is ['prog2'] + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + % scons -Q -c . + BUILD_TARGETS is ['.'] + Removed prog1.o + Removed prog1 + Removed prog2.o + Removed prog2 + + +
+ +
+ Command-Line <varname>variable</varname>=<varname>value</varname> Build Options + + + + You may want to control various aspects + of your build by allowing the user + to specify variable=value + values on the command line. + For example, suppose you + want users to be able to + build a debug version of a program + by running &SCons; as follows: + + + + + % scons -Q debug=1 + + + + + &SCons; provides an &ARGUMENTS; dictionary + that stores all of the + variable=value + assignments from the command line. + This allows you to modify + aspects of your build in response + to specifications on the command line. + (Note that unless you want to require + that users always + specify an option, + you probably want to use + the Python + ARGUMENTS.get() function, + which allows you to specify a default value + to be used if there is no specification + on the command line.) + + + + + + The following code sets the &cv-link-CCFLAGS; construction + variable in response to the debug + flag being set in the &ARGUMENTS; dictionary: + + + + + env = Environment() + debug = ARGUMENTS.get('debug', 0) + if int(debug): + env.Append(CCFLAGS = '-g') + env.Program('prog.c') + + + + + This results in the -g + compiler option being used when + debug=1 + is used on the command line: + + + + + % scons -Q debug=0 + 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 -o prog.o -c -g prog.c + cc -o prog prog.o + % scons -Q debug=1 + scons: `.' is up to date. + + + + + Notice that &SCons; keeps track of + the last values used to build the object files, + and as a result correctly rebuilds + the object and executable files + only when the value of the debug + argument has changed. + + + +
+ +
+ Controlling Command-Line Build Options + + + + Being able to use a command-line build option like + debug=1 is handy, + but it can be a chore to write specific Python code + to recognize each such option + and apply the values to a construction variable. + To help with this, + &SCons; supports a class to + define such build options easily, + and a mechanism to apply the + build options to a construction environment. + This allows you to control how the build options affect + construction environments. + + + + + + For example, suppose that you want users to set + a &RELEASE; construction variable on the + command line whenever the time comes to build + a program for release, + and that the value of this variable + should be added to the command line + with the appropriate -D option + (or other command line option) + to pass the value to the C compiler. + Here's how you might do that by setting + the appropriate value in a dictionary for the + &cv-link-CPPDEFINES; construction variable: + + + + + opts = Options() + opts.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(options = opts, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + + + + + This &SConstruct; file first creates an + &Options; object + (the opts = Options() call), + and then uses the object's &Add; + method to indicate that the &RELEASE; + option can be set on the command line, + and that it's default value will be 0 + (the third argument to the &Add; method). + The second argument is a line of help text; + we'll learn how to use it in the next section. + + + + + + We then pass the created &Options; + object as an &options; keyword argument + to the &Environment; call + used to create the construction environment. + This then allows a user to set the + &RELEASE; build option on the command line + and have the variable show up in + the command line used to build each object from + a C source file: + + + + + % scons -Q RELEASE=1 + 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 + + +
+ +
+ Providing Help for Command-Line Build Options + + + + To make command-line build options most useful, + you ideally want to provide + some help text that will describe + the available options + when the user runs scons -h. + You could write this text by hand, + but &SCons; provides an easier way. + &Options; objects support a + &GenerateHelpText; method + that will, as its name indicates, + generate text that describes + the various options that + have been added to it. + You then pass the output from this method to + the &Help; function: + + + + + opts = Options('custom.py') + opts.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(options = opts) + Help(opts.GenerateHelpText(env)) + + + + + &SCons; will now display some useful text + when the -h option is used: + + + + + % scons -Q -h + + RELEASE: Set to 1 to build for release + default: 0 + actual: 0 + + Use scons -H for help about command-line options. + + + + + Notice that the help output shows the default value, + and the current actual value of the build option. + + + +
+ +
+ Reading Build Options From a File + + + + Being able to use a command-line build option like + debug=1 is handy, + but it can be a chore to write specific Python code + to recognize each such option + and apply the values to a construction variable. + To help with this, + &SCons; supports a class to + define such build options easily + and to read build option values from a file. + This allows you to control how the build options affect + construction environments. + The way you do this is by specifying + a file name when you call &Options;, + like &custom_py; in the following example: + + + + + opts = Options('custom.py') + opts.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(options = opts, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + Help(opts.GenerateHelpText(env)) + + + + + This then allows us to control the &RELEASE; + variable by setting it in the &custom_py; file: + + + + + RELEASE = 1 + + + + + Note that this file is actually executed + like a Python script. + Now when we run &SCons;: + + + + + % scons -Q + 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 + + + + + And if we change the contents of &custom_py; to: + + + + + RELEASE = 0 + + + + + The object files are rebuilt appropriately + with the new option: + + + + + % scons -Q + cc -o bar.o -c -DRELEASE_BUILD=0 bar.c + cc -o foo.o -c -DRELEASE_BUILD=0 foo.c + cc -o foo foo.o bar.o + + +
+ +
+ Canned Build Options + + + + &SCons; provides a number of functions + that provide ready-made behaviors + for various types of command-line build options. + + + +
+ True/False Values: the &BoolOption; Build Option + + + + It's often handy to be able to specify an + option that controls a simple Boolean variable + with a &true; or &false; value. + It would be even more handy to accomodate + users who have different preferences for how to represent + &true; or &false; values. + The &BoolOption; function + makes it easy to accomodate a variety of + common values that represent + &true; or &false;. + + + + + + The &BoolOption; function takes three arguments: + the name of the build option, + the default value of the build option, + and the help string for the option. + It then returns appropriate information for + passing to the &Add; method of an &Options; object, like so: + + + + + opts = Options('custom.py') + opts.Add(BoolOption('RELEASE', 'Set to build for release', 0)) + env = Environment(options = opts, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program('foo.c') + + + + + With this build option, + the &RELEASE; variable can now be enabled by + setting it to the value yes + or t: + + + + + % scons -Q RELEASE=yes foo.o + cc -o foo.o -c -DRELEASE_BUILD=True foo.c + + + + % scons -Q RELEASE=t foo.o + cc -o foo.o -c -DRELEASE_BUILD=True foo.c + + + + + Other values that equate to &true; include + y, + 1, + on + and + all. + + + + + + Conversely, &RELEASE; may now be given a &false; + value by setting it to + no + or + f: + + + + + % scons -Q RELEASE=no foo.o + cc -o foo.o -c -DRELEASE_BUILD=False foo.c + + + + % scons -Q RELEASE=f foo.o + cc -o foo.o -c -DRELEASE_BUILD=False foo.c + + + + + Other values that equate to &false; include + n, + 0, + off + and + none. + + + + + + Lastly, if a user tries to specify + any other value, + &SCons; supplies an appropriate error message: + + + + + % scons -Q RELEASE=bad_value foo.o + + scons: *** Error converting option: RELEASE + Invalid value for boolean option: bad_value + File "/home/my/project/SConstruct", line 4, in <module> + + +
+ +
+ Single Value From a List: the &EnumOption; Build Option + + + + Suppose that we want a user to be able to + set a &COLOR; option + that selects a background color to be + displayed by an application, + but that we want to restrict the + choices to a specific set of allowed colors. + This can be set up quite easily + using the &EnumOption;, + which takes a list of &allowed_values + in addition to the variable name, + default value, + and help text arguments: + + + + + opts = Options('custom.py') + opts.Add(EnumOption('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'))) + env = Environment(options = opts, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + The user can now explicity set the &COLOR; build option + to any of the specified allowed values: + + + + + % scons -Q COLOR=red foo.o + cc -o foo.o -c -DCOLOR="red" foo.c + % scons -Q COLOR=blue foo.o + cc -o foo.o -c -DCOLOR="blue" foo.c + % scons -Q COLOR=green foo.o + cc -o foo.o -c -DCOLOR="green" foo.c + + + + + But, almost more importantly, + an attempt to set &COLOR; + to a value that's not in the list + generates an error message: + + + + + % scons -Q COLOR=magenta foo.o + + scons: *** Invalid value for option COLOR: magenta + File "/home/my/project/SConstruct", line 5, in <module> + + + + + The &EnumOption; function also supports a way + to map alternate names to allowed values. + Suppose, for example, + that we want to allow the user + to use the word navy as a synonym for + blue. + We do this by adding a ↦ dictionary + that will map its key values + to the desired legal value: + + + + + opts = Options('custom.py') + opts.Add(EnumOption('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'})) + env = Environment(options = opts, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + As desired, the user can then use + navy on the command line, + and &SCons; will translate it into blue + when it comes time to use the &COLOR; + option to build a target: + + + + + % scons -Q COLOR=navy foo.o + cc -o foo.o -c -DCOLOR="blue" foo.c + + + + + By default, when using the &EnumOption; function, + arguments that differ + from the legal values + only in case + are treated as illegal values: + + + + + % scons -Q COLOR=Red foo.o + + scons: *** Invalid value for option COLOR: Red + File "/home/my/project/SConstruct", line 5, in <module> + % scons -Q COLOR=BLUE foo.o + + scons: *** Invalid value for option COLOR: BLUE + File "/home/my/project/SConstruct", line 5, in <module> + % scons -Q COLOR=nAvY foo.o + + scons: *** Invalid value for option COLOR: nAvY + File "/home/my/project/SConstruct", line 5, in <module> + + + + + The &EnumOption; function can take an additional + &ignorecase; keyword argument that, + when set to 1, + tells &SCons; to allow case differences + when the values are specified: + + + + + opts = Options('custom.py') + opts.Add(EnumOption('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=1)) + env = Environment(options = opts, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + Which yields the output: + + + + + % scons -Q COLOR=Red foo.o + cc -o foo.o -c -DCOLOR="Red" foo.c + % scons -Q COLOR=BLUE foo.o + cc -o foo.o -c -DCOLOR="BLUE" foo.c + % scons -Q COLOR=nAvY foo.o + cc -o foo.o -c -DCOLOR="blue" foo.c + % scons -Q COLOR=green foo.o + cc -o foo.o -c -DCOLOR="green" foo.c + + + + + Notice that an &ignorecase; value of 1 + preserves the case-spelling that the user supplied. + If you want &SCons; to translate the names + into lower-case, + regardless of the case used by the user, + specify an &ignorecase; value of 2: + + + + + opts = Options('custom.py') + opts.Add(EnumOption('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=2)) + env = Environment(options = opts, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + Now &SCons; will use values of + red, + green or + blue + regardless of how the user spells + those values on the command line: + + + + + % scons -Q COLOR=Red foo.o + cc -o foo.o -c -DCOLOR="red" foo.c + % scons -Q COLOR=nAvY foo.o + cc -o foo.o -c -DCOLOR="blue" foo.c + % scons -Q COLOR=GREEN foo.o + cc -o foo.o -c -DCOLOR="green" foo.c + + +
+ +
+ Multiple Values From a List: the &ListOption; Build Option + + + + Another way in which you might want to allow users + to control build option is to + specify a list of one or more legal values. + &SCons; supports this through the &ListOption; function. + If, for example, we want a user to be able to set a + &COLORS; option to one or more of the legal list of values: + + + + + opts = Options('custom.py') + opts.Add(ListOption('COLORS', 'List of colors', 0, + ['red', 'green', 'blue'])) + env = Environment(options = opts, + CPPDEFINES={'COLORS' : '"${COLORS}"'}) + env.Program('foo.c') + + + + + A user can now specify a comma-separated list + of legal values, + which will get translated into a space-separated + list for passing to the any build commands: + + + + + % scons -Q COLORS=red,blue foo.o + cc -o foo.o -c -DCOLORS="red blue" foo.c + % scons -Q COLORS=blue,green,red foo.o + cc -o foo.o -c -DCOLORS="blue green red" foo.c + + + + + In addition, the &ListOption; function + allows the user to specify explicit keywords of + &all; or &none; + to select all of the legal values, + or none of them, respectively: + + + + + % scons -Q COLORS=all foo.o + cc -o foo.o -c -DCOLORS="red green blue" foo.c + % scons -Q COLORS=none foo.o + cc -o foo.o -c -DCOLORS="" foo.c + + + + + And, of course, an illegal value + still generates an error message: + + + + + % scons -Q COLORS=magenta foo.o + + scons: *** Error converting option: COLORS + Invalid value(s) for option: magenta + File "/home/my/project/SConstruct", line 5, in <module> + + +
+ +
+ Path Names: the &PathOption; Build Option + + + + &SCons; supports a &PathOption; function + to make it easy to create a build option + to control an expected path name. + If, for example, you need to + define a variable in the preprocessor + that control the location of a + configuration file: + + + + + opts = Options('custom.py') + opts.Add(PathOption('CONFIG', + 'Path to configuration file', + '/etc/my_config')) + env = Environment(options = opts, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + + + This then allows the user to + override the &CONFIG; build option + on the command line as necessary: + + + + + % scons -Q foo.o + 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. + + + + + By default, &PathOption; checks to make sure + that the specified path exists and generates an error if it + doesn't: + + + + + % scons -Q CONFIG=/does/not/exist foo.o + + scons: *** Path for option CONFIG does not exist: /does/not/exist + File "/home/my/project/SConstruct", line 6, in <module> + + + + + &PathOption; provides a number of methods + that you can use to change this behavior. + If you want to ensure that any specified paths are, + in fact, files and not directories, + use the &PathOption_PathIsFile; method: + + + + + opts = Options('custom.py') + opts.Add(PathOption('CONFIG', + 'Path to configuration file', + '/etc/my_config', + PathOption.PathIsFile)) + env = Environment(options = opts, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + + + Conversely, to ensure that any specified paths are + directories and not files, + use the &PathOption_PathIsDir; method: + + + + + opts = Options('custom.py') + opts.Add(PathOption('DBDIR', + 'Path to database directory', + '/var/my_dbdir', + PathOption.PathIsDir)) + env = Environment(options = opts, + CPPDEFINES={'DBDIR' : '"$DBDIR"'}) + env.Program('foo.c') + + + + + If you want to make sure that any specified paths + are directories, + and you would like the directory created + if it doesn't already exist, + use the &PathOption_PathIsDirCreate; method: + + + + + opts = Options('custom.py') + opts.Add(PathOption('DBDIR', + 'Path to database directory', + '/var/my_dbdir', + PathOption.PathIsDirCreate)) + env = Environment(options = opts, + CPPDEFINES={'DBDIR' : '"$DBDIR"'}) + env.Program('foo.c') + + + + + Lastly, if you don't care whether the path exists, + is a file, or a directory, + use the &PathOption_PathAccept; method + to accept any path that the user supplies: + + + + + opts = Options('custom.py') + opts.Add(PathOption('OUTPUT', + 'Path to output file or directory', + None, + PathOption.PathAccept)) + env = Environment(options = opts, + CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) + env.Program('foo.c') + + +
+ +
+ Enabled/Disabled Path Names: the &PackageOption; Build Option + + + + Sometimes you want to give users + even more control over a path name variable, + allowing them to explicitly enable or + disable the path name + by using yes or no keywords, + in addition to allow them + to supply an explicit path name. + &SCons; supports the &PackageOption; + function to support this: + + + + + opts = Options('custom.py') + opts.Add(PackageOption('PACKAGE', + 'Location package', + '/opt/location')) + env = Environment(options = opts, + CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) + env.Program('foo.c') + + + + + When the &SConscript; file uses the &PackageOption; funciton, + user can now still use the default + or supply an overriding path name, + but can now explicitly set the + specified variable to a value + that indicates the package should be enabled + (in which case the default should be used) + or disabled: + + + + + % scons -Q foo.o + cc -o foo.o -c -DPACKAGE="/opt/location" foo.c + % scons -Q PACKAGE=/usr/local/location foo.o + cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c + % scons -Q PACKAGE=yes foo.o + cc -o foo.o -c -DPACKAGE="True" foo.c + % scons -Q PACKAGE=no foo.o + cc -o foo.o -c -DPACKAGE="False" foo.c + + +
+ +
+ +
+ Adding Multiple Command-Line Build Options at Once + + + + Lastly, &SCons; provides a way to add + multiple build options to an &Options; object at once. + Instead of having to call the &Add; method + multiple times, + you can call the &AddOptions; + method with a list of build options + to be added to the object. + Each build option is specified + as either a tuple of arguments, + just like you'd pass to the &Add; method itself, + or as a call to one of the canned + functions for pre-packaged command-line build options. + in any order: + + + + + opts = Options() + opts.AddOptions( + ('RELEASE', 'Set to 1 to build for release', 0), + ('CONFIG', 'Configuration file', '/etc/my_config'), + BoolOption('warnings', 'compilation with -Wall and similiar', 1), + EnumOption('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=0), # case sensitive + ListOption('shared', + 'libraries to build as shared libraries', + 'all', + names = list_of_libs), + PackageOption('x11', + 'use X11 installed here (yes = search some places)', + 'yes'), + PathOption('qtdir', 'where the root of Qt is installed', qtdir), + ) + + + + + +
diff --git a/doc/user/copyright.sgml b/doc/user/copyright.sgml deleted file mode 100644 index 76e3e50..0000000 --- a/doc/user/copyright.sgml +++ /dev/null @@ -1,32 +0,0 @@ - - -
- - - SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight - - -
diff --git a/doc/user/copyright.xml b/doc/user/copyright.xml new file mode 100644 index 0000000..76e3e50 --- /dev/null +++ b/doc/user/copyright.xml @@ -0,0 +1,32 @@ + + +
+ + + SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight + + +
diff --git a/doc/user/depends.sgml b/doc/user/depends.sgml deleted file mode 100644 index 9e055ee..0000000 --- a/doc/user/depends.sgml +++ /dev/null @@ -1,917 +0,0 @@ - - - - - So far we've seen how &SCons; handles one-time builds. - But the real point of a build tool like &SCons; - is to rebuild only the necessary things - when source files change--or, put another way, - &SCons; should not - waste time rebuilding things that have already been built. - You can see this at work simply be re-invoking &SCons; - after building our simple &hello; example: - - - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q - scons: `.' is up to date. - - - - - The second time it is executed, - &SCons; realizes that the &hello; program - is up-to-date with respect to the current &hello_c; source file, - and avoids rebuilding it. - You can see this more clearly by naming - the &hello; program explicitly on the command line: - - - - - % scons -Q hello - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q hello - scons: `hello' is up to date. - - - - - Note that &SCons; reports "...is up to date" - only for target files named explicitly on the command line, - to avoid cluttering the output. - - - -
- Deciding When a Source File Has Changed: the &SourceSignatures; Function - - - - The other side of avoiding unnecessary rebuilds - is the fundamental build tool behavior - of rebuilding - things when a source file changes, - so that the built software is up to date. - &SCons; keeps track of this through a - &signature; for each source file, - and allows you to configure - whether you want to use the source - file contents or the modification time (timestamp) - as the signature. - - - -
- MD5 Source File Signatures - - - - By default, - &SCons; keeps track of whether a source file has changed - based on the file's contents, - not the modification time. - This means that you may be surprised by the - default &SCons; behavior if you are used to the - &Make; convention of forcing - a rebuild by updating the file's modification time - (using the &touch; command, for example): - - - - - % scons -Q hello - cc -o hello.o -c hello.c - cc -o hello hello.o - % touch hello.c - % scons -Q hello - scons: `hello' is up to date. - - - - - Even though the file's modification time has changed, - &SCons; realizes that the contents of the - &hello_c; file have not changed, - and therefore that the &hello; program - need not be rebuilt. - This avoids unnecessary rebuilds when, - for example, someone rewrites the - contents of a file without making a change. - But if the contents of the file really do change, - then &SCons; detects the change - and rebuilds the program as required: - - - - - % scons -Q hello - 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 -o hello.o -c hello.c - cc -o hello hello.o - - - - - Note that you can, if you wish, - specify this default behavior - (MD5 signatures) explicitly - using the &SourceSignatures; function as follows: - - - - - Program('hello.c') - SourceSignatures('MD5') - - -
- -
- Source File Time Stamps - - - - If you prefer, you can - configure &SCons; to use the modification time - of source files, - not the file contents, - when deciding if something needs to be rebuilt. - To do this, call the &SourceSignatures; - function as follows: - - - - - Program('hello.c') - SourceSignatures('timestamp') - - - - - This makes &SCons; act like &Make; - when a file's modification time is updated - (using the &touch; command, for example): - - - - - % scons -Q hello - cc -o hello.o -c hello.c - cc -o hello hello.o - % touch hello.c - % scons -Q hello - cc -o hello.o -c hello.c - cc -o hello hello.o - - -
- -
- -
- Deciding When a Target File Has Changed: the &TargetSignatures; Function - - - - As you've just seen, - &SCons; uses signatures to decide whether a - target file is up to date or must be rebuilt. - When a target file depends on another target file, - &SCons; allows you to configure separately - how the signatures of "intermediate" target files - are used when deciding if a dependent target file - must be rebuilt. - - - -
- Build Signatures - - - - Modifying a source file - will cause not only its direct target file to be rebuilt, - but also the target file(s) - that depend on that direct target file. - In our example, - changing the contents of the &hello_c; file causes - the &hello_o; file to be rebuilt, - which in turn causes the - &hello; program to be rebuilt: - - - - - % scons -Q hello - 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 -o hello.o -c hello.c - cc -o hello hello.o - - - - - What's not obvious, though, - is that &SCons; internally handles the signature of - the target file(s) - (&hello_o; in the above example) - differently from the signature of the source file - (&hello_c;). - By default, - &SCons; tracks whether a target file must be rebuilt - by using a &buildsignature; - that consists of the combined - signatures of all the files - that go into making the target file. - This is efficient because - the accumulated signatures - actually give &SCons; all of the - information it needs - to decide if the target file is out of date. - - - - - - If you wish, you can - specify this default behavior - (build signatures) explicitly - using the &TargetSignatures; function: - - - - - Program('hello.c') - TargetSignatures('build') - - -
- -
- File Contents - - - - Sometimes a source file can be changed - in such a way that the contents of the - rebuilt target file(s) - will be exactly the same as the last time - the file was built. - If so, then any other target files - that depend on such a built-but-not-changed target - file actually need not be rebuilt. - You can make &SCons; - realize that it does not need to rebuild - a dependent target file in this situation - using the &TargetSignatures; function as follows: - - - - - Program('hello.c') - TargetSignatures('content') - - - - - So if, for example, - a user were to only change a comment in a C file, - then the rebuilt &hello_o; file - would be exactly the same as the one previously built - (assuming the compiler doesn't put any build-specific - information in the object file). - &SCons; would then realize that it would not - need to rebuild the &hello; program as follows: - - - - - % scons -Q hello - 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 -o hello.o -c hello.c - scons: `hello' is up to date. - - - - - In essence, &SCons; has - "short-circuited" any dependent builds - when it realizes that a target file - has been rebuilt to exactly the same file as the last build. - So configured, - &SCons; does take some extra processing time - to scan the contents of the target (&hello_o;) file, - but this may save time - if the rebuild that was avoided - would have been very time-consuming and expensive. - - - -
- -
- -
- Implicit Dependencies: The &cv-CPPPATH; Construction Variable - - - - Now suppose that our "Hello, World!" program - actually has a #include line - to include the &hello_h; file in the compilation: - - - - - #include <hello.h> - int - main() - { - printf("Hello, %s!\n", string); - } - - - - - And, for completeness, the &hello_h; file looks like this: - - - - - - #define string "world" - - - - - In this case, we want &SCons; to recognize that, - if the contents of the &hello_h; file change, - the &hello; program must be recompiled. - To do this, we need to modify the - &SConstruct; file like so: - - - - - - Program('hello.c', CPPPATH = '.') - - - - - The &cv-link-CPPPATH; value - tells &SCons; to look in the current directory - ('.') - for any files included by C source files - (.c or .h files). - With this assignment in the &SConstruct; file: - - - - - % scons -Q hello - 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 -o hello.o -c -I. hello.c - cc -o hello hello.o - - - - - First, notice that &SCons; - added the -I. argument - from the &cv-CPPPATH; variable - so that the compilation would find the - &hello_h; file in the local directory. - - - - - - Second, realize that &SCons; knows that the &hello; - program must be rebuilt - because it scans the contents of - the &hello_c; file - for the #include lines that indicate - another file is being included in the compilation. - &SCons; records these as - implicit dependencies - of the target file, - Consequently, - when the &hello_h; file changes, - &SCons; realizes that the &hello_c; file includes it, - and rebuilds the resulting &hello; program - that depends on both the &hello_c; and &hello_h; files. - - - - - - Like the &cv-link-LIBPATH; variable, - the &cv-CPPPATH; variable - may be a list of directories, - or a string separated by - the system-specific path separate character - (':' on POSIX/Linux, ';' on Windows). - Either way, &SCons; creates the - right command-line options - so that the following example: - - - - - Program('hello.c', CPPPATH = ['include', '/home/project/inc']) - - - - - Will look like this on POSIX or Linux: - - - - - % scons -Q hello - cc -o hello.o -c -Iinclude -I/home/project/inc hello.c - cc -o hello hello.o - - - - - And like this on Windows: - - - - - C:\>scons -Q hello.exe - cl /nologo /Iinclude /I\home\project\inc /c hello.c /Fohello.obj - link /nologo /OUT:hello.exe hello.obj - - -
- -
- Caching Implicit Dependencies - - - - Scanning each file for #include lines - does take some extra processing time. - When you're doing a full build of a large system, - the scanning time is usually a very small percentage - of the overall time spent on the build. - You're most likely to notice the scanning time, - however, when you rebuild - all or part of a large system: - &SCons; will likely take some extra time to "think about" - what must be built before it issues the - first build command - (or decides that everything is up to date - and nothing must be rebuilt). - - - - - - - - In practice, having &SCons; scan files saves time - relative to the amount of potential time - lost to tracking down subtle problems - introduced by incorrect dependencies. - Nevertheless, the "waiting time" - while &SCons; scans files can annoy - individual developers waiting for their builds to finish. - Consequently, &SCons; lets you cache - the implicit dependencies - that its scanners find, - for use by later builds. - You can do this by specifying the - &implicit-cache; option on the command line: - - - - - % scons -Q --implicit-cache hello - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q hello - scons: `hello' is up to date. - - - - - If you don't want to specify &implicit-cache; - on the command line each time, - you can make it the default behavior for your build - by setting the &implicit_cache; option - in an &SConscript; file: - - - - - SetOption('implicit_cache', 1) - - - - - &SCons; does not cache implicit dependencies like this by default - because the &implicit-cache; causes &SCons; to simply use the implicit - dependencies stored during the last run, without any checking - for whether or not those dependencies are still correct. - Specifically, this means &implicit-cache; instructs &SCons; - to not rebuild "correctly" in the - following cases: - - - - - - - - - - When &implicit-cache; is used, &SCons; will ignore any changes that - may have been made to search paths - (like &cv-CPPPATH; or &cv-LIBPATH;,). - This can lead to &SCons; not rebuilding a file if a change to - &cv-CPPPATH; would normally cause a different, same-named file from - a different directory to be used. - - - - - - - - When &implicit-cache; is used, &SCons; will not detect if a - same-named file has been added to a directory that is earlier in - the search path than the directory in which the file was found - last time. - - - - - - -
- The &implicit-deps-changed; Option - - - - When using cached implicit dependencies, - sometimes you want to "start fresh" - and have &SCons; re-scan the files - for which it previously cached the dependencies. - For example, - if you have recently installed a new version of - external code that you use for compilation, - the external header files will have changed - and the previously-cached implicit dependencies - will be out of date. - You can update them by - running &SCons; with the &implicit-deps-changed; option: - - - - - % scons -Q --implicit-deps-changed hello - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q hello - scons: `hello' is up to date. - - - - - In this case, &SCons; will re-scan all of the implicit dependencies - and cache updated copies of the information. - - - -
- -
- The &implicit-deps-unchanged; Option - - - - By default when caching dependencies, - &SCons; notices when a file has been modified - and re-scans the file for any updated - implicit dependency information. - Sometimes, however, you may want - to force &SCons; to use the cached implicit dependencies, - even if the source files changed. - This can speed up a build for example, - when you have changed your source files - but know that you haven't changed - any #include lines. - In this case, - you can use the &implicit-deps-unchanged; option: - - - - - % scons -Q --implicit-deps-unchanged hello - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q hello - scons: `hello' is up to date. - - - - - In this case, - &SCons; will assume that the cached implicit - dependencies are correct and - will not bother to re-scan changed files. - For typical builds after small, - incremental changes to source files, - the savings may not be very big, - but sometimes every bit of - improved performance counts. - - - -
- - - -
- -
- Ignoring Dependencies: the &Ignore; Method - - - - Sometimes it makes sense - to not rebuild a program, - even if a dependency file changes. - In this case, - you would tell &SCons; specifically - to ignore a dependency as follows: - - - - - hello = Program('hello.c') - Ignore(hello, 'hello.h') - - - - - - - - % scons -Q hello - cc -c -o hello.o 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 - scons: `hello' is up to date. - - - - - Now, the above example is a little contrived, - because it's hard to imagine a real-world situation - where you wouldn't to rebuild &hello; - if the &hello_h; file changed. - A more realistic example - might be if the &hello; - program is being built in a - directory that is shared between multiple systems - that have different copies of the - &stdio_h; include file. - In that case, - &SCons; would notice the differences between - the different systems' copies of &stdio_h; - and would rebuild &hello; - each time you change systems. - You could avoid these rebuilds as follows: - - - - - hello = Program('hello.c') - Ignore(hello, '/usr/include/stdio.h') - - -
- -
- Explicit Dependencies: the &Depends; Method - - - - On the other hand, - sometimes a file depends on another file - that is not detected by an &SCons; scanner. - For this situation, - &SCons; allows you to specific explicitly that one file - depends on another file, - and must be rebuilt whenever that file changes. - This is specified using the &Depends; method: - - - - - hello = Program('hello.c') - Depends(hello, 'other_file') - - - - - - % scons -Q hello - cc -c hello.c -o hello.o - cc -o hello hello.o - % scons -Q hello - scons: `hello' is up to date. - % edit other_file - [CHANGE THE CONTENTS OF other_file] - % scons -Q hello - cc -c hello.c -o hello.o - cc -o hello hello.o - - -
- -
- The &AlwaysBuild; Method - - - - How &SCons; handles dependencies can also be affected - by the &AlwaysBuild; method. - When a file is passed to the &AlwaysBuild; method, - like so: - - - - - hello = Program('hello.c') - AlwaysBuild(hello) - - - - - Then the specified target file (&hello; in our example) - will always be considered out-of-date and - rebuilt whenever that target file is evaluated - while walking the dependency graph: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q - cc -o hello hello.o - - - - - The &AlwaysBuild; function has a somewhat misleading name, - because it does not actually mean the target file will - be rebuilt every single time &SCons; is invoked. - Instead, it means that the target will, in fact, - be rebuilt whenever the target file is encountered - while evaluating the targets specified on - the command line (and their dependencies). - So specifying some other target on the command line, - a target that does not - itself depend on the &AlwaysBuild; target, - will still be rebuilt only if it's out-of-date - with respect to its dependencies: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q hello.o - scons: `hello.o' is up to date. - - - - -
- - diff --git a/doc/user/depends.xml b/doc/user/depends.xml new file mode 100644 index 0000000..9e055ee --- /dev/null +++ b/doc/user/depends.xml @@ -0,0 +1,917 @@ + + + + + So far we've seen how &SCons; handles one-time builds. + But the real point of a build tool like &SCons; + is to rebuild only the necessary things + when source files change--or, put another way, + &SCons; should not + waste time rebuilding things that have already been built. + You can see this at work simply be re-invoking &SCons; + after building our simple &hello; example: + + + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q + scons: `.' is up to date. + + + + + The second time it is executed, + &SCons; realizes that the &hello; program + is up-to-date with respect to the current &hello_c; source file, + and avoids rebuilding it. + You can see this more clearly by naming + the &hello; program explicitly on the command line: + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + + + + + Note that &SCons; reports "...is up to date" + only for target files named explicitly on the command line, + to avoid cluttering the output. + + + +
+ Deciding When a Source File Has Changed: the &SourceSignatures; Function + + + + The other side of avoiding unnecessary rebuilds + is the fundamental build tool behavior + of rebuilding + things when a source file changes, + so that the built software is up to date. + &SCons; keeps track of this through a + &signature; for each source file, + and allows you to configure + whether you want to use the source + file contents or the modification time (timestamp) + as the signature. + + + +
+ MD5 Source File Signatures + + + + By default, + &SCons; keeps track of whether a source file has changed + based on the file's contents, + not the modification time. + This means that you may be surprised by the + default &SCons; behavior if you are used to the + &Make; convention of forcing + a rebuild by updating the file's modification time + (using the &touch; command, for example): + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % touch hello.c + % scons -Q hello + scons: `hello' is up to date. + + + + + Even though the file's modification time has changed, + &SCons; realizes that the contents of the + &hello_c; file have not changed, + and therefore that the &hello; program + need not be rebuilt. + This avoids unnecessary rebuilds when, + for example, someone rewrites the + contents of a file without making a change. + But if the contents of the file really do change, + then &SCons; detects the change + and rebuilds the program as required: + + + + + % scons -Q hello + 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 -o hello.o -c hello.c + cc -o hello hello.o + + + + + Note that you can, if you wish, + specify this default behavior + (MD5 signatures) explicitly + using the &SourceSignatures; function as follows: + + + + + Program('hello.c') + SourceSignatures('MD5') + + +
+ +
+ Source File Time Stamps + + + + If you prefer, you can + configure &SCons; to use the modification time + of source files, + not the file contents, + when deciding if something needs to be rebuilt. + To do this, call the &SourceSignatures; + function as follows: + + + + + Program('hello.c') + SourceSignatures('timestamp') + + + + + This makes &SCons; act like &Make; + when a file's modification time is updated + (using the &touch; command, for example): + + + + + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % touch hello.c + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ +
+ Deciding When a Target File Has Changed: the &TargetSignatures; Function + + + + As you've just seen, + &SCons; uses signatures to decide whether a + target file is up to date or must be rebuilt. + When a target file depends on another target file, + &SCons; allows you to configure separately + how the signatures of "intermediate" target files + are used when deciding if a dependent target file + must be rebuilt. + + + +
+ Build Signatures + + + + Modifying a source file + will cause not only its direct target file to be rebuilt, + but also the target file(s) + that depend on that direct target file. + In our example, + changing the contents of the &hello_c; file causes + the &hello_o; file to be rebuilt, + which in turn causes the + &hello; program to be rebuilt: + + + + + % scons -Q hello + 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 -o hello.o -c hello.c + cc -o hello hello.o + + + + + What's not obvious, though, + is that &SCons; internally handles the signature of + the target file(s) + (&hello_o; in the above example) + differently from the signature of the source file + (&hello_c;). + By default, + &SCons; tracks whether a target file must be rebuilt + by using a &buildsignature; + that consists of the combined + signatures of all the files + that go into making the target file. + This is efficient because + the accumulated signatures + actually give &SCons; all of the + information it needs + to decide if the target file is out of date. + + + + + + If you wish, you can + specify this default behavior + (build signatures) explicitly + using the &TargetSignatures; function: + + + + + Program('hello.c') + TargetSignatures('build') + + +
+ +
+ File Contents + + + + Sometimes a source file can be changed + in such a way that the contents of the + rebuilt target file(s) + will be exactly the same as the last time + the file was built. + If so, then any other target files + that depend on such a built-but-not-changed target + file actually need not be rebuilt. + You can make &SCons; + realize that it does not need to rebuild + a dependent target file in this situation + using the &TargetSignatures; function as follows: + + + + + Program('hello.c') + TargetSignatures('content') + + + + + So if, for example, + a user were to only change a comment in a C file, + then the rebuilt &hello_o; file + would be exactly the same as the one previously built + (assuming the compiler doesn't put any build-specific + information in the object file). + &SCons; would then realize that it would not + need to rebuild the &hello; program as follows: + + + + + % scons -Q hello + 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 -o hello.o -c hello.c + scons: `hello' is up to date. + + + + + In essence, &SCons; has + "short-circuited" any dependent builds + when it realizes that a target file + has been rebuilt to exactly the same file as the last build. + So configured, + &SCons; does take some extra processing time + to scan the contents of the target (&hello_o;) file, + but this may save time + if the rebuild that was avoided + would have been very time-consuming and expensive. + + + +
+ +
+ +
+ Implicit Dependencies: The &cv-CPPPATH; Construction Variable + + + + Now suppose that our "Hello, World!" program + actually has a #include line + to include the &hello_h; file in the compilation: + + + + + #include <hello.h> + int + main() + { + printf("Hello, %s!\n", string); + } + + + + + And, for completeness, the &hello_h; file looks like this: + + + + + + #define string "world" + + + + + In this case, we want &SCons; to recognize that, + if the contents of the &hello_h; file change, + the &hello; program must be recompiled. + To do this, we need to modify the + &SConstruct; file like so: + + + + + + Program('hello.c', CPPPATH = '.') + + + + + The &cv-link-CPPPATH; value + tells &SCons; to look in the current directory + ('.') + for any files included by C source files + (.c or .h files). + With this assignment in the &SConstruct; file: + + + + + % scons -Q hello + 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 -o hello.o -c -I. hello.c + cc -o hello hello.o + + + + + First, notice that &SCons; + added the -I. argument + from the &cv-CPPPATH; variable + so that the compilation would find the + &hello_h; file in the local directory. + + + + + + Second, realize that &SCons; knows that the &hello; + program must be rebuilt + because it scans the contents of + the &hello_c; file + for the #include lines that indicate + another file is being included in the compilation. + &SCons; records these as + implicit dependencies + of the target file, + Consequently, + when the &hello_h; file changes, + &SCons; realizes that the &hello_c; file includes it, + and rebuilds the resulting &hello; program + that depends on both the &hello_c; and &hello_h; files. + + + + + + Like the &cv-link-LIBPATH; variable, + the &cv-CPPPATH; variable + may be a list of directories, + or a string separated by + the system-specific path separate character + (':' on POSIX/Linux, ';' on Windows). + Either way, &SCons; creates the + right command-line options + so that the following example: + + + + + Program('hello.c', CPPPATH = ['include', '/home/project/inc']) + + + + + Will look like this on POSIX or Linux: + + + + + % scons -Q hello + cc -o hello.o -c -Iinclude -I/home/project/inc hello.c + cc -o hello hello.o + + + + + And like this on Windows: + + + + + C:\>scons -Q hello.exe + cl /nologo /Iinclude /I\home\project\inc /c hello.c /Fohello.obj + link /nologo /OUT:hello.exe hello.obj + + +
+ +
+ Caching Implicit Dependencies + + + + Scanning each file for #include lines + does take some extra processing time. + When you're doing a full build of a large system, + the scanning time is usually a very small percentage + of the overall time spent on the build. + You're most likely to notice the scanning time, + however, when you rebuild + all or part of a large system: + &SCons; will likely take some extra time to "think about" + what must be built before it issues the + first build command + (or decides that everything is up to date + and nothing must be rebuilt). + + + + + + + + In practice, having &SCons; scan files saves time + relative to the amount of potential time + lost to tracking down subtle problems + introduced by incorrect dependencies. + Nevertheless, the "waiting time" + while &SCons; scans files can annoy + individual developers waiting for their builds to finish. + Consequently, &SCons; lets you cache + the implicit dependencies + that its scanners find, + for use by later builds. + You can do this by specifying the + &implicit-cache; option on the command line: + + + + + % scons -Q --implicit-cache hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + + + + + If you don't want to specify &implicit-cache; + on the command line each time, + you can make it the default behavior for your build + by setting the &implicit_cache; option + in an &SConscript; file: + + + + + SetOption('implicit_cache', 1) + + + + + &SCons; does not cache implicit dependencies like this by default + because the &implicit-cache; causes &SCons; to simply use the implicit + dependencies stored during the last run, without any checking + for whether or not those dependencies are still correct. + Specifically, this means &implicit-cache; instructs &SCons; + to not rebuild "correctly" in the + following cases: + + + + + + + + + + When &implicit-cache; is used, &SCons; will ignore any changes that + may have been made to search paths + (like &cv-CPPPATH; or &cv-LIBPATH;,). + This can lead to &SCons; not rebuilding a file if a change to + &cv-CPPPATH; would normally cause a different, same-named file from + a different directory to be used. + + + + + + + + When &implicit-cache; is used, &SCons; will not detect if a + same-named file has been added to a directory that is earlier in + the search path than the directory in which the file was found + last time. + + + + + + +
+ The &implicit-deps-changed; Option + + + + When using cached implicit dependencies, + sometimes you want to "start fresh" + and have &SCons; re-scan the files + for which it previously cached the dependencies. + For example, + if you have recently installed a new version of + external code that you use for compilation, + the external header files will have changed + and the previously-cached implicit dependencies + will be out of date. + You can update them by + running &SCons; with the &implicit-deps-changed; option: + + + + + % scons -Q --implicit-deps-changed hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + + + + + In this case, &SCons; will re-scan all of the implicit dependencies + and cache updated copies of the information. + + + +
+ +
+ The &implicit-deps-unchanged; Option + + + + By default when caching dependencies, + &SCons; notices when a file has been modified + and re-scans the file for any updated + implicit dependency information. + Sometimes, however, you may want + to force &SCons; to use the cached implicit dependencies, + even if the source files changed. + This can speed up a build for example, + when you have changed your source files + but know that you haven't changed + any #include lines. + In this case, + you can use the &implicit-deps-unchanged; option: + + + + + % scons -Q --implicit-deps-unchanged hello + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + + + + + In this case, + &SCons; will assume that the cached implicit + dependencies are correct and + will not bother to re-scan changed files. + For typical builds after small, + incremental changes to source files, + the savings may not be very big, + but sometimes every bit of + improved performance counts. + + + +
+ + + +
+ +
+ Ignoring Dependencies: the &Ignore; Method + + + + Sometimes it makes sense + to not rebuild a program, + even if a dependency file changes. + In this case, + you would tell &SCons; specifically + to ignore a dependency as follows: + + + + + hello = Program('hello.c') + Ignore(hello, 'hello.h') + + + + + + + + % scons -Q hello + cc -c -o hello.o 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 + scons: `hello' is up to date. + + + + + Now, the above example is a little contrived, + because it's hard to imagine a real-world situation + where you wouldn't to rebuild &hello; + if the &hello_h; file changed. + A more realistic example + might be if the &hello; + program is being built in a + directory that is shared between multiple systems + that have different copies of the + &stdio_h; include file. + In that case, + &SCons; would notice the differences between + the different systems' copies of &stdio_h; + and would rebuild &hello; + each time you change systems. + You could avoid these rebuilds as follows: + + + + + hello = Program('hello.c') + Ignore(hello, '/usr/include/stdio.h') + + +
+ +
+ Explicit Dependencies: the &Depends; Method + + + + On the other hand, + sometimes a file depends on another file + that is not detected by an &SCons; scanner. + For this situation, + &SCons; allows you to specific explicitly that one file + depends on another file, + and must be rebuilt whenever that file changes. + This is specified using the &Depends; method: + + + + + hello = Program('hello.c') + Depends(hello, 'other_file') + + + + + + % scons -Q hello + cc -c hello.c -o hello.o + cc -o hello hello.o + % scons -Q hello + scons: `hello' is up to date. + % edit other_file + [CHANGE THE CONTENTS OF other_file] + % scons -Q hello + cc -c hello.c -o hello.o + cc -o hello hello.o + + +
+ +
+ The &AlwaysBuild; Method + + + + How &SCons; handles dependencies can also be affected + by the &AlwaysBuild; method. + When a file is passed to the &AlwaysBuild; method, + like so: + + + + + hello = Program('hello.c') + AlwaysBuild(hello) + + + + + Then the specified target file (&hello; in our example) + will always be considered out-of-date and + rebuilt whenever that target file is evaluated + while walking the dependency graph: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q + cc -o hello hello.o + + + + + The &AlwaysBuild; function has a somewhat misleading name, + because it does not actually mean the target file will + be rebuilt every single time &SCons; is invoked. + Instead, it means that the target will, in fact, + be rebuilt whenever the target file is encountered + while evaluating the targets specified on + the command line (and their dependencies). + So specifying some other target on the command line, + a target that does not + itself depend on the &AlwaysBuild; target, + will still be rebuilt only if it's out-of-date + with respect to its dependencies: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q hello.o + scons: `hello.o' is up to date. + + + + +
+ + diff --git a/doc/user/environments.sgml b/doc/user/environments.sgml deleted file mode 100644 index 67a5551..0000000 --- a/doc/user/environments.sgml +++ /dev/null @@ -1,1145 +0,0 @@ - - - - - - - It is rare that all of the software in a large, - complicated system needs to be built the same way. - For example, different source files may need different options - enabled on the command line, - or different executable programs need to be linked - with different libraries. - &SCons; accomodates these different build - requirements by allowing you to create and - configure multiple &consenvs; - that control how the software is built. - Technically, a &consenv; is an object - that has a number of associated - &consvars;, each with a name and a value. - (A construction environment also has an attached - set of &Builder; methods, - about which we'll learn more later.) - - - - - - A &consenv; is created by the &Environment; method: - - - - - env = Environment() - - - - - By default, &SCons; intializes every - new construction environment - with a set of &consvars; - based on the tools that it finds on your system, - plus the default set of builder methods - necessary for using those tools. - The construction variables - are initialized with values describing - the C compiler, - the Fortran compiler, - the linker, - etc., - as well as the command lines to invoke them. - - - - - - When you initialize a construction environment - you can set the values of the - environment's &consvars; - to control how a program is built. - For example: - - - - - env = Environment(CC = 'gcc', - CCFLAGS = '-O2') - - env.Program('foo.c') - - - - - The construction environment in this example - is still initialized with the same default - construction variable values, - except that the user has explicitly specified use of the - GNU C compiler &gcc;, - and further specifies that the -O2 - (optimization level two) - flag should be used when compiling the object file. - In other words, the explicit initializations of - &cv-link-CC; and &cv-link-CCFLAGS; - override the default values in the newly-created - construction environment. - So a run from this example would look like: - - - - - % scons -Q - gcc -o foo.o -c -O2 foo.c - gcc -o foo foo.o - - -
- Multiple &ConsEnvs; - - - - The real advantage of construction environments - is that you can create as many different construction - environments as you need, - each tailored to a different way to build - some piece of software or other file. - If, for example, we need to build - one program with the -O2 flag - and another with the -g (debug) flag, - we would do this like so: - - - - - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') - - opt.Program('foo', 'foo.c') - - dbg.Program('bar', 'bar.c') - - - - % scons -Q - cc -o bar.o -c -g bar.c - cc -o bar bar.o - cc -o foo.o -c -O2 foo.c - cc -o foo foo.o - - - - - We can even use multiple construction environments to build - multiple versions of a single program. - If you do this by simply trying to use the - &b-link-Program; builder with both environments, though, - like this: - - - - - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') - - opt.Program('foo', 'foo.c') - - dbg.Program('foo', 'foo.c') - - - - - Then &SCons; generates the following error: - - - - - % scons -Q - - scons: *** Two environments with different actions were specified for the same target: foo.o - File "/home/my/project/SConstruct", line 6, in ? - - - - - This is because the two &b-Program; calls have - each implicitly told &SCons; to generate an object file named - foo.o, - one with a &cv-link-CCFLAGS; value of - -O2 - and one with a &cv-link-CCFLAGS; value of - -g. - &SCons; can't just decide that one of them - should take precedence over the other, - so it generates the error. - To avoid this problem, - we must explicitly specify - that each environment compile - foo.c - to a separately-named object file - using the &b-link-Object; builder, like so: - - - - - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') - - o = opt.Object('foo-opt', 'foo.c') - opt.Program(o) - - d = dbg.Object('foo-dbg', 'foo.c') - dbg.Program(d) - - - - - Notice that each call to the &b-Object; builder - returns a value, - an internal &SCons; object that - represents the object file that will be built. - We then use that object - as input to the &b-Program; builder. - This avoids having to specify explicitly - the object file name in multiple places, - and makes for a compact, readable - &SConstruct; file. - Our &SCons; output then looks like: - - - - - % scons -Q - cc -o foo-dbg.o -c -g foo.c - cc -o foo-dbg foo-dbg.o - cc -o foo-opt.o -c -O2 foo.c - cc -o foo-opt foo-opt.o - - -
- -
- Copying &ConsEnvs; - - - - Sometimes you want more than one construction environment - to share the same values for one or more variables. - Rather than always having to repeat all of the common - variables when you create each construction environment, - you can use the &Clone; method - to create a copy of a construction environment. - - - - - - Like the &Environment; call that creates a construction environment, - the &Clone; method takes &consvar; assignments, - which will override the values in the copied construction environment. - For example, suppose we want to use &gcc; - to create three versions of a program, - one optimized, one debug, and one with neither. - We could do this by creating a "base" construction environment - that sets &cv-link-CC; to &gcc;, - and then creating two copies, - one which sets &cv-link-CCFLAGS; for optimization - and the other which sets &cv-CCFLAGS; for debugging: - - - - - env = Environment(CC = 'gcc') - opt = env.Clone(CCFLAGS = '-O2') - dbg = env.Clone(CCFLAGS = '-g') - - env.Program('foo', 'foo.c') - - o = opt.Object('foo-opt', 'foo.c') - opt.Program(o) - - d = dbg.Object('foo-dbg', 'foo.c') - dbg.Program(d) - - - - - Then our output would look like: - - - - - % scons -Q - gcc -o foo.o -c foo.c - gcc -o foo foo.o - gcc -o foo-dbg.o -c -g foo.c - gcc -o foo-dbg foo-dbg.o - gcc -o foo-opt.o -c -O2 foo.c - gcc -o foo-opt foo-opt.o - - -
- -
- Fetching Values From a &ConsEnv; - - - - You can fetch individual construction variables - using the normal syntax - for accessing individual named items in a Python dictionary: - - - - - env = Environment() - print "CC is:", env['CC'] - - - - - This example &SConstruct; file doesn't build anything, - but because it's actually a Python script, - it will print the value of &cv-link-CC; for us: - - - - - % scons -Q - CC is: cc - scons: `.' is up to date. - - - - - A construction environment, however, - is actually an object with associated methods, etc. - If you want to have direct access to only the - dictionary of construction variables, - you can fetch this using the &Dictionary; method: - - - - - env = Environment(FOO = 'foo', BAR = 'bar') - dict = env.Dictionary() - for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: - print "key = %s, value = %s" % (key, dict[key]) - - - - - This &SConstruct; file - will print the specified dictionary items for us on POSIX - systems as follows: - - - - - % scons -Q - key = OBJSUFFIX, value = .o - key = LIBSUFFIX, value = .a - key = PROGSUFFIX, value = - scons: `.' is up to date. - - - - - And on Windows: - - - - - C:\>scons -Q - key = OBJSUFFIX, value = .obj - key = LIBSUFFIX, value = .lib - key = PROGSUFFIX, value = .exe - scons: `.' is up to date. - - - - - If you want to loop through and print the values of - all of the construction variables in a construction environment, - the Python code to do that in sorted order might look something like: - - - - - env = Environment() - dict = env.Dictionary() - keys = dict.keys() - keys.sort() - for key in keys: - print "construction variable = '%s', value = '%s'" % (key, dict[key]) - - -
- -
- Expanding Values From a &ConsEnv; - - - - Another way to get information from - a construction environment. - is to use the &subst; method - on a string containing $-expansions - of construction variable names. - As a simple example, - the example from the previous - section that used - env['CC'] - to fetch the value of &cv-link-CC; - could also be written as: - - - - - env = Environment() - print "CC is:", env.subst('$CC') - - - - - The real advantage of using - &subst; to expand strings is - that construction variables - in the result get - re-expanded until - there are no expansions left in the string. - So a simple fetch of a value like - &cv-link-CCCOM;: - - - - - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env['CCCOM'] - - - - - Will print the unexpanded value of &cv-CCCOM;, - showing us the construction - variables that still need to be expanded: - - - - - % scons -Q - CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES - scons: `.' is up to date. - - - - - Calling the &subst; method on $CCOM, - however: - - - - - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env.subst('$CCCOM') - - - - - Will recursively expand all of - the $-prefixed construction variables, - showing us the final output: - - - - - % scons -Q - CCCOM is: gcc -DFOO -c -o - scons: `.' is up to date. - - - - - (Note that because we're not expanding this - in the context of building something - there are no target or source files - for &cv-link-TARGET; and &cv-link-SOURCES; to expand. - - - -
- -
- Modifying a &ConsEnv; - - - - &SCons; provides various methods that - support modifying existing values in a construction environment. - - - -
- Replacing Values in a &ConsEnv; - - - - You can replace existing construction variable values - using the &Replace; method: - - - - - env = Environment(CCFLAGS = '-DDEFINE1') - env.Replace(CCFLAGS = '-DDEFINE2') - env.Program('foo.c') - - - - - The replacing value - (-DDEFINE2 in the above example) - completely replaces the value in the - construction environment: - - - - - % scons -Q - cc -o foo.o -c -DDEFINE2 foo.c - cc -o foo foo.o - - - - - You can safely call &Replace; - for construction variables that - don't exist in the construction environment: - - - - - env = Environment() - env.Replace(NEW_VARIABLE = 'xyzzy') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - - - - - In this case, - the construction variable simply - gets added to the construction environment: - - - - - % scons -Q - NEW_VARIABLE = xyzzy - scons: `.' is up to date. - - - - - Because the variables - aren't expanded until the construction environment - is actually used to build the targets, - and because &SCons; function and method calls - are order-independent, - the last replacement "wins" - and is used to build all targets, - regardless of the order in which - the calls to Replace() are - interspersed with calls to - builder methods: - - - - - env = Environment(CCFLAGS = '-DDEFINE1') - print "CCFLAGS =", env['CCFLAGS'] - env.Program('foo.c') - - env.Replace(CCFLAGS = '-DDEFINE2') - print "CCFLAGS =", env['CCFLAGS'] - env.Program('bar.c') - - - - - The timing of when the replacement - actually occurs relative - to when the targets get built - becomes apparent - if we run &scons; without the -Q - option: - - - - - % scons - scons: Reading SConscript files ... - CCFLAGS = -DDEFINE1 - CCFLAGS = -DDEFINE2 - scons: done reading SConscript files. - scons: Building targets ... - cc -o bar.o -c -DDEFINE2 bar.c - cc -o bar bar.o - cc -o foo.o -c -DDEFINE2 foo.c - cc -o foo foo.o - scons: done building targets. - - - - - Because the replacement occurs while - the &SConscript; files are being read, - the &cv-link-CCFLAGS; - variable has already been set to - -DDEFINE2 - by the time the &foo_o; target is built, - even though the call to the &Replace; - method does not occur until later in - the &SConscript; file. - - - -
- - - -
- Appending to the End of Values in a &ConsEnv; - - - - You can append a value to - an existing construction variable - using the &Append; method: - - - - - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Append(CCFLAGS = ' -DLAST') - env.Program('foo.c') - - - - - &SCons; then supplies both the -DMY_VALUE and - -DLAST flags when compiling the object file: - - - - - % scons -Q - cc -o foo.o -c -DMY_VALUE -DLAST foo.c - cc -o foo foo.o - - - - - If the construction variable doesn't already exist, - the &Append; method will create it: - - - - - env = Environment() - env.Append(NEW_VARIABLE = 'added') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - - - - - Which yields: - - - - - % scons -Q - NEW_VARIABLE = added - scons: `.' is up to date. - - - - -
- -
- Appending to the Beginning of Values in a &ConsEnv; - - - - You can append a value to the beginning of - an existing construction variable - using the &Prepend; method: - - - - - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Prepend(CCFLAGS = '-DFIRST ') - env.Program('foo.c') - - - - - &SCons; then supplies both the -DFIRST and - -DMY_VALUE flags when compiling the object file: - - - - - % scons -Q - cc -o foo.o -c -DFIRST -DMY_VALUE foo.c - cc -o foo foo.o - - - - - If the construction variable doesn't already exist, - the &Prepend; method will create it: - - - - - env = Environment() - env.Prepend(NEW_VARIABLE = 'added') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - - - - - Which yields: - - - - - % scons -Q - NEW_VARIABLE = added - scons: `.' is up to date. - - - - -
- - - -
diff --git a/doc/user/environments.xml b/doc/user/environments.xml new file mode 100644 index 0000000..fbcef99 --- /dev/null +++ b/doc/user/environments.xml @@ -0,0 +1,1145 @@ + + + + + + + It is rare that all of the software in a large, + complicated system needs to be built the same way. + For example, different source files may need different options + enabled on the command line, + or different executable programs need to be linked + with different libraries. + &SCons; accomodates these different build + requirements by allowing you to create and + configure multiple &consenvs; + that control how the software is built. + Technically, a &consenv; is an object + that has a number of associated + &consvars;, each with a name and a value. + (A construction environment also has an attached + set of &Builder; methods, + about which we'll learn more later.) + + + + + + A &consenv; is created by the &Environment; method: + + + + + env = Environment() + + + + + By default, &SCons; intializes every + new construction environment + with a set of &consvars; + based on the tools that it finds on your system, + plus the default set of builder methods + necessary for using those tools. + The construction variables + are initialized with values describing + the C compiler, + the Fortran compiler, + the linker, + etc., + as well as the command lines to invoke them. + + + + + + When you initialize a construction environment + you can set the values of the + environment's &consvars; + to control how a program is built. + For example: + + + + + env = Environment(CC = 'gcc', + CCFLAGS = '-O2') + + env.Program('foo.c') + + + + + The construction environment in this example + is still initialized with the same default + construction variable values, + except that the user has explicitly specified use of the + GNU C compiler &gcc;, + and further specifies that the -O2 + (optimization level two) + flag should be used when compiling the object file. + In other words, the explicit initializations of + &cv-link-CC; and &cv-link-CCFLAGS; + override the default values in the newly-created + construction environment. + So a run from this example would look like: + + + + + % scons -Q + gcc -o foo.o -c -O2 foo.c + gcc -o foo foo.o + + +
+ Multiple &ConsEnvs; + + + + The real advantage of construction environments + is that you can create as many different construction + environments as you need, + each tailored to a different way to build + some piece of software or other file. + If, for example, we need to build + one program with the -O2 flag + and another with the -g (debug) flag, + we would do this like so: + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('bar', 'bar.c') + + + + % scons -Q + cc -o bar.o -c -g bar.c + cc -o bar bar.o + cc -o foo.o -c -O2 foo.c + cc -o foo foo.o + + + + + We can even use multiple construction environments to build + multiple versions of a single program. + If you do this by simply trying to use the + &b-link-Program; builder with both environments, though, + like this: + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('foo', 'foo.c') + + + + + Then &SCons; generates the following error: + + + + + % scons -Q + + scons: *** Two environments with different actions were specified for the same target: foo.o + File "/home/my/project/SConstruct", line 6, in <module> + + + + + This is because the two &b-Program; calls have + each implicitly told &SCons; to generate an object file named + foo.o, + one with a &cv-link-CCFLAGS; value of + -O2 + and one with a &cv-link-CCFLAGS; value of + -g. + &SCons; can't just decide that one of them + should take precedence over the other, + so it generates the error. + To avoid this problem, + we must explicitly specify + that each environment compile + foo.c + to a separately-named object file + using the &b-link-Object; builder, like so: + + + + + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + o = opt.Object('foo-opt', 'foo.c') + opt.Program(o) + + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + + + + + Notice that each call to the &b-Object; builder + returns a value, + an internal &SCons; object that + represents the object file that will be built. + We then use that object + as input to the &b-Program; builder. + This avoids having to specify explicitly + the object file name in multiple places, + and makes for a compact, readable + &SConstruct; file. + Our &SCons; output then looks like: + + + + + % scons -Q + cc -o foo-dbg.o -c -g foo.c + cc -o foo-dbg foo-dbg.o + cc -o foo-opt.o -c -O2 foo.c + cc -o foo-opt foo-opt.o + + +
+ +
+ Copying &ConsEnvs; + + + + Sometimes you want more than one construction environment + to share the same values for one or more variables. + Rather than always having to repeat all of the common + variables when you create each construction environment, + you can use the &Clone; method + to create a copy of a construction environment. + + + + + + Like the &Environment; call that creates a construction environment, + the &Clone; method takes &consvar; assignments, + which will override the values in the copied construction environment. + For example, suppose we want to use &gcc; + to create three versions of a program, + one optimized, one debug, and one with neither. + We could do this by creating a "base" construction environment + that sets &cv-link-CC; to &gcc;, + and then creating two copies, + one which sets &cv-link-CCFLAGS; for optimization + and the other which sets &cv-CCFLAGS; for debugging: + + + + + env = Environment(CC = 'gcc') + opt = env.Clone(CCFLAGS = '-O2') + dbg = env.Clone(CCFLAGS = '-g') + + env.Program('foo', 'foo.c') + + o = opt.Object('foo-opt', 'foo.c') + opt.Program(o) + + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + + + + + Then our output would look like: + + + + + % scons -Q + gcc -o foo.o -c foo.c + gcc -o foo foo.o + gcc -o foo-dbg.o -c -g foo.c + gcc -o foo-dbg foo-dbg.o + gcc -o foo-opt.o -c -O2 foo.c + gcc -o foo-opt foo-opt.o + + +
+ +
+ Fetching Values From a &ConsEnv; + + + + You can fetch individual construction variables + using the normal syntax + for accessing individual named items in a Python dictionary: + + + + + env = Environment() + print "CC is:", env['CC'] + + + + + This example &SConstruct; file doesn't build anything, + but because it's actually a Python script, + it will print the value of &cv-link-CC; for us: + + + + + % scons -Q + CC is: cc + scons: `.' is up to date. + + + + + A construction environment, however, + is actually an object with associated methods, etc. + If you want to have direct access to only the + dictionary of construction variables, + you can fetch this using the &Dictionary; method: + + + + + env = Environment(FOO = 'foo', BAR = 'bar') + dict = env.Dictionary() + for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: + print "key = %s, value = %s" % (key, dict[key]) + + + + + This &SConstruct; file + will print the specified dictionary items for us on POSIX + systems as follows: + + + + + % scons -Q + key = OBJSUFFIX, value = .o + key = LIBSUFFIX, value = .a + key = PROGSUFFIX, value = + scons: `.' is up to date. + + + + + And on Windows: + + + + + C:\>scons -Q + key = OBJSUFFIX, value = .obj + key = LIBSUFFIX, value = .lib + key = PROGSUFFIX, value = .exe + scons: `.' is up to date. + + + + + If you want to loop through and print the values of + all of the construction variables in a construction environment, + the Python code to do that in sorted order might look something like: + + + + + env = Environment() + dict = env.Dictionary() + keys = dict.keys() + keys.sort() + for key in keys: + print "construction variable = '%s', value = '%s'" % (key, dict[key]) + + +
+ +
+ Expanding Values From a &ConsEnv; + + + + Another way to get information from + a construction environment. + is to use the &subst; method + on a string containing $-expansions + of construction variable names. + As a simple example, + the example from the previous + section that used + env['CC'] + to fetch the value of &cv-link-CC; + could also be written as: + + + + + env = Environment() + print "CC is:", env.subst('$CC') + + + + + The real advantage of using + &subst; to expand strings is + that construction variables + in the result get + re-expanded until + there are no expansions left in the string. + So a simple fetch of a value like + &cv-link-CCCOM;: + + + + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env['CCCOM'] + + + + + Will print the unexpanded value of &cv-CCCOM;, + showing us the construction + variables that still need to be expanded: + + + + + % scons -Q + CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES + scons: `.' is up to date. + + + + + Calling the &subst; method on $CCOM, + however: + + + + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env.subst('$CCCOM') + + + + + Will recursively expand all of + the $-prefixed construction variables, + showing us the final output: + + + + + % scons -Q + CCCOM is: gcc -DFOO -c -o + scons: `.' is up to date. + + + + + (Note that because we're not expanding this + in the context of building something + there are no target or source files + for &cv-link-TARGET; and &cv-link-SOURCES; to expand. + + + +
+ +
+ Modifying a &ConsEnv; + + + + &SCons; provides various methods that + support modifying existing values in a construction environment. + + + +
+ Replacing Values in a &ConsEnv; + + + + You can replace existing construction variable values + using the &Replace; method: + + + + + env = Environment(CCFLAGS = '-DDEFINE1') + env.Replace(CCFLAGS = '-DDEFINE2') + env.Program('foo.c') + + + + + The replacing value + (-DDEFINE2 in the above example) + completely replaces the value in the + construction environment: + + + + + % scons -Q + cc -o foo.o -c -DDEFINE2 foo.c + cc -o foo foo.o + + + + + You can safely call &Replace; + for construction variables that + don't exist in the construction environment: + + + + + env = Environment() + env.Replace(NEW_VARIABLE = 'xyzzy') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + In this case, + the construction variable simply + gets added to the construction environment: + + + + + % scons -Q + NEW_VARIABLE = xyzzy + scons: `.' is up to date. + + + + + Because the variables + aren't expanded until the construction environment + is actually used to build the targets, + and because &SCons; function and method calls + are order-independent, + the last replacement "wins" + and is used to build all targets, + regardless of the order in which + the calls to Replace() are + interspersed with calls to + builder methods: + + + + + env = Environment(CCFLAGS = '-DDEFINE1') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('foo.c') + + env.Replace(CCFLAGS = '-DDEFINE2') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('bar.c') + + + + + The timing of when the replacement + actually occurs relative + to when the targets get built + becomes apparent + if we run &scons; without the -Q + option: + + + + + % scons + scons: Reading SConscript files ... + CCFLAGS = -DDEFINE1 + CCFLAGS = -DDEFINE2 + scons: done reading SConscript files. + scons: Building targets ... + cc -o bar.o -c -DDEFINE2 bar.c + cc -o bar bar.o + cc -o foo.o -c -DDEFINE2 foo.c + cc -o foo foo.o + scons: done building targets. + + + + + Because the replacement occurs while + the &SConscript; files are being read, + the &cv-link-CCFLAGS; + variable has already been set to + -DDEFINE2 + by the time the &foo_o; target is built, + even though the call to the &Replace; + method does not occur until later in + the &SConscript; file. + + + +
+ + + +
+ Appending to the End of Values in a &ConsEnv; + + + + You can append a value to + an existing construction variable + using the &Append; method: + + + + + env = Environment(CCFLAGS = '-DMY_VALUE') + env.Append(CCFLAGS = ' -DLAST') + env.Program('foo.c') + + + + + &SCons; then supplies both the -DMY_VALUE and + -DLAST flags when compiling the object file: + + + + + % scons -Q + cc -o foo.o -c -DMY_VALUE -DLAST foo.c + cc -o foo foo.o + + + + + If the construction variable doesn't already exist, + the &Append; method will create it: + + + + + env = Environment() + env.Append(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + Which yields: + + + + + % scons -Q + NEW_VARIABLE = added + scons: `.' is up to date. + + + + +
+ +
+ Appending to the Beginning of Values in a &ConsEnv; + + + + You can append a value to the beginning of + an existing construction variable + using the &Prepend; method: + + + + + env = Environment(CCFLAGS = '-DMY_VALUE') + env.Prepend(CCFLAGS = '-DFIRST ') + env.Program('foo.c') + + + + + &SCons; then supplies both the -DFIRST and + -DMY_VALUE flags when compiling the object file: + + + + + % scons -Q + cc -o foo.o -c -DFIRST -DMY_VALUE foo.c + cc -o foo foo.o + + + + + If the construction variable doesn't already exist, + the &Prepend; method will create it: + + + + + env = Environment() + env.Prepend(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + + + + Which yields: + + + + + % scons -Q + NEW_VARIABLE = added + scons: `.' is up to date. + + + + +
+ + + +
diff --git a/doc/user/errors.sgml b/doc/user/errors.sgml deleted file mode 100644 index 0891bf8..0000000 --- a/doc/user/errors.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - XXX - - - -
- XXX - - - - XXX - - - -
diff --git a/doc/user/errors.xml b/doc/user/errors.xml new file mode 100644 index 0000000..0891bf8 --- /dev/null +++ b/doc/user/errors.xml @@ -0,0 +1,41 @@ + + + + + XXX + + + +
+ XXX + + + + XXX + + + +
diff --git a/doc/user/example.sgml b/doc/user/example.sgml deleted file mode 100644 index 0891bf8..0000000 --- a/doc/user/example.sgml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - XXX - - - -
- XXX - - - - XXX - - - -
diff --git a/doc/user/example.xml b/doc/user/example.xml new file mode 100644 index 0000000..0891bf8 --- /dev/null +++ b/doc/user/example.xml @@ -0,0 +1,41 @@ + + + + + XXX + + + +
+ XXX + + + + XXX + + + +
diff --git a/doc/user/factories.in b/doc/user/factories.in index 6ef5249..d0c5313 100644 --- a/doc/user/factories.in +++ b/doc/user/factories.in @@ -298,9 +298,8 @@ Command("file.out", "file.in", [ - Copy("tempfile", "$SOURCE"), - "modify tempfile", - Move("$TARGET", "tempfile"), + Copy("$TARGET", "$SOURCE"), + Touch("$TARGET"), ]) @@ -310,9 +309,6 @@ SConscript('S') file.in - - touch $* -
diff --git a/doc/user/factories.sgml b/doc/user/factories.sgml deleted file mode 100644 index e0567f6..0000000 --- a/doc/user/factories.sgml +++ /dev/null @@ -1,427 +0,0 @@ - - - - - &SCons; provides a number of platform-independent functions, - called factories, - that perform common file system manipulations - like copying, moving or deleting files and directories, - or making directories. - These functions are factories - because they don't perform the action - at the time they're called, - they each return an &Action; object - that can be executed at the appropriate time. - - - -
- Copying Files or Directories: The &Copy; Factory - - - - Suppose you want to arrange to make a copy of a file, - and the &Install; builder isn't appropriate - because it may make a hard link on POSIX systems. - One way would be to use the &Copy; action factory - in conjunction with the &Command; builder: - - - - - Command("file.out", "file.in", Copy("$TARGET", "$SOURCE")) - - - - - Notice that the action returned by the &Copy; factory - will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings - at the time &file_out; is built, - and that the order of the arguments - is the same as that of a builder itself--that is, - target first, followed by source: - - - - - % scons -Q - Copy("file.out", "file.in") - - - - - You can, of course, name a file explicitly - instead of using &cv-TARGET; or &cv-SOURCE;: - - - - - Command("file.out", [], Copy("$TARGET", "file.in")) - - - - - Which executes as: - - - - - % scons -Q - Copy("file.out", "file.in") - - - - - The usefulness of the &Copy; factory - becomes more apparent when - you use it in a list of actions - passed to the &Command; builder. - For example, suppose you needed to run a - file through a utility that only modifies files in-place, - and can't "pipe" input to output. - One solution is to copy the source file - to a temporary file name, - run the utility, - and then copy the modified temporary file to the target, - which the &Copy; factory makes extremely easy: - - - - - Command("file.out", "file.in", - [ - Copy("tempfile", "$SOURCE"), - "modify tempfile", - Copy("$TARGET", "tempfile"), - ]) - - - - - The output then looks like: - - - - - % scons -Q - Copy("tempfile", "file.in") - modify tempfile - Copy("file.out", "tempfile") - - -
- -
- Deleting Files or Directories: The &Delete; Factory - - - - If you need to delete a file, - then the &Delete; factory - can be used in much the same way as - the &Copy; factory. - For example, if we want to make sure that - the temporary file - in our last example doesn't exist before - we copy to it, - we could add &Delete; to the beginning - of the command list: - - - - - Command("file.out", "file.in", - [ - Delete("tempfile"), - Copy("tempfile", "$SOURCE"), - "modify tempfile", - Copy("$TARGET", "tempfile"), - ]) - - - - - When then executes as follows: - - - - - % scons -Q - Delete("tempfile") - Copy("tempfile", "file.in") - modify tempfile - Copy("file.out", "tempfile") - - - - - Of course, like all of these &Action; factories, - the &Delete; factory also expands - &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately. - For example: - - - - - Command("file.out", "file.in", - [ - Delete("$TARGET"), - Copy("$TARGET", "$SOURCE") - ]) - - - - - Executes as: - - - - - % scons -Q - Delete("file.out") - Copy("file.out", "file.in") - - - - - (Note, however, that you typically don't need to - call the &Delete; factory explicitly in this way; - by default, &SCons; deletes its target(s) - for you before executing any action. - - - -
- -
- Moving (Renaming) Files or Directories: The &Move; Factory - - - - The &Move; factory - allows you to rename a file or directory. - For example, if we don't want to copy the temporary file, - we could: - - - - - Command("file.out", "file.in", - [ - Copy("tempfile", "$SOURCE"), - "modify tempfile", - Move("$TARGET", "tempfile"), - ]) - - - - - Which would execute as: - - - - - % scons -Q - Copy("tempfile", "file.in") - modify tempfile - Move("file.out", "tempfile") - - -
- -
- Updating the Modification Time of a File: The &Touch; Factory - - - - If you just need to update the - recorded modification time for a file, - use the &Touch; factory: - - - - - Command("file.out", "file.in", - [ - Copy("tempfile", "$SOURCE"), - "modify tempfile", - Move("$TARGET", "tempfile"), - ]) - - - - - Which executes as: - - - - - % scons -Q - Copy("tempfile", "file.in") - modify tempfile - Move("file.out", "tempfile") - - -
- -
- Creating a Directory: The &Mkdir; Factory - - - - If you need to create a directory, - use the &Mkdir; factory. - For example, if we need to process - a file in a temporary directory - in which the processing tool - will create other files that we don't care about, - you could: - - - - - Command("file.out", "file.in", - [ - Delete("tempdir"), - Mkdir("tempdir"), - Copy("tempdir/${SOURCE.file}", "$SOURCE"), - "process tempdir", - Move("$TARGET", "tempdir/output_file"), - Delete("tempdir"), - ]) - - - - - Which executes as: - - - - - % scons -Q - Delete("tempdir") - Mkdir("tempdir") - Copy("tempdir/file.in", "file.in") - process tempdir - Move("file.out", "tempdir/output_file") - scons: *** [file.out] No such file or directory - - -
- -
- Changing File or Directory Permissions: The &Chmod; Factory - - - - To change permissions on a file or directory, - use the &Chmod; factory. - The permission argument uses POSIX-style - permission bits and should typically - be expressed as an octal, - not decimal, number: - - - - - Command("file.out", "file.in", - [ - Copy("$TARGET", "$SOURCE"), - Chmod("$TARGET", 0755), - ]) - - - - - Which executes: - - - - - % scons -Q - Copy("file.out", "file.in") - Chmod("file.out", 0755) - - -
- -
- Executing an action immediately: the &Execute; Function - - - - We've been showing you how to use &Action; factories - in the &Command; function. - You can also execute an &Action; returned by a factory - (or actually, any &Action;) - at the time the &SConscript; file is read - by wrapping it up in the &Execute; function. - For example, if we need to make sure that - a directory exists before we build any targets, - - - - - - Execute(Mkdir('/tmp/my_temp_directory')) - - - - - Notice that this will - create the directory while - the &SConscript; file is being read: - - - - - % scons - scons: Reading SConscript files ... - Mkdir("/tmp/my_temp_directory") - scons: done reading SConscript files. - scons: Building targets ... - scons: `.' is up to date. - scons: done building targets. - - - - - If you're familiar with Python, - you may wonder why you would want to use this - instead of just calling the native Python - os.mkdir() function. - The advantage here is that the &Mkdir; - action will behave appropriately if the user - specifies the &SCons; or - options--that is, - it will print the action but not actually - make the directory when is specified, - or make the directory but not print the action - when is specified. - - - -
diff --git a/doc/user/factories.xml b/doc/user/factories.xml new file mode 100644 index 0000000..7c09e4b --- /dev/null +++ b/doc/user/factories.xml @@ -0,0 +1,425 @@ + + + + + &SCons; provides a number of platform-independent functions, + called factories, + that perform common file system manipulations + like copying, moving or deleting files and directories, + or making directories. + These functions are factories + because they don't perform the action + at the time they're called, + they each return an &Action; object + that can be executed at the appropriate time. + + + +
+ Copying Files or Directories: The &Copy; Factory + + + + Suppose you want to arrange to make a copy of a file, + and the &Install; builder isn't appropriate + because it may make a hard link on POSIX systems. + One way would be to use the &Copy; action factory + in conjunction with the &Command; builder: + + + + + Command("file.out", "file.in", Copy("$TARGET", "$SOURCE")) + + + + + Notice that the action returned by the &Copy; factory + will expand the &cv-link-TARGET; and &cv-link-SOURCE; strings + at the time &file_out; is built, + and that the order of the arguments + is the same as that of a builder itself--that is, + target first, followed by source: + + + + + % scons -Q + Copy("file.out", "file.in") + + + + + You can, of course, name a file explicitly + instead of using &cv-TARGET; or &cv-SOURCE;: + + + + + Command("file.out", [], Copy("$TARGET", "file.in")) + + + + + Which executes as: + + + + + % scons -Q + Copy("file.out", "file.in") + + + + + The usefulness of the &Copy; factory + becomes more apparent when + you use it in a list of actions + passed to the &Command; builder. + For example, suppose you needed to run a + file through a utility that only modifies files in-place, + and can't "pipe" input to output. + One solution is to copy the source file + to a temporary file name, + run the utility, + and then copy the modified temporary file to the target, + which the &Copy; factory makes extremely easy: + + + + + Command("file.out", "file.in", + [ + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Copy("$TARGET", "tempfile"), + ]) + + + + + The output then looks like: + + + + + % scons -Q + Copy("tempfile", "file.in") + modify tempfile + Copy("file.out", "tempfile") + + +
+ +
+ Deleting Files or Directories: The &Delete; Factory + + + + If you need to delete a file, + then the &Delete; factory + can be used in much the same way as + the &Copy; factory. + For example, if we want to make sure that + the temporary file + in our last example doesn't exist before + we copy to it, + we could add &Delete; to the beginning + of the command list: + + + + + Command("file.out", "file.in", + [ + Delete("tempfile"), + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Copy("$TARGET", "tempfile"), + ]) + + + + + When then executes as follows: + + + + + % scons -Q + Delete("tempfile") + Copy("tempfile", "file.in") + modify tempfile + Copy("file.out", "tempfile") + + + + + Of course, like all of these &Action; factories, + the &Delete; factory also expands + &cv-link-TARGET; and &cv-link-SOURCE; variables appropriately. + For example: + + + + + Command("file.out", "file.in", + [ + Delete("$TARGET"), + Copy("$TARGET", "$SOURCE") + ]) + + + + + Executes as: + + + + + % scons -Q + Delete("file.out") + Copy("file.out", "file.in") + + + + + (Note, however, that you typically don't need to + call the &Delete; factory explicitly in this way; + by default, &SCons; deletes its target(s) + for you before executing any action. + + + +
+ +
+ Moving (Renaming) Files or Directories: The &Move; Factory + + + + The &Move; factory + allows you to rename a file or directory. + For example, if we don't want to copy the temporary file, + we could: + + + + + Command("file.out", "file.in", + [ + Copy("tempfile", "$SOURCE"), + "modify tempfile", + Move("$TARGET", "tempfile"), + ]) + + + + + Which would execute as: + + + + + % scons -Q + Copy("tempfile", "file.in") + modify tempfile + Move("file.out", "tempfile") + + +
+ +
+ Updating the Modification Time of a File: The &Touch; Factory + + + + If you just need to update the + recorded modification time for a file, + use the &Touch; factory: + + + + + Command("file.out", "file.in", + [ + Copy("$TARGET", "$SOURCE"), + Touch("$TARGET"), + ]) + + + + + Which executes as: + + + + + % scons -Q + Copy("file.out", "file.in") + Touch("file.out") + + +
+ +
+ Creating a Directory: The &Mkdir; Factory + + + + If you need to create a directory, + use the &Mkdir; factory. + For example, if we need to process + a file in a temporary directory + in which the processing tool + will create other files that we don't care about, + you could: + + + + + Command("file.out", "file.in", + [ + Delete("tempdir"), + Mkdir("tempdir"), + Copy("tempdir/${SOURCE.file}", "$SOURCE"), + "process tempdir", + Move("$TARGET", "tempdir/output_file"), + Delete("tempdir"), + ]) + + + + + Which executes as: + + + + + % scons -Q + Delete("tempdir") + Mkdir("tempdir") + Copy("tempdir/file.in", "file.in") + process tempdir + Move("file.out", "tempdir/output_file") + scons: *** [file.out] No such file or directory + + +
+ +
+ Changing File or Directory Permissions: The &Chmod; Factory + + + + To change permissions on a file or directory, + use the &Chmod; factory. + The permission argument uses POSIX-style + permission bits and should typically + be expressed as an octal, + not decimal, number: + + + + + Command("file.out", "file.in", + [ + Copy("$TARGET", "$SOURCE"), + Chmod("$TARGET", 0755), + ]) + + + + + Which executes: + + + + + % scons -Q + Copy("file.out", "file.in") + Chmod("file.out", 0755) + + +
+ +
+ Executing an action immediately: the &Execute; Function + + + + We've been showing you how to use &Action; factories + in the &Command; function. + You can also execute an &Action; returned by a factory + (or actually, any &Action;) + at the time the &SConscript; file is read + by wrapping it up in the &Execute; function. + For example, if we need to make sure that + a directory exists before we build any targets, + + + + + + Execute(Mkdir('/tmp/my_temp_directory')) + + + + + Notice that this will + create the directory while + the &SConscript; file is being read: + + + + + % scons + scons: Reading SConscript files ... + Mkdir("/tmp/my_temp_directory") + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + + + + If you're familiar with Python, + you may wonder why you would want to use this + instead of just calling the native Python + os.mkdir() function. + The advantage here is that the &Mkdir; + action will behave appropriately if the user + specifies the &SCons; or + options--that is, + it will print the action but not actually + make the directory when is specified, + or make the directory but not print the action + when is specified. + + + +
diff --git a/doc/user/file-removal.sgml b/doc/user/file-removal.sgml deleted file mode 100644 index f64d394..0000000 --- a/doc/user/file-removal.sgml +++ /dev/null @@ -1,202 +0,0 @@ - - - - - There are two occasions when &SCons; will, - by default, remove target files. - The first is when &SCons; determines that - an target file needs to be rebuilt - and removes the existing version of the target - before executing - The second is when &SCons; is invoked with the - -c option to "clean" - a tree of its built targets. - - These behaviours can be suppressed with the - &Precious; and &NoClean; functions, respectively. - - - -
- Preventing target removal during build: the &Precious; Function - - - - By default, &SCons; removes targets before building them. - Sometimes, however, this is not what you want. - For example, you may want to update a library incrementally, - not by having it deleted and then rebuilt from all - of the constituent object files. - In such cases, you can use the - &Precious; method to prevent - &SCons; from removing the target before it is built: - - - - - env = Environment(RANLIBCOM='') - lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) - env.Precious(lib) - - - - - Although the output doesn't look any different, - &SCons; does not, in fact, - delete the target library before rebuilding it: - - - - - % scons -Q - cc -o f1.o -c f1.c - cc -o f2.o -c f2.c - cc -o f3.o -c f3.c - ar rc libfoo.a f1.o f2.o f3.o - - - - - &SCons; will, however, still delete files marked as &Precious; - when the -c option is used. - - - -
- -
- Preventing target removal during clean: the &NoClean; Function - - - - By default, &SCons; removes all built targets when invoked - with the -c option to clean a source tree - of built tragets. - Sometimes, however, this is not what you want. - For example, you may want to remove only intermediate generated files - (such as object files), - but leave the final targets - (the libraries) - untouched. - - In such cases, you can use the &NoClean; method to prevent &SCons; - from removing a target during a clean: - - - - - env = Environment(RANLIBCOM='') - lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) - env.NoClean(lib) - - - - - Notice that the libfoo.a - is not listed as a removed file: - - - - - % scons -Q - cc -o f1.o -c f1.c - cc -o f2.o -c f2.c - cc -o f3.o -c f3.c - ar rc libfoo.a f1.o f2.o f3.o - % scons -c - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Cleaning targets ... - Removed f1.o - Removed f2.o - Removed f3.o - scons: done cleaning targets. - - -
- -
- Removing additional files during clean: the &Clean; Function - - - - There may be additional files that you want removed - when the -c option is used, - but which &SCons; doesn't know about - because they're not normal target files. - For example, perhaps a command you invoke - creates a log file as - part of building the target file you want. - You would like the log file cleaned, - but you don't want to have to teach - SCons that the command - "builds" two files. - - - - - - You can use the &Clean; function to arrange for additional files - to be removed when the -c option is used. - Notice, however, that the &Clean; function takes two arguments, - and the second argument - is the name of the additional file you want cleaned - (foo.log in this example): - - - - - t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE') - Clean(t, 'foo.log') - - - - - The first argument is the target with which you want - the cleaning of this additional file associated. - In the above example, - we've used the return value from the - &Command; function, - which represents the - foo.out - target. - Now whenever the - foo.out target is cleaned - by the -c option, - the foo.log file - will be removed as well: - - - - - % scons -Q - build -o foo.out foo.in - % scons -Q -c - Removed foo.out - Removed foo.log - - -
diff --git a/doc/user/file-removal.xml b/doc/user/file-removal.xml new file mode 100644 index 0000000..f64d394 --- /dev/null +++ b/doc/user/file-removal.xml @@ -0,0 +1,202 @@ + + + + + There are two occasions when &SCons; will, + by default, remove target files. + The first is when &SCons; determines that + an target file needs to be rebuilt + and removes the existing version of the target + before executing + The second is when &SCons; is invoked with the + -c option to "clean" + a tree of its built targets. + + These behaviours can be suppressed with the + &Precious; and &NoClean; functions, respectively. + + + +
+ Preventing target removal during build: the &Precious; Function + + + + By default, &SCons; removes targets before building them. + Sometimes, however, this is not what you want. + For example, you may want to update a library incrementally, + not by having it deleted and then rebuilt from all + of the constituent object files. + In such cases, you can use the + &Precious; method to prevent + &SCons; from removing the target before it is built: + + + + + env = Environment(RANLIBCOM='') + lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Precious(lib) + + + + + Although the output doesn't look any different, + &SCons; does not, in fact, + delete the target library before rebuilding it: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o + + + + + &SCons; will, however, still delete files marked as &Precious; + when the -c option is used. + + + +
+ +
+ Preventing target removal during clean: the &NoClean; Function + + + + By default, &SCons; removes all built targets when invoked + with the -c option to clean a source tree + of built tragets. + Sometimes, however, this is not what you want. + For example, you may want to remove only intermediate generated files + (such as object files), + but leave the final targets + (the libraries) + untouched. + + In such cases, you can use the &NoClean; method to prevent &SCons; + from removing a target during a clean: + + + + + env = Environment(RANLIBCOM='') + lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.NoClean(lib) + + + + + Notice that the libfoo.a + is not listed as a removed file: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f2.o -c f2.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o + % scons -c + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Cleaning targets ... + Removed f1.o + Removed f2.o + Removed f3.o + scons: done cleaning targets. + + +
+ +
+ Removing additional files during clean: the &Clean; Function + + + + There may be additional files that you want removed + when the -c option is used, + but which &SCons; doesn't know about + because they're not normal target files. + For example, perhaps a command you invoke + creates a log file as + part of building the target file you want. + You would like the log file cleaned, + but you don't want to have to teach + SCons that the command + "builds" two files. + + + + + + You can use the &Clean; function to arrange for additional files + to be removed when the -c option is used. + Notice, however, that the &Clean; function takes two arguments, + and the second argument + is the name of the additional file you want cleaned + (foo.log in this example): + + + + + t = Command('foo.out', 'foo.in', 'build -o $TARGET $SOURCE') + Clean(t, 'foo.log') + + + + + The first argument is the target with which you want + the cleaning of this additional file associated. + In the above example, + we've used the return value from the + &Command; function, + which represents the + foo.out + target. + Now whenever the + foo.out target is cleaned + by the -c option, + the foo.log file + will be removed as well: + + + + + % scons -Q + build -o foo.out foo.in + % scons -Q -c + Removed foo.out + Removed foo.log + + +
diff --git a/doc/user/help.sgml b/doc/user/help.sgml deleted file mode 100644 index ca44a40..0000000 --- a/doc/user/help.sgml +++ /dev/null @@ -1,153 +0,0 @@ - - - - - It's often very useful to be able to give - users some help that describes the - specific targets, build options, etc., - that can be used for your build. - &SCons; provides the &Help; function - to allow you to specify this help text: - - - - - Help(""" - Type: 'scons program' to build the production program, - 'scons debug' to build the debug version. - """) - - - - - (Note the above use of the Python triple-quote syntax, - which comes in very handy for - specifying multi-line strings like help text.) - - - - - - When the &SConstruct; or &SConscript; files - contain such a call to the &Help; function, - the specified help text will be displayed in response to - the &SCons; -h option: - - - - - % scons -h - scons: Reading SConscript files ... - scons: done reading SConscript files. - - Type: 'scons program' to build the production program, - 'scons debug' to build the debug version. - - Use scons -H for help about command-line options. - - - - - The &SConscript; files may contain - multiple calls to the &Help; function, - in which case the specified text(s) - will be concatenated when displayed. - This allows you to split up the - help text across multiple &SConscript; files. - In this situation, the order in - which the &SConscript; files are called - will determine the order in which the &Help; functions are called, - which will determine the order in which - the various bits of text will get concatenated. - - - - - - Another use would be to make the help text conditional - on some variable. - For example, suppose you only want to display - a line about building a Windows-only - version of a program when actually - run on Windows. - The following &SConstruct; file: - - - - - env = Environment() - - Help("\nType: 'scons program' to build the production program.\n") - - if env['PLATFORM'] == 'win32': - Help("\nType: 'scons windebug' to build the Windows debug version.\n") - - - - - Will display the completely help text on Windows: - - - - - C:\>scons -h - scons: Reading SConscript files ... - scons: done reading SConscript files. - - Type: 'scons program' to build the production program. - - Type: 'scons windebug' to build the Windows debug version. - - Use scons -H for help about command-line options. - - - - - But only show the relevant option on a Linux or UNIX system: - - - - - % scons -h - scons: Reading SConscript files ... - scons: done reading SConscript files. - - Type: 'scons program' to build the production program. - - Use scons -H for help about command-line options. - - - - - If there is no &Help; text in the &SConstruct; or - &SConscript; files, - &SCons; will revert to displaying its - standard list that describes the &SCons; command-line - options. - This list is also always displayed whenever - the -H option is used. - - diff --git a/doc/user/help.xml b/doc/user/help.xml new file mode 100644 index 0000000..ca44a40 --- /dev/null +++ b/doc/user/help.xml @@ -0,0 +1,153 @@ + + + + + It's often very useful to be able to give + users some help that describes the + specific targets, build options, etc., + that can be used for your build. + &SCons; provides the &Help; function + to allow you to specify this help text: + + + + + Help(""" + Type: 'scons program' to build the production program, + 'scons debug' to build the debug version. + """) + + + + + (Note the above use of the Python triple-quote syntax, + which comes in very handy for + specifying multi-line strings like help text.) + + + + + + When the &SConstruct; or &SConscript; files + contain such a call to the &Help; function, + the specified help text will be displayed in response to + the &SCons; -h option: + + + + + % scons -h + scons: Reading SConscript files ... + scons: done reading SConscript files. + + Type: 'scons program' to build the production program, + 'scons debug' to build the debug version. + + Use scons -H for help about command-line options. + + + + + The &SConscript; files may contain + multiple calls to the &Help; function, + in which case the specified text(s) + will be concatenated when displayed. + This allows you to split up the + help text across multiple &SConscript; files. + In this situation, the order in + which the &SConscript; files are called + will determine the order in which the &Help; functions are called, + which will determine the order in which + the various bits of text will get concatenated. + + + + + + Another use would be to make the help text conditional + on some variable. + For example, suppose you only want to display + a line about building a Windows-only + version of a program when actually + run on Windows. + The following &SConstruct; file: + + + + + env = Environment() + + Help("\nType: 'scons program' to build the production program.\n") + + if env['PLATFORM'] == 'win32': + Help("\nType: 'scons windebug' to build the Windows debug version.\n") + + + + + Will display the completely help text on Windows: + + + + + C:\>scons -h + scons: Reading SConscript files ... + scons: done reading SConscript files. + + Type: 'scons program' to build the production program. + + Type: 'scons windebug' to build the Windows debug version. + + Use scons -H for help about command-line options. + + + + + But only show the relevant option on a Linux or UNIX system: + + + + + % scons -h + scons: Reading SConscript files ... + scons: done reading SConscript files. + + Type: 'scons program' to build the production program. + + Use scons -H for help about command-line options. + + + + + If there is no &Help; text in the &SConstruct; or + &SConscript; files, + &SCons; will revert to displaying its + standard list that describes the &SCons; command-line + options. + This list is also always displayed whenever + the -H option is used. + + diff --git a/doc/user/hierarchy.in b/doc/user/hierarchy.in index d93e811..e0d6b00 100644 --- a/doc/user/hierarchy.in +++ b/doc/user/hierarchy.in @@ -418,7 +418,7 @@ make no difference to the build. (Notice that the lib/foo1.o object file is built in the same directory as its source file. - See , below, + See , below, for information about how to build the object file in a different subdirectory.) @@ -470,7 +470,7 @@ make no difference to the build. (As was the case with top-relative path names, notice that the /usr/joe/lib/foo1.o object file is built in the same directory as its source file. - See , below, + See , below, for information about how to build the object file in a different subdirectory.) diff --git a/doc/user/hierarchy.sgml b/doc/user/hierarchy.sgml deleted file mode 100644 index 713d605..0000000 --- a/doc/user/hierarchy.sgml +++ /dev/null @@ -1,742 +0,0 @@ - - - - - - - The source code for large software projects - rarely stays in a single directory, - but is nearly always divided into a - hierarchy of directories. - Organizing a large software build using &SCons; - involves creating a hierarchy of build scripts - using the &SConscript; function. - - - -
- &SConscript; Files - - - - As we've already seen, - the build script at the top of the tree is called &SConstruct;. - The top-level &SConstruct; file can - use the &SConscript; function to - include other subsidiary scripts in the build. - These subsidiary scripts can, in turn, - use the &SConscript; function - to include still other scripts in the build. - By convention, these subsidiary scripts are usually - named &SConscript;. - For example, a top-level &SConstruct; file might - arrange for four subsidiary scripts to be included - in the build as follows: - - - - - SConscript(['drivers/display/SConscript', - 'drivers/mouse/SConscript', - 'parser/SConscript', - 'utilities/SConscript']) - - - - - In this case, the &SConstruct; file - lists all of the &SConscript; files in the build explicitly. - (Note, however, that not every directory in the tree - necessarily has an &SConscript; file.) - Alternatively, the drivers - subdirectory might contain an intermediate - &SConscript; file, - in which case the &SConscript; call in - the top-level &SConstruct; file - would look like: - - - - - SConscript(['drivers/SConscript', - 'parser/SConscript', - 'utilities/SConscript']) - - - - - And the subsidiary &SConscript; file in the - drivers subdirectory - would look like: - - - - - SConscript(['display/SConscript', - 'mouse/SConscript']) - - - - - Whether you list all of the &SConscript; files in the - top-level &SConstruct; file, - or place a subsidiary &SConscript; file in - intervening directories, - or use some mix of the two schemes, - is up to you and the needs of your software. - - - -
- -
- Path Names Are Relative to the &SConscript; Directory - - - - Subsidiary &SConscript; files make it easy to create a build - hierarchy because all of the file and directory names - in a subsidiary &SConscript; files are interpreted - relative to the directory in which the &SConscript; file lives. - Typically, this allows the &SConscript; file containing the - instructions to build a target file - to live in the same directory as the source files - from which the target will be built, - making it easy to update how the software is built - whenever files are added or deleted - (or other changes are made). - - - - - - For example, suppose we want to build two programs - &prog1; and &prog2; in two separate directories - with the same names as the programs. - One typical way to do this would be - with a top-level &SConstruct; file like this: - - - - - SConscript(['prog1/SConscript', - 'prog2/SConscript']) - - - - - And subsidiary &SConscript; files that look like this: - - - - - - env = Environment() - env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c']) - - - - - And this: - - - - - - env = Environment() - env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c']) - - - - - Then, when we run &SCons; in the top-level directory, - our build looks like: - - - - - % scons -Q - 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 -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 - - - - - Notice the following: - - First, you can have files with the same names - in multiple directories, like main.c in the above example. - - Second, unlike standard recursive use of &Make;, - &SCons; stays in the top-level directory - (where the &SConstruct; file lives) - and issues commands that use the path names - from the top-level directory to the - target and source files within the hierarchy. - - - -
- -
- Top-Level Path Names in Subsidiary &SConscript; Files - - - - If you need to use a file from another directory, - it's sometimes more convenient to specify - the path to a file in another directory - from the top-level &SConstruct; directory, - even when you're using that file in - a subsidiary &SConscript; file in a subdirectory. - You can tell &SCons; to interpret a path name - as relative to the top-level &SConstruct; directory, - not the local directory of the &SConscript; file, - by appending a &hash; (hash mark) - to the beginning of the path name: - - - - - env = Environment() - env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c']) - - - - - In this example, - the lib directory is - directly underneath the top-level &SConstruct; directory. - If the above &SConscript; file is in a subdirectory - named src/prog, - the output would look like: - - - - - % scons -Q - 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 - - - - - (Notice that the lib/foo1.o object file - is built in the same directory as its source file. - See , below, - for information about - how to build the object file in a different subdirectory.) - - - -
- -
- Absolute Path Names - - - - Of course, you can always specify - an absolute path name for a file--for example: - - - - - env = Environment() - env.Program('prog', ['main.c', '/usr/joe/lib/foo1.c', 'foo2.c']) - - - - - Which, when executed, would yield: - - - - - % scons -Q - 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 - - - - - (As was the case with top-relative path names, - notice that the /usr/joe/lib/foo1.o object file - is built in the same directory as its source file. - See , below, - for information about - how to build the object file in a different subdirectory.) - - - -
- -
- Sharing Environments (and Other Variables) Between &SConscript; Files - - - - In the previous example, - each of the subsidiary &SConscript; files - created its own construction environment - by calling &Environment; separately. - This obviously works fine, - but if each program must be built - with the same construction variables, - it's cumbersome and error-prone to initialize - separate construction environments - in the same way over and over in each subsidiary - &SConscript; file. - - - - - - &SCons; supports the ability to export variables - from a parent &SConscript; file - to its subsidiary &SConscript; files, - which allows you to share common initialized - values throughout your build hierarchy. - - - -
- Exporting Variables - - - - There are two ways to export a variable, - such as a construction environment, - from an &SConscript; file, - so that it may be used by other &SConscript; files. - First, you can call the &Export; - function with a list of variables, - or a string white-space separated variable names. - Each call to &Export; adds one - or more variables to a global list - of variables that are available for import - by other &SConscript; files. - - - - - env = Environment() - Export('env') - - - - - You may export more than one variable name at a time: - - - - - env = Environment() - debug = ARGUMENTS['debug'] - Export('env', 'debug') - - - - - Because white space is not legal in Python variable names, - the &Export; function will even automatically split - a string into separate names for you: - - - - - Export('env debug') - - - - - Second, you can specify a list of - variables to export as a second argument - to the &SConscript; function call: - - - - - SConscript('src/SConscript', 'env') - - - - - Or as the &exports; keyword argument: - - - - - SConscript('src/SConscript', exports='env') - - - - - These calls export the specified variables - to only the listed &SConscript; files. - You may, however, specify more than one - &SConscript; file in a list: - - - - - SConscript(['src1/SConscript', - 'src2/SConscript'], exports='env') - - - - - This is functionally equivalent to - calling the &SConscript; function - multiple times with the same &exports; argument, - one per &SConscript; file. - - - -
- -
- Importing Variables - - - - Once a variable has been exported from a calling - &SConscript; file, - it may be used in other &SConscript; files - by calling the &Import; function: - - - - - Import('env') - env.Program('prog', ['prog.c']) - - - - - The &Import; call makes the env construction - environment available to the &SConscript; file, - after which the variable can be used to build - programs, libraries, etc. - - - - - - Like the &Export; function, - the &Import; function can be used - with multiple variable names: - - - - - Import('env', 'debug') - env = env.Clone(DEBUG = debug) - env.Program('prog', ['prog.c']) - - - - - And the &Import; function will similarly - split a string along white-space - into separate variable names: - - - - - Import('env debug') - env = env.Clone(DEBUG = debug) - env.Program('prog', ['prog.c']) - - - - - Lastly, as a special case, - you may import all of the variables that - have been exported by supplying an asterisk - to the &Import; function: - - - - - Import('*') - env = env.Clone(DEBUG = debug) - env.Program('prog', ['prog.c']) - - - - - If you're dealing with a lot of &SConscript; files, - this can be a lot simpler than keeping - arbitrary lists of imported variables in each file. - - - -
- -
- Returning Values From an &SConscript; File - - - - Sometimes, you would like to be able to - use information from a subsidiary - &SConscript; file in some way. - For example, - suppose that you want to create one - library from source files - scattered throughout a number - of subsidiary &SConscript; files. - You can do this by using the &Return; - function to return values - from the subsidiary &SConscript; files - to the calling file. - - - - - - If, for example, we have two subdirectories - &foo; and &bar; - that should each contribute a source - file to a Library, - what we'd like to be able to do is - collect the object files - from the subsidiary &SConscript; calls - like this: - - - - - env = Environment() - Export('env') - objs = [] - for subdir in ['foo', 'bar']: - o = SConscript('%s/SConscript' % subdir) - objs.append(o) - env.Library('prog', objs) - - - - - We can do this by using the &Return; - function in the - foo/SConscript file like this: - - - - - - Import('env') - obj = env.Object('foo.c') - Return('obj') - - - - - (The corresponding - bar/SConscript - file should be pretty obvious.) - Then when we run &SCons;, - the object files from the subsidiary subdirectories - are all correctly archived in the desired library: - - - - - % scons -Q - 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/hierarchy.xml b/doc/user/hierarchy.xml new file mode 100644 index 0000000..4c84d5b --- /dev/null +++ b/doc/user/hierarchy.xml @@ -0,0 +1,742 @@ + + + + + + + The source code for large software projects + rarely stays in a single directory, + but is nearly always divided into a + hierarchy of directories. + Organizing a large software build using &SCons; + involves creating a hierarchy of build scripts + using the &SConscript; function. + + + +
+ &SConscript; Files + + + + As we've already seen, + the build script at the top of the tree is called &SConstruct;. + The top-level &SConstruct; file can + use the &SConscript; function to + include other subsidiary scripts in the build. + These subsidiary scripts can, in turn, + use the &SConscript; function + to include still other scripts in the build. + By convention, these subsidiary scripts are usually + named &SConscript;. + For example, a top-level &SConstruct; file might + arrange for four subsidiary scripts to be included + in the build as follows: + + + + + SConscript(['drivers/display/SConscript', + 'drivers/mouse/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + + + + + In this case, the &SConstruct; file + lists all of the &SConscript; files in the build explicitly. + (Note, however, that not every directory in the tree + necessarily has an &SConscript; file.) + Alternatively, the drivers + subdirectory might contain an intermediate + &SConscript; file, + in which case the &SConscript; call in + the top-level &SConstruct; file + would look like: + + + + + SConscript(['drivers/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + + + + + And the subsidiary &SConscript; file in the + drivers subdirectory + would look like: + + + + + SConscript(['display/SConscript', + 'mouse/SConscript']) + + + + + Whether you list all of the &SConscript; files in the + top-level &SConstruct; file, + or place a subsidiary &SConscript; file in + intervening directories, + or use some mix of the two schemes, + is up to you and the needs of your software. + + + +
+ +
+ Path Names Are Relative to the &SConscript; Directory + + + + Subsidiary &SConscript; files make it easy to create a build + hierarchy because all of the file and directory names + in a subsidiary &SConscript; files are interpreted + relative to the directory in which the &SConscript; file lives. + Typically, this allows the &SConscript; file containing the + instructions to build a target file + to live in the same directory as the source files + from which the target will be built, + making it easy to update how the software is built + whenever files are added or deleted + (or other changes are made). + + + + + + For example, suppose we want to build two programs + &prog1; and &prog2; in two separate directories + with the same names as the programs. + One typical way to do this would be + with a top-level &SConstruct; file like this: + + + + + SConscript(['prog1/SConscript', + 'prog2/SConscript']) + + + + + And subsidiary &SConscript; files that look like this: + + + + + + env = Environment() + env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c']) + + + + + And this: + + + + + + env = Environment() + env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c']) + + + + + Then, when we run &SCons; in the top-level directory, + our build looks like: + + + + + % scons -Q + 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 -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 + + + + + Notice the following: + + First, you can have files with the same names + in multiple directories, like main.c in the above example. + + Second, unlike standard recursive use of &Make;, + &SCons; stays in the top-level directory + (where the &SConstruct; file lives) + and issues commands that use the path names + from the top-level directory to the + target and source files within the hierarchy. + + + +
+ +
+ Top-Level Path Names in Subsidiary &SConscript; Files + + + + If you need to use a file from another directory, + it's sometimes more convenient to specify + the path to a file in another directory + from the top-level &SConstruct; directory, + even when you're using that file in + a subsidiary &SConscript; file in a subdirectory. + You can tell &SCons; to interpret a path name + as relative to the top-level &SConstruct; directory, + not the local directory of the &SConscript; file, + by appending a &hash; (hash mark) + to the beginning of the path name: + + + + + env = Environment() + env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c']) + + + + + In this example, + the lib directory is + directly underneath the top-level &SConstruct; directory. + If the above &SConscript; file is in a subdirectory + named src/prog, + the output would look like: + + + + + % scons -Q + 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 + + + + + (Notice that the lib/foo1.o object file + is built in the same directory as its source file. + See , below, + for information about + how to build the object file in a different subdirectory.) + + + +
+ +
+ Absolute Path Names + + + + Of course, you can always specify + an absolute path name for a file--for example: + + + + + env = Environment() + env.Program('prog', ['main.c', '/usr/joe/lib/foo1.c', 'foo2.c']) + + + + + Which, when executed, would yield: + + + + + % scons -Q + 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 + + + + + (As was the case with top-relative path names, + notice that the /usr/joe/lib/foo1.o object file + is built in the same directory as its source file. + See , below, + for information about + how to build the object file in a different subdirectory.) + + + +
+ +
+ Sharing Environments (and Other Variables) Between &SConscript; Files + + + + In the previous example, + each of the subsidiary &SConscript; files + created its own construction environment + by calling &Environment; separately. + This obviously works fine, + but if each program must be built + with the same construction variables, + it's cumbersome and error-prone to initialize + separate construction environments + in the same way over and over in each subsidiary + &SConscript; file. + + + + + + &SCons; supports the ability to export variables + from a parent &SConscript; file + to its subsidiary &SConscript; files, + which allows you to share common initialized + values throughout your build hierarchy. + + + +
+ Exporting Variables + + + + There are two ways to export a variable, + such as a construction environment, + from an &SConscript; file, + so that it may be used by other &SConscript; files. + First, you can call the &Export; + function with a list of variables, + or a string white-space separated variable names. + Each call to &Export; adds one + or more variables to a global list + of variables that are available for import + by other &SConscript; files. + + + + + env = Environment() + Export('env') + + + + + You may export more than one variable name at a time: + + + + + env = Environment() + debug = ARGUMENTS['debug'] + Export('env', 'debug') + + + + + Because white space is not legal in Python variable names, + the &Export; function will even automatically split + a string into separate names for you: + + + + + Export('env debug') + + + + + Second, you can specify a list of + variables to export as a second argument + to the &SConscript; function call: + + + + + SConscript('src/SConscript', 'env') + + + + + Or as the &exports; keyword argument: + + + + + SConscript('src/SConscript', exports='env') + + + + + These calls export the specified variables + to only the listed &SConscript; files. + You may, however, specify more than one + &SConscript; file in a list: + + + + + SConscript(['src1/SConscript', + 'src2/SConscript'], exports='env') + + + + + This is functionally equivalent to + calling the &SConscript; function + multiple times with the same &exports; argument, + one per &SConscript; file. + + + +
+ +
+ Importing Variables + + + + Once a variable has been exported from a calling + &SConscript; file, + it may be used in other &SConscript; files + by calling the &Import; function: + + + + + Import('env') + env.Program('prog', ['prog.c']) + + + + + The &Import; call makes the env construction + environment available to the &SConscript; file, + after which the variable can be used to build + programs, libraries, etc. + + + + + + Like the &Export; function, + the &Import; function can be used + with multiple variable names: + + + + + Import('env', 'debug') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + And the &Import; function will similarly + split a string along white-space + into separate variable names: + + + + + Import('env debug') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + Lastly, as a special case, + you may import all of the variables that + have been exported by supplying an asterisk + to the &Import; function: + + + + + Import('*') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + + + + + If you're dealing with a lot of &SConscript; files, + this can be a lot simpler than keeping + arbitrary lists of imported variables in each file. + + + +
+ +
+ Returning Values From an &SConscript; File + + + + Sometimes, you would like to be able to + use information from a subsidiary + &SConscript; file in some way. + For example, + suppose that you want to create one + library from source files + scattered throughout a number + of subsidiary &SConscript; files. + You can do this by using the &Return; + function to return values + from the subsidiary &SConscript; files + to the calling file. + + + + + + If, for example, we have two subdirectories + &foo; and &bar; + that should each contribute a source + file to a Library, + what we'd like to be able to do is + collect the object files + from the subsidiary &SConscript; calls + like this: + + + + + env = Environment() + Export('env') + objs = [] + for subdir in ['foo', 'bar']: + o = SConscript('%s/SConscript' % subdir) + objs.append(o) + env.Library('prog', objs) + + + + + We can do this by using the &Return; + function in the + foo/SConscript file like this: + + + + + + Import('env') + obj = env.Object('foo.c') + Return('obj') + + + + + (The corresponding + bar/SConscript + file should be pretty obvious.) + Then when we run &SCons;, + the object files from the subsidiary subdirectories + are all correctly archived in the desired library: + + + + + % scons -Q + 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 deleted file mode 100644 index 2a6d1b8..0000000 --- a/doc/user/install.sgml +++ /dev/null @@ -1,237 +0,0 @@ - - - - - Once a program is built, - it is often appropriate to install it in another - directory for public use. - You use the &Install; method - to arrange for a program, or any other file, - to be copied into a destination directory: - - - - - env = Environment() - hello = env.Program('hello.c') - env.Install('/usr/bin', hello) - - - - - Note, however, that installing a file is - still considered a type of file "build." - This is important when you remember that - the default behavior of &SCons; is - to build files in or below the current directory. - If, as in the example above, - you are installing files in a directory - outside of the top-level &SConstruct; file's directory tree, - you must specify that directory - (or a higher directory, such as /) - for it to install anything there: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q /usr/bin - Install file: "hello" as "/usr/bin/hello" - - - - - It can, however, be cumbersome to remember - (and type) the specific destination directory - in which the program (or any other file) - should be installed. - This is an area where the &Alias; - function comes in handy, - allowing you, for example, - to create a pseudo-target named install - that can expand to the specified destination directory: - - - - - env = Environment() - hello = env.Program('hello.c') - env.Install('/usr/bin', hello) - env.Alias('install', '/usr/bin') - - - - - This then yields the more natural - ability to install the program - in its destination as follows: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - % scons -Q install - Install file: "hello" as "/usr/bin/hello" - - -
- Installing Multiple Files in a Directory - - - - You can install multiple files into a directory - simply by calling the &Install; function multiple times: - - - - - env = Environment() - hello = env.Program('hello.c') - goodbye = env.Program('goodbye.c') - env.Install('/usr/bin', hello) - env.Install('/usr/bin', goodbye) - env.Alias('install', '/usr/bin') - - - - - Or, more succinctly, listing the multiple input - files in a list - (just like you can do with any other builder): - - - - - env = Environment() - hello = env.Program('hello.c') - goodbye = env.Program('goodbye.c') - env.Install('/usr/bin', [hello, goodbye]) - env.Alias('install', '/usr/bin') - - - - - Either of these two examples yields: - - - - - % scons -Q install - cc -o goodbye.o -c goodbye.c - cc -o goodbye goodbye.o - Install file: "goodbye" as "/usr/bin/goodbye" - cc -o hello.o -c hello.c - cc -o hello hello.o - Install file: "hello" as "/usr/bin/hello" - - -
- -
- Installing a File Under a Different Name - - - - The &Install; method preserves the name - of the file when it is copied into the - destination directory. - If you need to change the name of the file - when you copy it, use the &InstallAs; function: - - - - - env = Environment() - hello = env.Program('hello.c') - env.InstallAs('/usr/bin/hello-new', hello) - env.Alias('install', '/usr/bin') - - - - - This installs the hello - program with the name hello-new - as follows: - - - - - % scons -Q install - cc -o hello.o -c hello.c - cc -o hello hello.o - Install file: "hello" as "/usr/bin/hello-new" - - -
- -
- Installing Multiple Files Under Different Names - - - - Lastly, if you have multiple files that all - need to be installed with different file names, - you can either call the &InstallAs; function - multiple times, or as a shorthand, - you can supply same-length lists - for the both the target and source arguments: - - - - - env = Environment() - hello = env.Program('hello.c') - goodbye = env.Program('goodbye.c') - env.InstallAs(['/usr/bin/hello-new', - '/usr/bin/goodbye-new'], - [hello, goodbye]) - env.Alias('install', '/usr/bin') - - - - - In this case, the &InstallAs; function - loops through both lists simultaneously, - and copies each source file into its corresponding - target file name: - - - - - % scons -Q install - cc -o goodbye.o -c goodbye.c - cc -o goodbye goodbye.o - Install file: "goodbye" as "/usr/bin/goodbye-new" - 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/install.xml b/doc/user/install.xml new file mode 100644 index 0000000..2a6d1b8 --- /dev/null +++ b/doc/user/install.xml @@ -0,0 +1,237 @@ + + + + + Once a program is built, + it is often appropriate to install it in another + directory for public use. + You use the &Install; method + to arrange for a program, or any other file, + to be copied into a destination directory: + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('/usr/bin', hello) + + + + + Note, however, that installing a file is + still considered a type of file "build." + This is important when you remember that + the default behavior of &SCons; is + to build files in or below the current directory. + If, as in the example above, + you are installing files in a directory + outside of the top-level &SConstruct; file's directory tree, + you must specify that directory + (or a higher directory, such as /) + for it to install anything there: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q /usr/bin + Install file: "hello" as "/usr/bin/hello" + + + + + It can, however, be cumbersome to remember + (and type) the specific destination directory + in which the program (or any other file) + should be installed. + This is an area where the &Alias; + function comes in handy, + allowing you, for example, + to create a pseudo-target named install + that can expand to the specified destination directory: + + + + + env = Environment() + hello = env.Program('hello.c') + env.Install('/usr/bin', hello) + env.Alias('install', '/usr/bin') + + + + + This then yields the more natural + ability to install the program + in its destination as follows: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + % scons -Q install + Install file: "hello" as "/usr/bin/hello" + + +
+ Installing Multiple Files in a Directory + + + + You can install multiple files into a directory + simply by calling the &Install; function multiple times: + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.Install('/usr/bin', hello) + env.Install('/usr/bin', goodbye) + env.Alias('install', '/usr/bin') + + + + + Or, more succinctly, listing the multiple input + files in a list + (just like you can do with any other builder): + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.Install('/usr/bin', [hello, goodbye]) + env.Alias('install', '/usr/bin') + + + + + Either of these two examples yields: + + + + + % scons -Q install + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + Install file: "goodbye" as "/usr/bin/goodbye" + cc -o hello.o -c hello.c + cc -o hello hello.o + Install file: "hello" as "/usr/bin/hello" + + +
+ +
+ Installing a File Under a Different Name + + + + The &Install; method preserves the name + of the file when it is copied into the + destination directory. + If you need to change the name of the file + when you copy it, use the &InstallAs; function: + + + + + env = Environment() + hello = env.Program('hello.c') + env.InstallAs('/usr/bin/hello-new', hello) + env.Alias('install', '/usr/bin') + + + + + This installs the hello + program with the name hello-new + as follows: + + + + + % scons -Q install + cc -o hello.o -c hello.c + cc -o hello hello.o + Install file: "hello" as "/usr/bin/hello-new" + + +
+ +
+ Installing Multiple Files Under Different Names + + + + Lastly, if you have multiple files that all + need to be installed with different file names, + you can either call the &InstallAs; function + multiple times, or as a shorthand, + you can supply same-length lists + for the both the target and source arguments: + + + + + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.InstallAs(['/usr/bin/hello-new', + '/usr/bin/goodbye-new'], + [hello, goodbye]) + env.Alias('install', '/usr/bin') + + + + + In this case, the &InstallAs; function + loops through both lists simultaneously, + and copies each source file into its corresponding + target file name: + + + + + % scons -Q install + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + Install file: "goodbye" as "/usr/bin/goodbye-new" + 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/java.sgml b/doc/user/java.sgml deleted file mode 100644 index 1876916..0000000 --- a/doc/user/java.sgml +++ /dev/null @@ -1,413 +0,0 @@ - - - - - So far, we've been using examples of - building C and C++ programs - to demonstrate the features of &SCons;. - &SCons; also supports building Java programs, - but Java builds are handled slightly differently, - which reflects the ways in which - the Java compiler and tools - build programs differently than - other languages' tool chains. - - - -
- Building Java Class Files: the &b-Java; Builder - - - - The basic activity when programming in Java, - of course, is to take one or more .java files - containing Java source code - and to call the Java compiler - to turn them into one or more - .class files. - In &SCons;, you do this - by giving the &b-link-Java; Builder - a target directory in which - to put the .class files, - and a source directory that contains - the .java files: - - - - - Java('classes', 'src') - - - - - If the src directory contains - three .java source files, - then running &SCons; might look like this: - - - - - % scons -Q - javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java - - - - - &SCons; will actually search the src - directory tree for all of the .java files. - The Java compiler will then create the - necessary class files in the classes subdirectory, - based on the class names found in the .java files. - - - -
- -
- How &SCons; Handles Java Dependencies - - - - In addition to searching the source directory for - .java files, - &SCons; actually runs the .java files - through a stripped-down Java parser that figures out - what classes are defined. - In other words, &SCons; knows, - without you having to tell it, - what .class files - will be produced by the &javac; call. - So our one-liner example from the preceding section: - - - - - Java('classes', 'src') - - - - - Will not only tell you reliably - that the .class files - in the classes subdirectory - are up-to-date: - - - - - % scons -Q - javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java - % scons -Q classes - scons: `classes' is up to date. - - - - - But it will also remove all of the generated - .class files, - even for inner classes, - without you having to specify them manually. - For example, if our - Example1.java - and - Example3.java - files both define additional classes, - and the class defined in Example2.java - has an inner class, - running scons -c - will clean up all of those .class files - as well: - - - - - % scons -Q - javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java - % scons -Q -c classes - Removed classes/Example1.class - Removed classes/AdditionalClass1.class - Removed classes/Example2$Inner2.class - Removed classes/Example2.class - Removed classes/Example3.class - Removed classes/AdditionalClass3.class - - -
- -
- Building Java Archive (<filename>.jar</filename>) Files: the &b-Jar; Builder - - - - After building the class files, - it's common to collect them into - a Java archive (.jar) file, - which you do by calling the &b-link-Jar; Builder method. - If you want to just collect all of the - class files within a subdirectory, - you can just specify that subdirectory - as the &b-Jar; source: - - - - - Java(target = 'classes', source = 'src') - Jar(target = 'test.jar', source = 'classes') - - - - - &SCons; will then pass that directory - to the &jar; command, - which will collect all of the underlying - .class files: - - - - - % scons -Q - javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java - jar cf test.jar classes - - - - - If you want to keep all of the - .class files - for multiple programs in one location, - and only archive some of them in - each .jar file, - you can pass the &b-Jar; builder a - list of files as its source. - It's extremely simple to create multiple - .jar files this way, - using the lists of target class files created - by calls to the &b-link-Java; builder - as sources to the various &b-Jar; calls: - - - - - prog1_class_files = Java(target = 'classes', source = 'prog1') - prog2_class_files = Java(target = 'classes', source = 'prog2') - Jar(target = 'prog1.jar', source = prog1_class_files) - Jar(target = 'prog2.jar', source = prog2_class_files) - - - - - This will then create - prog1.jar - and prog2.jar - next to the subdirectories - that contain their .java files: - - - - - % scons -Q - javac -d classes -sourcepath prog1 prog1/Example1.java prog1/Example2.java - javac -d classes -sourcepath prog2 prog2/Example3.java prog2/Example4.java - jar cf prog1.jar classes/Example1.class classes/Example2.class - jar cf prog2.jar classes/Example3.class classes/Example4.class - - -
- -
- Building C Header and Stub Files: the &b-JavaH; Builder - - - - You can generate C header and source files - for implementing native methods, - by using the &b-link-JavaH; Builder. - There are several ways of using the &JavaH; Builder. - One typical invocation might look like: - - - - - classes = Java(target = 'classes', source = 'src/pkg/sub') - JavaH(target = 'native', source = classes) - - - - - The source is a list of class files generated by the - call to the &b-link-Java; Builder, - and the target is the output directory in - which we want the C header files placed. - The target - gets converted into the - when &SCons; runs &javah;: - - - - - % scons -Q - javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java - javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 - - - - - In this case, - the call to &javah; - will generate the header files - native/pkg_sub_Example1.h, - native/pkg_sub_Example2.h - and - native/pkg_sub_Example3.h. - Notice that &SCons; remembered that the class - files were generated with a target directory of - classes, - and that it then specified that target directory - as the option - to the call to &javah;. - - - - - - Although it's more convenient to use - the list of class files returned by - the &b-Java; Builder - as the source of a call to the &b-JavaH; Builder, - you can - specify the list of class files - by hand, if you prefer. - If you do, - you need to set the - &cv-link-JAVACLASSDIR; construction variable - when calling &b-JavaH;: - - - - - Java(target = 'classes', source = 'src/pkg/sub') - class_file_list = ['classes/pkg/sub/Example1.class', - 'classes/pkg/sub/Example2.class', - 'classes/pkg/sub/Example3.class'] - JavaH(target = 'native', source = class_file_list, JAVACLASSDIR = 'classes') - - - - - The &cv-JAVACLASSDIR; value then - gets converted into the - when &SCons; runs &javah;: - - - - - % scons -Q - javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java - javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 - - - - - Lastly, if you don't want a separate header file - generated for each source file, - you can specify an explicit File Node - as the target of the &b-JavaH; Builder: - - - - - classes = Java(target = 'classes', source = 'src/pkg/sub') - JavaH(target = File('native.h'), source = classes) - - - - - Because &SCons; assumes by default - that the target of the &b-JavaH; builder is a directory, - you need to use the &File; function - to make sure that &SCons; doesn't - create a directory named native.h. - When a file is used, though, - &SCons; correctly converts the file name - into the &javah; option: - - - - - % scons -Q - javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java - javah -o native.h -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 - - -
- -
- Building RMI Stub and Skeleton Class Files: the &b-RMIC; Builder - - - - You can generate Remote Method Invocation stubs - by using the &b-link-RMIC; Builder. - The source is a list of directories, - typically returned by a call to the &b-link-Java; Builder, - and the target is an output directory - where the _Stub.class - and _Skel.class files will - be placed: - - - - - classes = Java(target = 'classes', source = 'src/pkg/sub') - RMIC(target = 'outdir', source = classes) - - - - - As it did with the &b-link-JavaH; Builder, - &SCons; remembers the class directory - and passes it as the option - to &rmic;: - - - - - % scons -Q - javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java - rmic -d outdir -classpath classes pkg.sub.Example1 pkg.sub.Example2 - - - - - This example would generate the files - outdir/pkg/sub/Example1_Skel.class, - outdir/pkg/sub/Example1_Stub.class, - outdir/pkg/sub/Example2_Skel.class and - outdir/pkg/sub/Example2_Stub.class. - - - -
diff --git a/doc/user/java.xml b/doc/user/java.xml new file mode 100644 index 0000000..1876916 --- /dev/null +++ b/doc/user/java.xml @@ -0,0 +1,413 @@ + + + + + So far, we've been using examples of + building C and C++ programs + to demonstrate the features of &SCons;. + &SCons; also supports building Java programs, + but Java builds are handled slightly differently, + which reflects the ways in which + the Java compiler and tools + build programs differently than + other languages' tool chains. + + + +
+ Building Java Class Files: the &b-Java; Builder + + + + The basic activity when programming in Java, + of course, is to take one or more .java files + containing Java source code + and to call the Java compiler + to turn them into one or more + .class files. + In &SCons;, you do this + by giving the &b-link-Java; Builder + a target directory in which + to put the .class files, + and a source directory that contains + the .java files: + + + + + Java('classes', 'src') + + + + + If the src directory contains + three .java source files, + then running &SCons; might look like this: + + + + + % scons -Q + javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java + + + + + &SCons; will actually search the src + directory tree for all of the .java files. + The Java compiler will then create the + necessary class files in the classes subdirectory, + based on the class names found in the .java files. + + + +
+ +
+ How &SCons; Handles Java Dependencies + + + + In addition to searching the source directory for + .java files, + &SCons; actually runs the .java files + through a stripped-down Java parser that figures out + what classes are defined. + In other words, &SCons; knows, + without you having to tell it, + what .class files + will be produced by the &javac; call. + So our one-liner example from the preceding section: + + + + + Java('classes', 'src') + + + + + Will not only tell you reliably + that the .class files + in the classes subdirectory + are up-to-date: + + + + + % scons -Q + javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java + % scons -Q classes + scons: `classes' is up to date. + + + + + But it will also remove all of the generated + .class files, + even for inner classes, + without you having to specify them manually. + For example, if our + Example1.java + and + Example3.java + files both define additional classes, + and the class defined in Example2.java + has an inner class, + running scons -c + will clean up all of those .class files + as well: + + + + + % scons -Q + javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java + % scons -Q -c classes + Removed classes/Example1.class + Removed classes/AdditionalClass1.class + Removed classes/Example2$Inner2.class + Removed classes/Example2.class + Removed classes/Example3.class + Removed classes/AdditionalClass3.class + + +
+ +
+ Building Java Archive (<filename>.jar</filename>) Files: the &b-Jar; Builder + + + + After building the class files, + it's common to collect them into + a Java archive (.jar) file, + which you do by calling the &b-link-Jar; Builder method. + If you want to just collect all of the + class files within a subdirectory, + you can just specify that subdirectory + as the &b-Jar; source: + + + + + Java(target = 'classes', source = 'src') + Jar(target = 'test.jar', source = 'classes') + + + + + &SCons; will then pass that directory + to the &jar; command, + which will collect all of the underlying + .class files: + + + + + % scons -Q + javac -d classes -sourcepath src src/Example1.java src/Example2.java src/Example3.java + jar cf test.jar classes + + + + + If you want to keep all of the + .class files + for multiple programs in one location, + and only archive some of them in + each .jar file, + you can pass the &b-Jar; builder a + list of files as its source. + It's extremely simple to create multiple + .jar files this way, + using the lists of target class files created + by calls to the &b-link-Java; builder + as sources to the various &b-Jar; calls: + + + + + prog1_class_files = Java(target = 'classes', source = 'prog1') + prog2_class_files = Java(target = 'classes', source = 'prog2') + Jar(target = 'prog1.jar', source = prog1_class_files) + Jar(target = 'prog2.jar', source = prog2_class_files) + + + + + This will then create + prog1.jar + and prog2.jar + next to the subdirectories + that contain their .java files: + + + + + % scons -Q + javac -d classes -sourcepath prog1 prog1/Example1.java prog1/Example2.java + javac -d classes -sourcepath prog2 prog2/Example3.java prog2/Example4.java + jar cf prog1.jar classes/Example1.class classes/Example2.class + jar cf prog2.jar classes/Example3.class classes/Example4.class + + +
+ +
+ Building C Header and Stub Files: the &b-JavaH; Builder + + + + You can generate C header and source files + for implementing native methods, + by using the &b-link-JavaH; Builder. + There are several ways of using the &JavaH; Builder. + One typical invocation might look like: + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + JavaH(target = 'native', source = classes) + + + + + The source is a list of class files generated by the + call to the &b-link-Java; Builder, + and the target is the output directory in + which we want the C header files placed. + The target + gets converted into the + when &SCons; runs &javah;: + + + + + % scons -Q + javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java + javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 + + + + + In this case, + the call to &javah; + will generate the header files + native/pkg_sub_Example1.h, + native/pkg_sub_Example2.h + and + native/pkg_sub_Example3.h. + Notice that &SCons; remembered that the class + files were generated with a target directory of + classes, + and that it then specified that target directory + as the option + to the call to &javah;. + + + + + + Although it's more convenient to use + the list of class files returned by + the &b-Java; Builder + as the source of a call to the &b-JavaH; Builder, + you can + specify the list of class files + by hand, if you prefer. + If you do, + you need to set the + &cv-link-JAVACLASSDIR; construction variable + when calling &b-JavaH;: + + + + + Java(target = 'classes', source = 'src/pkg/sub') + class_file_list = ['classes/pkg/sub/Example1.class', + 'classes/pkg/sub/Example2.class', + 'classes/pkg/sub/Example3.class'] + JavaH(target = 'native', source = class_file_list, JAVACLASSDIR = 'classes') + + + + + The &cv-JAVACLASSDIR; value then + gets converted into the + when &SCons; runs &javah;: + + + + + % scons -Q + javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java + javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 + + + + + Lastly, if you don't want a separate header file + generated for each source file, + you can specify an explicit File Node + as the target of the &b-JavaH; Builder: + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + JavaH(target = File('native.h'), source = classes) + + + + + Because &SCons; assumes by default + that the target of the &b-JavaH; builder is a directory, + you need to use the &File; function + to make sure that &SCons; doesn't + create a directory named native.h. + When a file is used, though, + &SCons; correctly converts the file name + into the &javah; option: + + + + + % scons -Q + javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java + javah -o native.h -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3 + + +
+ +
+ Building RMI Stub and Skeleton Class Files: the &b-RMIC; Builder + + + + You can generate Remote Method Invocation stubs + by using the &b-link-RMIC; Builder. + The source is a list of directories, + typically returned by a call to the &b-link-Java; Builder, + and the target is an output directory + where the _Stub.class + and _Skel.class files will + be placed: + + + + + classes = Java(target = 'classes', source = 'src/pkg/sub') + RMIC(target = 'outdir', source = classes) + + + + + As it did with the &b-link-JavaH; Builder, + &SCons; remembers the class directory + and passes it as the option + to &rmic;: + + + + + % scons -Q + javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java + rmic -d outdir -classpath classes pkg.sub.Example1 pkg.sub.Example2 + + + + + This example would generate the files + outdir/pkg/sub/Example1_Skel.class, + outdir/pkg/sub/Example1_Stub.class, + outdir/pkg/sub/Example2_Skel.class and + outdir/pkg/sub/Example2_Stub.class. + + + +
diff --git a/doc/user/less-simple.in b/doc/user/less-simple.in index ccc59b6..76afa30 100644 --- a/doc/user/less-simple.in +++ b/doc/user/less-simple.in @@ -126,10 +126,10 @@ - Program(['main.c', 'file1.c', 'file2.c']) + Program(['prog.c', 'file1.c', 'file2.c']) - - int main() { printf("main.c\n"); } + + int main() { printf("prog.c\n"); } void file1() { printf("file1.c\n"); } @@ -173,9 +173,9 @@ - Program('program', ['main.c', 'file1.c', 'file2.c']) + Program('program', ['prog.c', 'file1.c', 'file2.c']) - + int main() { printf("prog.c\n"); } @@ -509,7 +509,7 @@ from the common source files, which can then be linked into resulting programs. (Creating libraries is discussed in - , below.) + , below.)
diff --git a/doc/user/less-simple.sgml b/doc/user/less-simple.sgml deleted file mode 100644 index ad20812..0000000 --- a/doc/user/less-simple.sgml +++ /dev/null @@ -1,562 +0,0 @@ - - - - - In this chapter, - you will see several examples of - very simple build configurations using &SCons;, - which will demonstrate how easy - it is to use &SCons; to - build programs from several different programming languages - on different types of systems. - - - -
- Specifying the Name of the Target (Output) File - - - - You've seen that when you call the &b-link-Program; builder method, - it builds the resulting program with the same - base name as the source file. - That is, the following call to build an - executable program from the &hello_c; source file - will build an executable program named &hello; on POSIX systems, - and an executable program named &hello_exe; on Windows systems: - - - - - Program('hello.c') - - - - - If you want to build a program with - a different name than the base of the source file name, - you simply put the target file name - to the left of the source file name: - - - - - Program('new_hello', 'hello.c') - - - - - (&SCons; requires the target file name first, - followed by the source file name, - so that the order mimics that of an - assignment statement in most programming languages, - including Python: - "program = source files".) - - - - - - Now &SCons; will build an executable program - named &new_hello; when run on a POSIX system: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o new_hello hello.o - - - - - And &SCons; will build an executable program - named &new_hello_exe; when run on a Windows system: - - - - - C:\>scons -Q - cl /nologo /c hello.c /Fohello.obj - link /nologo /OUT:new_hello.exe hello.obj - - -
- -
- Compiling Multiple Source Files - - - - You've just seen how to configure &SCons; - to compile a program from a single source file. - It's more common, of course, - that you'll need to build a program from - many input source files, not just one. - To do this, you need to put the - source files in a Python list - (enclosed in square brackets), - like so: - - - - - Program(['main.c', 'file1.c', 'file2.c']) - - - - - A build of the above example would look like: - - - - - % scons -Q - 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 - - - - - Notice that &SCons; - deduces the output program name - from the first source file specified - in the list--that is, - because the first source file was &prog_c;, - &SCons; will name the resulting program &prog; - (or &prog_exe; on a Windows system). - If you want to specify a different program name, - then (as we've seen in the previous section) - you slide the list of source files - over to the right - to make room for the output program file name. - (&SCons; puts the output file name to the left - of the source file names - so that the order mimics that of an - assignment statement: "program = source files".) - This makes our example: - - - - - Program('program', ['main.c', 'file1.c', 'file2.c']) - - - - - On Linux, a build of this example would look like: - - - - - % scons -Q - 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 - - - - - Or on Windows: - - - - - C:\>scons -Q - cl /nologo /c file1.c /Fofile1.obj - cl /nologo /c file2.c /Fofile2.obj - cl /nologo /c main.c /Fomain.obj - link /nologo /OUT:program.exe main.obj file1.obj file2.obj - - -
- -
- Specifying Single Files Vs. Lists of Files - - - - We've now shown you two ways to specify - the source for a program, - one with a list of files: - - - - - Program('hello', ['file1.c', 'file2.c']) - - - - - And one with a single file: - - - - - Program('hello', 'hello.c') - - - - - You could actually put a single file name in a list, too, - which you might prefer just for the sake of consistency: - - - - - Program('hello', ['hello.c']) - - - - - &SCons; functions will accept a single file name in either form. - In fact, internally, &SCons; treats all input as lists of files, - but allows you to omit the square brackets - to cut down a little on the typing - when there's only a single file name. - - - - - - - - Although &SCons; functions - are forgiving about whether or not you - use a string vs. a list for a single file name, - Python itself is more strict about - treating lists and strings differently. - So where &SCons; allows either - a string or list: - - - - - # The following two calls both work correctly: - Program('program1', 'program1.c') - Program('program2', ['program2.c']) - - - - - Trying to do "Python things" that mix strings and - lists will cause errors or lead to incorrect results: - - - - - common_sources = ['file1.c', 'file2.c'] - - # THE FOLLOWING IS INCORRECT AND GENERATES A PYTHON ERROR - # BECAUSE IT TRIES TO ADD A STRING TO A LIST: - Program('program1', common_sources + 'program1.c') - - # The following works correctly, because it's adding two - # lists together to make another list. - Program('program2', common_sources + ['program2.c']) - - - - -
- -
- Making Lists of Files Easier to Read - - - - One drawback to the use of a Python list - for source files is that - each file name must be enclosed in quotes - (either single quotes or double quotes). - This can get cumbersome and difficult to read - when the list of file names is long. - Fortunately, &SCons; and Python provide a number of ways - to make sure that - the &SConstruct; file stays easy to read. - - - - - - To make long lists of file names - easier to deal with, &SCons; provides a - &Split; function - that takes a quoted list of file names, - with the names separated by spaces or other white-space characters, - and turns it into a list of separate file names. - Using the &Split; function turns the - previous example into: - - - - - Program('program', Split('main.c file1.c file2.c')) - - - - - (If you're already familiar with Python, - you'll have realized that this is similar to the - split() method - in the Python standard string module. - Unlike the string.split() method, - however, the &Split; function - does not require a string as input - and will wrap up a single non-string object in a list, - or return its argument untouched if it's already a list. - This comes in handy as a way to make sure - arbitrary values can be passed to &SCons; functions - without having to check the type of the variable by hand.) - - - - - - Putting the call to the &Split; function - inside the &b-Program; call - can also be a little unwieldy. - A more readable alternative is to - assign the output from the &Split; call - to a variable name, - and then use the variable when calling the - &b-Program; function: - - - - - list = Split('main.c file1.c file2.c') - Program('program', list) - - - - - Lastly, the &Split; function - doesn't care how much white space separates - the file names in the quoted string. - This allows you to create lists of file - names that span multiple lines, - which often makes for easier editing: - - - - - list = Split("""main.c - file1.c - file2.c""") - Program('program', list) - - - - - (Note in this example that we used - the Python "triple-quote" syntax, - which allows a string to contain - multiple lines. - The three quotes can be either - single or double quotes.) - - - -
- -
- Keyword Arguments - - - - &SCons; also allows you to identify - the output file and input source files - using Python keyword arguments. - The output file is known as the - target, - and the source file(s) are known (logically enough) as the - source. - The Python syntax for this is: - - - - - list = Split('main.c file1.c file2.c') - Program(target = 'program', source = list) - - - - - Because the keywords explicitly identify - what each argument is, - you can actually reverse the order if you prefer: - - - - - list = Split('main.c file1.c file2.c') - Program(source = list, target = 'program') - - - - - Whether or not you choose to use keyword arguments - to identify the target and source files, - and the order in which you specify them - when using keywords, - are purely personal choices; - &SCons; functions the same regardless. - - - -
- -
- Compiling Multiple Programs - - - - In order to compile multiple programs - within the same &SConstruct; file, - simply call the &Program; method - multiple times, - once for each program you need to build: - - - - - Program('foo.c') - Program('bar', ['bar1.c', 'bar2.c']) - - - - - &SCons; would then build the programs as follows: - - - - - % scons -Q - cc -o bar1.o -c bar1.c - cc -o bar2.o -c bar2.c - cc -o bar bar1.o bar2.o - cc -o foo.o -c foo.c - cc -o foo foo.o - - - - - Notice that &SCons; does not necessarily build the - programs in the same order in which you specify - them in the &SConstruct; file. - &SCons; does, however, recognize that - the individual object files must be built - before the resulting program can be built. - We'll discuss this in greater detail in - the "Dependencies" section, below. - - - -
- -
- Sharing Source Files Between Multiple Programs - - - - It's common to re-use code by sharing source files - between multiple programs. - One way to do this is to create a library - from the common source files, - which can then be linked into resulting programs. - (Creating libraries is discussed in - , below.) - - - - - - A more straightforward, but perhaps less convenient, - way to share source files between multiple programs - is simply to include the common files - in the lists of source files for each program: - - - - - Program(Split('foo.c common1.c common2.c')) - Program('bar', Split('bar1.c bar2.c common1.c common2.c')) - - - - - &SCons; recognizes that the object files for - the &common1_c; and &common2_c; source files - each only need to be built once, - even though the resulting object files are - each linked in to both of the resulting executable programs: - - - - - % scons -Q - 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 -o foo.o -c foo.c - cc -o foo foo.o common1.o common2.o - - - - - If two or more programs - share a lot of common source files, - repeating the common files in the list for each program - can be a maintenance problem when you need to change the - list of common files. - You can simplify this by creating a separate Python list - to hold the common file names, - and concatenating it with other lists - using the Python + operator: - - - - - common = ['common1.c', 'common2.c'] - foo_files = ['foo.c'] + common - bar_files = ['bar1.c', 'bar2.c'] + common - Program('foo', foo_files) - Program('bar', bar_files) - - - - - This is functionally equivalent to the previous example. - - - -
diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml new file mode 100644 index 0000000..eea3425 --- /dev/null +++ b/doc/user/less-simple.xml @@ -0,0 +1,562 @@ + + + + + In this chapter, + you will see several examples of + very simple build configurations using &SCons;, + which will demonstrate how easy + it is to use &SCons; to + build programs from several different programming languages + on different types of systems. + + + +
+ Specifying the Name of the Target (Output) File + + + + You've seen that when you call the &b-link-Program; builder method, + it builds the resulting program with the same + base name as the source file. + That is, the following call to build an + executable program from the &hello_c; source file + will build an executable program named &hello; on POSIX systems, + and an executable program named &hello_exe; on Windows systems: + + + + + Program('hello.c') + + + + + If you want to build a program with + a different name than the base of the source file name, + you simply put the target file name + to the left of the source file name: + + + + + Program('new_hello', 'hello.c') + + + + + (&SCons; requires the target file name first, + followed by the source file name, + so that the order mimics that of an + assignment statement in most programming languages, + including Python: + "program = source files".) + + + + + + Now &SCons; will build an executable program + named &new_hello; when run on a POSIX system: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o new_hello hello.o + + + + + And &SCons; will build an executable program + named &new_hello_exe; when run on a Windows system: + + + + + C:\>scons -Q + cl /nologo /c hello.c /Fohello.obj + link /nologo /OUT:new_hello.exe hello.obj + + +
+ +
+ Compiling Multiple Source Files + + + + You've just seen how to configure &SCons; + to compile a program from a single source file. + It's more common, of course, + that you'll need to build a program from + many input source files, not just one. + To do this, you need to put the + source files in a Python list + (enclosed in square brackets), + like so: + + + + + Program(['prog.c', 'file1.c', 'file2.c']) + + + + + A build of the above example would look like: + + + + + % scons -Q + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o prog.o -c prog.c + cc -o prog prog.o file1.o file2.o + + + + + Notice that &SCons; + deduces the output program name + from the first source file specified + in the list--that is, + because the first source file was &prog_c;, + &SCons; will name the resulting program &prog; + (or &prog_exe; on a Windows system). + If you want to specify a different program name, + then (as we've seen in the previous section) + you slide the list of source files + over to the right + to make room for the output program file name. + (&SCons; puts the output file name to the left + of the source file names + so that the order mimics that of an + assignment statement: "program = source files".) + This makes our example: + + + + + Program('program', ['prog.c', 'file1.c', 'file2.c']) + + + + + On Linux, a build of this example would look like: + + + + + % scons -Q + cc -o file1.o -c file1.c + cc -o file2.o -c file2.c + cc -o prog.o -c prog.c + cc -o program prog.o file1.o file2.o + + + + + Or on Windows: + + + + + C:\>scons -Q + cl /nologo /c file1.c /Fofile1.obj + cl /nologo /c file2.c /Fofile2.obj + cl /nologo /c prog.c /Foprog.obj + link /nologo /OUT:program.exe prog.obj file1.obj file2.obj + + +
+ +
+ Specifying Single Files Vs. Lists of Files + + + + We've now shown you two ways to specify + the source for a program, + one with a list of files: + + + + + Program('hello', ['file1.c', 'file2.c']) + + + + + And one with a single file: + + + + + Program('hello', 'hello.c') + + + + + You could actually put a single file name in a list, too, + which you might prefer just for the sake of consistency: + + + + + Program('hello', ['hello.c']) + + + + + &SCons; functions will accept a single file name in either form. + In fact, internally, &SCons; treats all input as lists of files, + but allows you to omit the square brackets + to cut down a little on the typing + when there's only a single file name. + + + + + + + + Although &SCons; functions + are forgiving about whether or not you + use a string vs. a list for a single file name, + Python itself is more strict about + treating lists and strings differently. + So where &SCons; allows either + a string or list: + + + + + # The following two calls both work correctly: + Program('program1', 'program1.c') + Program('program2', ['program2.c']) + + + + + Trying to do "Python things" that mix strings and + lists will cause errors or lead to incorrect results: + + + + + common_sources = ['file1.c', 'file2.c'] + + # THE FOLLOWING IS INCORRECT AND GENERATES A PYTHON ERROR + # BECAUSE IT TRIES TO ADD A STRING TO A LIST: + Program('program1', common_sources + 'program1.c') + + # The following works correctly, because it's adding two + # lists together to make another list. + Program('program2', common_sources + ['program2.c']) + + + + +
+ +
+ Making Lists of Files Easier to Read + + + + One drawback to the use of a Python list + for source files is that + each file name must be enclosed in quotes + (either single quotes or double quotes). + This can get cumbersome and difficult to read + when the list of file names is long. + Fortunately, &SCons; and Python provide a number of ways + to make sure that + the &SConstruct; file stays easy to read. + + + + + + To make long lists of file names + easier to deal with, &SCons; provides a + &Split; function + that takes a quoted list of file names, + with the names separated by spaces or other white-space characters, + and turns it into a list of separate file names. + Using the &Split; function turns the + previous example into: + + + + + Program('program', Split('main.c file1.c file2.c')) + + + + + (If you're already familiar with Python, + you'll have realized that this is similar to the + split() method + in the Python standard string module. + Unlike the string.split() method, + however, the &Split; function + does not require a string as input + and will wrap up a single non-string object in a list, + or return its argument untouched if it's already a list. + This comes in handy as a way to make sure + arbitrary values can be passed to &SCons; functions + without having to check the type of the variable by hand.) + + + + + + Putting the call to the &Split; function + inside the &b-Program; call + can also be a little unwieldy. + A more readable alternative is to + assign the output from the &Split; call + to a variable name, + and then use the variable when calling the + &b-Program; function: + + + + + list = Split('main.c file1.c file2.c') + Program('program', list) + + + + + Lastly, the &Split; function + doesn't care how much white space separates + the file names in the quoted string. + This allows you to create lists of file + names that span multiple lines, + which often makes for easier editing: + + + + + list = Split("""main.c + file1.c + file2.c""") + Program('program', list) + + + + + (Note in this example that we used + the Python "triple-quote" syntax, + which allows a string to contain + multiple lines. + The three quotes can be either + single or double quotes.) + + + +
+ +
+ Keyword Arguments + + + + &SCons; also allows you to identify + the output file and input source files + using Python keyword arguments. + The output file is known as the + target, + and the source file(s) are known (logically enough) as the + source. + The Python syntax for this is: + + + + + list = Split('main.c file1.c file2.c') + Program(target = 'program', source = list) + + + + + Because the keywords explicitly identify + what each argument is, + you can actually reverse the order if you prefer: + + + + + list = Split('main.c file1.c file2.c') + Program(source = list, target = 'program') + + + + + Whether or not you choose to use keyword arguments + to identify the target and source files, + and the order in which you specify them + when using keywords, + are purely personal choices; + &SCons; functions the same regardless. + + + +
+ +
+ Compiling Multiple Programs + + + + In order to compile multiple programs + within the same &SConstruct; file, + simply call the &Program; method + multiple times, + once for each program you need to build: + + + + + Program('foo.c') + Program('bar', ['bar1.c', 'bar2.c']) + + + + + &SCons; would then build the programs as follows: + + + + + % scons -Q + cc -o bar1.o -c bar1.c + cc -o bar2.o -c bar2.c + cc -o bar bar1.o bar2.o + cc -o foo.o -c foo.c + cc -o foo foo.o + + + + + Notice that &SCons; does not necessarily build the + programs in the same order in which you specify + them in the &SConstruct; file. + &SCons; does, however, recognize that + the individual object files must be built + before the resulting program can be built. + We'll discuss this in greater detail in + the "Dependencies" section, below. + + + +
+ +
+ Sharing Source Files Between Multiple Programs + + + + It's common to re-use code by sharing source files + between multiple programs. + One way to do this is to create a library + from the common source files, + which can then be linked into resulting programs. + (Creating libraries is discussed in + , below.) + + + + + + A more straightforward, but perhaps less convenient, + way to share source files between multiple programs + is simply to include the common files + in the lists of source files for each program: + + + + + Program(Split('foo.c common1.c common2.c')) + Program('bar', Split('bar1.c bar2.c common1.c common2.c')) + + + + + &SCons; recognizes that the object files for + the &common1_c; and &common2_c; source files + each only need to be built once, + even though the resulting object files are + each linked in to both of the resulting executable programs: + + + + + % scons -Q + 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 -o foo.o -c foo.c + cc -o foo foo.o common1.o common2.o + + + + + If two or more programs + share a lot of common source files, + repeating the common files in the list for each program + can be a maintenance problem when you need to change the + list of common files. + You can simplify this by creating a separate Python list + to hold the common file names, + and concatenating it with other lists + using the Python + operator: + + + + + common = ['common1.c', 'common2.c'] + foo_files = ['foo.c'] + common + bar_files = ['bar1.c', 'bar2.c'] + common + Program('foo', foo_files) + Program('bar', bar_files) + + + + + This is functionally equivalent to the previous example. + + + +
diff --git a/doc/user/libraries.in b/doc/user/libraries.in index 0fe53b8..1ccb1c3 100644 --- a/doc/user/libraries.in +++ b/doc/user/libraries.in @@ -142,7 +142,7 @@ Of course, in this example, the object files must already exist for the build to succeed. - See , below, + See , below, for information about how you can build object files explicitly and include the built files in a library. @@ -159,7 +159,7 @@ The &b-link-Library; function builds a traditional static library. If you want to be explicit about the type of library being built, you can use the synonym &b-link-StaticLibrary; function - instead of &b-Library: + instead of &b-Library;: diff --git a/doc/user/libraries.sgml b/doc/user/libraries.sgml deleted file mode 100644 index 772810f..0000000 --- a/doc/user/libraries.sgml +++ /dev/null @@ -1,421 +0,0 @@ - - - - - It's often useful to organize large software projects - by collecting parts of the software into one or more libraries. - &SCons; makes it easy to create libraries - and to use them in the programs. - - - -
- Building Libraries - - - - You build your own libraries by specifying &b-link-Library; - instead of &b-link-Program;: - - - - - Library('foo', ['f1.c', 'f2.c', 'f3.c']) - - - - - &SCons; uses the appropriate library prefix and suffix for your system. - So on POSIX or Linux systems, - the above example would build as follows - (although &ranlib; may not be called on all systems): - - - - - % scons -Q - 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 - - - - - On a Windows system, - a build of the above example would look like: - - - - - C:\>scons -Q - cl /nologo /c f1.c /Fof1.obj - cl /nologo /c f2.c /Fof2.obj - cl /nologo /c f3.c /Fof3.obj - lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj - - - - - The rules for the target name of the library - are similar to those for programs: - if you don't explicitly specify a target library name, - &SCons; will deduce one from the - name of the first source file specified, - and &SCons; will add an appropriate - file prefix and suffix if you leave them off. - - - -
- Building Libraries From Source Code or Object Files - - - - The previous example shows building a library from a - list of source files. - You can, however, also give the &b-link-Library; call - object files, - and it will correctly realize - In fact, you can arbitrarily mix source code files - and object files in the source list: - - - - - Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o']) - - - - - And SCons realizes that only the source code files - must be compiled into object files - before creating the final library: - - - - - % scons -Q - cc -o f1.o -c f1.c - cc -o f3.o -c f3.c - ar rc libfoo.a f1.o f2.o f3.o f4.o - ranlib libfoo.a - - - - - Of course, in this example, the object files - must already exist for the build to succeed. - See , below, - for information about how you can - build object files explicitly - and include the built files in a library. - - - -
- -
- Building Static Libraries Explicitly: the &b-StaticLibrary; Builder - - - - The &b-link-Library; function builds a traditional static library. - If you want to be explicit about the type of library being built, - you can use the synonym &b-link-StaticLibrary; function - instead of &b-Library: - - - - - StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) - - - - - There is no functional difference between the - &b-link-StaticLibrary; and &b-Library; functions. - - - -
- -
- Building Shared (DLL) Libraries: the &b-SharedLibrary; Builder - - - - If you want to build a shared library (on POSIX systems) - or a DLL file (on Windows systems), - you use the &b-link-SharedLibrary; function: - - - - - SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) - - - - - The output on POSIX: - - - - - % scons -Q - 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 - - - - - And the output on Windows: - - - - - C:\>scons -Q - cl /nologo /c f1.c /Fof1.obj - cl /nologo /c f2.c /Fof2.obj - cl /nologo /c f3.c /Fof3.obj - link /nologo /dll /out:foo.dll /implib:foo.lib f1.obj f2.obj f3.obj - RegServerFunc(target, source, env) - - - - - Notice again that &SCons; takes care of - building the output file correctly, - adding the -shared option - for a POSIX compilation, - and the /dll option on Windows. - - - -
- -
- -
- Linking with Libraries - - - - Usually, you build a library - because you want to link it with one or more programs. - You link libraries with a program by specifying - the libraries in the &cv-link-LIBS; construction variable, - and by specifying the directory in which - the library will be found in the - &cv-link-LIBPATH; construction variable: - - - - - Library('foo', ['f1.c', 'f2.c', 'f3.c']) - Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.') - - - - - Notice, of course, that you don't need to specify a library - prefix (like lib) - or suffix (like .a or .lib). - &SCons; uses the correct prefix or suffix for the current system. - - - - - - On a POSIX or Linux system, - a build of the above example would look like: - - - - - % scons -Q - 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 -o prog.o -c prog.c - cc -o prog prog.o -L. -lfoo -lbar - - - - - On a Windows system, - a build of the above example would look like: - - - - - C:\>scons -Q - cl /nologo /c f1.c /Fof1.obj - cl /nologo /c f2.c /Fof2.obj - cl /nologo /c f3.c /Fof3.obj - lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj - cl /nologo /c prog.c /Foprog.obj - link /nologo /OUT:prog.exe /LIBPATH:. foo.lib bar.lib prog.obj - - - - - As usual, notice that &SCons; has taken care - of constructing the correct command lines - to link with the specified library on each system. - - - - - - Note also that, - if you only have a single library to link with, - you can specify the library name in single string, - instead of a Python list, - so that: - - - - - Program('prog.c', LIBS='foo', LIBPATH='.') - - - - - is equivalent to: - - - - - Program('prog.c', LIBS=['foo'], LIBPATH='.') - - - - - This is similar to the way that &SCons; - handles either a string or a list to - specify a single source file. - - - -
- -
- Finding Libraries: the &cv-LIBPATH; Construction Variable - - - - By default, the linker will only look in - certain system-defined directories for libraries. - &SCons; knows how to look for libraries - in directories that you specify with the - &cv-link-LIBPATH; construction variable. - &cv-LIBPATH; consists of a list of - directory names, like so: - - - - - Program('prog.c', LIBS = 'm', - LIBPATH = ['/usr/lib', '/usr/local/lib']) - - - - - Using a Python list is preferred because it's portable - across systems. Alternatively, you could put all of - the directory names in a single string, separated by the - system-specific path separator character: - a colon on POSIX systems: - - - - - LIBPATH = '/usr/lib:/usr/local/lib' - - - - - or a semi-colon on Windows systems: - - - - - LIBPATH = 'C:\\lib;D:\\lib' - - - - - (Note that Python requires that the backslash - separators in a Windows path name - be escaped within strings.) - - - - - - When the linker is executed, - &SCons; will create appropriate flags - so that the linker will look for - libraries in the same directories as &SCons;. - So on a POSIX or Linux system, - a build of the above example would look like: - - - - - % scons -Q - cc -o prog.o -c prog.c - cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm - - - - - On a Windows system, - a build of the above example would look like: - - - - - C:\>scons -Q - cl /nologo /c prog.c /Foprog.obj - link /nologo /OUT:prog.exe /LIBPATH:\usr\lib /LIBPATH:\usr\local\lib m.lib prog.obj - - - - - Note again that &SCons; has taken care of - the system-specific details of creating - the right command-line options. - - - -
diff --git a/doc/user/libraries.xml b/doc/user/libraries.xml new file mode 100644 index 0000000..035ebd3 --- /dev/null +++ b/doc/user/libraries.xml @@ -0,0 +1,421 @@ + + + + + It's often useful to organize large software projects + by collecting parts of the software into one or more libraries. + &SCons; makes it easy to create libraries + and to use them in the programs. + + + +
+ Building Libraries + + + + You build your own libraries by specifying &b-link-Library; + instead of &b-link-Program;: + + + + + Library('foo', ['f1.c', 'f2.c', 'f3.c']) + + + + + &SCons; uses the appropriate library prefix and suffix for your system. + So on POSIX or Linux systems, + the above example would build as follows + (although &ranlib; may not be called on all systems): + + + + + % scons -Q + 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 + + + + + On a Windows system, + a build of the above example would look like: + + + + + C:\>scons -Q + cl /nologo /c f1.c /Fof1.obj + cl /nologo /c f2.c /Fof2.obj + cl /nologo /c f3.c /Fof3.obj + lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj + + + + + The rules for the target name of the library + are similar to those for programs: + if you don't explicitly specify a target library name, + &SCons; will deduce one from the + name of the first source file specified, + and &SCons; will add an appropriate + file prefix and suffix if you leave them off. + + + +
+ Building Libraries From Source Code or Object Files + + + + The previous example shows building a library from a + list of source files. + You can, however, also give the &b-link-Library; call + object files, + and it will correctly realize + In fact, you can arbitrarily mix source code files + and object files in the source list: + + + + + Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o']) + + + + + And SCons realizes that only the source code files + must be compiled into object files + before creating the final library: + + + + + % scons -Q + cc -o f1.o -c f1.c + cc -o f3.o -c f3.c + ar rc libfoo.a f1.o f2.o f3.o f4.o + ranlib libfoo.a + + + + + Of course, in this example, the object files + must already exist for the build to succeed. + See , below, + for information about how you can + build object files explicitly + and include the built files in a library. + + + +
+ +
+ Building Static Libraries Explicitly: the &b-StaticLibrary; Builder + + + + The &b-link-Library; function builds a traditional static library. + If you want to be explicit about the type of library being built, + you can use the synonym &b-link-StaticLibrary; function + instead of &b-Library;: + + + + + StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) + + + + + There is no functional difference between the + &b-link-StaticLibrary; and &b-Library; functions. + + + +
+ +
+ Building Shared (DLL) Libraries: the &b-SharedLibrary; Builder + + + + If you want to build a shared library (on POSIX systems) + or a DLL file (on Windows systems), + you use the &b-link-SharedLibrary; function: + + + + + SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) + + + + + The output on POSIX: + + + + + % scons -Q + 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 + + + + + And the output on Windows: + + + + + C:\>scons -Q + cl /nologo /c f1.c /Fof1.obj + cl /nologo /c f2.c /Fof2.obj + cl /nologo /c f3.c /Fof3.obj + link /nologo /dll /out:foo.dll /implib:foo.lib f1.obj f2.obj f3.obj + RegServerFunc(target, source, env) + + + + + Notice again that &SCons; takes care of + building the output file correctly, + adding the -shared option + for a POSIX compilation, + and the /dll option on Windows. + + + +
+ +
+ +
+ Linking with Libraries + + + + Usually, you build a library + because you want to link it with one or more programs. + You link libraries with a program by specifying + the libraries in the &cv-link-LIBS; construction variable, + and by specifying the directory in which + the library will be found in the + &cv-link-LIBPATH; construction variable: + + + + + Library('foo', ['f1.c', 'f2.c', 'f3.c']) + Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.') + + + + + Notice, of course, that you don't need to specify a library + prefix (like lib) + or suffix (like .a or .lib). + &SCons; uses the correct prefix or suffix for the current system. + + + + + + On a POSIX or Linux system, + a build of the above example would look like: + + + + + % scons -Q + 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 -o prog.o -c prog.c + cc -o prog prog.o -L. -lfoo -lbar + + + + + On a Windows system, + a build of the above example would look like: + + + + + C:\>scons -Q + cl /nologo /c f1.c /Fof1.obj + cl /nologo /c f2.c /Fof2.obj + cl /nologo /c f3.c /Fof3.obj + lib /nologo /OUT:foo.lib f1.obj f2.obj f3.obj + cl /nologo /c prog.c /Foprog.obj + link /nologo /OUT:prog.exe /LIBPATH:. foo.lib bar.lib prog.obj + + + + + As usual, notice that &SCons; has taken care + of constructing the correct command lines + to link with the specified library on each system. + + + + + + Note also that, + if you only have a single library to link with, + you can specify the library name in single string, + instead of a Python list, + so that: + + + + + Program('prog.c', LIBS='foo', LIBPATH='.') + + + + + is equivalent to: + + + + + Program('prog.c', LIBS=['foo'], LIBPATH='.') + + + + + This is similar to the way that &SCons; + handles either a string or a list to + specify a single source file. + + + +
+ +
+ Finding Libraries: the &cv-LIBPATH; Construction Variable + + + + By default, the linker will only look in + certain system-defined directories for libraries. + &SCons; knows how to look for libraries + in directories that you specify with the + &cv-link-LIBPATH; construction variable. + &cv-LIBPATH; consists of a list of + directory names, like so: + + + + + Program('prog.c', LIBS = 'm', + LIBPATH = ['/usr/lib', '/usr/local/lib']) + + + + + Using a Python list is preferred because it's portable + across systems. Alternatively, you could put all of + the directory names in a single string, separated by the + system-specific path separator character: + a colon on POSIX systems: + + + + + LIBPATH = '/usr/lib:/usr/local/lib' + + + + + or a semi-colon on Windows systems: + + + + + LIBPATH = 'C:\\lib;D:\\lib' + + + + + (Note that Python requires that the backslash + separators in a Windows path name + be escaped within strings.) + + + + + + When the linker is executed, + &SCons; will create appropriate flags + so that the linker will look for + libraries in the same directories as &SCons;. + So on a POSIX or Linux system, + a build of the above example would look like: + + + + + % scons -Q + cc -o prog.o -c prog.c + cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm + + + + + On a Windows system, + a build of the above example would look like: + + + + + C:\>scons -Q + cl /nologo /c prog.c /Foprog.obj + link /nologo /OUT:prog.exe /LIBPATH:\usr\lib /LIBPATH:\usr\local\lib m.lib prog.obj + + + + + Note again that &SCons; has taken care of + the system-specific details of creating + the right command-line options. + + + +
diff --git a/doc/user/main.in b/doc/user/main.in index aaddb2a..c3cf2c2 100644 --- a/doc/user/main.in +++ b/doc/user/main.in @@ -1,3 +1,4 @@ + - + %version; @@ -41,47 +43,47 @@ %variables-mod; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/user/main.sgml b/doc/user/main.sgml deleted file mode 100644 index d864350..0000000 --- a/doc/user/main.sgml +++ /dev/null @@ -1,393 +0,0 @@ - - - - %version; - - - %scons; - - - %builders-mod; - - - %tools-mod; - - - %variables-mod; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]> - - - - - - SCons User Guide &buildversion; - - - Steven - Knight - - - Revision &buildrevision; (&builddate;) - - 2004, 2005, 2006, 2007 - - - 2004, 2005, 2006, 2007 - Steven Knight - - - - ©right; - - - version &buildversion; - - - - - Preface - &preface; - - - - Building and Installing &SCons; - &build-install; - - - - Simple Builds - &simple; - - - - Less Simple Things to Do With Builds - &less-simple; - - - - Building and Linking with Libraries - &libraries; - - - - Node Objects - &nodes; - - - - Dependencies - &depends; - - - - Construction Environments - &environments; - - - - - - Controlling the External Environment Used to Execute Build Commands - &ENV_file; - - - - Controlling a Build From the Command Line - &command-line; - - - - Providing Build Help: the &Help; Function - &help; - - - - Installing Files in Other Directories: the &Install; Builder - &install; - - - - Platform-Independent File System Manipulation - &factories; - - - - Preventing Removal of Targets - &file-removal; - - - - Hierarchical Builds - &hierarchy; - - - - Separating Source and Build Directories - &separate; - - - - Variant Builds - &variants; - - - - - - Writing Your Own Builders - &builders-writing; - - - - Not Writing a Builder: the &Command; Builder - &builders-commands; - - - - - - Writing Scanners - &scanners; - - - - Building From Code Repositories - &repositories; - - - - Multi-Platform Configuration (&Autoconf; Functionality) - &sconf; - - - - - - Caching Built Files - &caching; - - - - Alias Targets - &alias; - - - - Java Builds - &java; - - - - - - Troubleshooting - &troubleshoot; - - - - Construction Variables - &variables; - - - - Builders - &builders; - - - - Tools - &tools; - - - - Handling Common Tasks - &tasks; - - - - - diff --git a/doc/user/main.xml b/doc/user/main.xml new file mode 100644 index 0000000..c3cf2c2 --- /dev/null +++ b/doc/user/main.xml @@ -0,0 +1,398 @@ + + + + + %version; + + + %scons; + + + %builders-mod; + + + %tools-mod; + + + %variables-mod; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + + + SCons User Guide &buildversion; + + + Steven + Knight + + + Revision &buildrevision; (&builddate;) + + 2004, 2005, 2006, 2007 + + + 2004, 2005, 2006, 2007 + Steven Knight + + + + ©right; + + + version &buildversion; + + + + + Preface + &preface; + + + + Building and Installing &SCons; + &build-install; + + + + Simple Builds + &simple; + + + + Less Simple Things to Do With Builds + &less-simple; + + + + Building and Linking with Libraries + &libraries; + + + + Node Objects + &nodes; + + + + Dependencies + &depends; + + + + Construction Environments + &environments; + + + + + + Controlling the External Environment Used to Execute Build Commands + &ENV_file; + + + + Controlling a Build From the Command Line + &command-line; + + + + Providing Build Help: the &Help; Function + &help; + + + + Installing Files in Other Directories: the &Install; Builder + &install; + + + + Platform-Independent File System Manipulation + &factories; + + + + Preventing Removal of Targets + &file-removal; + + + + Hierarchical Builds + &hierarchy; + + + + Separating Source and Build Directories + &separate; + + + + Variant Builds + &variants; + + + + + + Writing Your Own Builders + &builders-writing; + + + + Not Writing a Builder: the &Command; Builder + &builders-commands; + + + + + + Writing Scanners + &scanners; + + + + Building From Code Repositories + &repositories; + + + + Multi-Platform Configuration (&Autoconf; Functionality) + &sconf; + + + + + + Caching Built Files + &caching; + + + + Alias Targets + &alias; + + + + Java Builds + &java; + + + + + + Troubleshooting + &troubleshoot; + + + + Construction Variables + &variables; + + + + Builders + &builders; + + + + Tools + &tools; + + + + Handling Common Tasks + &tasks; + + + + + diff --git a/doc/user/make.sgml b/doc/user/make.sgml deleted file mode 100644 index 72b2df1..0000000 --- a/doc/user/make.sgml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - XXX - - - -
- Differences Between &Make; and &SCons; - - - - XXX - - - -
- -
- Advantages of &SCons; Over &Make; - - - - XXX - - - -
diff --git a/doc/user/make.xml b/doc/user/make.xml new file mode 100644 index 0000000..72b2df1 --- /dev/null +++ b/doc/user/make.xml @@ -0,0 +1,121 @@ + + + + + + + XXX + + + +
+ Differences Between &Make; and &SCons; + + + + XXX + + + +
+ +
+ Advantages of &SCons; Over &Make; + + + + XXX + + + +
diff --git a/doc/user/nodes.sgml b/doc/user/nodes.sgml deleted file mode 100644 index 4ada5b7..0000000 --- a/doc/user/nodes.sgml +++ /dev/null @@ -1,373 +0,0 @@ - - - - - Internally, &SCons; represents all of the files - and directories it knows about as &Nodes;. - These internal objects - (not object files) - can be used in a variety of ways - to make your &SConscript; - files portable and easy to read. - - - -
- Builder Methods Return Lists of Target Nodes - - - - All builder methods return a list of - &Node; objects that identify the - target file or files that will be built. - These returned &Nodes; can be passed - as source files to other builder methods, - - - - - - For example, suppose that we want to build - the two object files that make up a program with different options. - This would mean calling the &b-link-Object; - builder once for each object file, - specifying the desired options: - - - - - Object('hello.c', CCFLAGS='-DHELLO') - Object('goodbye.c', CCFLAGS='-DGOODBYE') - - - - - One way to combine these object files - into the resulting program - would be to call the &b-link-Program; - builder with the names of the object files - listed as sources: - - - - - Object('hello.c', CCFLAGS='-DHELLO') - Object('goodbye.c', CCFLAGS='-DGOODBYE') - Program(['hello.o', 'goodbye.o']) - - - - - The problem with listing the names as strings - is that our &SConstruct; file is no longer portable - across operating systems. - It won't, for example, work on Windows - because the object files there would be - named &hello_obj; and &goodbye_obj;, - not &hello_o; and &goodbye_o;. - - - - - - A better solution is to assign the lists of targets - returned by the calls to the &b-Object; builder to variables, - which we can then concatenate in our - call to the &b-Program; builder: - - - - - hello_list = Object('hello.c', CCFLAGS='-DHELLO') - goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE') - Program(hello_list + goodbye_list) - - - - - This makes our &SConstruct; file portable again, - the build output on Linux looking like: - - - - - % scons -Q - cc -o goodbye.o -c -DGOODBYE goodbye.c - cc -o hello.o -c -DHELLO hello.c - cc -o hello hello.o goodbye.o - - - - - And on Windows: - - - - - C:\>scons -Q - cl -DGOODBYE /c goodbye.c /Fogoodbye.obj - cl -DHELLO /c hello.c /Fohello.obj - link /nologo /OUT:hello.exe hello.obj goodbye.obj - - - - - We'll see examples of using the list of nodes - returned by builder methods throughout - the rest of this guide. - - - -
- -
- Explicitly Creating File and Directory Nodes - - - - It's worth mentioning here that - &SCons; maintains a clear distinction - between Nodes that represent files - and Nodes that represent directories. - &SCons; supports &File; and &Dir; - functions that, repectively, - return a file or directory Node: - - - - - hello_c = File('hello.c') - Program(hello_c) - - classes = Dir('classes') - Java(classes, 'src') - - - - - Normally, you don't need to call - &File; or &Dir; directly, - because calling a builder method automatically - treats strings as the names of files or directories, - and translates them into - the Node objects for you. - The &File; and &Dir; functions can come in handy - in situations where you need to explicitly - instruct &SCons; about the type of Node being - passed to a builder or other function, - or unambiguously refer to a specific - file in a directory tree. - - - - - - - There are also times when you may need to - refer to an entry in a file system - without knowing in advance - whether it's a file or a directory. - For those situations, - &SCons; also supports an &Entry; function, - which returns a Node - that can represent either a file or a directory. - - - - - xyzzy = Entry('xyzzy') - - - - - The returned xyzzy Node - will be turned into a file or directory Node - the first time it is used by a builder method - or other function that - requires one vs. the other. - - - -
- -
- Printing &Node; File Names - - - - One of the most common things you can do - with a Node is use it to print the - file name that the node represents. - For example, the following &SConstruct; file: - - - - - hello_c = File('hello.c') - Program(hello_c) - - classes = Dir('classes') - Java(classes, 'src') - - object_list = Object('hello.c') - program_list = Program(object_list) - print "The object file is:", object_list[0] - print "The program file is:", program_list[0] - - - - - Would print the following file names on a POSIX system: - - - - - % scons -Q - The object file is: hello.o - The program file is: hello - cc -o hello.o -c hello.c - cc -o hello hello.o - - - - - And the following file names on a Windows system: - - - - - C:\>scons -Q - The object file is: hello.obj - The program file is: hello.exe - cl /nologo /c hello.c /Fohello.obj - link /nologo /OUT:hello.exe hello.obj - - -
- -
- Using a &Node;'s File Name as a String - - - - Printing a &Node;'s name - as described in the previous section - works because the string representation of a &Node; - is the name of the file. - If you want to do something other than - print the name of the file, - you can fetch it by using the builtin Python - &str; function. - For example, if you want to use the Python - os.path.exists - to figure out whether a file - exists while the &SConstruct; file - is being read and executed, - you can fetch the string as follows: - - - - - import os.path - program_list = Program('hello.c') - program_name = str(program_list[0]) - if not os.path.exists(program_name): - print program_name, "does not exist!" - - - - - Which executes as follows on a POSIX system: - - - - - % scons -Q - hello does not exist! - cc -o hello.o -c hello.c - cc -o hello hello.o - - -
- - - - diff --git a/doc/user/nodes.xml b/doc/user/nodes.xml new file mode 100644 index 0000000..4ada5b7 --- /dev/null +++ b/doc/user/nodes.xml @@ -0,0 +1,373 @@ + + + + + Internally, &SCons; represents all of the files + and directories it knows about as &Nodes;. + These internal objects + (not object files) + can be used in a variety of ways + to make your &SConscript; + files portable and easy to read. + + + +
+ Builder Methods Return Lists of Target Nodes + + + + All builder methods return a list of + &Node; objects that identify the + target file or files that will be built. + These returned &Nodes; can be passed + as source files to other builder methods, + + + + + + For example, suppose that we want to build + the two object files that make up a program with different options. + This would mean calling the &b-link-Object; + builder once for each object file, + specifying the desired options: + + + + + Object('hello.c', CCFLAGS='-DHELLO') + Object('goodbye.c', CCFLAGS='-DGOODBYE') + + + + + One way to combine these object files + into the resulting program + would be to call the &b-link-Program; + builder with the names of the object files + listed as sources: + + + + + Object('hello.c', CCFLAGS='-DHELLO') + Object('goodbye.c', CCFLAGS='-DGOODBYE') + Program(['hello.o', 'goodbye.o']) + + + + + The problem with listing the names as strings + is that our &SConstruct; file is no longer portable + across operating systems. + It won't, for example, work on Windows + because the object files there would be + named &hello_obj; and &goodbye_obj;, + not &hello_o; and &goodbye_o;. + + + + + + A better solution is to assign the lists of targets + returned by the calls to the &b-Object; builder to variables, + which we can then concatenate in our + call to the &b-Program; builder: + + + + + hello_list = Object('hello.c', CCFLAGS='-DHELLO') + goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE') + Program(hello_list + goodbye_list) + + + + + This makes our &SConstruct; file portable again, + the build output on Linux looking like: + + + + + % scons -Q + cc -o goodbye.o -c -DGOODBYE goodbye.c + cc -o hello.o -c -DHELLO hello.c + cc -o hello hello.o goodbye.o + + + + + And on Windows: + + + + + C:\>scons -Q + cl -DGOODBYE /c goodbye.c /Fogoodbye.obj + cl -DHELLO /c hello.c /Fohello.obj + link /nologo /OUT:hello.exe hello.obj goodbye.obj + + + + + We'll see examples of using the list of nodes + returned by builder methods throughout + the rest of this guide. + + + +
+ +
+ Explicitly Creating File and Directory Nodes + + + + It's worth mentioning here that + &SCons; maintains a clear distinction + between Nodes that represent files + and Nodes that represent directories. + &SCons; supports &File; and &Dir; + functions that, repectively, + return a file or directory Node: + + + + + hello_c = File('hello.c') + Program(hello_c) + + classes = Dir('classes') + Java(classes, 'src') + + + + + Normally, you don't need to call + &File; or &Dir; directly, + because calling a builder method automatically + treats strings as the names of files or directories, + and translates them into + the Node objects for you. + The &File; and &Dir; functions can come in handy + in situations where you need to explicitly + instruct &SCons; about the type of Node being + passed to a builder or other function, + or unambiguously refer to a specific + file in a directory tree. + + + + + + + There are also times when you may need to + refer to an entry in a file system + without knowing in advance + whether it's a file or a directory. + For those situations, + &SCons; also supports an &Entry; function, + which returns a Node + that can represent either a file or a directory. + + + + + xyzzy = Entry('xyzzy') + + + + + The returned xyzzy Node + will be turned into a file or directory Node + the first time it is used by a builder method + or other function that + requires one vs. the other. + + + +
+ +
+ Printing &Node; File Names + + + + One of the most common things you can do + with a Node is use it to print the + file name that the node represents. + For example, the following &SConstruct; file: + + + + + hello_c = File('hello.c') + Program(hello_c) + + classes = Dir('classes') + Java(classes, 'src') + + object_list = Object('hello.c') + program_list = Program(object_list) + print "The object file is:", object_list[0] + print "The program file is:", program_list[0] + + + + + Would print the following file names on a POSIX system: + + + + + % scons -Q + The object file is: hello.o + The program file is: hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + And the following file names on a Windows system: + + + + + C:\>scons -Q + The object file is: hello.obj + The program file is: hello.exe + cl /nologo /c hello.c /Fohello.obj + link /nologo /OUT:hello.exe hello.obj + + +
+ +
+ Using a &Node;'s File Name as a String + + + + Printing a &Node;'s name + as described in the previous section + works because the string representation of a &Node; + is the name of the file. + If you want to do something other than + print the name of the file, + you can fetch it by using the builtin Python + &str; function. + For example, if you want to use the Python + os.path.exists + to figure out whether a file + exists while the &SConstruct; file + is being read and executed, + you can fetch the string as follows: + + + + + import os.path + program_list = Program('hello.c') + program_name = str(program_list[0]) + if not os.path.exists(program_name): + print program_name, "does not exist!" + + + + + Which executes as follows on a POSIX system: + + + + + % scons -Q + hello does not exist! + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ + + + diff --git a/doc/user/parseconfig.sgml b/doc/user/parseconfig.sgml deleted file mode 100644 index 067ef37..0000000 --- a/doc/user/parseconfig.sgml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - Configuring the right options to build programs to work with the - libraries--especially shared libraries--installed on a POSIX system - can be very complicated. - Various utilies with names that end in config - can return command-line options for the - GNU Compiler Collection - - - - - - &SCons; construction environments have a &ParseConfig; method - that executes a utility and configures - the appropriate construction variables - in the environment - based on the command-line options - returned by the specified command. - - - - - - - env = Environment() - env.ParseConfig("pkg-config") - - - - - &SCons; will execute the specified command string - and XXX - - - - - % scons -Q - scons: `.' is up to date. - Must specify package names on the command line - - - - - XXX - - diff --git a/doc/user/parseconfig.xml b/doc/user/parseconfig.xml new file mode 100644 index 0000000..067ef37 --- /dev/null +++ b/doc/user/parseconfig.xml @@ -0,0 +1,72 @@ + + + + + Configuring the right options to build programs to work with the + libraries--especially shared libraries--installed on a POSIX system + can be very complicated. + Various utilies with names that end in config + can return command-line options for the + GNU Compiler Collection + + + + + + &SCons; construction environments have a &ParseConfig; method + that executes a utility and configures + the appropriate construction variables + in the environment + based on the command-line options + returned by the specified command. + + + + + + + env = Environment() + env.ParseConfig("pkg-config") + + + + + &SCons; will execute the specified command string + and XXX + + + + + % scons -Q + scons: `.' is up to date. + Must specify package names on the command line + + + + + XXX + + diff --git a/doc/user/preface.sgml b/doc/user/preface.sgml deleted file mode 100644 index 694f41b..0000000 --- a/doc/user/preface.sgml +++ /dev/null @@ -1,425 +0,0 @@ - - - - - Thank you for taking the time to read about &SCons;. - &SCons; is a next-generation - software construction tool, - or make tool--that is, a software utility - for building software (or other files) - and keeping built software up-to-date - whenever the underlying input files change. - - - - - - The most distinctive thing about &SCons; - is that its configuration files are - actually scripts, - written in the &Python; programming language. - This is in contrast to most alternative build tools, - which typically invent a new language to - configure the build. - &SCons; still has a learning curve, of course, - because you have to know what functions to call - to set up your build properly, - but the underlying syntax used should be familiar - to anyone who has ever looked at a Python script. - - - - - - Paradoxically, - using Python as the configuration file format - makes &SCons; - easier - for non-programmers to learn - than the cryptic languages of other build tools, - which are usually invented by programmers for other programmers. - This is in no small part due to the - consistency and readability that are built in to Python. - It just so happens that making a real, live - scripting language the basis for the - configuration files - makes it a snap for more accomplished programmers - to do more complicated things with builds, - as necessary. - - - - - -
- &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. - - - -
- - - - - -
- A Caveat About This Guide's Completeness - - - - One word of warning as you read through this Guide: - Like too much Open Source software out there, - the &SCons; documentation isn't always - kept up-to-date with the available features. - In other words, - there's a lot that &SCons; can do that - isn't yet covered in this User's Guide. - (Come to think of it, - that also describes a lot of proprietary software, doesn't it?) - - - - - - Although this User's Guide isn't as complete as we'd like it to be, - our development process does emphasize - making sure that the &SCons; man page is kept up-to-date - with new features. - So if you're trying to figure out how to do something - that &SCons; supports - but can't find enough (or any) information here, - it would be worth your while to look - at the man page to see if the information is covered there. - And if you do, - maybe you'd even consider contributing - a section to the User's Guide - so the next person looking for - that information won't have to - go through the same thing...? - - - -
- -
- 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. - - - - - - The entire &SCons; team have been - absolutely wonderful to work with, - and &SCons; would be nowhere near as useful a - tool without the energy, enthusiasm - and time people have contributed over the past few years. - The "core team" - of Chad Austin, Anthony Roach, Charles Crain, - Steve Leblanc, Gary Oberbrunner, Greg Spencer and Christoph Wiedemann - have been great about reviewing my (and other) changes - and catching problems before they get in the code base. - Of particular technical note: - Anthony's outstanding and innovative work on the tasking engine - has given &SCons; a vastly superior parallel build model; - Charles has been the master of the crucial Node infrastructure; - Christoph's work on the Configure infrastructure - has added crucial Autoconf-like functionality; - and Greg has provided excellent support - for Microsoft Visual Studio. - - - - - - Special thanks to David Snopek for contributing - his underlying "Autoscons" code that formed - the basis of Christoph's work with the Configure functionality. - David was extremely generous in making - this code available to &SCons;, - given that he initially released it under the GPL - and &SCons; is released under a less-restrictive MIT-style license. - - - - - - - - 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. - - - -
- -
- Contact - - - - The best way to contact people involved with SCons, - including the author, - is through the SCons mailing lists. - - - - - - If you want to ask general questions about how to use &SCons; - send email to &scons-users;. - - - - - - If you want to contact the &SCons; development community directly, - send email to &scons-devel;. - - - - - - If you want to receive announcements about &SCons;, - join the low-volume &scons-announce; mailing list. - - - -
diff --git a/doc/user/preface.xml b/doc/user/preface.xml new file mode 100644 index 0000000..694f41b --- /dev/null +++ b/doc/user/preface.xml @@ -0,0 +1,425 @@ + + + + + Thank you for taking the time to read about &SCons;. + &SCons; is a next-generation + software construction tool, + or make tool--that is, a software utility + for building software (or other files) + and keeping built software up-to-date + whenever the underlying input files change. + + + + + + The most distinctive thing about &SCons; + is that its configuration files are + actually scripts, + written in the &Python; programming language. + This is in contrast to most alternative build tools, + which typically invent a new language to + configure the build. + &SCons; still has a learning curve, of course, + because you have to know what functions to call + to set up your build properly, + but the underlying syntax used should be familiar + to anyone who has ever looked at a Python script. + + + + + + Paradoxically, + using Python as the configuration file format + makes &SCons; + easier + for non-programmers to learn + than the cryptic languages of other build tools, + which are usually invented by programmers for other programmers. + This is in no small part due to the + consistency and readability that are built in to Python. + It just so happens that making a real, live + scripting language the basis for the + configuration files + makes it a snap for more accomplished programmers + to do more complicated things with builds, + as necessary. + + + + + +
+ &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. + + + +
+ + + + + +
+ A Caveat About This Guide's Completeness + + + + One word of warning as you read through this Guide: + Like too much Open Source software out there, + the &SCons; documentation isn't always + kept up-to-date with the available features. + In other words, + there's a lot that &SCons; can do that + isn't yet covered in this User's Guide. + (Come to think of it, + that also describes a lot of proprietary software, doesn't it?) + + + + + + Although this User's Guide isn't as complete as we'd like it to be, + our development process does emphasize + making sure that the &SCons; man page is kept up-to-date + with new features. + So if you're trying to figure out how to do something + that &SCons; supports + but can't find enough (or any) information here, + it would be worth your while to look + at the man page to see if the information is covered there. + And if you do, + maybe you'd even consider contributing + a section to the User's Guide + so the next person looking for + that information won't have to + go through the same thing...? + + + +
+ +
+ 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. + + + + + + The entire &SCons; team have been + absolutely wonderful to work with, + and &SCons; would be nowhere near as useful a + tool without the energy, enthusiasm + and time people have contributed over the past few years. + The "core team" + of Chad Austin, Anthony Roach, Charles Crain, + Steve Leblanc, Gary Oberbrunner, Greg Spencer and Christoph Wiedemann + have been great about reviewing my (and other) changes + and catching problems before they get in the code base. + Of particular technical note: + Anthony's outstanding and innovative work on the tasking engine + has given &SCons; a vastly superior parallel build model; + Charles has been the master of the crucial Node infrastructure; + Christoph's work on the Configure infrastructure + has added crucial Autoconf-like functionality; + and Greg has provided excellent support + for Microsoft Visual Studio. + + + + + + Special thanks to David Snopek for contributing + his underlying "Autoscons" code that formed + the basis of Christoph's work with the Configure functionality. + David was extremely generous in making + this code available to &SCons;, + given that he initially released it under the GPL + and &SCons; is released under a less-restrictive MIT-style license. + + + + + + + + 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. + + + +
+ +
+ Contact + + + + The best way to contact people involved with SCons, + including the author, + is through the SCons mailing lists. + + + + + + If you want to ask general questions about how to use &SCons; + send email to &scons-users;. + + + + + + If you want to contact the &SCons; development community directly, + send email to &scons-devel;. + + + + + + If you want to receive announcements about &SCons;, + join the low-volume &scons-announce; mailing list. + + + +
diff --git a/doc/user/python.sgml b/doc/user/python.sgml deleted file mode 100644 index e2a7cdd..0000000 --- a/doc/user/python.sgml +++ /dev/null @@ -1,154 +0,0 @@ - - - diff --git a/doc/user/python.xml b/doc/user/python.xml new file mode 100644 index 0000000..e2a7cdd --- /dev/null +++ b/doc/user/python.xml @@ -0,0 +1,154 @@ + + + diff --git a/doc/user/repositories.sgml b/doc/user/repositories.sgml deleted file mode 100644 index f22611b..0000000 --- a/doc/user/repositories.sgml +++ /dev/null @@ -1,595 +0,0 @@ - - - - - Often, a software project will have - one or more central repositories, - directory trees that contain - source code, or derived files, or both. - You can eliminate additional unnecessary - rebuilds of files by having &SCons; - use files from one or more code repositories - to build files in your local build tree. - - - -
- The &Repository; Method - - - - - - It's often useful to allow multiple programmers working - on a project to build software from - source files and/or derived files that - are stored in a centrally-accessible repository, - 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.) - - You use the &Repository; method - to tell &SCons; to search one or more - central code repositories (in order) - for any source files and derived files - that are not present in the local build tree: - - - - - env = Environment() - env.Program('hello.c') - Repository('/usr/repository1', '/usr/repository2') - - - - - Multiple calls to the &Repository; method - will simply add repositories to the global list - that &SCons; maintains, - with the exception that &SCons; will automatically eliminate - the current directory and any non-existent - directories from the list. - - - -
- -
- Finding source files in repositories - - - - The above example - specifies that &SCons; - will first search for files under - the /usr/repository1 tree - and next under the /usr/repository2 tree. - &SCons; expects that any files it searches - for will be found in the same position - relative to the top-level directory. - In the above example, if the &hello_c; file is not - found in the local build tree, - &SCons; will search first for - a /usr/repository1/hello.c file - and then for a /usr/repository2/hello.c file - to use in its place. - - - - - - So given the &SConstruct; file above, - if the &hello_c; file exists in the local - build directory, - &SCons; will rebuild the &hello; program - as normal: - - - - - % scons -Q - cc -o hello.o -c hello.c - cc -o hello hello.o - - - - - If, however, there is no local &hello_c; file, - but one exists in /usr/repository1, - &SCons; will recompile the &hello; program - from the source file it finds in the repository: - - - - - - - % scons -Q - cc -o hello.o -c /usr/repository1/hello.c - cc -o hello hello.o - - - - - And similarly, if there is no local &hello_c; file - and no /usr/repository1/hello.c, - but one exists in /usr/repository2: - - - - - - - % scons -Q - 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-link-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-link-CFLAGS; - (or &cv-link-CXXFLAGS; or both) in your construction environment(s). - Make sure the option is used for all construction - environments that use C preprocessing! - - - - - - - - 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. - - - - - - -
- -
- -
- Finding the &SConstruct; file in repositories - - - - &SCons; will also search in repositories - for the &SConstruct; file and any specified &SConscript; files. - This poses a problem, though: how can &SCons; search a - repository tree for an &SConstruct; file - if the &SConstruct; file itself contains the information - about the pathname of the repository? - To solve this problem, &SCons; allows you - to specify repository directories - on the command line using the -Y option: - - - - - % scons -Q -Y /usr/repository1 -Y /usr/repository2 - - - - - When looking for source or derived files, - &SCons; will first search the repositories - specified on the command line, - and then search the repositories - specified in the &SConstruct; or &SConscript; files. - - - -
- -
- Finding derived files in repositories - - - - If a repository contains not only source files, - but also derived files (such as object files, - libraries, or executables), &SCons; will perform - its normal MD5 signature calculation to - decide if a derived file in a repository is up-to-date, - or the derived file must be rebuilt in the local build directory. - For the &SCons; signature calculation to work correctly, - a repository tree must contain the &sconsign; files - that &SCons; uses to keep track of signature information. - - - - - - Usually, this would be done by a build integrator - who would run &SCons; in the repository - to create all of its derived files and &sconsign; files, - or who would &SCons; in a separate build directory - and copying the resulting tree to the desired repository: - - - - - - - % cd /usr/repository1 - % scons -Q - 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 - - - - - (Note that this is safe even if the &SConstruct; file - lists /usr/repository1 as a repository, - because &SCons; will remove the current build directory - from its repository list for that invocation.) - - - - - - Now, with the repository populated, - we only need to create the one local source file - we're interested in working with at the moment, - and use the -Y option to - tell &SCons; to fetch any other files it needs - from the repository: - - - - - - % cd $HOME/build - % edit hello.c - % scons -Q -Y /usr/repository1 - cc -c -o hello.o hello.c - cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o - - - - - Notice that &SCons; realizes that it does not need to - rebuild local copies file1.o and file2.o files, - but instead uses the already-compiled files - from the repository. - - - -
- -
- Guaranteeing local copies of files - - - - If the repository tree contains the complete results of a build, - and we try to build from the repository - without any files in our local tree, - something moderately surprising happens: - - - - - % mkdir $HOME/build2 - % cd $HOME/build2 - % scons -Q -Y /usr/all/repository hello - scons: `hello' is up-to-date. - - - - - Why does &SCons; say that the &hello; program - is up-to-date when there is no &hello; program - in the local build directory? - Because the repository (not the local directory) - contains the up-to-date &hello; program, - and &SCons; correctly determines that nothing - needs to be done to rebuild that - up-to-date copy of the file. - - - - - - There are, however, many times when you want to ensure that a - local copy of a file always exists. - A packaging or testing script, for example, - may assume that certain generated files exist locally. - To tell &SCons; to make a copy of any up-to-date repository - file in the local build directory, - use the &Local; function: - - - - - env = Environment() - hello = env.Program('hello.c') - Local(hello) - - - - - If we then run the same command, - &SCons; will make a local copy of the program - from the repository copy, - and tell you that it is doing so: - - - - - % scons -Y /usr/all/repository hello - Local copy of hello from /usr/all/repository/hello - scons: `hello' is up-to-date. - - - - - (Notice that, because the act of making the local copy - is not considered a "build" of the &hello; file, - &SCons; still reports that it is up-to-date.) - - - -
diff --git a/doc/user/repositories.xml b/doc/user/repositories.xml new file mode 100644 index 0000000..f22611b --- /dev/null +++ b/doc/user/repositories.xml @@ -0,0 +1,595 @@ + + + + + Often, a software project will have + one or more central repositories, + directory trees that contain + source code, or derived files, or both. + You can eliminate additional unnecessary + rebuilds of files by having &SCons; + use files from one or more code repositories + to build files in your local build tree. + + + +
+ The &Repository; Method + + + + + + It's often useful to allow multiple programmers working + on a project to build software from + source files and/or derived files that + are stored in a centrally-accessible repository, + 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.) + + You use the &Repository; method + to tell &SCons; to search one or more + central code repositories (in order) + for any source files and derived files + that are not present in the local build tree: + + + + + env = Environment() + env.Program('hello.c') + Repository('/usr/repository1', '/usr/repository2') + + + + + Multiple calls to the &Repository; method + will simply add repositories to the global list + that &SCons; maintains, + with the exception that &SCons; will automatically eliminate + the current directory and any non-existent + directories from the list. + + + +
+ +
+ Finding source files in repositories + + + + The above example + specifies that &SCons; + will first search for files under + the /usr/repository1 tree + and next under the /usr/repository2 tree. + &SCons; expects that any files it searches + for will be found in the same position + relative to the top-level directory. + In the above example, if the &hello_c; file is not + found in the local build tree, + &SCons; will search first for + a /usr/repository1/hello.c file + and then for a /usr/repository2/hello.c file + to use in its place. + + + + + + So given the &SConstruct; file above, + if the &hello_c; file exists in the local + build directory, + &SCons; will rebuild the &hello; program + as normal: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + If, however, there is no local &hello_c; file, + but one exists in /usr/repository1, + &SCons; will recompile the &hello; program + from the source file it finds in the repository: + + + + + + + % scons -Q + cc -o hello.o -c /usr/repository1/hello.c + cc -o hello hello.o + + + + + And similarly, if there is no local &hello_c; file + and no /usr/repository1/hello.c, + but one exists in /usr/repository2: + + + + + + + % scons -Q + 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-link-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-link-CFLAGS; + (or &cv-link-CXXFLAGS; or both) in your construction environment(s). + Make sure the option is used for all construction + environments that use C preprocessing! + + + + + + + + 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. + + + + + + +
+ +
+ +
+ Finding the &SConstruct; file in repositories + + + + &SCons; will also search in repositories + for the &SConstruct; file and any specified &SConscript; files. + This poses a problem, though: how can &SCons; search a + repository tree for an &SConstruct; file + if the &SConstruct; file itself contains the information + about the pathname of the repository? + To solve this problem, &SCons; allows you + to specify repository directories + on the command line using the -Y option: + + + + + % scons -Q -Y /usr/repository1 -Y /usr/repository2 + + + + + When looking for source or derived files, + &SCons; will first search the repositories + specified on the command line, + and then search the repositories + specified in the &SConstruct; or &SConscript; files. + + + +
+ +
+ Finding derived files in repositories + + + + If a repository contains not only source files, + but also derived files (such as object files, + libraries, or executables), &SCons; will perform + its normal MD5 signature calculation to + decide if a derived file in a repository is up-to-date, + or the derived file must be rebuilt in the local build directory. + For the &SCons; signature calculation to work correctly, + a repository tree must contain the &sconsign; files + that &SCons; uses to keep track of signature information. + + + + + + Usually, this would be done by a build integrator + who would run &SCons; in the repository + to create all of its derived files and &sconsign; files, + or who would &SCons; in a separate build directory + and copying the resulting tree to the desired repository: + + + + + + + % cd /usr/repository1 + % scons -Q + 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 + + + + + (Note that this is safe even if the &SConstruct; file + lists /usr/repository1 as a repository, + because &SCons; will remove the current build directory + from its repository list for that invocation.) + + + + + + Now, with the repository populated, + we only need to create the one local source file + we're interested in working with at the moment, + and use the -Y option to + tell &SCons; to fetch any other files it needs + from the repository: + + + + + + % cd $HOME/build + % edit hello.c + % scons -Q -Y /usr/repository1 + cc -c -o hello.o hello.c + cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o + + + + + Notice that &SCons; realizes that it does not need to + rebuild local copies file1.o and file2.o files, + but instead uses the already-compiled files + from the repository. + + + +
+ +
+ Guaranteeing local copies of files + + + + If the repository tree contains the complete results of a build, + and we try to build from the repository + without any files in our local tree, + something moderately surprising happens: + + + + + % mkdir $HOME/build2 + % cd $HOME/build2 + % scons -Q -Y /usr/all/repository hello + scons: `hello' is up-to-date. + + + + + Why does &SCons; say that the &hello; program + is up-to-date when there is no &hello; program + in the local build directory? + Because the repository (not the local directory) + contains the up-to-date &hello; program, + and &SCons; correctly determines that nothing + needs to be done to rebuild that + up-to-date copy of the file. + + + + + + There are, however, many times when you want to ensure that a + local copy of a file always exists. + A packaging or testing script, for example, + may assume that certain generated files exist locally. + To tell &SCons; to make a copy of any up-to-date repository + file in the local build directory, + use the &Local; function: + + + + + env = Environment() + hello = env.Program('hello.c') + Local(hello) + + + + + If we then run the same command, + &SCons; will make a local copy of the program + from the repository copy, + and tell you that it is doing so: + + + + + % scons -Y /usr/all/repository hello + Local copy of hello from /usr/all/repository/hello + scons: `hello' is up-to-date. + + + + + (Notice that, because the act of making the local copy + is not considered a "build" of the &hello; file, + &SCons; still reports that it is up-to-date.) + + + +
diff --git a/doc/user/run.sgml b/doc/user/run.sgml deleted file mode 100644 index 56b8fa2..0000000 --- a/doc/user/run.sgml +++ /dev/null @@ -1,375 +0,0 @@ - - - - - - - XXX - - - -
- Command-Line Options - - - - XXX - - - -
- -
- Getting at Command-Line Arguments - - - - XXX - - - -
- -
- Selective Builds - - - - XXX - - - -
- - - -
- Overriding Construction Variables - - - - XXX - - - -
diff --git a/doc/user/run.xml b/doc/user/run.xml new file mode 100644 index 0000000..56b8fa2 --- /dev/null +++ b/doc/user/run.xml @@ -0,0 +1,375 @@ + + + + + + + XXX + + + +
+ Command-Line Options + + + + XXX + + + +
+ +
+ Getting at Command-Line Arguments + + + + XXX + + + +
+ +
+ Selective Builds + + + + XXX + + + +
+ + + +
+ Overriding Construction Variables + + + + XXX + + + +
diff --git a/doc/user/scanners.sgml b/doc/user/scanners.sgml deleted file mode 100644 index c068d03..0000000 --- a/doc/user/scanners.sgml +++ /dev/null @@ -1,317 +0,0 @@ - - - - - - - &SCons; has built-in scanners that know how to look in - C, Fortran and IDL source files for information about - other files that targets built from those files depend on--for example, - in the case of files that use the C preprocessor, - the .h files that are specified - using #include lines in the source. - You can use the same mechanisms that &SCons; uses to create - its built-in scanners to write scanners of your own for file types - that &SCons; does not know how to scan "out of the box." - - - -
- A Simple Scanner Example - - - - Suppose, for example, that we want to create a simple scanner - for .foo files. - A .foo file contains some text that - will be processed, - and can include other files on lines that begin - with include - followed by a file name: - - - - - include filename.foo - - - - - Scanning a file will be handled by a Python function - that you must supply. - Here is a function that will use the Python - re module - to scan for the include lines in our example: - - - - - import re - - include_re = re.compile(r'^include\s+(\S+)$', re.M) - - def kfile_scan(node, env, path, arg): - contents = node.get_contents() - return include_re.findall(contents) - - - - - The scanner function must - accept the four specified arguments - and return a list of implicit dependencies. - Presumably, these would be dependencies found - from examining the contents of the file, - although the function can perform any - manipulation at all to generate the list of - dependencies. - - - - - - - node - - - - - An &SCons; node object representing the file being scanned. - The path name to the file can be - used by converting the node to a string - using the str() function, - or an internal &SCons; get_contents() - object method can be used to fetch the contents. - - - - - - - env - - - - - The construction environment in effect for this scan. - The scanner function may choose to use construction - variables from this environment to affect its behavior. - - - - - - - path - - - - - A list of directories that form the search path for included files - for this scanner. - This is how &SCons; handles the &cv-link-CPPPATH; and &cv-link-LIBPATH; - variables. - - - - - - - arg - - - - - An optional argument that you can choose to - have passed to this scanner function by - various scanner instances. - - - - - - - - - - A Scanner object is created using the &Scanner; function, - which typically takes an skeys argument - to associate the type of file suffix with this scanner. - The Scanner object must then be associated with the - &cv-link-SCANNERS; construction variable of a construction environment, - typically by using the &Append; method: - - - - - kscan = Scanner(function = kfile_scan, - skeys = ['.k']) - env.Append(SCANNERS = kscan) - - - - - When we put it all together, it looks like: - - - - - import re - - include_re = re.compile(r'^include\s+(\S+)$', re.M) - - def kfile_scan(node, env, path): - contents = node.get_contents() - includes = include_re.findall(contents) - return includes - - kscan = Scanner(function = kfile_scan, - skeys = ['.k']) - - env = Environment(ENV = {'PATH' : '/usr/local/bin'}) - env.Append(SCANNERS = kscan) - - env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET') - - - - -
diff --git a/doc/user/scanners.xml b/doc/user/scanners.xml new file mode 100644 index 0000000..c068d03 --- /dev/null +++ b/doc/user/scanners.xml @@ -0,0 +1,317 @@ + + + + + + + &SCons; has built-in scanners that know how to look in + C, Fortran and IDL source files for information about + other files that targets built from those files depend on--for example, + in the case of files that use the C preprocessor, + the .h files that are specified + using #include lines in the source. + You can use the same mechanisms that &SCons; uses to create + its built-in scanners to write scanners of your own for file types + that &SCons; does not know how to scan "out of the box." + + + +
+ A Simple Scanner Example + + + + Suppose, for example, that we want to create a simple scanner + for .foo files. + A .foo file contains some text that + will be processed, + and can include other files on lines that begin + with include + followed by a file name: + + + + + include filename.foo + + + + + Scanning a file will be handled by a Python function + that you must supply. + Here is a function that will use the Python + re module + to scan for the include lines in our example: + + + + + import re + + include_re = re.compile(r'^include\s+(\S+)$', re.M) + + def kfile_scan(node, env, path, arg): + contents = node.get_contents() + return include_re.findall(contents) + + + + + The scanner function must + accept the four specified arguments + and return a list of implicit dependencies. + Presumably, these would be dependencies found + from examining the contents of the file, + although the function can perform any + manipulation at all to generate the list of + dependencies. + + + + + + + node + + + + + An &SCons; node object representing the file being scanned. + The path name to the file can be + used by converting the node to a string + using the str() function, + or an internal &SCons; get_contents() + object method can be used to fetch the contents. + + + + + + + env + + + + + The construction environment in effect for this scan. + The scanner function may choose to use construction + variables from this environment to affect its behavior. + + + + + + + path + + + + + A list of directories that form the search path for included files + for this scanner. + This is how &SCons; handles the &cv-link-CPPPATH; and &cv-link-LIBPATH; + variables. + + + + + + + arg + + + + + An optional argument that you can choose to + have passed to this scanner function by + various scanner instances. + + + + + + + + + + A Scanner object is created using the &Scanner; function, + which typically takes an skeys argument + to associate the type of file suffix with this scanner. + The Scanner object must then be associated with the + &cv-link-SCANNERS; construction variable of a construction environment, + typically by using the &Append; method: + + + + + kscan = Scanner(function = kfile_scan, + skeys = ['.k']) + env.Append(SCANNERS = kscan) + + + + + When we put it all together, it looks like: + + + + + import re + + include_re = re.compile(r'^include\s+(\S+)$', re.M) + + def kfile_scan(node, env, path): + contents = node.get_contents() + includes = include_re.findall(contents) + return includes + + kscan = Scanner(function = kfile_scan, + skeys = ['.k']) + + env = Environment(ENV = {'PATH' : '/usr/local/bin'}) + env.Append(SCANNERS = kscan) + + env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET') + + + + +
diff --git a/doc/user/sconf.sgml b/doc/user/sconf.sgml deleted file mode 100644 index df530fe..0000000 --- a/doc/user/sconf.sgml +++ /dev/null @@ -1,486 +0,0 @@ - - - - - &SCons; has integrated support for multi-platform build configuration - similar to that offered by GNU &Autoconf;, - such as - figuring out what libraries or header files - are available on the local system. - This section describes how to use - this &SCons; feature. - - - - - - This chapter is still under development, - so not everything is explained as well as it should be. - See the &SCons; man page for additional information. - - - -
- &Configure_Contexts; - - - - The basic framework for multi-platform build configuration - in &SCons; is to attach a &configure_context; to a - construction environment by calling the &Configure; function, - perform a number of checks for - libraries, functions, header files, etc., - and to then call the configure context's &Finish; method - to finish off the configuration: - - - - - env = Environment() - conf = Configure(env) - # Checks for libraries, header files, etc. go here! - env = conf.Finish() - - - - - &SCons; provides a number of basic checks, - as well as a mechanism for adding your own custom checks. - - - - - - Note that &SCons; uses its own dependency - mechanism to determine when a check - needs to be run--that is, - &SCons; does not run the checks - every time it is invoked, - but caches the values returned by previous checks - and uses the cached values unless something has changed. - This saves a tremendous amount - of developer time while working on - cross-platform build issues. - - - - - - The next sections describe - the basic checks that &SCons; supports, - as well as how to add your own custom checks. - - - -
- -
- Checking for the Existence of Header Files - - - - Testing the existence of a header file - requires knowing what language the header file is. - A configure context has a &CheckCHeader; method - that checks for the existence of a C header file: - - - - - env = Environment() - conf = Configure(env) - if not conf.CheckCHeader('math.h'): - print 'Math.h must be installed!' - Exit(1) - if conf.CheckCHeader('foo.h'): - conf.env.Append('-DHAS_FOO_H') - env = conf.Finish() - - - - - Note that you can choose to terminate - the build if a given header file doesn't exist, - or you can modify the contstruction environment - based on the existence of a header file. - - - - - - If you need to check for the existence - a C++ header file, - use the &CheckCXXHeader; method: - - - - - env = Environment() - conf = Configure(env) - if not conf.CheckCXXHeader('vector.h'): - print 'vector.h must be installed!' - Exit(1) - env = conf.Finish() - - -
- -
- Checking for the Availability of a Function - - - - Check for the availability of a specific function - using the &CheckFunc; method: - - - - - env = Environment() - conf = Configure(env) - if not conf.CheckFunc('strcpy'): - print 'Did not find strcpy(), using local version' - conf.env.Append('-Dstrcpy=my_local_strcpy') - env = conf.Finish() - - -
- -
- Checking for the Availability of a Library - - - - Check for the availability of a library - using the &CheckLib; method. - You only specify the basename of the library, - you don't need to add a lib - prefix or a .a or .lib suffix: - - - - - env = Environment() - conf = Configure(env) - if not conf.CheckLib('m'): - print 'Did not find libm.a or m.lib, exiting!' - Exit(1) - env = conf.Finish() - - - - - Because the ability to use a library successfully - often depends on having access to a header file - that describes the library's interface, - you can check for a library - and a header file - at the same time by using the - &CheckLibWithHeader; method: - - - - - env = Environment() - conf = Configure(env) - if not conf.CheckLibWithHeader('m', 'math.h'): - print 'Did not find libm.a or m.lib, exiting!' - Exit(1) - env = conf.Finish() - - - - - This is essentially shorthand for - separate calls to the &CheckHeader; and &CheckLib; - functions. - - - -
- -
- Checking for the Availability of a &typedef; - - - - Check for the availability of a &typedef; - by using the &CheckType; method: - - - - - env = Environment() - conf = Configure(env) - if not conf.CheckType('off_t'): - print 'Did not find off_t typedef, assuming int' - conf.env.Append(CCFLAGS = '-Doff_t=int') - env = conf.Finish() - - - - - You can also add a string that will be - placed at the beginning of the test file - that will be used to check for the &typedef;. - This provide a way to specify - files that must be included to find the &typedef;: - - - - - env = Environment() - conf = Configure(env) - if not conf.CheckType('off_t', '#include <sys/types.h>\n'): - print 'Did not find off_t typedef, assuming int' - conf.env.Append(CCFLAGS = '-Doff_t=int') - env = conf.Finish() - - -
- -
- Adding Your Own Custom Checks - - - - A custom check is a Python function - that checks for a certain condition to exist - on the running system, - usually using methods that &SCons; - supplies to take care of the details - of checking whether a compilation succeeds, - a link succeeds, - a program is runnable, - etc. - A simple custom check for the existence of - a specific library might look as follows: - - - - - mylib_test_source_file = """ - #include <mylib.h> - int main(int argc, char **argv) - { - MyLibrary mylib(argc, argv); - return 0; - } - """ - - def CheckMyLibrary(context): - context.Message('Checking for MyLibrary...') - result = context.TryLink(mylib_test_source_file, '.c') - context.Result(result) - return result - - - - - The &Message; and &Result; methods - should typically begin and end a custom check to - let the user know what's going on: - the &Message; call prints the - specified message (with no trailing newline) - and the &Result; call prints - ok if the check succeeds and - failed if it doesn't. - The &TryLink; method - actually tests for whether the - specified program text - will successfully link. - - - - - - (Note that a custom check can modify - its check based on any arguments you - choose to pass it, - or by using or modifying the configure context environment - in the context.env attribute.) - - - - - - This custom check function is - then attached to the &configure_context; - by passing a dictionary - to the &Configure; call - that maps a name of the check - to the underlying function: - - - - - env = Environment() - conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) - - - - - You'll typically want to make - the check and the function name the same, - as we've done here, - to avoid potential confusion. - - - - - - We can then put these pieces together - and actually call the CheckMyLibrary check - as follows: - - - - - mylib_test_source_file = """ - #include <mylib.h> - int main(int argc, char **argv) - { - MyLibrary mylib(argc, argv); - return 0; - } - """ - - def CheckMyLibrary(context): - context.Message('Checking for MyLibrary... ') - result = context.TryLink(mylib_test_source_file, '.c') - context.Result(result) - return result - - env = Environment() - conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) - if not conf.CheckMyLibrary(): - print 'MyLibrary is not installed!' - Exit(1) - env = conf.Finish() - - # We would then add actual calls like Program() to build - # something using the "env" construction environment. - - - - - If MyLibrary is not installed on the system, - the output will look like: - - - - - % scons - scons: Reading SConscript file ... - Checking for MyLibrary... failed - MyLibrary is not installed! - - - - - If MyLibrary is installed, - the output will look like: - - - - - % scons - scons: Reading SConscript file ... - Checking for MyLibrary... failed - scons: done reading SConscript - scons: Building targets ... - . - . - . - - -
- -
- Not Configuring When Cleaning Targets - - - - Using multi-platform configuration - as described in the previous sections - will run the configuration commands - even when invoking - scons -c - to clean targets: - - - - - % scons -Q -c - Checking for MyLibrary... ok - Removed foo.o - Removed foo - - - - - Although running the platform checks - when removing targets doesn't hurt anything, - it's usually unnecessary. - You can avoid this by using the - &GetOption;(); method to - check whether the (clean) - option has been invoked on the command line: - - - - - env = Environment() - if not env.GetOption('clean'): - conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) - if not conf.CheckMyLibrary(): - print 'MyLibrary is not installed!' - Exit(1) - env = conf.Finish() - - - - % scons -Q -c - Removed foo.o - Removed foo - - -
- - diff --git a/doc/user/sconf.xml b/doc/user/sconf.xml new file mode 100644 index 0000000..df530fe --- /dev/null +++ b/doc/user/sconf.xml @@ -0,0 +1,486 @@ + + + + + &SCons; has integrated support for multi-platform build configuration + similar to that offered by GNU &Autoconf;, + such as + figuring out what libraries or header files + are available on the local system. + This section describes how to use + this &SCons; feature. + + + + + + This chapter is still under development, + so not everything is explained as well as it should be. + See the &SCons; man page for additional information. + + + +
+ &Configure_Contexts; + + + + The basic framework for multi-platform build configuration + in &SCons; is to attach a &configure_context; to a + construction environment by calling the &Configure; function, + perform a number of checks for + libraries, functions, header files, etc., + and to then call the configure context's &Finish; method + to finish off the configuration: + + + + + env = Environment() + conf = Configure(env) + # Checks for libraries, header files, etc. go here! + env = conf.Finish() + + + + + &SCons; provides a number of basic checks, + as well as a mechanism for adding your own custom checks. + + + + + + Note that &SCons; uses its own dependency + mechanism to determine when a check + needs to be run--that is, + &SCons; does not run the checks + every time it is invoked, + but caches the values returned by previous checks + and uses the cached values unless something has changed. + This saves a tremendous amount + of developer time while working on + cross-platform build issues. + + + + + + The next sections describe + the basic checks that &SCons; supports, + as well as how to add your own custom checks. + + + +
+ +
+ Checking for the Existence of Header Files + + + + Testing the existence of a header file + requires knowing what language the header file is. + A configure context has a &CheckCHeader; method + that checks for the existence of a C header file: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckCHeader('math.h'): + print 'Math.h must be installed!' + Exit(1) + if conf.CheckCHeader('foo.h'): + conf.env.Append('-DHAS_FOO_H') + env = conf.Finish() + + + + + Note that you can choose to terminate + the build if a given header file doesn't exist, + or you can modify the contstruction environment + based on the existence of a header file. + + + + + + If you need to check for the existence + a C++ header file, + use the &CheckCXXHeader; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckCXXHeader('vector.h'): + print 'vector.h must be installed!' + Exit(1) + env = conf.Finish() + + +
+ +
+ Checking for the Availability of a Function + + + + Check for the availability of a specific function + using the &CheckFunc; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckFunc('strcpy'): + print 'Did not find strcpy(), using local version' + conf.env.Append('-Dstrcpy=my_local_strcpy') + env = conf.Finish() + + +
+ +
+ Checking for the Availability of a Library + + + + Check for the availability of a library + using the &CheckLib; method. + You only specify the basename of the library, + you don't need to add a lib + prefix or a .a or .lib suffix: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckLib('m'): + print 'Did not find libm.a or m.lib, exiting!' + Exit(1) + env = conf.Finish() + + + + + Because the ability to use a library successfully + often depends on having access to a header file + that describes the library's interface, + you can check for a library + and a header file + at the same time by using the + &CheckLibWithHeader; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckLibWithHeader('m', 'math.h'): + print 'Did not find libm.a or m.lib, exiting!' + Exit(1) + env = conf.Finish() + + + + + This is essentially shorthand for + separate calls to the &CheckHeader; and &CheckLib; + functions. + + + +
+ +
+ Checking for the Availability of a &typedef; + + + + Check for the availability of a &typedef; + by using the &CheckType; method: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckType('off_t'): + print 'Did not find off_t typedef, assuming int' + conf.env.Append(CCFLAGS = '-Doff_t=int') + env = conf.Finish() + + + + + You can also add a string that will be + placed at the beginning of the test file + that will be used to check for the &typedef;. + This provide a way to specify + files that must be included to find the &typedef;: + + + + + env = Environment() + conf = Configure(env) + if not conf.CheckType('off_t', '#include <sys/types.h>\n'): + print 'Did not find off_t typedef, assuming int' + conf.env.Append(CCFLAGS = '-Doff_t=int') + env = conf.Finish() + + +
+ +
+ Adding Your Own Custom Checks + + + + A custom check is a Python function + that checks for a certain condition to exist + on the running system, + usually using methods that &SCons; + supplies to take care of the details + of checking whether a compilation succeeds, + a link succeeds, + a program is runnable, + etc. + A simple custom check for the existence of + a specific library might look as follows: + + + + + mylib_test_source_file = """ + #include <mylib.h> + int main(int argc, char **argv) + { + MyLibrary mylib(argc, argv); + return 0; + } + """ + + def CheckMyLibrary(context): + context.Message('Checking for MyLibrary...') + result = context.TryLink(mylib_test_source_file, '.c') + context.Result(result) + return result + + + + + The &Message; and &Result; methods + should typically begin and end a custom check to + let the user know what's going on: + the &Message; call prints the + specified message (with no trailing newline) + and the &Result; call prints + ok if the check succeeds and + failed if it doesn't. + The &TryLink; method + actually tests for whether the + specified program text + will successfully link. + + + + + + (Note that a custom check can modify + its check based on any arguments you + choose to pass it, + or by using or modifying the configure context environment + in the context.env attribute.) + + + + + + This custom check function is + then attached to the &configure_context; + by passing a dictionary + to the &Configure; call + that maps a name of the check + to the underlying function: + + + + + env = Environment() + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + + + + + You'll typically want to make + the check and the function name the same, + as we've done here, + to avoid potential confusion. + + + + + + We can then put these pieces together + and actually call the CheckMyLibrary check + as follows: + + + + + mylib_test_source_file = """ + #include <mylib.h> + int main(int argc, char **argv) + { + MyLibrary mylib(argc, argv); + return 0; + } + """ + + def CheckMyLibrary(context): + context.Message('Checking for MyLibrary... ') + result = context.TryLink(mylib_test_source_file, '.c') + context.Result(result) + return result + + env = Environment() + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + if not conf.CheckMyLibrary(): + print 'MyLibrary is not installed!' + Exit(1) + env = conf.Finish() + + # We would then add actual calls like Program() to build + # something using the "env" construction environment. + + + + + If MyLibrary is not installed on the system, + the output will look like: + + + + + % scons + scons: Reading SConscript file ... + Checking for MyLibrary... failed + MyLibrary is not installed! + + + + + If MyLibrary is installed, + the output will look like: + + + + + % scons + scons: Reading SConscript file ... + Checking for MyLibrary... failed + scons: done reading SConscript + scons: Building targets ... + . + . + . + + +
+ +
+ Not Configuring When Cleaning Targets + + + + Using multi-platform configuration + as described in the previous sections + will run the configuration commands + even when invoking + scons -c + to clean targets: + + + + + % scons -Q -c + Checking for MyLibrary... ok + Removed foo.o + Removed foo + + + + + Although running the platform checks + when removing targets doesn't hurt anything, + it's usually unnecessary. + You can avoid this by using the + &GetOption;(); method to + check whether the (clean) + option has been invoked on the command line: + + + + + env = Environment() + if not env.GetOption('clean'): + conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) + if not conf.CheckMyLibrary(): + print 'MyLibrary is not installed!' + Exit(1) + env = conf.Finish() + + + + % scons -Q -c + Removed foo.o + Removed foo + + +
+ + diff --git a/doc/user/separate.sgml b/doc/user/separate.sgml deleted file mode 100644 index 57acd48..0000000 --- a/doc/user/separate.sgml +++ /dev/null @@ -1,461 +0,0 @@ - - - - - - - It's often useful to keep any built files completely - separate from the source files. - This is usually done by creating one or more separate - build directories - that are used to hold the built objects files, libraries, - and executable programs, etc. - for a specific flavor of build. - &SCons; provides two ways to do this, - one through the &SConscript; function that we've already seen, - and the second through a more flexible &BuildDir; function. - - - -
- Specifying a Build Directory as Part of an &SConscript; Call - - - - The most straightforward way to establish a build directory - uses the fact that the usual way to - set up a build hierarchy is to have an - &SConscript; file in the source subdirectory. - If you then pass a &build_dir; argument to the - &SConscript; function call: - - - - - SConscript('src/SConscript', build_dir='build') - - - - - &SCons; will then build all of the files in - the &build; subdirectory: - - - - - % ls src - SConscript hello.c - % scons -Q - cc -o build/hello.o -c build/hello.c - cc -o build/hello build/hello.o - % ls build - SConscript hello hello.c hello.o - - - - - But wait a minute--what's going on here? - &SCons; created the object file - build/hello.o - in the &build; subdirectory, - as expected. - But even though our &hello_c; file lives in the &src; subdirectory, - &SCons; has actually compiled a - build/hello.c file - to create the object file. - - - - - - What's happened is that &SCons; has duplicated - the &hello_c; file from the &src; subdirectory - to the &build; subdirectory, - and built the program from there. - The next section explains why &SCons; does this. - - - -
- -
- Why &SCons; Duplicates Source Files in a Build Directory - - - - &SCons; duplicates source files in build directories - because it's the most straightforward way to guarantee a correct build - regardless of include-file directory paths, - relative references between files, - or tool support for putting files in different locations, - and the &SCons; philosophy is to, by default, - guarantee a correct build in all cases. - - - - - - The most direct reason to duplicate source files - in build directories - is simply that some tools (mostly older vesions) - are written to only build their output files - in the same directory as the source files. - In this case, the choices are either - to build the output file in the source directory - and move it to the build directory, - or to duplicate the source files in the build directory. - - - - - - Additionally, - relative references between files - can cause problems if we don't - just duplicate the hierarchy of source files - in the build directory. - You can see this at work in - use of the C preprocessor #include - mechanism with double quotes, not angle brackets: - - - - - #include "file.h" - - - - - The de facto standard behavior - for most C compilers in this case - is to first look in the same directory - as the source file that contains the #include line, - then to look in the directories in the preprocessor search path. - Add to this that the &SCons; implementation of - support for code repositories - (described below) - means not all of the files - will be found in the same directory hierarchy, - and the simplest way to make sure - that the right include file is found - is to duplicate the source files into the build directory, - which provides a correct build - regardless of the original location(s) of the source files. - - - - - - Although source-file duplication guarantees a correct build - even in these end-cases, - it can usually be safely disabled. - The next section describes - how you can disable the duplication of source files - in the build directory. - - - -
- -
- Telling &SCons; to Not Duplicate Source Files in the Build Directory - - - - In most cases and with most tool sets, - &SCons; can place its target files in a build subdirectory - without - duplicating the source files - and everything will work just fine. - You can disable the default &SCons; behavior - by specifying duplicate=0 - when you call the &SConscript; function: - - - - - SConscript('src/SConscript', build_dir='build', duplicate=0) - - - - - When this flag is specified, - &SCons; uses the build directory - like most people expect--that is, - the output files are placed in the build directory - while the source files stay in the source directory: - - - - - % ls src - SConscript - hello.c - % scons -Q - cc -c src/hello.c -o build/hello.o - cc -o build/hello build/hello.o - % ls build - hello - hello.o - - -
- -
- The &BuildDir; Function - - - - Use the &BuildDir; function to establish that target - files should be built in a separate directory - from the source files: - - - - - BuildDir('build', 'src') - env = Environment() - env.Program('build/hello.c') - - - - - Note that when you're not using - an &SConscript; file in the &src; subdirectory, - you must actually specify that - the program must be built from - the build/hello.c - file that &SCons; will duplicate in the - &build; subdirectory. - - - - - - When using the &BuildDir; function directly, - &SCons; still duplicates the source files - in the build directory by default: - - - - - % ls src - hello.c - % scons -Q - cc -o build/hello.o -c build/hello.c - cc -o build/hello build/hello.o - % ls build - hello hello.c hello.o - - - - - You can specify the same duplicate=0 argument - that you can specify for an &SConscript; call: - - - - - BuildDir('build', 'src', duplicate=0) - env = Environment() - env.Program('build/hello.c') - - - - - In which case &SCons; - will disable duplication of the source files: - - - - - % ls src - hello.c - % scons -Q - cc -o build/hello.o -c src/hello.c - cc -o build/hello build/hello.o - % ls build - hello hello.o - - -
- -
- Using &BuildDir; With an &SConscript; File - - - - Even when using the &BuildDir; function, - it's much more natural to use it with - a subsidiary &SConscript; file. - For example, if the - src/SConscript - looks like this: - - - - - env = Environment() - env.Program('hello.c') - - - - - Then our &SConstruct; file could look like: - - - - - - BuildDir('build', 'src') - SConscript('build/SConscript') - - - - - Yielding the following output: - - - - - % ls src - SConscript hello.c - % scons -Q - cc -o build/hello.o -c build/hello.c - cc -o build/hello build/hello.o - % ls build - SConscript hello hello.c hello.o - - - - - Notice that this is completely equivalent - to the use of &SConscript; that we - learned about in the previous section. - - - -
- - diff --git a/doc/user/separate.xml b/doc/user/separate.xml new file mode 100644 index 0000000..57acd48 --- /dev/null +++ b/doc/user/separate.xml @@ -0,0 +1,461 @@ + + + + + + + It's often useful to keep any built files completely + separate from the source files. + This is usually done by creating one or more separate + build directories + that are used to hold the built objects files, libraries, + and executable programs, etc. + for a specific flavor of build. + &SCons; provides two ways to do this, + one through the &SConscript; function that we've already seen, + and the second through a more flexible &BuildDir; function. + + + +
+ Specifying a Build Directory as Part of an &SConscript; Call + + + + The most straightforward way to establish a build directory + uses the fact that the usual way to + set up a build hierarchy is to have an + &SConscript; file in the source subdirectory. + If you then pass a &build_dir; argument to the + &SConscript; function call: + + + + + SConscript('src/SConscript', build_dir='build') + + + + + &SCons; will then build all of the files in + the &build; subdirectory: + + + + + % ls src + SConscript hello.c + % scons -Q + cc -o build/hello.o -c build/hello.c + cc -o build/hello build/hello.o + % ls build + SConscript hello hello.c hello.o + + + + + But wait a minute--what's going on here? + &SCons; created the object file + build/hello.o + in the &build; subdirectory, + as expected. + But even though our &hello_c; file lives in the &src; subdirectory, + &SCons; has actually compiled a + build/hello.c file + to create the object file. + + + + + + What's happened is that &SCons; has duplicated + the &hello_c; file from the &src; subdirectory + to the &build; subdirectory, + and built the program from there. + The next section explains why &SCons; does this. + + + +
+ +
+ Why &SCons; Duplicates Source Files in a Build Directory + + + + &SCons; duplicates source files in build directories + because it's the most straightforward way to guarantee a correct build + regardless of include-file directory paths, + relative references between files, + or tool support for putting files in different locations, + and the &SCons; philosophy is to, by default, + guarantee a correct build in all cases. + + + + + + The most direct reason to duplicate source files + in build directories + is simply that some tools (mostly older vesions) + are written to only build their output files + in the same directory as the source files. + In this case, the choices are either + to build the output file in the source directory + and move it to the build directory, + or to duplicate the source files in the build directory. + + + + + + Additionally, + relative references between files + can cause problems if we don't + just duplicate the hierarchy of source files + in the build directory. + You can see this at work in + use of the C preprocessor #include + mechanism with double quotes, not angle brackets: + + + + + #include "file.h" + + + + + The de facto standard behavior + for most C compilers in this case + is to first look in the same directory + as the source file that contains the #include line, + then to look in the directories in the preprocessor search path. + Add to this that the &SCons; implementation of + support for code repositories + (described below) + means not all of the files + will be found in the same directory hierarchy, + and the simplest way to make sure + that the right include file is found + is to duplicate the source files into the build directory, + which provides a correct build + regardless of the original location(s) of the source files. + + + + + + Although source-file duplication guarantees a correct build + even in these end-cases, + it can usually be safely disabled. + The next section describes + how you can disable the duplication of source files + in the build directory. + + + +
+ +
+ Telling &SCons; to Not Duplicate Source Files in the Build Directory + + + + In most cases and with most tool sets, + &SCons; can place its target files in a build subdirectory + without + duplicating the source files + and everything will work just fine. + You can disable the default &SCons; behavior + by specifying duplicate=0 + when you call the &SConscript; function: + + + + + SConscript('src/SConscript', build_dir='build', duplicate=0) + + + + + When this flag is specified, + &SCons; uses the build directory + like most people expect--that is, + the output files are placed in the build directory + while the source files stay in the source directory: + + + + + % ls src + SConscript + hello.c + % scons -Q + cc -c src/hello.c -o build/hello.o + cc -o build/hello build/hello.o + % ls build + hello + hello.o + + +
+ +
+ The &BuildDir; Function + + + + Use the &BuildDir; function to establish that target + files should be built in a separate directory + from the source files: + + + + + BuildDir('build', 'src') + env = Environment() + env.Program('build/hello.c') + + + + + Note that when you're not using + an &SConscript; file in the &src; subdirectory, + you must actually specify that + the program must be built from + the build/hello.c + file that &SCons; will duplicate in the + &build; subdirectory. + + + + + + When using the &BuildDir; function directly, + &SCons; still duplicates the source files + in the build directory by default: + + + + + % ls src + hello.c + % scons -Q + cc -o build/hello.o -c build/hello.c + cc -o build/hello build/hello.o + % ls build + hello hello.c hello.o + + + + + You can specify the same duplicate=0 argument + that you can specify for an &SConscript; call: + + + + + BuildDir('build', 'src', duplicate=0) + env = Environment() + env.Program('build/hello.c') + + + + + In which case &SCons; + will disable duplication of the source files: + + + + + % ls src + hello.c + % scons -Q + cc -o build/hello.o -c src/hello.c + cc -o build/hello build/hello.o + % ls build + hello hello.o + + +
+ +
+ Using &BuildDir; With an &SConscript; File + + + + Even when using the &BuildDir; function, + it's much more natural to use it with + a subsidiary &SConscript; file. + For example, if the + src/SConscript + looks like this: + + + + + env = Environment() + env.Program('hello.c') + + + + + Then our &SConstruct; file could look like: + + + + + + BuildDir('build', 'src') + SConscript('build/SConscript') + + + + + Yielding the following output: + + + + + % ls src + SConscript hello.c + % scons -Q + cc -o build/hello.o -c build/hello.c + cc -o build/hello build/hello.o + % ls build + SConscript hello hello.c hello.o + + + + + Notice that this is completely equivalent + to the use of &SConscript; that we + learned about in the previous section. + + + +
+ + diff --git a/doc/user/simple.in b/doc/user/simple.in index 57fdd3a..b468f9a 100644 --- a/doc/user/simple.in +++ b/doc/user/simple.in @@ -239,7 +239,7 @@ We'll cover Java builds in more detail, including building Java archive (.jar) and other types of file, - in . + in . @@ -389,7 +389,7 @@ (and any other files) whenever it's necessary. (We'll learn more about how &SCons; decides when building or rebuilding a file - is necessary in , below.) + is necessary in , below.) diff --git a/doc/user/simple.sgml b/doc/user/simple.sgml deleted file mode 100644 index 9d3617d..0000000 --- a/doc/user/simple.sgml +++ /dev/null @@ -1,551 +0,0 @@ - - - - - In this chapter, - you will see several examples of - very simple build configurations using &SCons;, - which will demonstrate how easy - it is to use &SCons; to - build programs from several different programming languages - on different types of systems. - - - -
- Building Simple C / C++ Programs - - - - Here's the famous "Hello, World!" program in C: - - - - - int - main() - { - printf("Hello, world!\n"); - } - - - - - And here's how to build it using &SCons;. - Enter the following into a file named &SConstruct;: - - - - - Program('hello.c') - - - - - This minimal configuration file gives - &SCons; two pieces of information: - what you want to build - (an executable program), - and the input file from - which you want it built - (the hello.c file). - &b-link-Program; is a builder_method, - a Python call that tells &SCons; that you want to build an - executable program. - - - - - - That's it. Now run the &scons; command to build the program. - On a POSIX-compliant system like Linux or UNIX, - you'll see something like: - - - - - % scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - cc -o hello.o -c hello.c - cc -o hello hello.o - scons: done building targets. - - - - - On a Windows system with the Microsoft Visual C++ compiler, - you'll see something like: - - - - - C:\>scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - cl /nologo /c hello.c /Fohello.obj - link /nologo /OUT:hello.exe hello.obj - scons: done building targets. - - - - - First, notice that you only need - to specify the name of the source file, - and that &SCons; correctly deduces the names of - the object and executable files to be built - from the base of the source file name. - - - - - - Second, notice that the same input &SConstruct; file, - without any changes, - generates the correct output file names on both systems: - hello.o and hello - on POSIX systems, - hello.obj and hello.exe - on Windows systems. - This is a simple example of how &SCons; - makes it extremely easy to - write portable software builds. - - - - - - (Note that we won't provide duplicate side-by-side - POSIX and Windows output for all of the examples in this guide; - just keep in mind that, unless otherwise specified, - any of the examples should work equally well on both types of systems.) - - - -
- -
- Building Object Files - - - - The &b-link-Program; builder method is only one of - many builder methods that &SCons; provides - to build different types of files. - Another is the &b-link-Object; builder method, - which tells &SCons; to build an object file - from the specified source file: - - - - - Object('hello.c') - - - - - Now when you run the &scons; command to build the program, - it will build just the &hello_o; object file on a POSIX system: - - - - - % scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - cc -o hello.o -c hello.c - scons: done building targets. - - - - - And just the &hello_obj; object file - on a Windows system (with the Microsoft Visual C++ compiler): - - - - - C:\>scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - cl /nologo /c hello.c /Fohello.obj - scons: done building targets. - - -
- -
- Simple Java Builds - - - - &SCons; also makes building with Java extremely easy. - Unlike the &b-link-Program; and &b-link-Object; builder methods, - however, the &b-link-Java; builder method - requires that you specify - the name of a destination directory in which - you want the class files placed, - followed by the source directory - in which the .java files live: - - - - - Java('classes', 'src') - - - - - If the src directory - contains a single hello.java file, - then the output from running the &scons; command - would look something like this - (on a POSIX system): - - - - - % scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - javac -d classes -sourcepath src src/hello.java - scons: done building targets. - - - - - We'll cover Java builds in more detail, - including building Java archive (.jar) - and other types of file, - in . - - - -
- -
- Cleaning Up After a Build - - - - When using &SCons;, it is unnecessary to add special - commands or target names to clean up after a build. - Instead, you simply use the - -c or --clean - option when you invoke &SCons;, - and &SCons; removes the appropriate built files. - So if we build our example above - and then invoke scons -c - afterwards, the output on POSIX looks like: - - - - - - - % scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - cc -o hello.o -c hello.c - cc -o hello hello.o - scons: done building targets. - % scons -c - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Cleaning targets ... - Removed hello.o - Removed hello - scons: done cleaning targets. - - - - - And the output on Windows looks like: - - - - - C:\>scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - cl /nologo /c hello.c /Fohello.obj - link /nologo /OUT:hello.exe hello.obj - scons: done building targets. - C:\>scons -c - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Cleaning targets ... - Removed hello.obj - Removed hello.exe - scons: done cleaning targets. - - - - - Notice that &SCons; changes its output to tell you that it - is Cleaning targets ... and - done cleaning targets. - - - -
- -
- The &SConstruct; File - - - - If you're used to build systems like &Make; - you've already figured out that the &SConstruct; file - is the &SCons; equivalent of a &Makefile;. - That is, the &SConstruct; file is the input file - that &SCons; reads to control the build. - - - -
- &SConstruct; Files Are Python Scripts - - - - There is, however, an important difference between - an &SConstruct; file and a &Makefile;: - the &SConstruct; file is actually a Python script. - If you're not already familiar with Python, don't worry. - This User's Guide will introduce you step-by-step - to the relatively small amount of Python you'll - need to know to be able to use &SCons; effectively. - And Python is very easy to learn. - - - - - - One aspect of using Python as the - scripting language is that you can put comments - in your &SConstruct; file using Python's commenting convention; - that is, everything between a '#' and the end of the line - will be ignored: - - - - - # Arrange to build the "hello" program. - Program('hello.c') # "hello.c" is the source file. - - - - - You'll see throughout the remainder of this Guide - that being able to use the power of a - real scripting language - can greatly simplify the solutions - to complex requirements of real-world builds. - - - -
- -
- &SCons; Functions Are Order-Independent - - - - One important way in which the &SConstruct; - file is not exactly like a normal Python script, - and is more like a &Makefile;, - is that the order in which - the &SCons; functions are called in - the &SConstruct; file - does not - affect the order in which &SCons; - actually builds the programs and object files - you want it to build. - In programming parlance, - the &SConstruct; file is - declarative, - meaning you tell &SCons; what you want done - and let it figure out the order in which to do it, - rather than strictly imperative, - where you specify explicitly the order in - which to do things. - - - In other words, when you call the &b-link-Program; builder - (or any other builder method), - you're not telling &SCons; to build - the program at the instant the builder method is called. - Instead, you're telling &SCons; to build the program - that you want, for example, - a program built from a file named &hello_c;, - and it's up to &SCons; to build that program - (and any other files) whenever it's necessary. - (We'll learn more about how - &SCons; decides when building or rebuilding a file - is necessary in , below.) - - - - - - &SCons; reflects this distinction between - calling a builder method like &b-Program;> - and actually building the program - by printing the status messages that indicate - when it's "just reading" the &SConstruct; file, - and when it's actually building the target files. - This is to make it clear when &SCons; is - executing the Python statements that make up the &SConstruct; file, - and when &SCons; is actually executing the - commands or other actions to - build the necessary files. - - - - - - Let's clarify this with an example. - Python has a print statement that - prints a string of characters to the screen. - If we put print statements around - our calls to the &b-Program; builder method: - - - - - print "Calling Program('hello.c')" - Program('hello.c') - print "Calling Program('goodbye.c')" - Program('goodbye.c') - print "Finished calling Program()" - - - - - Then when we execute &SCons;, - we see the output from the print - statements in between the messages about - reading the &SConscript; files, - indicating that that is when the - Python statements are being executed: - - - - - % scons - scons: Reading SConscript files ... - Calling Program('hello.c') - Calling Program('goodbye.c') - Finished calling Program() - scons: done reading SConscript files. - scons: Building targets ... - cc -o goodbye.o -c goodbye.c - cc -o goodbye goodbye.o - cc -o hello.o -c hello.c - cc -o hello hello.o - scons: done building targets. - - - - - Notice also that &SCons; built the &goodbye; program first, - even though the "reading &SConscript;" output - shows that we called Program('hello.c') - first in the &SConstruct; file. - - - -
- -
- -
- Making the &SCons; Output Less Verbose - - - - You've already seen how &SCons; prints - some messages about what it's doing, - surrounding the actual commands used to build the software: - - - - - C:\>scons - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - cl /nologo /c hello.c /Fohello.obj - link /nologo /OUT:hello.exe hello.obj - scons: done building targets. - - - - - These messages emphasize the - order in which &SCons; does its work: - all of the configuration files - (generically referred to as &SConscript; files) - are read and executed first, - and only then are the target files built. - Among other benefits, these messages help to distinguish between - errors that occur while the configuration files are read, - and errors that occur while targets are being built. - - - - - - One drawback, of course, is that these messages clutter the output. - Fortunately, they're easily disabled by using - the &Q; option when invoking &SCons;: - - - - - C:\>scons -Q - cl /nologo /c hello.c /Fohello.obj - link /nologo /OUT:hello.exe hello.obj - - - - - Because we want this User's Guide to focus - on what &SCons; is actually doing, - we're going use the &Q; option - to remove these messages from the - output of all the remaining examples in this Guide. - - - -
diff --git a/doc/user/simple.xml b/doc/user/simple.xml new file mode 100644 index 0000000..c5ef430 --- /dev/null +++ b/doc/user/simple.xml @@ -0,0 +1,551 @@ + + + + + In this chapter, + you will see several examples of + very simple build configurations using &SCons;, + which will demonstrate how easy + it is to use &SCons; to + build programs from several different programming languages + on different types of systems. + + + +
+ Building Simple C / C++ Programs + + + + Here's the famous "Hello, World!" program in C: + + + + + int + main() + { + printf("Hello, world!\n"); + } + + + + + And here's how to build it using &SCons;. + Enter the following into a file named &SConstruct;: + + + + + Program('hello.c') + + + + + This minimal configuration file gives + &SCons; two pieces of information: + what you want to build + (an executable program), + and the input file from + which you want it built + (the hello.c file). + &b-link-Program; is a builder_method, + a Python call that tells &SCons; that you want to build an + executable program. + + + + + + That's it. Now run the &scons; command to build the program. + On a POSIX-compliant system like Linux or UNIX, + you'll see something like: + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cc -o hello.o -c hello.c + cc -o hello hello.o + scons: done building targets. + + + + + On a Windows system with the Microsoft Visual C++ compiler, + you'll see something like: + + + + + C:\>scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cl /nologo /c hello.c /Fohello.obj + link /nologo /OUT:hello.exe hello.obj + scons: done building targets. + + + + + First, notice that you only need + to specify the name of the source file, + and that &SCons; correctly deduces the names of + the object and executable files to be built + from the base of the source file name. + + + + + + Second, notice that the same input &SConstruct; file, + without any changes, + generates the correct output file names on both systems: + hello.o and hello + on POSIX systems, + hello.obj and hello.exe + on Windows systems. + This is a simple example of how &SCons; + makes it extremely easy to + write portable software builds. + + + + + + (Note that we won't provide duplicate side-by-side + POSIX and Windows output for all of the examples in this guide; + just keep in mind that, unless otherwise specified, + any of the examples should work equally well on both types of systems.) + + + +
+ +
+ Building Object Files + + + + The &b-link-Program; builder method is only one of + many builder methods that &SCons; provides + to build different types of files. + Another is the &b-link-Object; builder method, + which tells &SCons; to build an object file + from the specified source file: + + + + + Object('hello.c') + + + + + Now when you run the &scons; command to build the program, + it will build just the &hello_o; object file on a POSIX system: + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cc -o hello.o -c hello.c + scons: done building targets. + + + + + And just the &hello_obj; object file + on a Windows system (with the Microsoft Visual C++ compiler): + + + + + C:\>scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cl /nologo /c hello.c /Fohello.obj + scons: done building targets. + + +
+ +
+ Simple Java Builds + + + + &SCons; also makes building with Java extremely easy. + Unlike the &b-link-Program; and &b-link-Object; builder methods, + however, the &b-link-Java; builder method + requires that you specify + the name of a destination directory in which + you want the class files placed, + followed by the source directory + in which the .java files live: + + + + + Java('classes', 'src') + + + + + If the src directory + contains a single hello.java file, + then the output from running the &scons; command + would look something like this + (on a POSIX system): + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + javac -d classes -sourcepath src src/hello.java + scons: done building targets. + + + + + We'll cover Java builds in more detail, + including building Java archive (.jar) + and other types of file, + in . + + + +
+ +
+ Cleaning Up After a Build + + + + When using &SCons;, it is unnecessary to add special + commands or target names to clean up after a build. + Instead, you simply use the + -c or --clean + option when you invoke &SCons;, + and &SCons; removes the appropriate built files. + So if we build our example above + and then invoke scons -c + afterwards, the output on POSIX looks like: + + + + + + + % scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cc -o hello.o -c hello.c + cc -o hello hello.o + scons: done building targets. + % scons -c + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Cleaning targets ... + Removed hello.o + Removed hello + scons: done cleaning targets. + + + + + And the output on Windows looks like: + + + + + C:\>scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cl /nologo /c hello.c /Fohello.obj + link /nologo /OUT:hello.exe hello.obj + scons: done building targets. + C:\>scons -c + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Cleaning targets ... + Removed hello.obj + Removed hello.exe + scons: done cleaning targets. + + + + + Notice that &SCons; changes its output to tell you that it + is Cleaning targets ... and + done cleaning targets. + + + +
+ +
+ The &SConstruct; File + + + + If you're used to build systems like &Make; + you've already figured out that the &SConstruct; file + is the &SCons; equivalent of a &Makefile;. + That is, the &SConstruct; file is the input file + that &SCons; reads to control the build. + + + +
+ &SConstruct; Files Are Python Scripts + + + + There is, however, an important difference between + an &SConstruct; file and a &Makefile;: + the &SConstruct; file is actually a Python script. + If you're not already familiar with Python, don't worry. + This User's Guide will introduce you step-by-step + to the relatively small amount of Python you'll + need to know to be able to use &SCons; effectively. + And Python is very easy to learn. + + + + + + One aspect of using Python as the + scripting language is that you can put comments + in your &SConstruct; file using Python's commenting convention; + that is, everything between a '#' and the end of the line + will be ignored: + + + + + # Arrange to build the "hello" program. + Program('hello.c') # "hello.c" is the source file. + + + + + You'll see throughout the remainder of this Guide + that being able to use the power of a + real scripting language + can greatly simplify the solutions + to complex requirements of real-world builds. + + + +
+ +
+ &SCons; Functions Are Order-Independent + + + + One important way in which the &SConstruct; + file is not exactly like a normal Python script, + and is more like a &Makefile;, + is that the order in which + the &SCons; functions are called in + the &SConstruct; file + does not + affect the order in which &SCons; + actually builds the programs and object files + you want it to build. + In programming parlance, + the &SConstruct; file is + declarative, + meaning you tell &SCons; what you want done + and let it figure out the order in which to do it, + rather than strictly imperative, + where you specify explicitly the order in + which to do things. + + + In other words, when you call the &b-link-Program; builder + (or any other builder method), + you're not telling &SCons; to build + the program at the instant the builder method is called. + Instead, you're telling &SCons; to build the program + that you want, for example, + a program built from a file named &hello_c;, + and it's up to &SCons; to build that program + (and any other files) whenever it's necessary. + (We'll learn more about how + &SCons; decides when building or rebuilding a file + is necessary in , below.) + + + + + + &SCons; reflects this distinction between + calling a builder method like &b-Program;> + and actually building the program + by printing the status messages that indicate + when it's "just reading" the &SConstruct; file, + and when it's actually building the target files. + This is to make it clear when &SCons; is + executing the Python statements that make up the &SConstruct; file, + and when &SCons; is actually executing the + commands or other actions to + build the necessary files. + + + + + + Let's clarify this with an example. + Python has a print statement that + prints a string of characters to the screen. + If we put print statements around + our calls to the &b-Program; builder method: + + + + + print "Calling Program('hello.c')" + Program('hello.c') + print "Calling Program('goodbye.c')" + Program('goodbye.c') + print "Finished calling Program()" + + + + + Then when we execute &SCons;, + we see the output from the print + statements in between the messages about + reading the &SConscript; files, + indicating that that is when the + Python statements are being executed: + + + + + % scons + scons: Reading SConscript files ... + Calling Program('hello.c') + Calling Program('goodbye.c') + Finished calling Program() + scons: done reading SConscript files. + scons: Building targets ... + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + cc -o hello.o -c hello.c + cc -o hello hello.o + scons: done building targets. + + + + + Notice also that &SCons; built the &goodbye; program first, + even though the "reading &SConscript;" output + shows that we called Program('hello.c') + first in the &SConstruct; file. + + + +
+ +
+ +
+ Making the &SCons; Output Less Verbose + + + + You've already seen how &SCons; prints + some messages about what it's doing, + surrounding the actual commands used to build the software: + + + + + C:\>scons + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + cl /nologo /c hello.c /Fohello.obj + link /nologo /OUT:hello.exe hello.obj + scons: done building targets. + + + + + These messages emphasize the + order in which &SCons; does its work: + all of the configuration files + (generically referred to as &SConscript; files) + are read and executed first, + and only then are the target files built. + Among other benefits, these messages help to distinguish between + errors that occur while the configuration files are read, + and errors that occur while targets are being built. + + + + + + One drawback, of course, is that these messages clutter the output. + Fortunately, they're easily disabled by using + the &Q; option when invoking &SCons;: + + + + + C:\>scons -Q + cl /nologo /c hello.c /Fohello.obj + link /nologo /OUT:hello.exe hello.obj + + + + + Because we want this User's Guide to focus + on what &SCons; is actually doing, + we're going use the &Q; option + to remove these messages from the + output of all the remaining examples in this Guide. + + + +
diff --git a/doc/user/sourcecode.sgml b/doc/user/sourcecode.sgml deleted file mode 100644 index 6cb4162..0000000 --- a/doc/user/sourcecode.sgml +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - - XXX - - - -
- Fetching Source Code From BitKeeper - - - - XXX - - - - - env = Environment() - env.SourceCode('.', env.BitKeeper()) - env.Program('hello.c') - - - - % scons -Q - bk get - - bk get hello.c - cc -o hello.o -c hello.c - cc -o hello hello.o - - -
- -
- Fetching Source Code From CVS - - - - XXX - - - - - env = Environment() - env.SourceCode('.', env.CVS('/usr/local/CVS')) - env.Program('hello.c') - - - - % scons -Q - cvs -d /usr/local/CVS co - - cvs -d /usr/local/CVS co hello.c - cc -o hello.o -c hello.c - cc -o hello hello.o - - -
- -
- Fetching Source Code From RCS - - - - XXX - - - - - env = Environment() - env.SourceCode('.', env.RCS()) - env.Program('hello.c') - - - - % scons -Q - co - - co hello.c - cc -o hello.o -c hello.c - cc -o hello hello.o - - -
- -
- Fetching Source Code From SCCS - - - - XXX - - - - - env = Environment() - env.SourceCode('.', env.SCCS()) - env.Program('hello.c') - - - - % scons -Q - sccs get - - sccs get hello.c - cc -o hello.o -c hello.c - cc -o hello hello.o - - -
- - diff --git a/doc/user/sourcecode.xml b/doc/user/sourcecode.xml new file mode 100644 index 0000000..6cb4162 --- /dev/null +++ b/doc/user/sourcecode.xml @@ -0,0 +1,161 @@ + + + + + + + XXX + + + +
+ Fetching Source Code From BitKeeper + + + + XXX + + + + + env = Environment() + env.SourceCode('.', env.BitKeeper()) + env.Program('hello.c') + + + + % scons -Q + bk get - + bk get hello.c + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Fetching Source Code From CVS + + + + XXX + + + + + env = Environment() + env.SourceCode('.', env.CVS('/usr/local/CVS')) + env.Program('hello.c') + + + + % scons -Q + cvs -d /usr/local/CVS co - + cvs -d /usr/local/CVS co hello.c + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Fetching Source Code From RCS + + + + XXX + + + + + env = Environment() + env.SourceCode('.', env.RCS()) + env.Program('hello.c') + + + + % scons -Q + co - + co hello.c + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ +
+ Fetching Source Code From SCCS + + + + XXX + + + + + env = Environment() + env.SourceCode('.', env.SCCS()) + env.Program('hello.c') + + + + % scons -Q + sccs get - + sccs get hello.c + cc -o hello.o -c hello.c + cc -o hello hello.o + + +
+ + diff --git a/doc/user/tasks.sgml b/doc/user/tasks.sgml deleted file mode 100644 index 7be3562..0000000 --- a/doc/user/tasks.sgml +++ /dev/null @@ -1,109 +0,0 @@ - - - -There is a common set of simple tasks that many build configurations rely -on as they become more complex. Most build tools have special -purpose constructs for performing these tasks, but since &SConscript; -files are &Python; scripts, you can use more flexible built-in &Python; -services to perform these tasks. This appendix lists a number of these -tasks and how to implement them in &Python;. - - - -Wildcard globbing to create a list of filenames - -import glob -files = glob.glob(wildcard) - - - - -Filename extension substitution - -import os.path -filename = os.path.splitext(filename)[0]+extension - - - - -Appending a path prefix to a list of filenames - -import os.path -filenames = [os.path.join(prefix, x) for x in filenames] - - -or in Python 1.5.2: - - -import os.path -new_filenames = [] -for x in filenames: - new_filenames.append(os.path.join(prefix, x)) - - - - -Substituting a path prefix with another one - -if filename.find(old_prefix) == 0: - filename = filename.replace(old_prefix, new_prefix) - - -or in Python 1.5.2: - - -import string -if string.find(filename, old_prefix) == 0: - filename = string.replace(filename, old_prefix, new_prefix) - - - - -Filtering a filename list to exclude/retain only a specific set -of extensions - -import os.path -filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions] - - -or in Python 1.5.2: - - -import os.path -new_filenames = [] -for x in filenames: - if os.path.splitext(x)[1] in extensions: - new_filenames.append(x) - - - - -The "backtick function": run a shell command and capture the -output -import os -output = os.popen(command).read() - - diff --git a/doc/user/tasks.xml b/doc/user/tasks.xml new file mode 100644 index 0000000..7be3562 --- /dev/null +++ b/doc/user/tasks.xml @@ -0,0 +1,109 @@ + + + +There is a common set of simple tasks that many build configurations rely +on as they become more complex. Most build tools have special +purpose constructs for performing these tasks, but since &SConscript; +files are &Python; scripts, you can use more flexible built-in &Python; +services to perform these tasks. This appendix lists a number of these +tasks and how to implement them in &Python;. + + + +Wildcard globbing to create a list of filenames + +import glob +files = glob.glob(wildcard) + + + + +Filename extension substitution + +import os.path +filename = os.path.splitext(filename)[0]+extension + + + + +Appending a path prefix to a list of filenames + +import os.path +filenames = [os.path.join(prefix, x) for x in filenames] + + +or in Python 1.5.2: + + +import os.path +new_filenames = [] +for x in filenames: + new_filenames.append(os.path.join(prefix, x)) + + + + +Substituting a path prefix with another one + +if filename.find(old_prefix) == 0: + filename = filename.replace(old_prefix, new_prefix) + + +or in Python 1.5.2: + + +import string +if string.find(filename, old_prefix) == 0: + filename = string.replace(filename, old_prefix, new_prefix) + + + + +Filtering a filename list to exclude/retain only a specific set +of extensions + +import os.path +filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions] + + +or in Python 1.5.2: + + +import os.path +new_filenames = [] +for x in filenames: + if os.path.splitext(x)[1] in extensions: + new_filenames.append(x) + + + + +The "backtick function": run a shell command and capture the +output +import os +output = os.popen(command).read() + + diff --git a/doc/user/tools.sgml b/doc/user/tools.sgml deleted file mode 100644 index 512bf97..0000000 --- a/doc/user/tools.sgml +++ /dev/null @@ -1,38 +0,0 @@ - - - - -This appendix contains descriptions of all of the -Tools modules that are -available "out of the box" in this version of SCons. - - - - - -&tools-gen; - - diff --git a/doc/user/tools.xml b/doc/user/tools.xml new file mode 100644 index 0000000..512bf97 --- /dev/null +++ b/doc/user/tools.xml @@ -0,0 +1,38 @@ + + + + +This appendix contains descriptions of all of the +Tools modules that are +available "out of the box" in this version of SCons. + + + + + +&tools-gen; + + diff --git a/doc/user/troubleshoot.sgml b/doc/user/troubleshoot.sgml deleted file mode 100644 index 3df9c67..0000000 --- a/doc/user/troubleshoot.sgml +++ /dev/null @@ -1,1240 +0,0 @@ - - - - - The experience of configuring any - software build tool to build a large code base - usually, at some point, - involves trying to figure out why - the tool is behaving a certain way, - and how to get it to behave the way you want. - &SCons; is no different. - This appendix contains a number of - different ways in which you can - get some additional insight into &SCons;' behavior. - - - - - - Note that we're always interested in trying to - improve how you can troubleshoot configuration problems. - If you run into a problem that has - you scratching your head, - and which there just doesn't seem to be a good way to debug, - odds are pretty good that someone else will run into - the same problem, too. - If so, please let the SCons development team know - (preferably by filing a bug report - or feature request at our project pages at tigris.org) - so that we can use your feedback - to try to come up with a better way to help you, - and others, get the necessary insight into &SCons; behavior - to help identify and fix configuration issues. - - - -
- Why is That Target Being Rebuilt? the &debug-explain; Option - - - - Let's look at a simple example of - a misconfigured build - that causes a target to be rebuilt - every time &SCons; is run: - - - - - # Intentionally misspell the output file name in the - # command used to create the file: - Command('file.out', 'file.in', 'cp $SOURCE file.oout') - - - - - (Note to Windows users: The POSIX &cp; command - copies the first file named on the command line - to the second file. - In our example, it copies the &file_in; file - to the &file_out; file.) - - - - - - Now if we run &SCons; multiple times on this example, - we see that it re-runs the &cp; - command every time: - - - - - % scons -Q - cp file.in file.oout - % scons -Q - cp file.in file.oout - % scons -Q - cp file.in file.oout - - - - - In this example, - the underlying cause is obvious: - we've intentionally misspelled the output file name - in the &cp; command, - so the command doesn't actually - build the &file_out; file that we've told &SCons; to expect. - But if the problem weren't obvious, - it would be helpful - to specify the &debug-explain; option - on the command line - to have &SCons; tell us very specifically - why it's decided to rebuild the target: - - - - - % scons -Q --debug=explain - scons: building `file.out' because it doesn't exist - cp file.in file.oout - - - - - If this had been a more complicated example - involving a lot of build output, - having &SCons; tell us that - it's trying to rebuild the target file - because it doesn't exist - would be an important clue - that something was wrong with - the command that we invoked to build it. - - - - - - The &debug-explain; option also comes in handy - to help figure out what input file changed. - Given a simple configuration that builds - a program from three source files, - changing one of the source files - and rebuilding with the &debug-explain; - option shows very specifically - why &SCons; rebuilds the files that it does: - - - - - - - % scons -Q - 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 -o file2.o -c file2.c - scons: rebuilding `prog' because `file2.o' changed - cc -o prog file1.o file2.o file3.o - - - - - This becomes even more helpful - in identifying when a file is rebuilt - due to a change in an implicit dependency, - such as an incuded .h file. - If the file1.c - and file3.c files - in our example - both included a &hello_h; file, - then changing that included file - and re-running &SCons; with the &debug-explain; option - will pinpoint that it's the change to the included file - that starts the chain of rebuilds: - - - - - - - % scons -Q - 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 -o file1.o -c -I. file1.c - scons: rebuilding `file3.o' because `hello.h' changed - cc -o file3.o -c -I. file3.c - scons: rebuilding `prog' because: - `file1.o' changed - `file3.o' changed - cc -o prog file1.o file2.o file3.o - - - - - (Note that the &debug-explain; option will only tell you - why &SCons; decided to rebuild necessary targets. - It does not tell you what files it examined - when deciding not - to rebuild a target file, - which is often a more valuable question to answer.) - - - -
- -
- What's in That Construction Environment? the &Dump; Method - - - - When you create a construction environment, - &SCons; populates it - with construction variables that are set up - for various compilers, linkers and utilities - that it finds on your system. - Although this is usually helpful and what you want, - it might be frustrating if &SCons; - doesn't set certain variables that you - expect to be sit. - In situations like this, - it's sometimes helpful to use the - construction environment &Dump; method - to print all or some of - the construction variables. - Note that the &Dump; method - returns - the representation of the variables - in the environment - for you to print (or otherwise manipulate): - - - - - env = Environment() - print env.Dump() - - - - - On a POSIX system with gcc installed, - this might generate: - - - - - % scons - scons: Reading SConscript files ... - { 'BUILDERS': {}, - 'CONFIGUREDIR': '#/.sconf_temp', - 'CONFIGURELOG': '#/config.log', - 'CPPSUFFIXES': [ '.c', - '.C', - '.cxx', - '.cpp', - '.c++', - '.cc', - '.h', - '.H', - '.hxx', - '.hpp', - '.hh', - '.F', - '.fpp', - '.FPP', - '.m', - '.mm', - '.S', - '.spp', - '.SPP'], - 'DSUFFIXES': ['.d'], - 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c3fdac>, - 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c3fdcc>, - 'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'}, - 'ESCAPE': <function escape at 0xb7ba1f0c>, - 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c3fdec>, - 'IDLSUFFIXES': ['.idl', '.IDL'], - 'INSTALL': <function installFunc at 0xb7c4317c>, - 'INSTALLSTR': <function installStr at 0xb7c431b4>, - 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'], - 'LIBPREFIX': 'lib', - 'LIBPREFIXES': '$LIBPREFIX', - 'LIBSUFFIX': '.a', - 'LIBSUFFIXES': ['$LIBSUFFIX', '$SHLIBSUFFIX'], - 'MAXLINELENGTH': 128072, - 'OBJPREFIX': '', - 'OBJSUFFIX': '.o', - 'PLATFORM': 'posix', - 'PROGPREFIX': '', - 'PROGSUFFIX': '', - 'PSPAWN': <function piped_env_spawn at 0xb7bb12cc>, - 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c3fe0c>, - 'SCANNERS': [], - 'SHELL': 'sh', - 'SHLIBPREFIX': '$LIBPREFIX', - 'SHLIBSUFFIX': '.so', - 'SHOBJPREFIX': '$OBJPREFIX', - 'SHOBJSUFFIX': '$OBJSUFFIX', - 'SPAWN': <function spawnvpe_spawn at 0xb7ba1d4c>, - 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7bce89c>, - 'TEMPFILEPREFIX': '@', - 'TOOLS': [], - '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', - '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', - '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', - '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', - '__RPATH': '$_RPATH', - '_concat': <function _concat at 0xb7c43224>, - '_defines': <function _defines at 0xb7c432cc>, - '_installStr': <function installStr at 0xb7c431b4>, - '_stripixes': <function _stripixes at 0xb7c43294>} - scons: done reading SConscript files. - scons: Building targets ... - scons: `.' is up to date. - scons: done building targets. - - - - - On a Windows system with Visual C++ - the output might look like: - - - - - C:\>scons - scons: Reading SConscript files ... - { 'BUILDERS': {'Object': <SCons.Builder.CompositeBuilder instance at 0xb7b6354c>, 'SharedObject': <SCons.Builder.CompositeBuilder instance at 0xb7b636cc>, 'StaticObject': <SCons.Builder.CompositeBuilder instance at 0xb7b6354c>, 'PCH': <SCons.Builder.BuilderBase instance at 0xb7bd6e8c>, 'RES': <SCons.Builder.BuilderBase instance at 0xb7b5b9ec>}, - 'CC': 'cl', - 'CCCOM': <SCons.Action.FunctionAction instance at 0xb7b63b6c>, - 'CCCOMFLAGS': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS', - 'CCFLAGS': ['/nologo'], - 'CCPCHFLAGS': ['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'], - 'CCPDBFLAGS': ['${(PDB and "/Z7") or ""}'], - 'CFILESUFFIX': '.c', - 'CFLAGS': [], - 'CONFIGUREDIR': '#/.sconf_temp', - 'CONFIGURELOG': '#/config.log', - 'CPPDEFPREFIX': '/D', - 'CPPDEFSUFFIX': '', - 'CPPSUFFIXES': [ '.c', - '.C', - '.cxx', - '.cpp', - '.c++', - '.cc', - '.h', - '.H', - '.hxx', - '.hpp', - '.hh', - '.F', - '.fpp', - '.FPP', - '.m', - '.mm', - '.S', - '.spp', - '.SPP'], - 'CXX': '$CC', - 'CXXCOM': '$CXX $CXXFLAGS $CCCOMFLAGS', - 'CXXFILESUFFIX': '.cc', - 'CXXFLAGS': ['$CCFLAGS', '$(', '/TP', '$)'], - 'DSUFFIXES': ['.d'], - 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c5adac>, - 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c5adcc>, - 'ENV': { 'INCLUDE': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\include', - 'LIB': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\lib', - 'PATH': 'C:\\Program Files\\Microsoft Visual Studio\\Common\\tools\\WIN95;C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\bin;C:\\Program Files\\Microsoft Visual Studio\\Common\\tools;C:\\Program Files\\Microsoft Visual Studio/VC98\\bin', - 'PATHEXT': '.COM;.EXE;.BAT;.CMD', - 'SystemRoot': 'C:/WINDOWS'}, - 'ESCAPE': <function escape at 0xb7bcf454>, - 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c5adec>, - 'IDLSUFFIXES': ['.idl', '.IDL'], - 'INCPREFIX': '/I', - 'INCSUFFIX': '', - 'INSTALL': <function installFunc at 0xb7c5e17c>, - 'INSTALLSTR': <function installStr at 0xb7c5e1b4>, - 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'], - 'LIBPREFIX': '', - 'LIBPREFIXES': ['$LIBPREFIX'], - 'LIBSUFFIX': '.lib', - 'LIBSUFFIXES': ['$LIBSUFFIX'], - 'MAXLINELENGTH': 2048, - 'MSVS': {'VERSION': '6.0', 'VERSIONS': ['6.0']}, - 'MSVS_VERSION': '6.0', - 'OBJPREFIX': '', - '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 ""}'], - 'PLATFORM': 'win32', - 'PROGPREFIX': '', - 'PROGSUFFIX': '.exe', - 'PSPAWN': <function piped_spawn at 0xb7bcf3ac>, - 'RC': 'rc', - 'RCCOM': '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES', - 'RCFLAGS': [], - 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c5ae0c>, - 'SCANNERS': [], - 'SHCC': '$CC', - 'SHCCCOM': <SCons.Action.FunctionAction instance at 0xb7b63bcc>, - 'SHCCFLAGS': ['$CCFLAGS'], - 'SHCFLAGS': ['$CFLAGS'], - 'SHCXX': '$CXX', - 'SHCXXCOM': '$SHCXX $SHCXXFLAGS $CCCOMFLAGS', - 'SHCXXFLAGS': ['$CXXFLAGS'], - 'SHELL': None, - 'SHLIBPREFIX': '', - 'SHLIBSUFFIX': '.dll', - 'SHOBJPREFIX': '$OBJPREFIX', - 'SHOBJSUFFIX': '$OBJSUFFIX', - 'SPAWN': <function spawn at 0xb7bcf41c>, - 'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1, - 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7be989c>, - 'TEMPFILEPREFIX': '@', - 'TOOLS': ['msvc'], - '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', - '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', - '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', - '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', - '_concat': <function _concat at 0xb7c5e224>, - '_defines': <function _defines at 0xb7c5e2cc>, - '_installStr': <function installStr at 0xb7c5e1b4>, - '_stripixes': <function _stripixes at 0xb7c5e294>} - scons: done reading SConscript files. - scons: Building targets ... - scons: `.' is up to date. - scons: done building targets. - - - - - The construction environments in these examples have - actually been restricted to just gcc and Visual C++, - respectively. - In a real-life situation, - the construction environments will - likely contain a great many more variables. - - - - - - To make it easier to see just what you're - interested in, - the &Dump; method allows you to - specify a specific constrcution variable - that you want to disply. - For example, - it's not unusual to want to verify - the external environment used to execute build commands, - to make sure that the PATH and other - environment variables are set up the way they should be. - You can do this as follows: - - - - - env = Environment() - print env.Dump('ENV') - - - - - Which might display the following when executed on a POSIX system: - - - - - % scons - scons: Reading SConscript files ... - {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'} - scons: done reading SConscript files. - scons: Building targets ... - scons: `.' is up to date. - scons: done building targets. - - - - - And the following when executed on a Windows system: - - - - - C:\>scons - scons: Reading SConscript files ... - { 'INCLUDE': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\include', - 'LIB': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\lib', - 'PATH': 'C:\\Program Files\\Microsoft Visual Studio\\Common\\tools\\WIN95;C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\bin;C:\\Program Files\\Microsoft Visual Studio\\Common\\tools;C:\\Program Files\\Microsoft Visual Studio/VC98\\bin', - 'PATHEXT': '.COM;.EXE;.BAT;.CMD', - 'SystemRoot': 'C:/WINDOWS'} - scons: done reading SConscript files. - scons: Building targets ... - scons: `.' is up to date. - scons: done building targets. - - -
- -
- - What Dependencies Does &SCons; Know About? the &tree; Option - - - - Sometimes the best way to try to figure out what - &SCons; is doing is simply to take a look at the - dependency graph that it constructs - based on your &SConscript; files. - The --tree option - will display all or part of the - &SCons; dependency graph in an - "ASCII art" graphical format - that shows the dependency hierarchy. - - - - - - For example, given the following input &SConstruct; file: - - - - - env = Environment(CPPPATH = ['.']) - env.Program('prog', ['f1.c', 'f2.c', 'f3.c']) - - - - - Running &SCons; with the --tree=all - option yields: - - - - - % scons -Q --tree=all - cc -o f1.o -c -I. f1.c - cc -o f2.o -c -I. f2.c - cc -o f3.o -c -I. f3.c - cc -o prog f1.o f2.o f3.o - +-. - +-- - +-SConstruct - +-f1.c - +-f1.o - | +-f1.c - | +-inc.h - +-f2.c - +-f2.o - | +-f2.c - | +-inc.h - +-f3.c - +-f3.o - | +-f3.c - | +-inc.h - +-inc.h - +-prog - +-f1.o - | +-f1.c - | +-inc.h - +-f2.o - | +-f2.c - | +-inc.h - +-f3.o - +-f3.c - +-inc.h - - - - - The tree will also be printed when the - -n (no execute) option is used, - which allows you to examine the dependency graph - for a configuration without actually - rebuilding anything in the tree. - - - - - - The --tree option only prints - the dependency graph for the specified targets - (or the default target(s) if none are specified on the command line). - So if you specify a target like f2.o - on the command line, - the --tree option will only - print the dependency graph for that file: - - - - - % scons -Q --tree=all f2.o - cc -o f2.o -c -I. f2.c - +-f2.o - +-f2.c - +-inc.h - - - - - This is, of course, useful for - restricting the output from a very large - build configuration to just a - portion in which you're interested. - Multiple targets are fine, - in which case a tree will be printed - for each specified target: - - - - - % scons -Q --tree=all f1.o f3.o - cc -o f1.o -c -I. f1.c - +-f1.o - +-f1.c - +-inc.h - cc -o f3.o -c -I. f3.c - +-f3.o - +-f3.c - +-inc.h - - - - - The status argument may be used - to tell &SCons; to print status information about - each file in the dependency graph: - - - - - % scons -Q --tree=status - cc -o f1.o -c -I. f1.c - cc -o f2.o -c -I. f2.c - cc -o f3.o -c -I. f3.c - cc -o prog f1.o f2.o f3.o - E = exists - R = exists in repository only - b = implicit builder - B = explicit builder - S = side effect - P = precious - A = always build - C = current - N = no clean - H = no cache - - [E b ]+-. - [ ] +-- - [E ] +-SConstruct - [E ] +-f1.c - [E B C ] +-f1.o - [E ] | +-f1.c - [E ] | +-inc.h - [E ] +-f2.c - [E B C ] +-f2.o - [E ] | +-f2.c - [E ] | +-inc.h - [E ] +-f3.c - [E B C ] +-f3.o - [E ] | +-f3.c - [E ] | +-inc.h - [E ] +-inc.h - [E B C ] +-prog - [E B C ] +-f1.o - [E ] | +-f1.c - [E ] | +-inc.h - [E B C ] +-f2.o - [E ] | +-f2.c - [E ] | +-inc.h - [E B C ] +-f3.o - [E ] +-f3.c - [E ] +-inc.h - - - - - Note that --tree=all,status is equivalent; - the all - is assumed if only status is present. - As an alternative to all, - you can specify --tree=derived - to have &SCons; only print derived targets - in the tree output, - skipping source files - (like .c and .h files): - - - - - % scons -Q --tree=derived - cc -o f1.o -c -I. f1.c - cc -o f2.o -c -I. f2.c - cc -o f3.o -c -I. f3.c - cc -o prog f1.o f2.o f3.o - +-. - - - - - You can use the status - modifier with derived as well: - - - - - % scons -Q --tree=derived,status - cc -o f1.o -c -I. f1.c - cc -o f2.o -c -I. f2.c - cc -o f3.o -c -I. f3.c - cc -o prog f1.o f2.o f3.o - E = exists - R = exists in repository only - b = implicit builder - B = explicit builder - S = side effect - P = precious - A = always build - C = current - N = no clean - H = no cache - - [E b ]+-. - [E B C ] +-f1.o - [E B C ] +-f2.o - [E B C ] +-f3.o - [E B C ] +-prog - [E B C ] +-f1.o - [E B C ] +-f2.o - [E B C ] +-f3.o - - - - - Note that the order of the --tree= - arguments doesn't matter; - --tree=status,derived is - completely equivalent. - - - - - - The default behavior of the --tree option - is to repeat all of the dependencies each time the library dependency - (or any other dependency file) is encountered in the tree. - If certain target files share other target files, - such as two programs that use the same library: - - - - - env = Environment(CPPPATH = ['.'], - LIBS = ['foo'], - LIBPATH = ['.']) - env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) - env.Program('prog1.c') - env.Program('prog2.c') - - - - - Then there can be a lot of repetition in the - --tree= output: - - - - - % scons -Q --tree=all - cc -o f1.o -c -I. f1.c - cc -o f2.o -c -I. f2.c - cc -o f3.o -c -I. f3.c - ar rc libfoo.a f1.o f2.o f3.o - ranlib libfoo.a - cc -o prog1.o -c -I. prog1.c - cc -o prog1 prog1.o -L. -lfoo - cc -o prog2.o -c -I. prog2.c - cc -o prog2 prog2.o -L. -lfoo - +-. - +-- - +-SConstruct - +-f1.c - +-f1.o - | +-f1.c - | +-inc.h - +-f2.c - +-f2.o - | +-f2.c - | +-inc.h - +-f3.c - +-f3.o - | +-f3.c - | +-inc.h - +-inc.h - +-libfoo.a - | +-f1.o - | | +-f1.c - | | +-inc.h - | +-f2.o - | | +-f2.c - | | +-inc.h - | +-f3.o - | +-f3.c - | +-inc.h - +-prog1 - | +-prog1.o - | | +-prog1.c - | | +-inc.h - | +-libfoo.a - | +-f1.o - | | +-f1.c - | | +-inc.h - | +-f2.o - | | +-f2.c - | | +-inc.h - | +-f3.o - | +-f3.c - | +-inc.h - +-prog1.c - +-prog1.o - | +-prog1.c - | +-inc.h - +-prog2 - | +-prog2.o - | | +-prog2.c - | | +-inc.h - | +-libfoo.a - | +-f1.o - | | +-f1.c - | | +-inc.h - | +-f2.o - | | +-f2.c - | | +-inc.h - | +-f3.o - | +-f3.c - | +-inc.h - +-prog2.c - +-prog2.o - +-prog2.c - +-inc.h - - - - - In a large configuration with many internal libraries - and include files, - this can very quickly lead to huge output trees. - To help make this more manageable, - a prune modifier may - be added to the option list, - in which case &SCons; - will print the name of a target that has - already been visited during the tree-printing - in [square brackets] - as an indication that the dependencies - of the target file may be found - by looking farther up the tree: - - - - - % scons -Q --tree=prune - cc -o f1.o -c -I. f1.c - cc -o f2.o -c -I. f2.c - cc -o f3.o -c -I. f3.c - ar rc libfoo.a f1.o f2.o f3.o - ranlib libfoo.a - cc -o prog1.o -c -I. prog1.c - cc -o prog1 prog1.o -L. -lfoo - cc -o prog2.o -c -I. prog2.c - cc -o prog2 prog2.o -L. -lfoo - +-. - +-- - +-SConstruct - +-f1.c - +-f1.o - | +-[f1.c] - | +-inc.h - +-f2.c - +-f2.o - | +-[f2.c] - | +-[inc.h] - +-f3.c - +-f3.o - | +-[f3.c] - | +-[inc.h] - +-[inc.h] - +-libfoo.a - | +-[f1.o] - | +-[f2.o] - | +-[f3.o] - +-prog1 - | +-prog1.o - | | +-prog1.c - | | +-[inc.h] - | +-[libfoo.a] - +-[prog1.c] - +-[prog1.o] - +-prog2 - | +-prog2.o - | | +-prog2.c - | | +-[inc.h] - | +-[libfoo.a] - +-[prog2.c] - +-[prog2.o] - - - - - Like the status keyword, - the prune argument by itself - is equivalent to --tree=all,prune. - - - -
- -
- - How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option - - - - Sometimes it's useful to look at the - pre-substitution string - that &SCons; uses to generate - the command lines it executes. - This can be done with the &debug-presub; option: - - - - - - - - - % scons -Q --debug=presub - Building prog.o with action: - $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCOMCOM $SOURCES - cc -o prog.o -c -I. prog.c - Building prog with action: - $SMART_LINKCOM - cc -o prog prog.o - - -
- -
- - Where is &SCons; Searching for Libraries? the &debug-findlibs; Option - - - - To get some insight into what library names - &SCons; is searching for, - and in which directories it is searching, - Use the --debug=findlibs option. - Given the following input &SConstruct; file: - - - - - env = Environment(LIBPATH = ['libs1', 'libs2']) - env.Program('prog.c', LIBS=['foo', 'bar']) - - - - - And the libraries libfoo.a - and libbar.a - in libs1 and libs2, - respectively, - use of the --debug=findlibs option yields: - - - - - % scons -Q --debug=findlibs - findlibs: looking for 'libfoo.a' in 'libs1' ... - findlibs: ... FOUND 'libfoo.a' in 'libs1' - findlibs: looking for 'libfoo.so' in 'libs1' ... - findlibs: looking for 'libfoo.so' in 'libs2' ... - findlibs: looking for 'libbar.a' in 'libs1' ... - findlibs: looking for 'libbar.a' in 'libs2' ... - findlibs: ... FOUND 'libbar.a' in 'libs2' - findlibs: looking for 'libbar.so' in 'libs1' ... - findlibs: looking for 'libbar.so' in 'libs2' ... - cc -o prog.o -c prog.c - cc -o prog prog.o -Llibs1 -Llibs2 -lfoo -lbar - - -
- - - -
- - Where is &SCons; Blowing Up? the &debug-stacktrace; Option - - - - In general, &SCons; tries to keep its error - messages short and informative. - That means we usually try to avoid showing - the stack traces that are familiar - to experienced Python programmers, - since they usually contain much more - information than is useful to most people. - - - - - - For example, the following &SConstruct; file: - - - - - Program('prog.c') - - - - - Generates the following error if the - prog.c file - does not exist: - - - - - % scons -Q - scons: *** Source `prog.c' not found, needed by target `prog.o'. Stop. - - - - - In this case, - the error is pretty obvious. - But if it weren't, - and you wanted to try to get more information - about the error, - the &debug-stacktrace; option - would show you exactly where in the &SCons; source code - the problem occurs: - - - - - % scons -Q --debug=stacktrace - scons: *** Source `prog.c' not found, needed by target `prog.o'. Stop. - scons: internal stack trace: - File "/home/knight/SCons/scons.0.96.C763/bootstrap/src/engine/SCons/Job.py", line 111, in start - task.prepare() - File "/home/knight/SCons/scons.0.96.C763/bootstrap/src/engine/SCons/Taskmaster.py", line 166, in prepare - t.prepare() - File "/home/knight/SCons/scons.0.96.C763/bootstrap/src/engine/SCons/Node/FS.py", line 2137, in prepare - SCons.Node.Node.prepare(self) - File "/home/knight/SCons/scons.0.96.C763/bootstrap/src/engine/SCons/Node/__init__.py", line 806, in prepare - raise SCons.Errors.StopError, desc - - - - - Of course, if you do need to dive into the &SCons; source code, - we'd like to know if, or how, - the error messages or troubleshooting options - could have been improved to avoid that. - Not everyone has the necessary time or - Python skill to dive into the source code, - and we'd like to improve &SCons; - for those people as well... - - - -
- -
- - How is &SCons; Making Its Decisions? the &taskmastertrace; Option - - - - The internal &SCons; subsystem that handles walking - the dependency graph - and controls the decision-making about what to rebuild - is the Taskmaster. - &SCons; supports a --taskmastertrace - option that tells the Taskmaster to print - information about the children (dependencies) - of the various Nodes on its walk down the graph, - which specific dependent Nodes are being evaluated, - and in what order. - - - - - - The --taskmastertrace option - takes as an argument the name of a file in - which to put the trace output, - with - (a single hyphen) - indicating that the trace messages - should be printed to the standard output: - - - - - env = Environment(CPPPATH = ['.']) - env.Program('prog.c') - - - - % scons -Q --taskmastertrace=- prog - Taskmaster: 'prog': children: - ['prog.o'] - waiting on unstarted children: - ['prog.o'] - Taskmaster: 'prog.o': children: - ['inc.h', 'prog.c'] - evaluating prog.o - cc -o prog.o -c -I. prog.c - Taskmaster: 'prog': children: - ['prog.o'] - evaluating prog - cc -o prog prog.o - Taskmaster: 'prog': already handled (executed) - - - - - The --taskmastertrace option - doesn't provide information about the actual - calculations involved in deciding if a file is up-to-date, - but it does show all of the dependencies - it knows about for each Node, - and the order in which those dependencies are evaluated. - This can be useful as an alternate way to determine - whether or not your &SCons; configuration, - or the implicit dependency scan, - has actually identified all the correct dependencies - you want it to. - - - -
- - - - diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml new file mode 100644 index 0000000..6ec6185 --- /dev/null +++ b/doc/user/troubleshoot.xml @@ -0,0 +1,1232 @@ + + + + + The experience of configuring any + software build tool to build a large code base + usually, at some point, + involves trying to figure out why + the tool is behaving a certain way, + and how to get it to behave the way you want. + &SCons; is no different. + This appendix contains a number of + different ways in which you can + get some additional insight into &SCons;' behavior. + + + + + + Note that we're always interested in trying to + improve how you can troubleshoot configuration problems. + If you run into a problem that has + you scratching your head, + and which there just doesn't seem to be a good way to debug, + odds are pretty good that someone else will run into + the same problem, too. + If so, please let the SCons development team know + (preferably by filing a bug report + or feature request at our project pages at tigris.org) + so that we can use your feedback + to try to come up with a better way to help you, + and others, get the necessary insight into &SCons; behavior + to help identify and fix configuration issues. + + + +
+ Why is That Target Being Rebuilt? the &debug-explain; Option + + + + Let's look at a simple example of + a misconfigured build + that causes a target to be rebuilt + every time &SCons; is run: + + + + + # Intentionally misspell the output file name in the + # command used to create the file: + Command('file.out', 'file.in', 'cp $SOURCE file.oout') + + + + + (Note to Windows users: The POSIX &cp; command + copies the first file named on the command line + to the second file. + In our example, it copies the &file_in; file + to the &file_out; file.) + + + + + + Now if we run &SCons; multiple times on this example, + we see that it re-runs the &cp; + command every time: + + + + + % scons -Q + cp file.in file.oout + % scons -Q + cp file.in file.oout + % scons -Q + cp file.in file.oout + + + + + In this example, + the underlying cause is obvious: + we've intentionally misspelled the output file name + in the &cp; command, + so the command doesn't actually + build the &file_out; file that we've told &SCons; to expect. + But if the problem weren't obvious, + it would be helpful + to specify the &debug-explain; option + on the command line + to have &SCons; tell us very specifically + why it's decided to rebuild the target: + + + + + % scons -Q --debug=explain + scons: building `file.out' because it doesn't exist + cp file.in file.oout + + + + + If this had been a more complicated example + involving a lot of build output, + having &SCons; tell us that + it's trying to rebuild the target file + because it doesn't exist + would be an important clue + that something was wrong with + the command that we invoked to build it. + + + + + + The &debug-explain; option also comes in handy + to help figure out what input file changed. + Given a simple configuration that builds + a program from three source files, + changing one of the source files + and rebuilding with the &debug-explain; + option shows very specifically + why &SCons; rebuilds the files that it does: + + + + + + + % scons -Q + 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 -o file2.o -c file2.c + scons: rebuilding `prog' because `file2.o' changed + cc -o prog file1.o file2.o file3.o + + + + + This becomes even more helpful + in identifying when a file is rebuilt + due to a change in an implicit dependency, + such as an incuded .h file. + If the file1.c + and file3.c files + in our example + both included a &hello_h; file, + then changing that included file + and re-running &SCons; with the &debug-explain; option + will pinpoint that it's the change to the included file + that starts the chain of rebuilds: + + + + + + + % scons -Q + 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 -o file1.o -c -I. file1.c + scons: rebuilding `file3.o' because `hello.h' changed + cc -o file3.o -c -I. file3.c + scons: rebuilding `prog' because: + `file1.o' changed + `file3.o' changed + cc -o prog file1.o file2.o file3.o + + + + + (Note that the &debug-explain; option will only tell you + why &SCons; decided to rebuild necessary targets. + It does not tell you what files it examined + when deciding not + to rebuild a target file, + which is often a more valuable question to answer.) + + + +
+ +
+ What's in That Construction Environment? the &Dump; Method + + + + When you create a construction environment, + &SCons; populates it + with construction variables that are set up + for various compilers, linkers and utilities + that it finds on your system. + Although this is usually helpful and what you want, + it might be frustrating if &SCons; + doesn't set certain variables that you + expect to be sit. + In situations like this, + it's sometimes helpful to use the + construction environment &Dump; method + to print all or some of + the construction variables. + Note that the &Dump; method + returns + the representation of the variables + in the environment + for you to print (or otherwise manipulate): + + + + + env = Environment() + print env.Dump() + + + + + On a POSIX system with gcc installed, + this might generate: + + + + + % scons + scons: Reading SConscript files ... + { 'BUILDERS': {'InstallAs': <function InstallAsBuilderWrapper at 0xb23a28>, 'Install': <function InstallBuilderWrapper at 0xb1b7d0>}, + 'CONFIGUREDIR': '#/.sconf_temp', + 'CONFIGURELOG': '#/config.log', + 'CPPSUFFIXES': [ '.c', + '.C', + '.cxx', + '.cpp', + '.c++', + '.cc', + '.h', + '.H', + '.hxx', + '.hpp', + '.hh', + '.F', + '.fpp', + '.FPP', + '.m', + '.mm', + '.S', + '.spp', + '.SPP'], + 'DSUFFIXES': ['.d'], + 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xa1eb48>, + 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xa1eb90>, + 'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'}, + 'ESCAPE': <function escape at 0xb1fa28>, + 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xa1ebd8>, + 'IDLSUFFIXES': ['.idl', '.IDL'], + 'INSTALL': <function copyFunc at 0xb23aa0>, + 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'], + 'LIBPREFIX': 'lib', + 'LIBPREFIXES': '$LIBPREFIX', + 'LIBSUFFIX': '.a', + 'LIBSUFFIXES': ['$LIBSUFFIX', '$SHLIBSUFFIX'], + 'MAXLINELENGTH': 128072, + 'OBJPREFIX': '', + 'OBJSUFFIX': '.o', + 'PLATFORM': 'posix', + 'PROGPREFIX': '', + 'PROGSUFFIX': '', + 'PSPAWN': <function piped_env_spawn at 0xb23230>, + 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xa1ec20>, + 'SCANNERS': [], + 'SHELL': 'sh', + 'SHLIBPREFIX': '$LIBPREFIX', + 'SHLIBSUFFIX': '.so', + 'SHOBJPREFIX': '$OBJPREFIX', + 'SHOBJSUFFIX': '$OBJSUFFIX', + 'SPAWN': <function spawnvpe_spawn at 0xb1f7d0>, + 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xa4e170>, + 'TEMPFILEPREFIX': '@', + 'TOOLS': ['install', 'install'], + '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', + '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', + '__RPATH': '$_RPATH', + '_concat': <function _concat at 0xa3d398>, + '_defines': <function _defines at 0xa3d500>, + '_stripixes': <function _stripixes at 0xa3d488>} + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + + + + On a Windows system with Visual C++ + the output might look like: + + + + + C:\>scons + scons: Reading SConscript files ... + { 'BUILDERS': {'RES': <SCons.Builder.BuilderBase instance at 0xb39518>, 'Object': <SCons.Builder.CompositeBuilder instance at 0xb4a710>, 'InstallAs': <function InstallAsBuilderWrapper at 0xb45c08>, 'PCH': <SCons.Builder.BuilderBase instance at 0xb1cef0>, 'Install': <function InstallBuilderWrapper at 0xb1b7d0>, 'SharedObject': <SCons.Builder.CompositeBuilder instance at 0xb4aa28>, 'StaticObject': <SCons.Builder.CompositeBuilder instance at 0xb4a710>}, + 'CC': 'cl', + 'CCCOM': <SCons.Action.FunctionAction instance at 0xb4c290>, + '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', + '.C', + '.cxx', + '.cpp', + '.c++', + '.cc', + '.h', + '.H', + '.hxx', + '.hpp', + '.hh', + '.F', + '.fpp', + '.FPP', + '.m', + '.mm', + '.S', + '.spp', + '.SPP'], + 'CXX': '$CC', + 'CXXCOM': '$CXX $CXXFLAGS $CCCOMFLAGS', + 'CXXFILESUFFIX': '.cc', + 'CXXFLAGS': ['$CCFLAGS', '$(', '/TP', '$)'], + 'DSUFFIXES': ['.d'], + 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xa1eb48>, + 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xa1eb90>, + 'ENV': { 'INCLUDE': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\include', + 'LIB': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\lib', + 'PATH': 'C:\\Program Files\\Microsoft Visual Studio\\Common\\tools\\WIN95;C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\bin;C:\\Program Files\\Microsoft Visual Studio\\Common\\tools;C:\\Program Files\\Microsoft Visual Studio/VC98\\bin', + 'PATHEXT': '.COM;.EXE;.BAT;.CMD', + 'SystemRoot': 'C:/WINDOWS'}, + 'ESCAPE': <function escape at 0xb24848>, + 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xa1ebd8>, + 'IDLSUFFIXES': ['.idl', '.IDL'], + 'INCPREFIX': '/I', + 'INCSUFFIX': '', + 'INSTALL': <function copyFunc at 0xb45c80>, + 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'], + 'LIBPREFIX': '', + 'LIBPREFIXES': ['$LIBPREFIX'], + 'LIBSUFFIX': '.lib', + 'LIBSUFFIXES': ['$LIBSUFFIX'], + 'MAXLINELENGTH': 2048, + 'MSVS': {'VERSION': '6.0', 'VERSIONS': ['6.0']}, + 'MSVS_VERSION': '6.0', + 'OBJPREFIX': '', + '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 ""}'], + 'PLATFORM': 'win32', + 'PROGPREFIX': '', + 'PROGSUFFIX': '.exe', + 'PSPAWN': <function piped_spawn at 0xb20488>, + 'RC': 'rc', + 'RCCOM': '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES', + 'RCFLAGS': [], + 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xa1ec20>, + 'SCANNERS': [], + 'SHCC': '$CC', + 'SHCCCOM': <SCons.Action.FunctionAction instance at 0xb4c320>, + 'SHCCFLAGS': ['$CCFLAGS'], + 'SHCFLAGS': ['$CFLAGS'], + 'SHCXX': '$CXX', + 'SHCXXCOM': '$SHCXX $SHCXXFLAGS $CCCOMFLAGS', + 'SHCXXFLAGS': ['$CXXFLAGS'], + 'SHELL': None, + 'SHLIBPREFIX': '', + 'SHLIBSUFFIX': '.dll', + 'SHOBJPREFIX': '$OBJPREFIX', + 'SHOBJSUFFIX': '$OBJSUFFIX', + 'SPAWN': <function spawn at 0xb247d0>, + 'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1, + 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xa4e170>, + 'TEMPFILEPREFIX': '@', + 'TOOLS': ['msvc', 'install', 'install'], + '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', + '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', + '_concat': <function _concat at 0xa3d398>, + '_defines': <function _defines at 0xa3d500>, + '_stripixes': <function _stripixes at 0xa3d488>} + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + + + + The construction environments in these examples have + actually been restricted to just gcc and Visual C++, + respectively. + In a real-life situation, + the construction environments will + likely contain a great many more variables. + + + + + + To make it easier to see just what you're + interested in, + the &Dump; method allows you to + specify a specific constrcution variable + that you want to disply. + For example, + it's not unusual to want to verify + the external environment used to execute build commands, + to make sure that the PATH and other + environment variables are set up the way they should be. + You can do this as follows: + + + + + env = Environment() + print env.Dump('ENV') + + + + + Which might display the following when executed on a POSIX system: + + + + + % scons + scons: Reading SConscript files ... + {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'} + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + + + + And the following when executed on a Windows system: + + + + + C:\>scons + scons: Reading SConscript files ... + { 'INCLUDE': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\include', + 'LIB': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\lib', + 'PATH': 'C:\\Program Files\\Microsoft Visual Studio\\Common\\tools\\WIN95;C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\bin;C:\\Program Files\\Microsoft Visual Studio\\Common\\tools;C:\\Program Files\\Microsoft Visual Studio/VC98\\bin', + 'PATHEXT': '.COM;.EXE;.BAT;.CMD', + 'SystemRoot': 'C:/WINDOWS'} + scons: done reading SConscript files. + scons: Building targets ... + scons: `.' is up to date. + scons: done building targets. + + +
+ +
+ + What Dependencies Does &SCons; Know About? the &tree; Option + + + + Sometimes the best way to try to figure out what + &SCons; is doing is simply to take a look at the + dependency graph that it constructs + based on your &SConscript; files. + The --tree option + will display all or part of the + &SCons; dependency graph in an + "ASCII art" graphical format + that shows the dependency hierarchy. + + + + + + For example, given the following input &SConstruct; file: + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog', ['f1.c', 'f2.c', 'f3.c']) + + + + + Running &SCons; with the --tree=all + option yields: + + + + + % scons -Q --tree=all + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + +-. + +-- + +-SConstruct + +-f1.c + +-f1.o + | +-f1.c + | +-inc.h + +-f2.c + +-f2.o + | +-f2.c + | +-inc.h + +-f3.c + +-f3.o + | +-f3.c + | +-inc.h + +-inc.h + +-prog + +-f1.o + | +-f1.c + | +-inc.h + +-f2.o + | +-f2.c + | +-inc.h + +-f3.o + +-f3.c + +-inc.h + + + + + The tree will also be printed when the + -n (no execute) option is used, + which allows you to examine the dependency graph + for a configuration without actually + rebuilding anything in the tree. + + + + + + The --tree option only prints + the dependency graph for the specified targets + (or the default target(s) if none are specified on the command line). + So if you specify a target like f2.o + on the command line, + the --tree option will only + print the dependency graph for that file: + + + + + % scons -Q --tree=all f2.o + cc -o f2.o -c -I. f2.c + +-f2.o + +-f2.c + +-inc.h + + + + + This is, of course, useful for + restricting the output from a very large + build configuration to just a + portion in which you're interested. + Multiple targets are fine, + in which case a tree will be printed + for each specified target: + + + + + % scons -Q --tree=all f1.o f3.o + cc -o f1.o -c -I. f1.c + +-f1.o + +-f1.c + +-inc.h + cc -o f3.o -c -I. f3.c + +-f3.o + +-f3.c + +-inc.h + + + + + The status argument may be used + to tell &SCons; to print status information about + each file in the dependency graph: + + + + + % scons -Q --tree=status + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + E = exists + R = exists in repository only + b = implicit builder + B = explicit builder + S = side effect + P = precious + A = always build + C = current + N = no clean + H = no cache + + [E b ]+-. + [ ] +-- + [E ] +-SConstruct + [E ] +-f1.c + [E B C ] +-f1.o + [E ] | +-f1.c + [E ] | +-inc.h + [E ] +-f2.c + [E B C ] +-f2.o + [E ] | +-f2.c + [E ] | +-inc.h + [E ] +-f3.c + [E B C ] +-f3.o + [E ] | +-f3.c + [E ] | +-inc.h + [E ] +-inc.h + [E B C ] +-prog + [E B C ] +-f1.o + [E ] | +-f1.c + [E ] | +-inc.h + [E B C ] +-f2.o + [E ] | +-f2.c + [E ] | +-inc.h + [E B C ] +-f3.o + [E ] +-f3.c + [E ] +-inc.h + + + + + Note that --tree=all,status is equivalent; + the all + is assumed if only status is present. + As an alternative to all, + you can specify --tree=derived + to have &SCons; only print derived targets + in the tree output, + skipping source files + (like .c and .h files): + + + + + % scons -Q --tree=derived + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + +-. + + + + + You can use the status + modifier with derived as well: + + + + + % scons -Q --tree=derived,status + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + cc -o prog f1.o f2.o f3.o + E = exists + R = exists in repository only + b = implicit builder + B = explicit builder + S = side effect + P = precious + A = always build + C = current + N = no clean + H = no cache + + [E b ]+-. + [E B C ] +-f1.o + [E B C ] +-f2.o + [E B C ] +-f3.o + [E B C ] +-prog + [E B C ] +-f1.o + [E B C ] +-f2.o + [E B C ] +-f3.o + + + + + Note that the order of the --tree= + arguments doesn't matter; + --tree=status,derived is + completely equivalent. + + + + + + The default behavior of the --tree option + is to repeat all of the dependencies each time the library dependency + (or any other dependency file) is encountered in the tree. + If certain target files share other target files, + such as two programs that use the same library: + + + + + env = Environment(CPPPATH = ['.'], + LIBS = ['foo'], + LIBPATH = ['.']) + env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Program('prog1.c') + env.Program('prog2.c') + + + + + Then there can be a lot of repetition in the + --tree= output: + + + + + % scons -Q --tree=all + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + ar rc libfoo.a f1.o f2.o f3.o + ranlib libfoo.a + cc -o prog1.o -c -I. prog1.c + cc -o prog1 prog1.o -L. -lfoo + cc -o prog2.o -c -I. prog2.c + cc -o prog2 prog2.o -L. -lfoo + +-. + +-- + +-SConstruct + +-f1.c + +-f1.o + | +-f1.c + | +-inc.h + +-f2.c + +-f2.o + | +-f2.c + | +-inc.h + +-f3.c + +-f3.o + | +-f3.c + | +-inc.h + +-inc.h + +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog1 + | +-prog1.o + | | +-prog1.c + | | +-inc.h + | +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog1.c + +-prog1.o + | +-prog1.c + | +-inc.h + +-prog2 + | +-prog2.o + | | +-prog2.c + | | +-inc.h + | +-libfoo.a + | +-f1.o + | | +-f1.c + | | +-inc.h + | +-f2.o + | | +-f2.c + | | +-inc.h + | +-f3.o + | +-f3.c + | +-inc.h + +-prog2.c + +-prog2.o + +-prog2.c + +-inc.h + + + + + In a large configuration with many internal libraries + and include files, + this can very quickly lead to huge output trees. + To help make this more manageable, + a prune modifier may + be added to the option list, + in which case &SCons; + will print the name of a target that has + already been visited during the tree-printing + in [square brackets] + as an indication that the dependencies + of the target file may be found + by looking farther up the tree: + + + + + % scons -Q --tree=prune + cc -o f1.o -c -I. f1.c + cc -o f2.o -c -I. f2.c + cc -o f3.o -c -I. f3.c + ar rc libfoo.a f1.o f2.o f3.o + ranlib libfoo.a + cc -o prog1.o -c -I. prog1.c + cc -o prog1 prog1.o -L. -lfoo + cc -o prog2.o -c -I. prog2.c + cc -o prog2 prog2.o -L. -lfoo + +-. + +-- + +-SConstruct + +-f1.c + +-f1.o + | +-f1.c + | +-inc.h + +-f2.c + +-f2.o + | +-f2.c + | +-inc.h + +-f3.c + +-f3.o + | +-f3.c + | +-inc.h + +-inc.h + +-libfoo.a + | +-[f1.o] + | +-[f2.o] + | +-[f3.o] + +-prog1 + | +-prog1.o + | | +-prog1.c + | | +-inc.h + | +-[libfoo.a] + +-prog1.c + +-[prog1.o] + +-prog2 + | +-prog2.o + | | +-prog2.c + | | +-inc.h + | +-[libfoo.a] + +-prog2.c + +-[prog2.o] + + + + + Like the status keyword, + the prune argument by itself + is equivalent to --tree=all,prune. + + + +
+ +
+ + How is &SCons; Constructing the Command Lines It Executes? the &debug-presub; Option + + + + Sometimes it's useful to look at the + pre-substitution string + that &SCons; uses to generate + the command lines it executes. + This can be done with the &debug-presub; option: + + + + + + + + + % scons -Q --debug=presub + Building prog.o with action: + $CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCOMCOM $SOURCES + cc -o prog.o -c -I. prog.c + Building prog with action: + $SMART_LINKCOM + cc -o prog prog.o + + +
+ +
+ + Where is &SCons; Searching for Libraries? the &debug-findlibs; Option + + + + To get some insight into what library names + &SCons; is searching for, + and in which directories it is searching, + Use the --debug=findlibs option. + Given the following input &SConstruct; file: + + + + + env = Environment(LIBPATH = ['libs1', 'libs2']) + env.Program('prog.c', LIBS=['foo', 'bar']) + + + + + And the libraries libfoo.a + and libbar.a + in libs1 and libs2, + respectively, + use of the --debug=findlibs option yields: + + + + + % scons -Q --debug=findlibs + findlibs: looking for 'libfoo.a' in 'libs1' ... + findlibs: ... FOUND 'libfoo.a' in 'libs1' + findlibs: looking for 'libfoo.so' in 'libs1' ... + findlibs: looking for 'libfoo.so' in 'libs2' ... + findlibs: looking for 'libbar.a' in 'libs1' ... + findlibs: looking for 'libbar.a' in 'libs2' ... + findlibs: ... FOUND 'libbar.a' in 'libs2' + findlibs: looking for 'libbar.so' in 'libs1' ... + findlibs: looking for 'libbar.so' in 'libs2' ... + cc -o prog.o -c prog.c + cc -o prog prog.o -Llibs1 -Llibs2 -lfoo -lbar + + +
+ + + +
+ + Where is &SCons; Blowing Up? the &debug-stacktrace; Option + + + + In general, &SCons; tries to keep its error + messages short and informative. + That means we usually try to avoid showing + the stack traces that are familiar + to experienced Python programmers, + since they usually contain much more + information than is useful to most people. + + + + + + For example, the following &SConstruct; file: + + + + + Program('prog.c') + + + + + Generates the following error if the + prog.c file + does not exist: + + + + + % scons -Q + scons: *** Source `prog.c' not found, needed by target `prog.o'. Stop. + + + + + In this case, + the error is pretty obvious. + But if it weren't, + and you wanted to try to get more information + about the error, + the &debug-stacktrace; option + would show you exactly where in the &SCons; source code + the problem occurs: + + + + + % scons -Q --debug=stacktrace + scons: *** Source `prog.c' not found, needed by target `prog.o'. Stop. + scons: internal stack trace: + File "bootstrap/src/engine/SCons/Job.py", line 114, in start + File "bootstrap/src/engine/SCons/Taskmaster.py", line 169, in prepare + File "bootstrap/src/engine/SCons/Node/FS.py", line 2220, in prepare + File "bootstrap/src/engine/SCons/Node/__init__.py", line 819, in prepare + + + + + Of course, if you do need to dive into the &SCons; source code, + we'd like to know if, or how, + the error messages or troubleshooting options + could have been improved to avoid that. + Not everyone has the necessary time or + Python skill to dive into the source code, + and we'd like to improve &SCons; + for those people as well... + + + +
+ +
+ + How is &SCons; Making Its Decisions? the &taskmastertrace; Option + + + + The internal &SCons; subsystem that handles walking + the dependency graph + and controls the decision-making about what to rebuild + is the Taskmaster. + &SCons; supports a --taskmastertrace + option that tells the Taskmaster to print + information about the children (dependencies) + of the various Nodes on its walk down the graph, + which specific dependent Nodes are being evaluated, + and in what order. + + + + + + The --taskmastertrace option + takes as an argument the name of a file in + which to put the trace output, + with - (a single hyphen) + indicating that the trace messages + should be printed to the standard output: + + + + + env = Environment(CPPPATH = ['.']) + env.Program('prog.c') + + + + % scons -Q --taskmastertrace=- prog + Taskmaster: 'prog': children: + ['prog.o'] + waiting on unstarted children: + ['prog.o'] + Taskmaster: 'prog.o': children: + ['inc.h', 'prog.c'] + evaluating prog.o + cc -o prog.o -c -I. prog.c + Taskmaster: 'prog': children: + ['prog.o'] + evaluating prog + cc -o prog prog.o + Taskmaster: 'prog': already handled (executed) + + + + + The --taskmastertrace option + doesn't provide information about the actual + calculations involved in deciding if a file is up-to-date, + but it does show all of the dependencies + it knows about for each Node, + and the order in which those dependencies are evaluated. + This can be useful as an alternate way to determine + whether or not your &SCons; configuration, + or the implicit dependency scan, + has actually identified all the correct dependencies + you want it to. + + + +
+ + + + diff --git a/doc/user/variables.sgml b/doc/user/variables.sgml deleted file mode 100644 index 7009996..0000000 --- a/doc/user/variables.sgml +++ /dev/null @@ -1,56 +0,0 @@ - - - - -This appendix contains descriptions of all of the -construction variables that are potentially -available "out of the box" in this version of SCons. -Whether or not setting a construction variable -in a construction environment -will actually have an effect depends on -whether any of the Tools and/or Builders -that use the variable have been -included in the construction environment. - - - - - -In this appendix, we have -appended the initial $ -(dollar sign) to the beginning of each -variable name when it appears in the text, -but left off the dollar sign -in the left-hand column -where the name appears for each entry. - - - - - -&variables-gen; - - diff --git a/doc/user/variables.xml b/doc/user/variables.xml new file mode 100644 index 0000000..7009996 --- /dev/null +++ b/doc/user/variables.xml @@ -0,0 +1,56 @@ + + + + +This appendix contains descriptions of all of the +construction variables that are potentially +available "out of the box" in this version of SCons. +Whether or not setting a construction variable +in a construction environment +will actually have an effect depends on +whether any of the Tools and/or Builders +that use the variable have been +included in the construction environment. + + + + + +In this appendix, we have +appended the initial $ +(dollar sign) to the beginning of each +variable name when it appears in the text, +but left off the dollar sign +in the left-hand column +where the name appears for each entry. + + + + + +&variables-gen; + + diff --git a/doc/user/variants.sgml b/doc/user/variants.sgml deleted file mode 100644 index 6727859..0000000 --- a/doc/user/variants.sgml +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - The &build_dir; keyword argument of - the &SConscript; function provides everything - we need to show how easy it is to create - variant builds using &SCons;. - Suppose, for example, that we want to - build a program for both Windows and Linux platforms, - but that we want to build it in a shared directory - with separate side-by-side build directories - for the Windows and Linux versions of the program. - - - - - platform = ARGUMENTS.get('OS', Platform()) - - include = "#export/$PLATFORM/include" - lib = "#export/$PLATFORM/lib" - bin = "#export/$PLATFORM/bin" - - env = Environment(PLATFORM = platform, - BINDIR = bin, - INCDIR = include, - LIBDIR = lib, - CPPPATH = [include], - LIBPATH = [lib], - LIBS = 'world') - - Export('env') - - env.SConscript('src/SConscript', build_dir='build/$PLATFORM') - - - - - This SConstruct file, - when run on a Linux system, yields: - - - - - % scons -Q OS=linux - Install file: "build/linux/world/world.h" as "export/linux/include/world.h" - 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 - Install file: "build/linux/hello/hello" as "export/linux/bin/hello" - - - - - The same SConstruct file on Windows would build: - - - - - C:\>scons -Q OS=windows - Install file: "build/windows/world/world.h" as "export/windows/include/world.h" - cl /nologo /Iexport\windows\include /c build\windows\hello\hello.c /Fobuild\windows\hello\hello.obj - cl /nologo /Iexport\windows\include /c build\windows\world\world.c /Fobuild\windows\world\world.obj - lib /nologo /OUT:build\windows\world\world.lib build\windows\world\world.obj - Install file: "build/windows/world/world.lib" as "export/windows/lib/world.lib" - link /nologo /OUT:build\windows\hello\hello.exe /LIBPATH:export\windows\lib world.lib build\windows\hello\hello.obj - Install file: "build/windows/hello/hello.exe" as "export/windows/bin/hello.exe" - - - diff --git a/doc/user/variants.xml b/doc/user/variants.xml new file mode 100644 index 0000000..6727859 --- /dev/null +++ b/doc/user/variants.xml @@ -0,0 +1,134 @@ + + + + + + + The &build_dir; keyword argument of + the &SConscript; function provides everything + we need to show how easy it is to create + variant builds using &SCons;. + Suppose, for example, that we want to + build a program for both Windows and Linux platforms, + but that we want to build it in a shared directory + with separate side-by-side build directories + for the Windows and Linux versions of the program. + + + + + platform = ARGUMENTS.get('OS', Platform()) + + include = "#export/$PLATFORM/include" + lib = "#export/$PLATFORM/lib" + bin = "#export/$PLATFORM/bin" + + env = Environment(PLATFORM = platform, + BINDIR = bin, + INCDIR = include, + LIBDIR = lib, + CPPPATH = [include], + LIBPATH = [lib], + LIBS = 'world') + + Export('env') + + env.SConscript('src/SConscript', build_dir='build/$PLATFORM') + + + + + This SConstruct file, + when run on a Linux system, yields: + + + + + % scons -Q OS=linux + Install file: "build/linux/world/world.h" as "export/linux/include/world.h" + 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 + Install file: "build/linux/hello/hello" as "export/linux/bin/hello" + + + + + The same SConstruct file on Windows would build: + + + + + C:\>scons -Q OS=windows + Install file: "build/windows/world/world.h" as "export/windows/include/world.h" + cl /nologo /Iexport\windows\include /c build\windows\hello\hello.c /Fobuild\windows\hello\hello.obj + cl /nologo /Iexport\windows\include /c build\windows\world\world.c /Fobuild\windows\world\world.obj + lib /nologo /OUT:build\windows\world\world.lib build\windows\world\world.obj + Install file: "build/windows/world/world.lib" as "export/windows/lib/world.lib" + link /nologo /OUT:build\windows\hello\hello.exe /LIBPATH:export\windows\lib world.lib build\windows\hello\hello.obj + Install file: "build/windows/hello/hello.exe" as "export/windows/bin/hello.exe" + + + diff --git a/rpm/scons.spec.in b/rpm/scons.spec.in index 86cd7f5..f4564e4 100644 --- a/rpm/scons.spec.in +++ b/rpm/scons.spec.in @@ -1,6 +1,7 @@ %define name scons -%define version 0.97 +%define version __VERSION__ %define release 1 +%define _unpackaged_files_terminate_build 0 Summary: an Open Source software construction tool Name: %{name} diff --git a/src/CHANGES.txt b/src/CHANGES.txt index f34b02f..3699e95 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,7 +8,27 @@ -RELEASE 0.97.X - XXX +RELEASE 0.XX - XXX + + From Steven Knight: + + - Fix the wix Tool module to handle null entries in $PATH variables. + + - Move the documentation of Install() and InstallAs() from the list + of functions to the list of Builders (now that they're implemented + as such). + + - Allow env.CacheDir() to be set per construction environment. The + global CacheDir() function now sets an overridable global default. + + + +RELEASE 0.97.0d20070809 - Fri, 10 Aug 2007 10:51:27 -0500 + + From Lars Albertsson: + + - Don't error if a #include line happens to match a directory + somewhere on a path (like $CPPPATH, $FORTRANPATH, etc.). From Mark Bertoglio: @@ -64,17 +84,63 @@ RELEASE 0.97.X - XXX - Add a $SWIGOUTDIR variable to allow setting the swig -outdir option, and use it to identify files created by the swig -java option. + - Add a $SWIGPATH variable that specifies the path to be searched + for included SWIG files, Also add related $SWIGINCPREFIX and + $SWIGINCSUFFIX variables that specify the prefix and suffix to + be be added to each $SWIGPATH directory when expanded on the SWIG + command line. + + - More efficient copying of construction environments (mostly borrowed + from copy.deepcopy() in the standard Python library). + + - When printing --tree=prune output, don't print [brackets] around + source files, only do so for built targets with children. + + - Fix interpretation of Builder source arguments when the Builder has + a src_suffix *and* a source_builder and the argument has no suffix. + + - Fix use of expansions like ${TARGET.dir} or ${SOURCE.dir} in the + following construction variables: $FORTRANMODDIR, $JARCHDIR, + $JARFLAGS, $LEXFLAGS, $SWIGFLAGS, $SWIGOUTDIR and $YACCFLAGS. + + - Fix dependencies on Java files generated by SWIG so they can be + detected and built in one pass. + + - Fix SWIG when used with a BuildDir(). + From Leanid Nazdrynau: - When applying Tool modules after a construction environment has already been created, don't overwrite existing $CFILESUFFIX and $CXXFILESUFFIX value. + - Support passing the Java() builder a list of explicit .java files + (not only a list of directories to be scanned for .java files). + + - Support passing .java files to the Jar() and JavaH() builders, which + then use the builder underlying the Java() builder to turn them into + .class files. (That is, the Jar()-Java() chain of builders become + multi-step, like the Program()-Object()-CFile() builders.) + + - Support passing SWIG .i files to the Java builders (Java(), + Jar(), JavaH()), to cause intermediate .java files to be created + automatically. + + - Add $JAVACLASSPATH and $JAVASOURCEPATH variables, that get added to + the javac "-classpath" and "-sourcepath" options. (Note that SCons + does *not* currently search these paths for implicit dependencies.) + + - Commonize initialization of Java-related builders. + From Jan Nijtmans: - Find Java anonymous classes when the next token after the name is an open parenthesis. + From Gary Oberbrunner: + + - Fix a code example in the man page. + From Tilo Prutz: - Add support for the file names that Java 1.5 (and 1.6) generates for diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 6dd66aa..195697e 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -20,13 +20,75 @@ more effectively, please sign up for the scons-users mailing list at: -RELEASE 0.97 - Thu, 12 Apr 2007 12:36:25 -0500 +RELEASE 0.97.0d20070809 - Fri, 10 Aug 2007 10:51:27 -0500 This is the eighth beta release of SCons. Please consult the CHANGES.txt file for a list of specific changes since last release. Please note the following important changes since release 0.97: + -- env.CacheDir() NOW ONLY AFFECTS CONSTRUCTION ENVIRONMENT TARGETS + + The env.CacheDir() method now only causes derived files to be + retrieved from the specified cache directory for targets built + with the specified specified construction environment ("env"). + + Previously, any call to env.CacheDir() or CacheDir() would modify + a global setting and cause all built targets to be retrieved + from the specified cache directory. This behavior was changed so + that env.CacheDir() would be consistent with other construction + environment methods, which only affect targets built with the + specified construction environment. + + The old behavior of changing the global behavior may be preserved + by changing any env.CacheDir() calls to: + + CacheDir('/path/to/cache/directory') + + The above change is backwards-compatible and works in all earlier + versions of SCons that support CacheDir(). + + -- INTERPRETATION OF SUFFIX-LESS SOURCE ARGUMENTS HAS CHANGED + + The interpretation of source arguments (files) without suffixes + has changed in one specific configuration. + + Previously, if a Builder had a src_suffix specified (indicating + that source files without suffixes should have that suffix + appended), the suffix would only be applied to suffix-less source + arguments if the Builder did *not* have one or more attached + source Builders (that is, the Builder was not a "multi-stage" + Builder). So in the following configuration: + + build_foo = Builder(src_suffix = '.foo') + build_bar = Builder(src_suffix = '.bar', + src_builder = build_bar) + + env = Environment(BUILDERS = { + 'Foo' : build_foo, + 'Boo' : build_bar, + }) + + env.Foo('tgt1', 'src1') + env.Bar('tgt2', 'src2') + + SCons would have expected to find a source file 'src1.foo' for the + env.Foo() call, but a source file 'src2' for the env.Bar() call. + + This behavior has now been made consistent, so that the two + above calls would expect source files named 'src1.foo' and + 'src2.bar', respectively. + + Note that, if genuinely desired, the old behavior of building + from a source file without a suffix at all (when the Builder has + a src_suffix *and* a src_builder) can be specified explicity by + turning the string into a File Node directly: + + env.Bar('tgt2', File('src2')) + + The above use of File() is backwards-compatible and will work + on earlier versions of SCons. + -- THE DEFAULT EXECUTION PATH FOR Solaris HAS CHANGED On Solaris systems, SCons now adds the "/opt/SUNWspro/bin" diff --git a/src/engine/MANIFEST-xml.in b/src/engine/MANIFEST-xml.in index c43f210..f852e92 100644 --- a/src/engine/MANIFEST-xml.in +++ b/src/engine/MANIFEST-xml.in @@ -45,6 +45,7 @@ SCons/Tool/ilink32.xml SCons/Tool/ilink.xml SCons/Tool/__init__.xml SCons/Tool/intelc.xml +SCons/Tool/install.xml SCons/Tool/jar.xml SCons/Tool/javac.xml SCons/Tool/javah.xml @@ -63,6 +64,8 @@ SCons/Tool/msvs.xml SCons/Tool/mwcc.xml SCons/Tool/mwld.xml SCons/Tool/nasm.xml +SCons/Tool/packaging.xml +SCons/Tool/packaging/__init__.xml SCons/Tool/pdf.xml SCons/Tool/pdflatex.xml SCons/Tool/pdftex.xml diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 4bcd1e0..9eccbdb 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -9,6 +9,7 @@ SCons/compat/_scons_subprocess.py SCons/compat/_scons_textwrap.py SCons/compat/_scons_UserString.py SCons/compat/builtins.py +SCons/CacheDir.py SCons/Conftest.py SCons/cpp.py SCons/dblite.py @@ -51,10 +52,10 @@ SCons/Scanner/LaTeX.py SCons/Scanner/Prog.py SCons/SConf.py SCons/SConsign.py +SCons/Script/__init__.py SCons/Script/Main.py SCons/Script/SConscript.py SCons/Script/SConsOptions.py -SCons/Script/__init__.py SCons/Sig/__init__.py SCons/Sig/MD5.py SCons/Sig/TimeStamp.py @@ -75,14 +76,15 @@ SCons/Tool/c++.py SCons/Tool/cc.py SCons/Tool/cvf.py SCons/Tool/CVS.py -SCons/Tool/dmd.py SCons/Tool/default.py +SCons/Tool/dmd.py SCons/Tool/dvi.py SCons/Tool/dvipdf.py SCons/Tool/dvips.py SCons/Tool/f77.py SCons/Tool/f90.py SCons/Tool/f95.py +SCons/Tool/filesystem.py SCons/Tool/fortran.py SCons/Tool/g++.py SCons/Tool/g77.py @@ -93,17 +95,18 @@ SCons/Tool/gs.py SCons/Tool/hpc++.py SCons/Tool/hpcc.py SCons/Tool/hplink.py -SCons/Tool/jar.py -SCons/Tool/javac.py -SCons/Tool/JavaCommon.py -SCons/Tool/javah.py SCons/Tool/icc.py SCons/Tool/icl.py SCons/Tool/ifl.py SCons/Tool/ifort.py SCons/Tool/ilink.py SCons/Tool/ilink32.py +SCons/Tool/install.py SCons/Tool/intelc.py +SCons/Tool/jar.py +SCons/Tool/JavaCommon.py +SCons/Tool/javac.py +SCons/Tool/javah.py SCons/Tool/latex.py SCons/Tool/lex.py SCons/Tool/link.py @@ -119,6 +122,17 @@ SCons/Tool/msvs.py SCons/Tool/mwcc.py SCons/Tool/mwld.py SCons/Tool/nasm.py +SCons/Tool/packaging/__init__.py +SCons/Tool/packaging/ipk.py +SCons/Tool/packaging/msi.py +SCons/Tool/packaging/packager.py +SCons/Tool/packaging/rpm.py +SCons/Tool/packaging/src_tarbz2.py +SCons/Tool/packaging/src_targz.py +SCons/Tool/packaging/src_zip.py +SCons/Tool/packaging/tarbz2.py +SCons/Tool/packaging/targz.py +SCons/Tool/packaging/zip.py SCons/Tool/pdf.py SCons/Tool/pdflatex.py SCons/Tool/pdftex.py @@ -128,20 +142,22 @@ SCons/Tool/qt.py SCons/Tool/RCS.py SCons/Tool/rmic.py SCons/Tool/rpcgen.py +SCons/Tool/rpm.py SCons/Tool/SCCS.py SCons/Tool/sgiar.py SCons/Tool/sgic++.py SCons/Tool/sgicc.py SCons/Tool/sgilink.py +SCons/Tool/Subversion.py SCons/Tool/sunar.py SCons/Tool/sunc++.py SCons/Tool/suncc.py SCons/Tool/sunlink.py -SCons/Tool/Subversion.py SCons/Tool/swig.py SCons/Tool/tar.py SCons/Tool/tex.py SCons/Tool/tlib.py +SCons/Tool/wix.py SCons/Tool/yacc.py SCons/Tool/zip.py SCons/Util.py diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 87fb4b0..01d0992 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -156,7 +156,7 @@ class Environment: return self.d def Clone(self, **kw): res = Environment() - res.d = SCons.Environment.our_deepcopy(self.d) + res.d = SCons.Util.semi_deepcopy(self.d) for k, v in kw.items(): res.d[k] = v return res diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index d21de46..ff31c5e 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -467,28 +467,28 @@ class BuilderBase: executor.add_sources(slist) return executor + def _adjustixes(self, files, pre, suf): + if not files: + return [] + result = [] + if not SCons.Util.is_List(files): + files = [files] + + for f in files: + if SCons.Util.is_String(f): + f = SCons.Util.adjustixes(f, pre, suf) + result.append(f) + return result + def _create_nodes(self, env, target = None, source = None): """Create and return lists of target and source nodes. """ - def _adjustixes(files, pre, suf): - if not files: - return [] - result = [] - if not SCons.Util.is_List(files): - files = [files] - - for f in files: - if SCons.Util.is_String(f): - f = SCons.Util.adjustixes(f, pre, suf) - result.append(f) - return result - src_suf = self.get_src_suffix(env) target_factory = env.get_factory(self.target_factory) source_factory = env.get_factory(self.source_factory) - source = _adjustixes(source, None, src_suf) + source = self._adjustixes(source, None, src_suf) slist = env.arg2nodes(source, source_factory) pre = self.get_prefix(env, slist) @@ -505,7 +505,7 @@ class BuilderBase: splitext = lambda S,self=self,env=env: self.splitext(S,env) tlist = [ t_from_s(pre, suf, splitext) ] else: - target = _adjustixes(target, pre, suf) + target = self._adjustixes(target, pre, suf) tlist = env.arg2nodes(target, target_factory) if self.emitter: @@ -558,7 +558,7 @@ class BuilderBase: return result overwarn.warn() - + tlist, slist = self._create_nodes(env, target, source) # Check for errors with the specified target/source lists. @@ -716,20 +716,14 @@ class BuilderBase: return sdict def src_builder_sources(self, env, source, overwarn={}): - source_factory = env.get_factory(self.source_factory) - slist = env.arg2nodes(source, source_factory) - sdict = self._get_sdict(env) src_suffixes = self.src_suffixes(env) - lengths_dict = {} - for l in map(len, src_suffixes): - lengths_dict[l] = None - lengths = lengths_dict.keys() + lengths = list(set(map(len, src_suffixes))) - def match_src_suffix(node, src_suffixes=src_suffixes, lengths=lengths): - node_suffixes = map(lambda l, n=node: n.name[-l:], lengths) + def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): + node_suffixes = map(lambda l, n=name: n[-l:], lengths) for suf in src_suffixes: if suf in node_suffixes: return suf @@ -737,25 +731,38 @@ class BuilderBase: result = [] - for snode in slist: - match_suffix = match_src_suffix(snode) + if SCons.Util.is_List(source): + source = SCons.Util.flatten(source) + else: + source = [source] + for s in source: + if SCons.Util.is_String(s): + match_suffix = match_src_suffix(s) + if not match_suffix and not '.' in s: + src_suf = self.get_src_suffix(env) + s = self._adjustixes(s, None, src_suf)[0] + else: + match_suffix = match_src_suffix(s.name) if match_suffix: try: bld = sdict[match_suffix] except KeyError: - result.append(snode) + result.append(s) else: - tlist = bld._execute(env, None, [snode], overwarn) + tlist = bld._execute(env, None, [s], overwarn) # If the subsidiary Builder returned more than one # target, then filter out any sources that this # Builder isn't capable of building. if len(tlist) > 1: - tlist = filter(match_src_suffix, tlist) + mss = lambda t, m=match_src_suffix: m(t.name) + tlist = filter(mss, tlist) result.extend(tlist) else: - result.append(snode) + result.append(s) - return result + source_factory = env.get_factory(self.source_factory) + + return env.arg2nodes(result, source_factory) def _get_src_builders_key(self, env): return id(env) diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 3996d5c..bc4c52d 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -43,6 +43,8 @@ import SCons.Action import SCons.Builder import SCons.Environment import SCons.Errors +import SCons.Subst +import SCons.Util sys.stdout = StringIO.StringIO() @@ -92,12 +94,11 @@ class Environment: return self.d.get(s, s) def subst_target_source(self, string, raw=0, target=None, source=None, dict=None, conv=None): - return SCons.Util.scons_subst(string, self, raw, target, - source, dict, conv) - def subst_list(self, string, raw=0, target=None, - source=None, dict=None, conv=None): - return SCons.Util.scons_subst_list(string, self, raw, target, - source, dict, conv) + return SCons.Subst.scons_subst(string, self, raw, target, + source, dict, conv) + def subst_list(self, string, raw=0, target=None, source=None, conv=None): + return SCons.Subst.scons_subst_list(string, self, raw, target, + source, {}, {}, conv) def arg2nodes(self, args, factory, **kw): global env_arg2nodes_called env_arg2nodes_called = 1 @@ -792,12 +793,12 @@ class BuilderTestCase(unittest.TestCase): tgt = builder2(env, source=[]) assert tgt == [], tgt - tgt = builder2(env, target='baz', - source=['test.bar', 'test2.foo', 'test3.txt'])[0] + sources = ['test.bar', 'test2.foo', 'test3.txt', 'test4'] + tgt = builder2(env, target='baz', source=sources)[0] s = str(tgt) assert s == 'baz', s s = map(str, tgt.sources) - assert s == ['test.foo', 'test2.foo', 'test3.txt'], s + assert s == ['test.foo', 'test2.foo', 'test3.txt', 'test4.foo'], s s = map(str, tgt.sources[0].sources) assert s == ['test.bar'], s @@ -899,7 +900,8 @@ class BuilderTestCase(unittest.TestCase): assert tgt.builder.source_scanner is None, tgt.builder.source_scanner assert tgt.get_source_scanner(bar_y) is None, tgt.get_source_scanner(bar_y) assert not src.has_builder(), src.has_builder() - assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y) + s = src.get_source_scanner(bar_y) + assert isinstance(s, SCons.Util.Null), repr(s) # An Environment that has suffix-specified SCANNERS should # provide a source scanner to the target. @@ -926,7 +928,8 @@ class BuilderTestCase(unittest.TestCase): assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y) assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y) assert not src.has_builder(), src.has_builder() - assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y) + s = src.get_source_scanner(bar_y) + assert isinstance(s, SCons.Util.Null), repr(s) # Can't simply specify the scanner as a builder argument; it's # global to all invocations of this builder. @@ -937,7 +940,8 @@ class BuilderTestCase(unittest.TestCase): assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y) assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y) assert not src.has_builder(), src.has_builder() - assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y) + s = src.get_source_scanner(bar_y) + assert isinstance(s, SCons.Util.Null), s # Now use a builder that actually has scanners and ensure that # the target is set accordingly (using the specified scanner @@ -955,7 +959,8 @@ class BuilderTestCase(unittest.TestCase): assert tgt.get_source_scanner(bar_y) == scanner, tgt.get_source_scanner(bar_y) assert str(tgt.get_source_scanner(bar_y)) == 'TestScanner', tgt.get_source_scanner(bar_y) assert not src.has_builder(), src.has_builder() - assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y) + s = src.get_source_scanner(bar_y) + assert isinstance(s, SCons.Util.Null), s @@ -1595,7 +1600,7 @@ class CompositeBuilderTestCase(unittest.TestCase): assert str(e) == expect, e flag = 0 - tgt = builder(env, target='t7', source=['test7'])[0] + tgt = builder(env, target='t7', source=[env.fs.File('test7')])[0] try: tgt.build() except SCons.Errors.UserError, e: diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py new file mode 100644 index 0000000..6a1cc04 --- /dev/null +++ b/src/engine/SCons/CacheDir.py @@ -0,0 +1,207 @@ +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +__doc__ = """ +CacheDir support +""" + +import os.path +import stat +import string +import sys + +import SCons.Action + +cache_debug = False +cache_force = False +cache_show = False + +def CacheRetrieveFunc(target, source, env): + t = target[0] + fs = t.fs + cd = env.get_CacheDir() + cachedir, cachefile = cd.cachepath(t) + if not fs.exists(cachefile): + cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) + return 1 + cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) + if SCons.Action.execute_actions: + if fs.islink(cachefile): + fs.symlink(fs.readlink(cachefile), t.path) + else: + fs.copy2(cachefile, t.path) + st = fs.stat(cachefile) + fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + return 0 + +def CacheRetrieveString(target, source, env): + t = target[0] + fs = t.fs + cd = env.get_CacheDir() + cachedir, cachefile = cd.cachepath(t) + if t.fs.exists(cachefile): + return "Retrieved `%s' from cache" % t.path + return None + +CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) + +CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) + +def CachePushFunc(target, source, env): + t = target[0] + if t.nocache: + return + fs = t.fs + cd = env.get_CacheDir() + cachedir, cachefile = cd.cachepath(t) + if fs.exists(cachefile): + # Don't bother copying it if it's already there. Note that + # usually this "shouldn't happen" because if the file already + # existed in cache, we'd have retrieved the file from there, + # not built it. This can happen, though, in a race, if some + # other person running the same build pushes their copy to + # the cache after we decide we need to build it but before our + # build completes. + cd.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile) + return + + cd.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile) + + if not fs.isdir(cachedir): + fs.makedirs(cachedir) + + tempfile = cachefile+'.tmp' + try: + if fs.islink(t.path): + fs.symlink(fs.readlink(t.path), tempfile) + else: + fs.copy2(t.path, tempfile) + fs.rename(tempfile, cachefile) + st = fs.stat(t.path) + fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + except (IOError, OSError): + # It's possible someone else tried writing the file at the + # same time we did, or else that there was some problem like + # the CacheDir being on a separate file system that's full. + # In any case, inability to push a file to cache doesn't affect + # the correctness of the build, so just print a warning. + SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, + "Unable to copy %s to cache. Cache file is %s" + % (str(target), cachefile)) + +CachePush = SCons.Action.Action(CachePushFunc, None) + +class CacheDir: + + def __init__(self, path): + try: + import SCons.Sig.MD5 + except ImportError: + msg = "No MD5 module available, CacheDir() not supported" + SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) + else: + self.path = path + + def CacheDebugWrite(self, fmt, target, cachefile): + self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) + + def CacheDebugQuiet(self, fmt, target, cachefile): + pass + + def CacheDebugInit(self, fmt, target, cachefile): + if cache_debug: + if cache_debug == '-': + self.debugFP = sys.stdout + else: + self.debugFP = open(cache_debug, 'w') + self.CacheDebug = self.CacheDebugWrite + self.CacheDebug(fmt, target, cachefile) + else: + self.CacheDebug = self.CacheDebugQuiet + + CacheDebug = CacheDebugInit + + def cachepath(self, node): + """ + """ + sig = node.get_cachedir_bsig() + subdir = string.upper(sig[0]) + dir = os.path.join(self.path, subdir) + return dir, os.path.join(dir, sig) + + def retrieve(self, node): + """ + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + + Note that there's a special trick here with the execute flag + (one that's not normally done for other actions). Basically + if the user requested a no_exec (-n) build, then + SCons.Action.execute_actions is set to 0 and when any action + is called, it does its showing but then just returns zero + instead of actually calling the action execution operation. + The problem for caching is that if the file does NOT exist in + cache then the CacheRetrieveString won't return anything to + show for the task, but the Action.__call__ won't call + CacheRetrieveFunc; instead it just returns zero, which makes + the code below think that the file *was* successfully + retrieved from the cache, therefore it doesn't do any + subsequent building. However, the CacheRetrieveString didn't + print anything because it didn't actually exist in the cache, + and no more build actions will be performed, so the user just + sees nothing. The fix is to tell Action.__call__ to always + execute the CacheRetrieveFunc and then have the latter + explicitly check SCons.Action.execute_actions itself. + """ + retrieved = False + + if cache_show: + if CacheRetrieveSilent(node, [], node.get_build_env(), execute=1) == 0: + node.build(presub=0, execute=0) + retrieved = 1 + else: + if CacheRetrieve(node, [], node.get_build_env(), execute=1) == 0: + retrieved = 1 + if retrieved: + # Record build signature information, but don't + # push it out to cache. (We just got it from there!) + node.set_state(SCons.Node.executed) + SCons.Node.Node.built(node) + + return retrieved + + def push(self, node): + return CachePush(node, [], node.get_build_env()) + + def push_if_forced(self, node): + if cache_force: + return self.push(node) + +class Null(SCons.Util.Null): + def repr(self): + return 'CacheDir.Null()' + def retrieve(self, node): + return False diff --git a/src/engine/SCons/CacheDirTests.py b/src/engine/SCons/CacheDirTests.py new file mode 100644 index 0000000..564b762 --- /dev/null +++ b/src/engine/SCons/CacheDirTests.py @@ -0,0 +1,295 @@ +# +# __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 shutil +import sys +import unittest + +from TestCmd import TestCmd + +import SCons.CacheDir + +built_it = None + +class Action: + def __call__(self, targets, sources, env, errfunc, **kw): + global built_it + if kw.get('execute', 1): + built_it = 1 + return 0 + +class Builder: + def __init__(self, environment, action): + self.env = environment + self.action = action + self.overrides = {} + +class Environment: + def __init__(self, cachedir): + self.cachedir = cachedir + def Override(self, overrides): + return self + def get_CacheDir(self): + return self.cachedir + +class BaseTestCase(unittest.TestCase): + """ + Base fixtures common to our other unittest classes. + """ + def setUp(self): + self.test = TestCmd(workdir='') + + import SCons.Node.FS + self.fs = SCons.Node.FS.FS() + + self._CacheDir = SCons.CacheDir.CacheDir('cache') + + def File(self, name, bsig=None, action=Action()): + node = self.fs.File(name) + node.builder_set(Builder(Environment(self._CacheDir), action)) + if bsig: + node.binfo = node.BuildInfo(node) + node.binfo.ninfo.bsig = bsig + return node + +class CacheDirTestCase(BaseTestCase): + """ + Test calling CacheDir code directly. + """ + def test_cachepath(self): + """Test the cachepath() method""" + + # Verify how the cachepath() method determines the name + # of the file in cache. + def my_collect(list): + return list[0] + save_collect = SCons.Sig.MD5.collect + SCons.Sig.MD5.collect = my_collect + + try: + f5 = self.File("cd.f5", 'a_fake_bsig') + result = self._CacheDir.cachepath(f5) + dirname = os.path.join('cache', 'A') + filename = os.path.join(dirname, 'a_fake_bsig') + assert result == (dirname, filename), result + finally: + SCons.Sig.MD5.collect = save_collect + +class FileTestCase(BaseTestCase): + """ + Test calling CacheDir code through Node.FS.File interfaces. + """ + # These tests were originally in Nodes/FSTests.py and got moved + # when the CacheDir support was refactored into its own module. + # Look in the history for Node/FSTests.py if any of this needs + # to be re-examined. + def retrieve_succeed(self, target, source, env, execute=1): + self.retrieved.append(target) + return 0 + + def retrieve_fail(self, target, source, env, execute=1): + self.retrieved.append(target) + return 1 + + def push(self, target, source, env): + self.pushed.append(target) + return 0 + + def test_CacheRetrieve(self): + """Test the CacheRetrieve() function""" + + save_CacheRetrieve = SCons.CacheDir.CacheRetrieve + self.retrieved = [] + + f1 = self.File("cd.f1") + try: + SCons.CacheDir.CacheRetrieve = self.retrieve_succeed + self.retrieved = [] + built_it = None + + r = f1.retrieve_from_cache() + assert r == 1, r + assert self.retrieved == [f1], self.retrieved + assert built_it is None, built_it + + SCons.CacheDir.CacheRetrieve = self.retrieve_fail + self.retrieved = [] + built_it = None + + r = f1.retrieve_from_cache() + assert not r, r + assert self.retrieved == [f1], self.retrieved + assert built_it is None, built_it + finally: + SCons.CacheDir.CacheRetrieve = save_CacheRetrieve + + def test_CacheRetrieveSilent(self): + """Test the CacheRetrieveSilent() function""" + + save_CacheRetrieveSilent = SCons.CacheDir.CacheRetrieveSilent + + SCons.CacheDir.cache_show = 1 + + f2 = self.File("cd.f2", 'f2_bsig') + try: + SCons.CacheDir.CacheRetrieveSilent = self.retrieve_succeed + self.retrieved = [] + built_it = None + + r = f2.retrieve_from_cache() + assert r == 1, r + assert self.retrieved == [f2], self.retrieved + assert built_it is None, built_it + + SCons.CacheDir.CacheRetrieveSilent = self.retrieve_fail + self.retrieved = [] + built_it = None + + r = f2.retrieve_from_cache() + assert r is False, r + assert self.retrieved == [f2], self.retrieved + assert built_it is None, built_it + finally: + SCons.CacheDir.CacheRetrieveSilent = save_CacheRetrieveSilent + + def test_CachePush(self): + """Test the CachePush() function""" + + save_CachePush = SCons.CacheDir.CachePush + + SCons.CacheDir.CachePush = self.push + + try: + self.pushed = [] + + cd_f3 = self.test.workpath("cd.f3") + f3 = self.File(cd_f3) + f3.built() + assert self.pushed == [], self.pushed + self.test.write(cd_f3, "cd.f3\n") + f3.built() + assert self.pushed == [f3], self.pushed + + self.pushed = [] + + cd_f4 = self.test.workpath("cd.f4") + f4 = self.File(cd_f4) + f4.visited() + assert self.pushed == [], self.pushed + self.test.write(cd_f4, "cd.f4\n") + f4.clear() + f4.visited() + assert self.pushed == [], self.pushed + SCons.CacheDir.cache_force = 1 + f4.clear() + f4.visited() + assert self.pushed == [f4], self.pushed + finally: + SCons.CacheDir.CachePush = save_CachePush + + def test_no_bsig(self): + """Test that no bsig raises an InternalError""" + + f6 = self.File("cd.f6") + f6.binfo = f6.BuildInfo(f6) + exc_caught = 0 + try: + cp = self._CacheDir.cachepath(f6) + except SCons.Errors.InternalError: + exc_caught = 1 + assert exc_caught + + def test_warning(self): + """Test raising a warning if we can't copy a file to cache.""" + + test = TestCmd(workdir='') + + save_copy2 = shutil.copy2 + def copy2(src, dst): + raise OSError + shutil.copy2 = copy2 + save_mkdir = os.mkdir + def mkdir(dir, mode=0): + pass + os.mkdir = mkdir + old_warn_exceptions = SCons.Warnings.warningAsException(1) + SCons.Warnings.enableWarningClass(SCons.Warnings.CacheWriteErrorWarning) + + try: + cd_f7 = self.test.workpath("cd.f7") + self.test.write(cd_f7, "cd.f7\n") + f7 = self.File(cd_f7, 'f7_bsig') + + warn_caught = 0 + try: + f7.built() + except SCons.Warnings.CacheWriteErrorWarning: + warn_caught = 1 + assert warn_caught + finally: + shutil.copy2 = save_copy2 + os.mkdir = save_mkdir + SCons.Warnings.warningAsException(old_warn_exceptions) + SCons.Warnings.suppressWarningClass(SCons.Warnings.CacheWriteErrorWarning) + + def test_no_strfunction(self): + """Test handling no strfunction() for an action.""" + + save_CacheRetrieveSilent = SCons.CacheDir.CacheRetrieveSilent + + f8 = self.File("cd.f8", 'f8_bsig') + try: + SCons.CacheDir.CacheRetrieveSilent = self.retrieve_succeed + self.retrieved = [] + built_it = None + + r = f8.retrieve_from_cache() + assert r == 1, r + assert self.retrieved == [f8], self.retrieved + assert built_it is None, built_it + + SCons.CacheDir.CacheRetrieveSilent = self.retrieve_fail + self.retrieved = [] + built_it = None + + r = f8.retrieve_from_cache() + assert r is False, r + assert self.retrieved == [f8], self.retrieved + assert built_it is None, built_it + finally: + SCons.CacheDir.CacheRetrieveSilent = save_CacheRetrieveSilent + +if __name__ == "__main__": + suite = unittest.TestSuite() + tclasses = [ + CacheDirTestCase, + FileTestCase, + ] + for tclass in tclasses: + names = unittest.getTestCaseNames(tclass, 'test_') + suite.addTests(map(tclass, names)) + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 9d0ad82..29868ec 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -46,6 +46,7 @@ import sys import SCons.Action import SCons.Builder +import SCons.CacheDir import SCons.Environment import SCons.PathList import SCons.Sig @@ -66,6 +67,7 @@ def DefaultEnvironment(*args, **kw): _default_env = apply(SCons.Environment.Environment, args, kw) _default_env._build_signature = 1 _default_env._calc_module = SCons.Sig.default_module + _default_env._CacheDir = SCons.CacheDir.Null() return _default_env # Emitters for setting the shared attribute on object files, diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 8a7721e..012c36c 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -67,6 +67,8 @@ _null = _Null CleanTargets = {} CalculatorArgs = {} +semi_deepcopy = SCons.Util.semi_deepcopy + # Pull UserError into the global name space for the benefit of # Environment().SourceSignatures(), which has some import statements # which seem to mess up its ability to reference SCons directly. @@ -82,23 +84,6 @@ AliasBuilder = SCons.Builder.Builder(action = alias_builder, is_explicit = None, name='AliasBuilder') -def our_deepcopy(x): - """deepcopy lists and dictionaries, and just copy the reference - for everything else.""" - if SCons.Util.is_Dict(x): - copy = {} - for key in x.keys(): - copy[key] = our_deepcopy(x[key]) - elif SCons.Util.is_List(x): - copy = map(our_deepcopy, x) - try: - copy = x.__class__(copy) - except AttributeError: - pass - else: - copy = x - return copy - def apply_tools(env, tools, toolpath): # Store the toolpath in the Environment. if toolpath is not None: @@ -122,7 +107,7 @@ reserved_construction_var_names = \ ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES'] def copy_non_reserved_keywords(dict): - result = our_deepcopy(dict) + result = semi_deepcopy(dict) for k in result.keys(): if k in reserved_construction_var_names: SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, @@ -191,6 +176,9 @@ class BuilderDict(UserDict): self.env = env UserDict.__init__(self, dict) + def __semi_deepcopy__(self): + return self.__class__(self.data, self.env) + def __setitem__(self, item, val): UserDict.__setitem__(self, item, val) try: @@ -255,7 +243,7 @@ class SubstitutionEnvironment: """Initialization of an underlying SubstitutionEnvironment class. """ if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment') - self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS() + self.fs = SCons.Node.FS.get_default_fs() self.ans = SCons.Node.Alias.default_ans self.lookup_list = SCons.Node.arg2nodes_lookups self._dict = kw.copy() @@ -754,10 +742,10 @@ class Base(SubstitutionEnvironment): """ if __debug__: logInstanceCreation(self, 'Environment.Base') self._memo = {} - self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS() + self.fs = SCons.Node.FS.get_default_fs() self.ans = SCons.Node.Alias.default_ans self.lookup_list = SCons.Node.arg2nodes_lookups - self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment) + self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment) self._init_special() self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) @@ -825,6 +813,14 @@ class Base(SubstitutionEnvironment): c = SCons.Defaults.DefaultEnvironment().get_calculator() return c + def get_CacheDir(self): + try: + return self._CacheDir + except AttributeError: + cd = SCons.Defaults.DefaultEnvironment()._CacheDir + self._CacheDir = cd + return cd + def get_factory(self, factory, default='File'): """Return a factory function for creating Nodes for this construction environment. @@ -980,7 +976,11 @@ class Base(SubstitutionEnvironment): try: update_dict(val) except (AttributeError, TypeError, ValueError): - orig[val] = None + if SCons.Util.is_Dict(val): + for k, v in val.items(): + orig[k] = v + else: + orig[val] = None self.scanner_map_delete(kw) def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep): @@ -1039,7 +1039,7 @@ class Base(SubstitutionEnvironment): objects in the original Environment. """ clone = copy.copy(self) - clone._dict = our_deepcopy(self._dict) + clone._dict = semi_deepcopy(self._dict) try: cbd = clone._dict['BUILDERS'] clone._dict['BUILDERS'] = BuilderDict(cbd, clone) @@ -1235,7 +1235,11 @@ class Base(SubstitutionEnvironment): try: update_dict(val) except (AttributeError, TypeError, ValueError): - orig[val] = None + if SCons.Util.is_Dict(val): + for k, v in val.items(): + orig[k] = v + else: + orig[val] = None self.scanner_map_delete(kw) def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep): @@ -1290,13 +1294,13 @@ class Base(SubstitutionEnvironment): with new construction variables and/or values. """ try: - kwbd = our_deepcopy(kw['BUILDERS']) + kwbd = semi_deepcopy(kw['BUILDERS']) del kw['BUILDERS'] self.__setitem__('BUILDERS', kwbd) except KeyError: pass kw = copy_non_reserved_keywords(kw) - self._update(our_deepcopy(kw)) + self._update(semi_deepcopy(kw)) self.scanner_map_delete(kw) def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix): @@ -1470,7 +1474,11 @@ class Base(SubstitutionEnvironment): return apply(SCons.Builder.Builder, [], nkw) def CacheDir(self, path): - self.fs.CacheDir(self.subst(path)) + import SCons.CacheDir + if path is None: + self._CacheDir = SCons.CacheDir.Null() + else: + self._CacheDir = SCons.CacheDir.CacheDir(self.subst(path)) def Clean(self, targets, files): global CleanTargets @@ -1830,7 +1838,7 @@ class OverrideEnvironment(Base): # Overridden public construction environment methods. def Replace(self, **kw): kw = copy_non_reserved_keywords(kw) - self.__dict__['overrides'].update(our_deepcopy(kw)) + self.__dict__['overrides'].update(semi_deepcopy(kw)) # The entry point that will be used by the external world # to refer to a construction environment. This allows the wrapper diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 20c1eac..82f220a 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -2507,18 +2507,13 @@ def generate(env): def test_CacheDir(self): """Test the CacheDir() method""" - class MyFS: - def CacheDir(self, path): - self.CD = path - env = self.TestEnvironment(CD = 'CacheDir') - env.fs = MyFS() env.CacheDir('foo') - assert env.fs.CD == 'foo', env.fs.CD + assert env._CacheDir.path == 'foo', env._CacheDir.path env.CacheDir('$CD') - assert env.fs.CD == 'CacheDir', env.fs.CD + assert env._CacheDir.path == 'CacheDir', env._CacheDir.path def test_Clean(self): """Test the Clean() method""" diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py index 12114bc..7d8df68 100644 --- a/src/engine/SCons/Executor.py +++ b/src/engine/SCons/Executor.py @@ -296,10 +296,8 @@ class Null(_Executor): kw['action'] = [] apply(_Executor.__init__, (self,), kw) def get_build_env(self): - class NullEnvironment: - def get_scanner(self, key): - return None - return NullEnvironment() + import SCons.Util + return SCons.Util.Null() def get_build_scanner_path(self): return None def cleanup(self): diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index ad21a4d..74dc655 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -223,77 +223,6 @@ def get_MkdirBuilder(): name = "MkdirBuilder") return MkdirBuilder -def CacheRetrieveFunc(target, source, env): - t = target[0] - fs = t.fs - cachedir, cachefile = t.cachepath() - if not fs.exists(cachefile): - fs.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) - return 1 - fs.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) - if SCons.Action.execute_actions: - if fs.islink(cachefile): - fs.symlink(fs.readlink(cachefile), t.path) - else: - fs.copy2(cachefile, t.path) - st = fs.stat(cachefile) - fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - return 0 - -def CacheRetrieveString(target, source, env): - t = target[0] - cachedir, cachefile = t.cachepath() - if t.fs.exists(cachefile): - return "Retrieved `%s' from cache" % t.path - return None - -CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) - -CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) - -def CachePushFunc(target, source, env): - t = target[0] - if t.nocache: - return - fs = t.fs - cachedir, cachefile = t.cachepath() - if fs.exists(cachefile): - # Don't bother copying it if it's already there. Note that - # usually this "shouldn't happen" because if the file already - # existed in cache, we'd have retrieved the file from there, - # not built it. This can happen, though, in a race, if some - # other person running the same build pushes their copy to - # the cache after we decide we need to build it but before our - # build completes. - fs.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile) - return - - fs.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile) - - if not fs.isdir(cachedir): - fs.makedirs(cachedir) - - tempfile = cachefile+'.tmp' - try: - if fs.islink(t.path): - fs.symlink(fs.readlink(t.path), tempfile) - else: - fs.copy2(t.path, tempfile) - fs.rename(tempfile, cachefile) - st = fs.stat(t.path) - fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - except (IOError, OSError): - # It's possible someone else tried writing the file at the - # same time we did, or else that there was some problem like - # the CacheDir being on a separate file system that's full. - # In any case, inability to push a file to cache doesn't affect - # the correctness of the build, so just print a warning. - SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, - "Unable to copy %s to cache. Cache file is %s" - % (str(target), cachefile)) - -CachePush = SCons.Action.Action(CachePushFunc, None) - class _Null: pass @@ -738,6 +667,15 @@ class Base(SCons.Node.Node): return ret def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext): + """ + + Generates a target entry that corresponds to this entry (usually + a source file) with the specified prefix and suffix. + + Note that this method can be overridden dynamically for generated + files that need different behavior. See Tool/swig.py for + an example. + """ return self.dir.Entry(prefix + splitext(self.name)[0] + suffix) def _Rfindalldirs_key(self, pathlist): @@ -783,6 +721,26 @@ class Base(SCons.Node.Node): cwd = self.cwd or self.fs._cwd return cwd.Rfindalldirs(pathlist) + memoizer_counters.append(SCons.Memoize.CountValue('rentry')) + + def rentry(self): + try: + return self._memo['rentry'] + except KeyError: + pass + result = self + if not self.exists(): + norm_name = _my_normcase(self.name) + for dir in self.dir.get_all_rdirs(): + try: + node = dir.entries[norm_name] + except KeyError: + if dir.entry_exists_on_disk(self.name): + result = dir.Entry(self.name) + break + self._memo['rentry'] = result + return result + class Entry(Base): """This is the class for generic Node.FS entries--that is, things that could be a File or a Dir, but we're just not sure yet. @@ -993,9 +951,6 @@ class FS(LocalFS): self.Root = {} self.SConstruct_dir = None - self.CachePath = None - self.cache_force = None - self.cache_show = None self.max_drift = default_max_drift self.Top = None @@ -1259,30 +1214,6 @@ class FS(LocalFS): d = self.Dir(d) self.Top.addRepository(d) - def CacheDebugWrite(self, fmt, target, cachefile): - self.CacheDebugFP.write(fmt % (target, os.path.split(cachefile)[1])) - - def CacheDebugQuiet(self, fmt, target, cachefile): - pass - - CacheDebug = CacheDebugQuiet - - def CacheDebugEnable(self, file): - if file == '-': - self.CacheDebugFP = sys.stdout - else: - self.CacheDebugFP = open(file, 'w') - self.CacheDebug = self.CacheDebugWrite - - def CacheDir(self, path): - try: - import SCons.Sig.MD5 - except ImportError: - msg = "No MD5 module available, CacheDir() not supported" - SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) - else: - self.CachePath = path - def build_dir_target_climb(self, orig, dir, tail): """Create targets in corresponding build directories @@ -1681,9 +1612,9 @@ class Dir(Base): def srcdir_duplicate(self, name): for dir in self.srcdir_list(): if dir.entry_exists_on_disk(name): - srcnode = dir.File(name) + srcnode = dir.Entry(name).disambiguate() if self.duplicate: - node = self.File(name) + node = self.Entry(name).disambiguate() node.do_duplicate(srcnode) return node else: @@ -1750,7 +1681,37 @@ class Dir(Base): diskcheck_sccs(self, name): try: return self.File(name) except TypeError: pass - return self.srcdir_duplicate(name) + node = self.srcdir_duplicate(name) + if isinstance(node, Dir): + node = None + return node + + def walk(self, func, arg): + """ + Walk this directory tree by calling the specified function + for each directory in the tree. + + This behaves like the os.path.walk() function, but for in-memory + Node.FS.Dir objects. The function takes the same arguments as + the functions passed to os.path.walk(): + + func(arg, dirname, fnames) + + Except that "dirname" will actually be the directory *Node*, + not the string. The '.' and '..' entries are excluded from + fnames. The fnames list may be modified in-place to filter the + subdirectories visited or otherwise impose a specific order. + The "arg" argument is always passed to func() and may be used + in any way (or ignored, passing None is common). + """ + entries = self.entries + names = entries.keys() + names.remove('.') + names.remove('..') + func(arg, self, names) + select_dirs = lambda n, e=entries: isinstance(e[n], Dir) + for dirname in filter(select_dirs, names): + entries[dirname].walk(func, arg) class RootDir(Dir): """A class for the root directory of a file system. @@ -2048,53 +2009,17 @@ class File(Base): so only do thread safe stuff here. Do thread unsafe stuff in built(). - Note that there's a special trick here with the execute flag - (one that's not normally done for other actions). Basically - if the user requested a no_exec (-n) build, then - SCons.Action.execute_actions is set to 0 and when any action - is called, it does its showing but then just returns zero - instead of actually calling the action execution operation. - The problem for caching is that if the file does NOT exist in - cache then the CacheRetrieveString won't return anything to - show for the task, but the Action.__call__ won't call - CacheRetrieveFunc; instead it just returns zero, which makes - the code below think that the file *was* successfully - retrieved from the cache, therefore it doesn't do any - subsequent building. However, the CacheRetrieveString didn't - print anything because it didn't actually exist in the cache, - and no more build actions will be performed, so the user just - sees nothing. The fix is to tell Action.__call__ to always - execute the CacheRetrieveFunc and then have the latter - explicitly check SCons.Action.execute_actions itself. - 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(): + if not self.is_derived(): return None - - retrieved = None - if b and self.fs.CachePath: - if self.fs.cache_show: - if CacheRetrieveSilent(self, [], None, execute=1) == 0: - self.build(presub=0, execute=0) - retrieved = 1 - else: - if CacheRetrieve(self, [], None, execute=1) == 0: - retrieved = 1 - if retrieved: - # Record build signature information, but don't - # push it out to cache. (We just got it from there!) - self.set_state(SCons.Node.executed) - SCons.Node.Node.built(self) - - return retrieved - + return self.get_build_env().get_CacheDir().retrieve(self) def built(self): - """Called just after this node is successfully built. + """ + Called just after this node is successfully built. """ # Push this file out to cache before the superclass Node.built() # method has a chance to clear the build signature, which it @@ -2104,13 +2029,13 @@ class File(Base): # cache so that the memoization of the self.exists() return # value doesn't interfere. self.clear_memoized_values() - if self.fs.CachePath and self.exists(): - CachePush(self, [], None) + if self.exists(): + self.get_build_env().get_CacheDir().push(self) SCons.Node.Node.built(self) def visited(self): - if self.fs.CachePath and self.fs.cache_force and self.exists(): - CachePush(self, None, None) + if self.exists(): + self.get_build_env().get_CacheDir().push_if_forced(self) def has_src_builder(self): """Return whether this Node has a source builder or not. @@ -2333,9 +2258,8 @@ class File(Base): def rstr(self): return str(self.rfile()) - def cachepath(self): - if self.nocache or not self.fs.CachePath: - return None, None + def get_cachedir_bsig(self): + import SCons.Sig.MD5 ninfo = self.get_binfo().ninfo if not hasattr(ninfo, 'bsig'): import SCons.Errors @@ -2346,14 +2270,16 @@ class File(Base): # Add the path to the cache signature, because multiple # targets built by the same action will all have the same # build signature, and we have to differentiate them somehow. - import SCons.Sig.MD5 - cache_sig = SCons.Sig.MD5.collect([ninfo.bsig, self.path]) - subdir = string.upper(cache_sig[0]) - dir = os.path.join(self.fs.CachePath, subdir) - return dir, os.path.join(dir, cache_sig) + return SCons.Sig.MD5.collect([ninfo.bsig, self.path]) default_fs = None +def get_default_fs(): + global default_fs + if not default_fs: + default_fs = FS() + return default_fs + class FileFinder: """ """ diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 0b60fb9..ed8d6ec 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -89,6 +89,7 @@ class Action: pass def strfunction(self, targets, sources, env): return "" + class Builder: def __init__(self, factory, action=Action()): self.factory = factory @@ -2041,6 +2042,74 @@ class RepositoryTestCase(_tempdirTestCase): r = map(lambda x, np=os.path.normpath: np(str(x)), rep) assert r == expect, r + def test_rentry(self): + """Test the Base.entry() method""" + return_true = lambda: 1 + return_false = lambda: 0 + + d1 = self.fs.Dir('d1') + d2 = self.fs.Dir('d2') + d3 = self.fs.Dir('d3') + + e1 = self.fs.Entry('e1') + e2 = self.fs.Entry('e2') + e3 = self.fs.Entry('e3') + + f1 = self.fs.File('f1') + f2 = self.fs.File('f2') + f3 = self.fs.File('f3') + + self.test.write([self.rep1, 'd2'], "") + self.test.subdir([self.rep2, 'd3']) + self.test.write([self.rep3, 'd3'], "") + + self.test.write([self.rep1, 'e2'], "") + self.test.subdir([self.rep2, 'e3']) + self.test.write([self.rep3, 'e3'], "") + + self.test.write([self.rep1, 'f2'], "") + self.test.subdir([self.rep2, 'f3']) + self.test.write([self.rep3, 'f3'], "") + + r = d1.rentry() + assert r is d1, r + + r = d2.rentry() + assert not r is d2, r + r = str(r) + assert r == os.path.join(self.rep1, 'd2'), r + + r = d3.rentry() + assert not r is d3, r + r = str(r) + assert r == os.path.join(self.rep2, 'd3'), r + + r = e1.rentry() + assert r is e1, r + + r = e2.rentry() + assert not r is e2, r + r = str(r) + assert r == os.path.join(self.rep1, 'e2'), r + + r = e3.rentry() + assert not r is e3, r + r = str(r) + assert r == os.path.join(self.rep2, 'e3'), r + + r = f1.rentry() + assert r is f1, r + + r = f2.rentry() + assert not r is f2, r + r = str(r) + assert r == os.path.join(self.rep1, 'f2'), r + + r = f3.rentry() + assert not r is f3, r + r = str(r) + assert r == os.path.join(self.rep2, 'f3'), r + def test_rdir(self): """Test the Dir.rdir() method""" return_true = lambda: 1 @@ -2468,200 +2537,6 @@ class SConstruct_dirTestCase(unittest.TestCase): fs.set_SConstruct_dir(fs.Dir('xxx')) assert fs.SConstruct_dir.path == 'xxx' -class CacheDirTestCase(unittest.TestCase): - def runTest(self): - """Test CacheDir functionality""" - test = TestCmd(workdir='') - - global built_it - - fs = SCons.Node.FS.FS() - assert fs.CachePath is None, fs.CachePath - assert fs.cache_force is None, fs.cache_force - assert fs.cache_show is None, fs.cache_show - - fs.CacheDir('cache') - assert fs.CachePath == 'cache', fs.CachePath - - save_CacheRetrieve = SCons.Node.FS.CacheRetrieve - self.retrieved = [] - def retrieve_succeed(target, source, env, self=self, execute=1): - self.retrieved.append(target) - return 0 - def retrieve_fail(target, source, env, self=self, execute=1): - self.retrieved.append(target) - return 1 - - f1 = fs.File("cd.f1") - f1.builder_set(Builder(fs.File)) - f1.env_set(Environment()) - try: - SCons.Node.FS.CacheRetrieve = retrieve_succeed - self.retrieved = [] - built_it = None - - r = f1.retrieve_from_cache() - assert r == 1, r - assert self.retrieved == [f1], self.retrieved - assert built_it is None, built_it - - SCons.Node.FS.CacheRetrieve = retrieve_fail - self.retrieved = [] - built_it = None - - r = f1.retrieve_from_cache() - assert r is None, r - assert self.retrieved == [f1], self.retrieved - assert built_it is None, built_it - finally: - SCons.Node.FS.CacheRetrieve = save_CacheRetrieve - - save_CacheRetrieveSilent = SCons.Node.FS.CacheRetrieveSilent - - fs.cache_show = 1 - - f2 = fs.File("cd.f2") - f2.builder_set(Builder(fs.File)) - f2.env_set(Environment()) - try: - SCons.Node.FS.CacheRetrieveSilent = retrieve_succeed - self.retrieved = [] - built_it = None - - r = f2.retrieve_from_cache() - assert r == 1, r - assert self.retrieved == [f2], self.retrieved - assert built_it is None, built_it - - SCons.Node.FS.CacheRetrieveSilent = retrieve_fail - self.retrieved = [] - built_it = None - - r = f2.retrieve_from_cache() - assert r is None, r - assert self.retrieved == [f2], self.retrieved - assert built_it is None, built_it - finally: - SCons.Node.FS.CacheRetrieveSilent = save_CacheRetrieveSilent - - save_CachePush = SCons.Node.FS.CachePush - def push(target, source, env, self=self): - self.pushed.append(target) - return 0 - SCons.Node.FS.CachePush = push - - try: - self.pushed = [] - - cd_f3 = test.workpath("cd.f3") - f3 = fs.File(cd_f3) - f3.built() - assert self.pushed == [], self.pushed - test.write(cd_f3, "cd.f3\n") - f3.built() - assert self.pushed == [f3], self.pushed - - self.pushed = [] - - cd_f4 = test.workpath("cd.f4") - f4 = fs.File(cd_f4) - f4.visited() - assert self.pushed == [], self.pushed - test.write(cd_f4, "cd.f4\n") - f4.visited() - assert self.pushed == [], self.pushed - fs.cache_force = 1 - f4.visited() - assert self.pushed == [f4], self.pushed - finally: - SCons.Node.FS.CachePush = save_CachePush - - # Verify how the cachepath() method determines the name - # of the file in cache. - def my_collect(list): - return list[0] - save_collect = SCons.Sig.MD5.collect - SCons.Sig.MD5.collect = my_collect - try: - f5 = fs.File("cd.f5") - f5.binfo = f5.BuildInfo(f5) - f5.binfo.ninfo.bsig = 'a_fake_bsig' - cp = f5.cachepath() - dirname = os.path.join('cache', 'A') - filename = os.path.join(dirname, 'a_fake_bsig') - assert cp == (dirname, filename), cp - finally: - SCons.Sig.MD5.collect = save_collect - - # Verify that no bsig raises an InternalERror - f6 = fs.File("cd.f6") - f6.binfo = f6.BuildInfo(f6) - exc_caught = 0 - try: - cp = f6.cachepath() - except SCons.Errors.InternalError: - exc_caught = 1 - assert exc_caught - - # Verify that we raise a warning if we can't copy a file to cache. - save_copy2 = shutil.copy2 - def copy2(src, dst): - raise OSError - shutil.copy2 = copy2 - save_mkdir = os.mkdir - def mkdir(dir, mode=0): - pass - os.mkdir = mkdir - old_warn_exceptions = SCons.Warnings.warningAsException(1) - SCons.Warnings.enableWarningClass(SCons.Warnings.CacheWriteErrorWarning) - - try: - cd_f7 = test.workpath("cd.f7") - test.write(cd_f7, "cd.f7\n") - f7 = fs.File(cd_f7) - f7.binfo = f7.BuildInfo(f7) - f7.binfo.ninfo.bsig = 'f7_bsig' - - warn_caught = 0 - try: - f7.built() - except SCons.Warnings.CacheWriteErrorWarning: - warn_caught = 1 - assert warn_caught - finally: - shutil.copy2 = save_copy2 - os.mkdir = save_mkdir - SCons.Warnings.warningAsException(old_warn_exceptions) - SCons.Warnings.suppressWarningClass(SCons.Warnings.CacheWriteErrorWarning) - - # Verify that we don't blow up if there's no strfunction() - # for an action. - act = Action() - act.strfunction = None - f8 = fs.File("cd.f8") - f8.builder_set(Builder(fs.File, action=act)) - f8.env_set(Environment()) - try: - SCons.Node.FS.CacheRetrieveSilent = retrieve_succeed - self.retrieved = [] - built_it = None - - r = f8.retrieve_from_cache() - assert r == 1, r - assert self.retrieved == [f8], self.retrieved - assert built_it is None, built_it - - SCons.Node.FS.CacheRetrieveSilent = retrieve_fail - self.retrieved = [] - built_it = None - - r = f8.retrieve_from_cache() - assert r is None, r - assert self.retrieved == [f8], self.retrieved - assert built_it is None, built_it - finally: - SCons.Node.FS.CacheRetrieveSilent = save_CacheRetrieveSilent - class clearTestCase(unittest.TestCase): def runTest(self): """Test clearing FS nodes of cached data.""" @@ -3007,7 +2882,6 @@ if __name__ == "__main__": suite.addTest(has_src_builderTestCase()) suite.addTest(prepareTestCase()) suite.addTest(SConstruct_dirTestCase()) - suite.addTest(CacheDirTestCase()) suite.addTest(clearTestCase()) suite.addTest(disambiguateTestCase()) suite.addTest(postprocessTestCase()) diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index ad9eb66..09ab5c7 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -33,6 +33,7 @@ import UserList import SCons.Errors import SCons.Node +import SCons.Util @@ -977,7 +978,7 @@ class NodeTestCase(unittest.TestCase): target = SCons.Node.Node() source = SCons.Node.Node() s = target.get_source_scanner(source) - assert s is None, s + assert isinstance(s, SCons.Util.Null), s ts1 = Scanner() ts2 = Scanner() diff --git a/src/engine/SCons/PathList.py b/src/engine/SCons/PathList.py index 943e9ad..be645ca 100644 --- a/src/engine/SCons/PathList.py +++ b/src/engine/SCons/PathList.py @@ -138,7 +138,8 @@ class _PathList: value = string.join(map(str, value), '') elif type == TYPE_OBJECT: value = node_conv(value) - result.append(value) + if value: + result.append(value) return tuple(result) diff --git a/src/engine/SCons/PathListTests.py b/src/engine/SCons/PathListTests.py index d0ba2a0..8203ccf 100644 --- a/src/engine/SCons/PathListTests.py +++ b/src/engine/SCons/PathListTests.py @@ -47,7 +47,7 @@ class subst_pathTestCase(unittest.TestCase): s = self.kw[s] return s - self.env = FakeEnvironment(AAA = 'aaa') + self.env = FakeEnvironment(AAA = 'aaa', NULL = '') def test_node(self): """Test the subst_path() method on a Node @@ -111,10 +111,10 @@ class subst_pathTestCase(unittest.TestCase): assert result == ('x',), result def test_subst(self): - """Test the subst_path() method on a substitution string + """Test the subst_path() method on substitution strings """ - pl = SCons.PathList.PathList(('$AAA',)) + pl = SCons.PathList.PathList(('$AAA', '$NULL')) result = pl.subst_path(self.env, 'y', 'z') diff --git a/src/engine/SCons/Scanner/LaTeX.py b/src/engine/SCons/Scanner/LaTeX.py index 645a894..c0a38b5 100644 --- a/src/engine/SCons/Scanner/LaTeX.py +++ b/src/engine/SCons/Scanner/LaTeX.py @@ -29,12 +29,12 @@ This module implements the dependency scanner for LaTeX code. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import os.path +import string import SCons.Scanner -import string -import os.path -def LaTeXScanner(fs = SCons.Node.FS.default_fs): +def LaTeXScanner(): """Return a prototype Scanner instance for scanning LaTeX source files""" ds = LaTeX(name = "LaTeXScanner", suffixes = '$LATEXSUFFIXES', diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index a3c0a51..a76165f 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -54,6 +54,7 @@ import traceback # 'lib', # 'scons-%d' % SCons.__version__)] + sys.path[1:] +import SCons.CacheDir import SCons.Debug import SCons.Defaults import SCons.Environment @@ -641,10 +642,15 @@ def _load_site_scons_dir(topdir, site_dir_name=None): 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" + version = module.__version__ + build = module.__build__ + if build: + if build[0] != '.': + build = '.' + build + version = version + build + fmt = "\t%s: v%s, %s, by %s on %s\n" return fmt % (label, - module.__version__, - module.__build__, + version, module.__date__, module.__developer__, module.__buildsys__) @@ -720,7 +726,7 @@ def _main(parser): # Now that we're in the top-level SConstruct directory, go ahead # and initialize the FS object that represents the file system, # and make it the build engine default. - fs = SCons.Node.FS.default_fs = SCons.Node.FS.FS() + fs = SCons.Node.FS.get_default_fs() for rep in options.repository: fs.Repository(rep) @@ -770,15 +776,14 @@ def _main(parser): if options.silent: SCons.Action.print_actions = None - if options.cache_debug: - fs.CacheDebugEnable(options.cache_debug) if options.cache_disable: - def disable(self): pass - fs.CacheDir = disable + SCons.CacheDir.CacheDir = SCons.Util.Null() + if options.cache_debug: + SCons.CacheDir.cache_debug = options.cache_debug if options.cache_force: - fs.cache_force = 1 + SCons.CacheDir.cache_force = True if options.cache_show: - fs.cache_show = 1 + SCons.CacheDir.cache_show = True if options.site_dir: _load_site_scons_dir(d, options.site_dir) diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py index 053fff1..46ece27 100644 --- a/src/engine/SCons/Script/SConsOptions.py +++ b/src/engine/SCons/Script/SConsOptions.py @@ -166,6 +166,65 @@ class SConsValues(optparse.Values): self.__SConscript_settings__[name] = value +class SConsOption(optparse.Option): + def convert_value(self, opt, value): + if value is not None: + if self.nargs in (1, '?'): + return self.check_value(opt, value) + else: + return tuple(map(lambda v, o=opt, s=self: s.check_value(o, v), value)) + + def process(self, opt, value, values, parser): + + # First, convert the value(s) to the right type. Howl if any + # value(s) are bogus. + value = self.convert_value(opt, value) + + # And then take whatever action is expected of us. + # This is a separate method to make life easier for + # subclasses to add new actions. + return self.take_action( + self.action, self.dest, opt, value, values, parser) + + def _check_nargs_optional(self): + if self.nargs == '?' and self._short_opts: + fmt = "option %s: nargs='?' is incompatible with short options" + raise SCons.Errors.UserError, fmt % self._short_opts[0] + + try: + _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS + + _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS + + except AttributeError: + # optparse.Option had no CONST_ACTIONS before Python 2.5. + + _orig_CONST_ACTIONS = ("store_const",) + + def _check_const(self): + if self.action not in self.CONST_ACTIONS and self.const is not None: + raise OptionError( + "'const' must not be supplied for action %r" % self.action, + self) + + # optparse.Option collects its list of unbound check functions + # up front. This sucks because it means we can't just override + # the _check_const() function like a normal method, we have to + # actually replace it in the list. This seems to be the most + # straightforward way to do that. + + _orig_CHECK_METHODS = [optparse.Option._check_action, + optparse.Option._check_type, + optparse.Option._check_choice, + optparse.Option._check_dest, + _check_const, + optparse.Option._check_nargs, + optparse.Option._check_callback] + + CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional] + + CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS + class SConsOptionGroup(optparse.OptionGroup): """ A subclass for SCons-specific option groups. @@ -232,7 +291,12 @@ class SConsOptionParser(optparse.OptionParser): option = self._long_opt[opt] if option.takes_value(): nargs = option.nargs - if len(rargs) < nargs: + if nargs == '?': + if had_explicit_value: + value = rargs.pop(0) + else: + value = option.const + elif len(rargs) < nargs: if nargs == 1: self.error(_("%s option requires an argument") % opt) else: @@ -396,7 +460,8 @@ def Parser(version): formatter = SConsIndentedHelpFormatter(max_help_position=30) - op = SConsOptionParser(add_help_option=False, + op = SConsOptionParser(option_class=SConsOption, + add_help_option=False, formatter=formatter, usage="usage: scons [OPTION] [TARGET] ...",) diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 8204c65..4010d80 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -296,6 +296,8 @@ GlobalDefaultEnvironmentFunctions = [ 'Execute', 'File', 'FindFile', + 'FindInstalledFiles', + 'FindSourceFiles', 'Flatten', 'GetBuildPath', 'Ignore', @@ -311,11 +313,9 @@ GlobalDefaultEnvironmentFunctions = [ 'SourceCode', 'SourceSignatures', 'Split', + 'Tag', 'TargetSignatures', 'Value', - 'Tag', - 'FindInstalledFiles', - 'FindSourceFiles', ] GlobalDefaultBuilders = [ diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py index 40f0a47..358f675 100644 --- a/src/engine/SCons/Tool/JavaCommonTests.py +++ b/src/engine/SCons/Tool/JavaCommonTests.py @@ -418,6 +418,39 @@ public class NestedExample expect = [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ] assert expect == classes, (expect, classes) + def test_private_inner_class_instantiation(self): + """Test anonymous inner class generated by private instantiation""" + + input = """\ +class test +{ + test() + { + super(); + new inner(); + } + + static class inner + { + private inner() {} + } +} +""" + + # This is what we *should* generate, apparently due to the + # private instantiation of the inner class, but don't today. + #expect = [ 'test$1', 'test$inner', 'test' ] + + # What our parser currently generates, which doesn't match + # what the Java compiler actually generates. + expect = [ 'test$inner', 'test' ] + + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4') + assert expect == classes, (expect, classes) + + pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5') + assert expect == classes, (expect, classes) + if __name__ == "__main__": diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index bf3df56..97a4e70 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -43,6 +43,7 @@ import sys import SCons.Builder import SCons.Errors +import SCons.Node.FS import SCons.Scanner import SCons.Scanner.C import SCons.Scanner.D @@ -176,6 +177,9 @@ class Tool: def __str__(self): return self.name +########################################################################## +# Create common executable program / library / object builders + def createProgBuilder(env): """This is a utility function that creates the Program Builder in an Environment if it is not there already. @@ -347,6 +351,79 @@ def createCFileBuilders(env): return (c_file, cxx_file) +########################################################################## +# Create common Java builders + +def CreateJarBuilder(env): + try: + java_jar = env['BUILDERS']['Jar'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR') + java_jar = SCons.Builder.Builder(action = jar_com, + suffix = '$JARSUFFIX', + src_suffix = '$JAVACLASSSUFIX', + src_builder = 'JavaClassFile', + source_factory = fs.Entry) + env['BUILDERS']['Jar'] = java_jar + return java_jar + +def CreateJavaHBuilder(env): + try: + java_javah = env['BUILDERS']['JavaH'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR') + java_javah = SCons.Builder.Builder(action = java_javah_com, + src_suffix = '$JAVACLASSSUFFIX', + target_factory = fs.Entry, + source_factory = fs.File, + src_builder = 'JavaClassFile') + env['BUILDERS']['JavaH'] = java_javah + return java_javah + +def CreateJavaClassFileBuilder(env): + try: + java_class_file = env['BUILDERS']['JavaClassFile'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') + java_class_file = SCons.Builder.Builder(action = javac_com, + emitter = {}, + #suffix = '$JAVACLASSSUFFIX', + src_suffix = '$JAVASUFFIX', + src_builder = ['JavaFile'], + target_factory = fs.Entry, + source_factory = fs.File) + env['BUILDERS']['JavaClassFile'] = java_class_file + return java_class_file + +def CreateJavaClassDirBuilder(env): + try: + java_class_dir = env['BUILDERS']['JavaClassDir'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') + java_class_dir = SCons.Builder.Builder(action = javac_com, + emitter = {}, + target_factory = fs.Dir, + source_factory = fs.Dir) + env['BUILDERS']['JavaClassDir'] = java_class_dir + return java_class_dir + +def CreateJavaFileBuilder(env): + try: + java_file = env['BUILDERS']['JavaFile'] + except KeyError: + java_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$JAVASUFFIX'}) + env['BUILDERS']['JavaFile'] = java_file + env['JAVASUFFIX'] = '.java' + return java_file + + + def FindTool(tools, env): for tool in tools: t = Tool(tool) diff --git a/src/engine/SCons/Tool/aixlink.py b/src/engine/SCons/Tool/aixlink.py index 2deb1f1..5c3b383 100644 --- a/src/engine/SCons/Tool/aixlink.py +++ b/src/engine/SCons/Tool/aixlink.py @@ -44,7 +44,7 @@ cplusplus = __import__('c++', globals(), locals(), []) def smart_linkflags(source, target, env, for_signature): if cplusplus.iscplusplus(source): - build_dir = env.subst('$BUILDDIR') + build_dir = env.subst('$BUILDDIR', target=target, source=source) if build_dir: return '-qtempinc=' + os.path.join(build_dir, 'tempinc') return '' diff --git a/src/engine/SCons/Tool/filesystem.py b/src/engine/SCons/Tool/filesystem.py index 5e631fd..46aefbb 100644 --- a/src/engine/SCons/Tool/filesystem.py +++ b/src/engine/SCons/Tool/filesystem.py @@ -29,6 +29,9 @@ selection method. # 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 SCons from SCons.Tool.install import copyFunc diff --git a/src/engine/SCons/Tool/fortran.py b/src/engine/SCons/Tool/fortran.py index 8494fd6..b748303 100644 --- a/src/engine/SCons/Tool/fortran.py +++ b/src/engine/SCons/Tool/fortran.py @@ -78,8 +78,8 @@ def _fortranEmitter(target, source, env): # Remove unique items from the list modules = SCons.Util.unique(modules) # Convert module name to a .mod filename - suffix = env.subst('$FORTRANMODSUFFIX') - moddir = env.subst('$FORTRANMODDIR') + suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source) + moddir = env.subst('$FORTRANMODDIR', target=target, source=source) modules = map(lambda x, s=suffix: string.lower(x) + s, modules) for m in modules: target.append(env.fs.File(m, moddir)) diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index 5083c9f..828bb9e 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -29,8 +29,14 @@ selection method. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import shutil +import stat + import SCons.Action -import shutil, os, stat from SCons.Util import make_path_relative # diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml new file mode 100644 index 0000000..4b57a68 --- /dev/null +++ b/src/engine/SCons/Tool/install.xml @@ -0,0 +1,52 @@ + + + + +Sets construction variables for file +and directory installation. + + +INSTALL +INSTALLSTR + + + + + +Installs one or more source files or directories +in the specified target, +which must be a directory. +The names of the specified source files or directories +remain the same within the destination directory. + + +env.Install('/usr/local/bin', source = ['foo', 'bar']) + + + + + + +Installs one or more source files or directories +to specific names, +allowing changing a file or directory name +as part of the installation. +It is an error if the +target +and +source +arguments list different numbers of files or directories. + + +env.InstallAs(target = '/usr/local/bin/foo', + source = 'foo_debug') +env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], + source = ['libFOO.a', 'libBAR.a']) + + + diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index cb0a8eb..4f221c0 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -33,14 +33,12 @@ selection method. __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') + jarchdir = env.subst('$JARCHDIR', target=target, source=source) if jarchdir: jarchdir = env.fs.Dir(jarchdir) result = [] @@ -67,7 +65,7 @@ def jarManifest(target, source, env, for_signature): def jarFlags(target, source, env, for_signature): """If we have a manifest, make sure that the 'm' flag is specified.""" - jarflags = env.subst('$JARFLAGS') + jarflags = env.subst('$JARFLAGS', target=target, source=source) for src in source: contents = src.get_contents() if contents[:16] == "Manifest-Version": @@ -76,25 +74,17 @@ def jarFlags(target, source, env, for_signature): break return jarflags -JarAction = SCons.Action.Action('$JARCOM', '$JARCOMSTR') - -JarBuilder = SCons.Builder.Builder(action = JarAction, - source_factory = SCons.Node.FS.Entry, - suffix = '$JARSUFFIX') - def generate(env): """Add Builders and construction variables for jar to an Environment.""" - try: - env['BUILDERS']['Jar'] - except KeyError: - env['BUILDERS']['Jar'] = JarBuilder + SCons.Tool.CreateJarBuilder(env) env['JAR'] = 'jar' env['JARFLAGS'] = SCons.Util.CLVar('cf') env['_JARFLAGS'] = jarFlags env['_JARMANIFEST'] = jarManifest env['_JARSOURCES'] = jarSources - env['JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES' + env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES' + env['JARCOM'] = "${TEMPFILE('$_JARCOM')}" env['JARSUFFIX'] = '.jar' def exists(env): diff --git a/src/engine/SCons/Tool/jar.xml b/src/engine/SCons/Tool/jar.xml index 0deaaae..a0d730e 100644 --- a/src/engine/SCons/Tool/jar.xml +++ b/src/engine/SCons/Tool/jar.xml @@ -22,7 +22,13 @@ JARCOMSTR Builds a Java archive (.jar) file -from a source tree of .class files. +from the specified list of sources. +Any directories in the source list +will be searched for .class files). +Any .java files in the source list +will be compiled to .class files +by calling the &b-link-Java; Builder. + If the &cv-link-JARCHDIR; value is set, the &jar; command will change to the specified directory using the @@ -39,6 +45,9 @@ option set. env.Jar(target = 'foo.jar', source = 'classes') + +env.Jar(target = 'bar.jar', + source = ['bar1.java', 'bar2.java']) diff --git a/src/engine/SCons/Tool/javac.py b/src/engine/SCons/Tool/javac.py index 85dfc3f..5703b2d 100644 --- a/src/engine/SCons/Tool/javac.py +++ b/src/engine/SCons/Tool/javac.py @@ -54,43 +54,73 @@ def emit_java_classes(target, source, env): java_suffix = env.get('JAVASUFFIX', '.java') class_suffix = env.get('JAVACLASSSUFFIX', '.class') + target[0].must_be_same(SCons.Node.FS.Dir) + classdir = target[0] + + s = source[0].rentry().disambiguate() + if isinstance(s, SCons.Node.FS.File): + sourcedir = s.dir.rdir() + elif isinstance(s, SCons.Node.FS.Dir): + sourcedir = s.rdir() + else: + raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__) + slist = [] js = _my_normcase(java_suffix) - for sdir in source: - def visit(arg, dirname, names, js=js, dirnode=sdir.rdir()): - java_files = filter(lambda n, js=js: - _my_normcase(n[-len(js):]) == js, - names) - # The on-disk entries come back in arbitrary order. Sort them - # so our target and source lists are determinate. - java_files.sort() - mydir = dirnode.Dir(dirname) - java_paths = map(lambda f, d=mydir: d.File(f), java_files) - arg.extend(java_paths) - os.path.walk(sdir.rdir().get_abspath(), visit, slist) + find_java = lambda n, js=js, ljs=len(js): _my_normcase(n[-ljs:]) == js + for entry in source: + entry = entry.rentry().disambiguate() + if isinstance(entry, SCons.Node.FS.File): + slist.append(entry) + elif isinstance(entry, SCons.Node.FS.Dir): + result = SCons.Util.OrderedDict() + def visit(arg, dirname, names, fj=find_java, dirnode=entry.rdir()): + java_files = filter(fj, names) + # The on-disk entries come back in arbitrary order. Sort + # them so our target and source lists are determinate. + java_files.sort() + mydir = dirnode.Dir(dirname) + java_paths = map(lambda f, d=mydir: d.File(f), java_files) + for jp in java_paths: + arg[jp] = True + + os.path.walk(entry.rdir().get_abspath(), visit, result) + entry.walk(visit, result) + + slist.extend(result.keys()) + else: + raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__) + version = env.get('JAVAVERSION', '1.4') tlist = [] for f in slist: - version = env.get('JAVAVERSION', '1.4') - pkg_dir, classes = parse_java_file(f.get_abspath(), version) - if pkg_dir: - for c in classes: - t = target[0].Dir(pkg_dir).File(c+class_suffix) - t.attributes.java_classdir = target[0] - t.attributes.java_classname = classname(pkg_dir + os.sep + c) - tlist.append(t) - elif classes: - for c in classes: - t = target[0].File(c+class_suffix) - t.attributes.java_classdir = target[0] - t.attributes.java_classname = classname(c) - tlist.append(t) - else: - # This is an odd end case: no package and no classes. - # Just do our best based on the source file name. - base = str(f)[:-len(java_suffix)] - t = target[0].File(base + class_suffix) - t.attributes.java_classdir = target[0] + source_file_based = True + pkg_dir = None + if not f.is_derived(): + pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version) + if classes: + source_file_based = False + if pkg_dir: + d = target[0].Dir(pkg_dir) + p = pkg_dir + os.sep + else: + d = target[0] + p = '' + for c in classes: + t = d.File(c + class_suffix) + t.attributes.java_classdir = classdir + t.attributes.java_sourcedir = sourcedir + t.attributes.java_classname = classname(p + c) + tlist.append(t) + + if source_file_based: + base = f.name[:-len(java_suffix)] + if pkg_dir: + t = target[0].Dir(pkg_dir).File(base + class_suffix) + else: + t = target[0].File(base + class_suffix) + t.attributes.java_classdir = classdir + t.attributes.java_sourcedir = f.dir t.attributes.java_classname = classname(base) tlist.append(t) @@ -100,18 +130,81 @@ JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') JavaBuilder = SCons.Builder.Builder(action = JavaAction, emitter = emit_java_classes, - target_factory = SCons.Node.FS.Dir, - source_factory = SCons.Node.FS.Dir) + target_factory = SCons.Node.FS.Entry, + source_factory = SCons.Node.FS.Entry) + +def getClassPath(env,target, source, for_signature): + path = "" + if env.has_key('JAVACLASSPATH') and env['JAVACLASSPATH']: + path = SCons.Util.AppendPath(path, env['JAVACLASSPATH']) + return "-classpath %s" % (path) + else: + return "" + +def getSourcePath(env,target, source, for_signature): + path = "" + if env.has_key('JAVASOURCEPATH') and env['JAVASOURCEPATH']: + path = SCons.Util.AppendPath(path, env['JAVASOURCEPATH']) + path = SCons.Util.AppendPath(path,['${TARGET.attributes.java_sourcedir}']) + return "-sourcepath %s" % (path) + +def Java(env, target, source, *args, **kw): + """ + A pseudo-Builder wrapper around the separate JavaClass{File,Dir} + Builders. + """ + if not SCons.Util.is_List(target): + target = [target] + if not SCons.Util.is_List(source): + source = [source] + + # Pad the target list with repetitions of the last element in the + # list so we have a target for every source element. + target = target + ([target[-1]] * (len(source) - len(target))) + + java_suffix = env.subst('$JAVASUFFIX') + result = [] + + for t, s in zip(target, source): + if isinstance(s, SCons.Node.FS.Base): + if isinstance(s, SCons.Node.FS.File): + b = env.JavaClassFile + else: + b = env.JavaClassDir + else: + if os.path.isfile(s): + b = env.JavaClassFile + elif os.path.isdir(s): + b = env.JavaClassDir + elif s[-len(java_suffix):] == java_suffix: + b = env.JavaClassFile + else: + b = env.JavaClassDir + result.extend(apply(b, (t, s) + args, kw)) + + return result def generate(env): """Add Builders and construction variables for javac to an Environment.""" - env['BUILDERS']['Java'] = JavaBuilder + java_file = SCons.Tool.CreateJavaFileBuilder(env) + java_class = SCons.Tool.CreateJavaClassFileBuilder(env) + java_class_dir = SCons.Tool.CreateJavaClassDirBuilder(env) + java_class.add_emitter(None, emit_java_classes) + java_class.add_emitter(env.subst('$JAVASUFFIX'), emit_java_classes) + java_class_dir.emitter = emit_java_classes + + env.AddMethod(Java) env['JAVAC'] = 'javac' env['JAVACFLAGS'] = SCons.Util.CLVar('') - env['JAVACCOM'] = '$JAVAC $JAVACFLAGS -d ${TARGET.attributes.java_classdir} -sourcepath ${SOURCE.dir.rdir()} $SOURCES' + env['JAVACLASSPATH'] = [] + env['JAVASOURCEPATH'] = [] + env['_JAVACLASSPATH'] = getClassPath + env['_JAVASOURCEPATH'] = getSourcePath + env['_JAVACCOM'] = '$JAVAC $JAVACFLAGS $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES' + env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM')}" env['JAVACLASSSUFFIX'] = '.class' env['JAVASUFFIX'] = '.java' def exists(env): - return env.Detect('javac') + return 1 diff --git a/src/engine/SCons/Tool/javac.xml b/src/engine/SCons/Tool/javac.xml index 248eda2..f5af975 100644 --- a/src/engine/SCons/Tool/javac.xml +++ b/src/engine/SCons/Tool/javac.xml @@ -14,6 +14,8 @@ JAVACFLAGS JAVACCOM JAVACLASSSUFFIX JAVASUFFIX +JAVACLASSPATH +JAVASOURCEPATH JAVACCOMSTR @@ -22,16 +24,21 @@ JAVACCOMSTR -Builds one or more Java class files -from one or more source trees of .java files. -The class files will be placed underneath -the specified target directory. +Builds one or more Java class files. +The sources may be any combination of explicit +.java files, +or directory trees which will be scanned +for .java files. + SCons will parse each source .java file to find the classes (including inner classes) defined within that file, and from that figure out the target .class files that will be created. +The class files will be placed underneath +the specified target directory. + SCons will also search each Java file for the Java package name, which it assumes can be found on a line @@ -58,6 +65,7 @@ Example: env.Java(target = 'classes', source = 'src') env.Java(target = 'classes', source = ['src1', 'src2']) +env.Java(target = 'classes', source = ['File1.java', 'File2.java']) @@ -107,6 +115,27 @@ builder. + + +Specifies the list of directories that +will be searched for Java +.class file. +The directories in this list will be added to the +&javac; and &javah; command lines +via the option. +The individual directory names will be +separated by the operating system's path separate character +(: on UNIX/Linux/POSIX, +; on Windows). + +Note that this currently just adds the specified +directory via the option. +&SCons; does not currently search the +&cv-JAVACLASSPATH; directories for dependency +.class files. + + + The suffix for Java class files; @@ -115,6 +144,27 @@ by default. + + +Specifies the list of directories that +will be searched for input +.java file. +The directories in this list will be added to the +&javac; command line +via the option. +The individual directory names will be +separated by the operating system's path separate character +(: on UNIX/Linux/POSIX, +; on Windows). + +Note that this currently just adds the specified +directory via the option. +&SCons; does not currently search the +&cv-JAVASOURCEPATH; directories for dependency +.java files. + + + The suffix for Java files; diff --git a/src/engine/SCons/Tool/javah.py b/src/engine/SCons/Tool/javah.py index 26fcc53..7eb4969 100644 --- a/src/engine/SCons/Tool/javah.py +++ b/src/engine/SCons/Tool/javah.py @@ -60,6 +60,7 @@ def emit_java_headers(target, source, env): except AttributeError: classdir = '.' classdir = env.Dir(classdir).rdir() + if str(classdir) == '.': c_ = None else: @@ -77,10 +78,13 @@ def emit_java_headers(target, source, env): classname = classname[:-len(class_suffix)] classname = SCons.Tool.javac.classname(classname) s = src.rfile() - s.attributes.java_classdir = classdir s.attributes.java_classname = classname slist.append(s) + s = source[0].rfile() + if not hasattr(s.attributes, 'java_classdir'): + s.attributes.java_classdir = classdir + if target[0].__class__ is SCons.Node.FS.File: tlist = target else: @@ -106,22 +110,22 @@ def JavaHOutFlagGenerator(target, source, env, for_signature): except AttributeError: return '-o ' + str(t) -JavaHAction = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR') - -JavaHBuilder = SCons.Builder.Builder(action = JavaHAction, - emitter = emit_java_headers, - src_suffix = '$JAVACLASSSUFFIX', - target_factory = SCons.Node.FS.Entry, - source_factory = SCons.Node.FS.File) +def getJavaHClassPath(env,target, source, for_signature): + path = "${SOURCE.attributes.java_classdir}" + if env.has_key('JAVACLASSPATH') and env['JAVACLASSPATH']: + path = SCons.Util.AppendPath(path, env['JAVACLASSPATH']) + return "-classpath %s" % (path) def generate(env): """Add Builders and construction variables for javah to an Environment.""" - env['BUILDERS']['JavaH'] = JavaHBuilder + java_javah = SCons.Tool.CreateJavaHBuilder(env) + java_javah.emitter = emit_java_headers env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator env['JAVAH'] = 'javah' env['JAVAHFLAGS'] = SCons.Util.CLVar('') - env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}' + env['_JAVAHCLASSPATH'] = getJavaHClassPath + env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG $_JAVAHCLASSPATH ${SOURCES.attributes.java_classname}' env['JAVACLASSSUFFIX'] = '.class' def exists(env): diff --git a/src/engine/SCons/Tool/javah.xml b/src/engine/SCons/Tool/javah.xml index c6487e7..20fe3ee 100644 --- a/src/engine/SCons/Tool/javah.xml +++ b/src/engine/SCons/Tool/javah.xml @@ -16,6 +16,7 @@ JAVACLASSSUFFIX JAVAHCOMSTR +JAVACLASSPATH @@ -27,7 +28,10 @@ The target can be either a directory in which the header files will be written, or a header file name which will contain all of the definitions. -The source can be either the names of .class files, +The source can be the names of .class files, +the names of .java files +to be compiled into .class files +by calling the &b-link-Java; builder method, or the objects returned from the &b-Java; builder method. diff --git a/src/engine/SCons/Tool/lex.py b/src/engine/SCons/Tool/lex.py index 31f21a9..e599f8a 100644 --- a/src/engine/SCons/Tool/lex.py +++ b/src/engine/SCons/Tool/lex.py @@ -55,7 +55,8 @@ def lexEmitter(target, source, env): # Different options that are used to trigger the creation of extra files. fileGenOptions = ["--header-file=", "--tables-file="] - for option in SCons.Util.CLVar(env.subst("$LEXFLAGS")): + lexflags = env.subst("$LEXFLAGS", target=target, source=source) + for option in SCons.Util.CLVar(lexflags): for fileGenOption in fileGenOptions: l = len(fileGenOption) if option[:l] == fileGenOption: diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py index c5f40b7..e8f6a09 100644 --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -175,6 +175,15 @@ def Package(env, target=None, source=None, **kw): targets.extend(env.Alias( 'package', targets )) return targets +def build_source(ss, sources): + for s in ss: + if s.__class__==SCons.Node.FS.Dir: + build_source(s.all_children()) + elif not s.has_builder() and s.__class__==SCons.Node.FS.File: + sources.append(s) + else: + build_source(s.sources) + def FindSourceFiles(env, target=None, source=None ): """ returns a list of all children of the target nodes, which have no children. This selects all leaves of the DAG that gets build by SCons for @@ -185,17 +194,8 @@ def FindSourceFiles(env, target=None, source=None ): nodes = env.arg2nodes(target, env.fs.Entry) sources = [] - def build_source(ss): - for s in ss: - if s.__class__==SCons.Node.FS.Dir: - build_source(s.all_children()) - elif not s.has_builder() and s.__class__==SCons.Node.FS.File: - sources.append(s) - else: - build_source(s.sources) - for node in nodes: - build_source(node.all_children()) + build_source(node.all_children(), sources) # now strip the build_node from the sources by calling the srcnode # function diff --git a/src/engine/SCons/Tool/packaging/__init__.xml b/src/engine/SCons/Tool/packaging/__init__.xml index 8e7f652..bf4e96e 100644 --- a/src/engine/SCons/Tool/packaging/__init__.xml +++ b/src/engine/SCons/Tool/packaging/__init__.xml @@ -6,36 +6,46 @@ See its __doc__ string for a discussion of the format. --> -TODO +Sets construction variables for the &b-Package; Builder. + + + + -Builds software distribution packages. Packages consist of files to install -and packaging information. The former may be specified with the source -parameter and may be left out, in which case the &-bFindInstalledFiles; -function will collect all files that have an &-bInstall; or -&-bInstallAs; Builder attached. The target, if not specified will be deduced -from additional information given to this Builder. - -The packaging information is specified with the help of construction Variables -documented below. This information is called a tag to stress that some of them -can also be attached to files with the &-bTag; Builder.The mandatory ones will -complain if they were not specified. They vary depending on chosen target -packager. - -The target packager may be selected with the "PACKAGETYPE" command line option -or with the &-tPACKAGETYPE; construction variable. Currently there are six -packagers available: + +Builds software distribution packages. Packages consist of files +to install and packaging information. The former may be specified +with the &source; parameter and may be left out, in which case the +&FindInstalledFiles; function will collect all files that have an +&b-Install; or &b-InstallAs; Builder attached. If the ⌖, is +not specified it will be deduced from additional information given to +this Builder. + +The packaging information is specified with the help of construction +variables documented below. This information is called a tag to stress +that some of them can also be attached to files with the &Tag; function. +The mandatory ones will complain if they were not specified. They vary +depending on chosen target packager. + +The target packager may be selected with the "PACKAGETYPE" command line +option or with the &cv-PACKAGETYPE; construction variable. Currently +the following packagers available: * msi - Microsoft Installer * rpm - Redhat Package Manger * ipkg - Itsy Package Management System - * tarbz, tarbz and zip + * tarbz2 - compressed tar + * targz - compressed tar + * zip - zip file + * src_tarbz2 - compressed tar source + * src_targz - compressed tar source + * src_zip - zip file source An updated list is always available under the "package_type" option when running "scons --help" on a project that has packaging activated. - env = Environment(tools=['default', 'packaging']) env.Install('/bin/', 'my_program') @@ -50,148 +60,229 @@ env.Package( NAME = 'foo', SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' ) + + - + -A long description of what the project is about. +Specifies the system architecture for which +the package is being built. +The default is the system architecture +of the machine on which SCons is running. +This is used to fill in the +Architecture: +field in an Ipkg +control file, +and as part of the name of a generated RPM file. - + -TODO +A hook for modifying the file that controls the packaging build +(the .spec for RPM, +the control for Ipkg, +the .wxs for MSI). +If set, the function will be called +after the SCons template for the file has been written. +XXX + + +The name of a file containing the change log text +to be included in the package. +This is included as the +%changelog +section of the RPM +.spec file. + + - + -A short summary of what the project is about. +A long description of the project being packaged. +This is included in the relevant section +of the file that controls the packaging build. - + -The shorthand of the license this project is under (gpl, lpgl, bsd etc.). +A language-specific long description for +the specified lang. +This is used to populate a +%description -l +section of an RPM +.spec file. - + -Specfies the name of the project to package. +The abbreviated name of the license under which +this project is released (gpl, lpgl, bsd etc.). +See http://www.opensource.org/licenses/alphabetical +for a list of license names. - + -The version of the project, given as a string. +Specfies the name of the project to package. - + -The version of the package, if only changes in the package were done. Currently -only used by the rpm packager. +Specifies the directory where all files in resulting archive will be +placed if applicable. The default value is "$NAME-$VERSION". -Selects the package type to build. Currently those are available: +Selects the package type to build. Currently these are available: * msi - Microsoft Installer * rpm - Redhat Package Manger * ipkg - Itsy Package Management System - * tarbz2, targz and zip - tarball and zip packager - * src_tarbz2, src_targz and src_zip - source tarbarll and zip packager + * tarbz2 - compressed tar + * targz - compressed tar + * zip - zip file + * src_tarbz2 - compressed tar source + * src_targz - compressed tar source + * src_zip - zip file source This may be overridden with the "package_type" command line option. - + -TODO +The version of the package (not the underlying project). +This is currently only used by the rpm packager +and should reflect changes in the packaging, +not the underlying project code itself. -TODO +The URL +(web address) +of the location from which the project was retrieved. +This is used to fill in the +Source: +field in the controlling information for Ipkg and RPM packages. - + -TODO +A short summary of what the project is about. +This is used to fill in the +Summary: +field in the controlling information for Ipkg and RPM packages, +and as the +Description: +field in MSI packages. - + -defines the directory where all files in resulting archive will be placed if -applicable. The default value is "$NAME-$VERSION". +The person or organization who supply the packaged software. +This is used to fill in the +Vendor: +field in the controlling information for RPM packages, +and the +Manufacturer: +field in the controlling information for MSI packages. - + -Short name of the license your package is under. Example: gpl, lgpl, bsd ... -See http://www.opensource.org/licenses/alphabetical +The version of the project, specified as a string. - + + -TODO +This is used to fill in the +Depends: +field in the controlling information for Ipkg packages. - + -TODO +This is used to fill in the +Description: +field in the controlling information for Ipkg packages. +The default value is +$SUMMARY\n$DESCRIPTION - + -TODO +This is used to fill in the +Maintainer: +field in the controlling information for Ipkg packages. - + -TODO +This is used to fill in the +Priority: +field in the controlling information for Ipkg packages. - + -TODO +This is used to fill in the +Section: +field in the controlling information for Ipkg packages. - + + + -TODO +This is used to fill in the +Language: +attribute in the controlling information for MSI packages. - + -TODO +The text of the software license in RTF format. +Carriage return characters will be +replaced with the RTF equivalent \\par. - + TODO - + + -internal, but overridable +This is used to fill in the +AutoReqProv: +field in the RPM +.spec file. @@ -201,188 +292,254 @@ internal, but overridable - + -internal, but overridable +This is used to fill in the +BuildRequires: +field in the RPM +.spec file. - + internal, but overridable - + -TODO +internal, but overridable - + -TODO +This is used to fill in the +Conflicts: +field in the RPM +.spec file. - + -TODO +This value is used as the default attributes +for the files in the RPM package. +The default value is +(-,root,root). -TODO +This is used to fill in the +Distribution: +field in the RPM +.spec file. - + -TODO +This is used to fill in the +Epoch: +field in the controlling information for RPM packages. - + -TODO +This is used to fill in the +ExcludeArch: +field in the RPM +.spec file. - + -TODO +This is used to fill in the +ExclusiveArch: +field in the RPM +.spec file. - + -TODO +This is used to fill in the +Group: +field in the RPM +.spec file. - + -TODO +This is used to fill in the +Group(lang): +field in the RPM +.spec file. +Note that +lang +is not literal +and should be replaced by +the appropriate language code. - + -TODO +This is used to fill in the +Icon: +field in the RPM +.spec file. - + -TODO +internal, but overridable - + -TODO +This is used to fill in the +Packager: +field in the RPM +.spec file. - + -TODO +This is used to fill in the +Provides: +field in the RPM +.spec file. - + -TODO +This is used to fill in the +%post: +section in the RPM +.spec file. - + -TODO +This is used to fill in the +%pre: +section in the RPM +.spec file. -TODO +This is used to fill in the +Prefix: +field in the RPM +.spec file. - - -TODO - - - - + internal, but overridable - + -TODO +This is used to fill in the +%postun: +section in the RPM +.spec file. - + -The text of the software license in rtf format. Carriage return chars will be -replaced with the rtf equivalent \\par. +This is used to fill in the +%preun: +section in the RPM +.spec file. - + -TODO +This is used to fill in the +Requires: +field in the RPM +.spec file. - + -TODO +This is used to fill in the +Serial: +field in the RPM +.spec file. - + -TODO +This is used to fill in the +Url: +field in the RPM +.spec file. - + + + - - -TODO - - - + diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py index 49b8ff8..94b7b7a 100644 --- a/src/engine/SCons/Tool/packaging/rpm.py +++ b/src/engine/SCons/Tool/packaging/rpm.py @@ -41,9 +41,7 @@ def package(env, target, source, PACKAGEROOT, NAME, VERSION, # initialize the rpm tool SCons.Tool.Tool('rpm').generate(env) - # create the neccesary builder bld = env['BUILDERS']['Rpm'] - env['RPMFLAGS'] = SCons.Util.CLVar('-ta') bld.push_emitter(targz_emitter) bld.push_emitter(specfile_emitter) @@ -67,8 +65,9 @@ def package(env, target, source, PACKAGEROOT, NAME, VERSION, if kw.has_key('ARCHITECTURE'): buildarchitecture = kw['ARCHITECTURE'] - srcrpm = '%s-%s-%s.src.rpm' % (NAME, VERSION, PACKAGEVERSION) - binrpm = string.replace(srcrpm, 'src', buildarchitecture) + fmt = '%s-%s-%s.%s.rpm' + srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src') + binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture) target = [ srcrpm, binrpm ] diff --git a/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py index 5326e8d..8ca1b89 100644 --- a/src/engine/SCons/Tool/swig.py +++ b/src/engine/SCons/Tool/swig.py @@ -33,57 +33,30 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import os.path +import re + import SCons.Action import SCons.Defaults +import SCons.Scanner import SCons.Tool import SCons.Util -from SCons.Scanner import Scanner -import os -import re SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR') def swigSuffixEmitter(env, source): - if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS")): + if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS", source=source)): return '$SWIGCXXFILESUFFIX' else: return '$SWIGCFILESUFFIX' -_reInclude = re.compile(r'%include\s+(\S+)') _reModule = re.compile(r'%module\s+(.+)') -def recurse(path, searchPath): - global _reInclude - f = open(path) - try: contents = f.read() - finally: f.close() - - found = [] - # Better code for when we drop Python 1.5.2. - #for m in _reInclude.finditer(contents): - # fname = m.group(1) - for fname in _reInclude.findall(contents): - for dpath in searchPath: - absPath = os.path.join(dpath, fname) - if os.path.isfile(absPath): - found.append(absPath) - break - - # Equivalent code for when we drop Python 1.5.2. - #for f in [f for f in found if os.path.splitext(f)[1] == ".i"]: - # found += recurse(f, searchPath) - for f in filter(lambda f: os.path.splitext(f)[1] == ".i", found): - found = found + recurse(f, searchPath) - return found - -def _scanSwig(node, env, path): - r = recurse(str(node), [os.path.abspath(os.path.dirname(str(node))), os.path.abspath(os.path.join("include", "swig"))]) - return r - def _swigEmitter(target, source, env): + swigflags = env.subst("$SWIGFLAGS", target=target, source=source) + flags = SCons.Util.CLVar(swigflags) for src in source: - src = str(src) - flags = SCons.Util.CLVar(env.subst("$SWIGFLAGS")) + src = str(src.rfile()) mnames = None if "-python" in flags and "-noproxy" not in flags: if mnames is None: @@ -94,9 +67,13 @@ def _swigEmitter(target, source, env): mnames = _reModule.findall(open(src).read()) java_files = map(lambda m: [m + ".java", m + "JNI.java"], mnames) java_files = SCons.Util.flatten(java_files) - outdir = env.subst('$SWIGOUTDIR') + outdir = env.subst('$SWIGOUTDIR', target=target, source=source) if outdir: java_files = map(lambda j, o=outdir: os.path.join(o, j), java_files) + java_files = map(env.fs.File, java_files) + for jf in java_files: + t_from_s = lambda t, p, s, x: t.dir + SCons.Util.AddMethod(jf, t_from_s, 'target_from_source') target.extend(java_files) return (target, source) @@ -112,13 +89,28 @@ def generate(env): cxx_file.add_action('.i', SwigAction) cxx_file.add_emitter('.i', _swigEmitter) + java_file = SCons.Tool.CreateJavaFileBuilder(env) + + java_file.suffix['.i'] = swigSuffixEmitter + + java_file.add_action('.i', SwigAction) + java_file.add_emitter('.i', _swigEmitter) + env['SWIG'] = 'swig' env['SWIGFLAGS'] = SCons.Util.CLVar('') env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX' - env['_SWIGOUTDIR'] = '${"-outdir " + SWIGOUTDIR}' - env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} $SWIGFLAGS $SOURCES' - env.Append(SCANNERS=Scanner(function=_scanSwig, skeys=[".i"])) + env['_SWIGOUTDIR'] = '${"-outdir " + str(SWIGOUTDIR)}' + env['SWIGPATH'] = [] + env['SWIGINCPREFIX'] = '-I' + env['SWIGINCSUFFIX'] = '' + env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES' + + expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' + scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr) + + env.Append(SCANNERS = scanner) def exists(env): return env.Detect(['swig']) diff --git a/src/engine/SCons/Tool/swig.xml b/src/engine/SCons/Tool/swig.xml index 679d683..580ef97 100644 --- a/src/engine/SCons/Tool/swig.xml +++ b/src/engine/SCons/Tool/swig.xml @@ -13,7 +13,11 @@ SWIG SWIGFLAGS SWIGCFILESUFFIX SWIGCXXFILESUFFIX +_SWIGINCFLAGS +SWIGINCPREFIX +SWIGINCSUFFIX SWIGCOM +SWIGPATH SWIGCOMSTR @@ -94,6 +98,36 @@ variable. + + +An automatically-generated construction variable +containing the SWIG command-line options +for specifying directories to be searched for included files. +The value of &cv-_SWIGINCFLAGS; is created +by appending &cv-SWIGINCPREFIX; and &cv-SWIGINCSUFFIX; +to the beginning and end +of each directory in &cv-SWIGPATH;. + + + + + +The prefix used to specify an include directory on the SWIG command line. +This will be appended to the beginning of each directory +in the &cv-SWIGPATH; construction variable +when the &cv-_SWIGINCFLAGS; variable is automatically generated. + + + + + +The suffix used to specify an include directory on the SWIG command line. +This will be appended to the end of each directory +in the &cv-SWIGPATH; construction variable +when the &cv-_SWIGINCFLAGS; variable is automatically generated. + + + Specifies the output directory in which @@ -105,3 +139,55 @@ and translated into the swig -outdir option on the command line. + + + +The list of directories that the scripting language wrapper +and interface generate will search for included files. +The SWIG implicit dependency scanner will search these +directories for include files. +The default is to use the same path +specified as &cv-CPPPATH;. + +Don't explicitly put include directory +arguments in SWIGFLAGS; +the result will be non-portable +and the directories will not be searched by the dependency scanner. +Note: directory names in SWIGPATH will be looked-up relative to the SConscript +directory when they are used in a command. +To force +&scons; +to look-up a directory relative to the root of the source tree use #: + + +env = Environment(SWIGPATH='#/include') + + +The directory look-up can also be forced using the +&Dir;() +function: + + +include = Dir('include') +env = Environment(SWIGPATH=include) + + +The directory list will be added to command lines +through the automatically-generated +&cv-_SWIGINCFLAGS; +construction variable, +which is constructed by +appending the values of the +&cv-SWIGINCPREFIX; and &cv-SWIGINCSUFFIX; +construction variables +to the beginning and end +of each directory in &cv-SWIGPATH;. +Any command lines you define that need +the SWIGPATH directory list should +include &cv-_SWIGINCFLAGS;: + + +env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SORUCES") + + + diff --git a/src/engine/SCons/Tool/wix.py b/src/engine/SCons/Tool/wix.py index b133947..3aa3375 100644 --- a/src/engine/SCons/Tool/wix.py +++ b/src/engine/SCons/Tool/wix.py @@ -69,6 +69,9 @@ def exists(env): # add the install directory to light libpath. #for path in os.environ['PATH'].split(os.pathsep): for path in string.split(os.environ['PATH'], os.pathsep): + if not path: + continue + # workaround for some weird python win32 bug. if path[0] == '"' and path[-1:]=='"': path = path[1:-1] diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index cbccb29..34f60cb 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -43,7 +43,8 @@ import SCons.Util YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") def _yaccEmitter(target, source, env, ysuf, hsuf): - flags = SCons.Util.CLVar(env.subst("$YACCFLAGS")) + yaccflags = env.subst("$YACCFLAGS", target=target, source=source) + flags = SCons.Util.CLVar(yaccflags) targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0])) if '.ym' in ysuf: # If using Objective-C diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 22aca08..3dfa287 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -312,16 +312,16 @@ 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): + children = child_func(root) + + if prune and visited.has_key(rname) and children: print string.join(tags + margins + ['+-[', rname, ']'], '') return print string.join(tags + margins + ['+-', rname], '') - if prune: - visited[rname] = 1 + visited[rname] = 1 - children = child_func(root) if children: margin.append(1) map(lambda C, cf=child_func, p=prune, i=IDX(showtags), m=margin, v=visited: @@ -404,6 +404,64 @@ def flatten(sequence, scalarp=is_Scalar, result=None): flatten(item, scalarp, result) return result + + +# The SCons "semi-deep" copy. +# +# This makes separate copies of lists (including UserList objects) +# dictionaries (including UserDict objects) and tuples, but just copies +# references to anything else it finds. +# +# A special case is any object that has a __semi_deepcopy__() method, +# which we invoke to create the copy, which is used by the BuilderDict +# class because of its extra initialization argument. +# +# The dispatch table approach used here is a direct rip-off from the +# normal Python copy module. + +_semi_deepcopy_dispatch = d = {} + +def _semi_deepcopy_dict(x): + copy = {} + for key, val in x.items(): + # The regular Python copy.deepcopy() also deepcopies the key, + # as follows: + # + # copy[semi_deepcopy(key)] = semi_deepcopy(val) + # + # Doesn't seem like we need to, but we'll comment it just in case. + copy[key] = semi_deepcopy(val) + return copy +d[types.DictionaryType] = _semi_deepcopy_dict + +def _semi_deepcopy_list(x): + return map(semi_deepcopy, x) +d[types.ListType] = _semi_deepcopy_list + +def _semi_deepcopy_tuple(x): + return tuple(map(semi_deepcopy, x)) +d[types.TupleType] = _semi_deepcopy_tuple + +def _semi_deepcopy_inst(x): + if hasattr(x, '__semi_deepcopy__'): + return x.__semi_deepcopy__() + elif isinstance(x, UserDict): + return x.__class__(_semi_deepcopy_dict(x)) + elif isinstance(x, UserList): + return x.__class__(_semi_deepcopy_list(x)) + else: + return x +d[types.InstanceType] = _semi_deepcopy_inst + +def semi_deepcopy(x): + copier = _semi_deepcopy_dispatch.get(type(x)) + if copier: + return copier(x) + else: + return x + + + class Proxy: """A simple generic Proxy class, forwarding all calls to subject. So, for the benefit of the python newbie, what does @@ -1044,4 +1102,35 @@ def RenameFunction(function, name): +# From Dinu C. Gherman, +# Python Cookbook, second edition, recipe 6.17, p. 277. +# Also: +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 +# ASPN: Python Cookbook: Null Object Design Pattern + +class Null: + """ Null objects always and reliably "do nothging." """ + + def __new__(cls, *args, **kwargs): + if not '_inst' in vars(cls): + #cls._inst = type.__new__(cls, *args, **kwargs) + cls._inst = apply(type.__new__, (cls,) + args, kwargs) + return cls._inst + def __init__(self, *args, **kwargs): + pass + def __call__(self, *args, **kwargs): + return self + def __repr__(self): + return "Null()" + def __nonzero__(self): + return False + def __getattr__(self, mname): + return self + def __setattr__(self, name, value): + return self + def __delattr__(self, name): + return self + + + del __revision__ diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index db6d9f6..1149f35 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -106,7 +106,7 @@ class UtilTestCase(unittest.TestCase): return foo, expect, withtags - def tree_case_2(self): + def tree_case_2(self, prune=1): """Fixture for the render_tree() and print_tree() tests.""" stdlib_h = self.Node("stdlib.h") @@ -124,6 +124,10 @@ class UtilTestCase(unittest.TestCase): +-[stdlib.h] """ + if not prune: + expect = string.replace(expect, '[', '') + expect = string.replace(expect, ']', '') + lines = string.split(expect, '\n')[:-1] lines = map(lambda l: '[E BSPACN ]'+l, lines) withtags = string.join(lines, '\n') + '\n' @@ -163,7 +167,7 @@ class UtilTestCase(unittest.TestCase): actual = sys.stdout.getvalue() assert withtags == actual, (withtags, actual) - node, expect, withtags = self.tree_case_2() + node, expect, withtags = self.tree_case_2(prune=0) sys.stdout = StringIO.StringIO() print_tree(node, get_children, 1) diff --git a/src/engine/setup.py b/src/engine/setup.py index 4b511b0..692b0a0 100644 --- a/src/engine/setup.py +++ b/src/engine/setup.py @@ -63,7 +63,6 @@ software.""", keywords = "scons, cons, make, build tool, make tool", packages = ["SCons", "SCons.Node", - "SCons.Optik", "SCons.Scanner", "SCons.Sig", "SCons.Script"]) diff --git a/src/setup.py b/src/setup.py index 5614adb..7123304 100644 --- a/src/setup.py +++ b/src/setup.py @@ -29,7 +29,7 @@ import stat import string import sys -Version = "0.97" +Version = "__VERSION__" man_pages = [ 'scons.1', @@ -376,13 +376,13 @@ arguments = { 'packages' : ["SCons", "SCons.compat", "SCons.Node", - "SCons.Optik", "SCons.Options", "SCons.Platform", "SCons.Scanner", "SCons.Script", "SCons.Sig", - "SCons.Tool"], + "SCons.Tool", + "SCons.Tool.packaging"], 'package_dir' : {'' : 'engine'}, 'data_files' : [('man/man1', man_pages)], 'scripts' : scripts, diff --git a/src/test_interrupts.py b/src/test_interrupts.py index d18bece..9e91cc5 100644 --- a/src/test_interrupts.py +++ b/src/test_interrupts.py @@ -62,8 +62,13 @@ else: scons_lib_dir = os.path.join(cwd, 'build', 'scons') MANIFEST = os.path.join(scons_lib_dir, 'MANIFEST') -files = string.split(open(MANIFEST).read()) -files = filter(lambda f: f[-3:] == '.py', files) +try: + fp = open(MANIFEST) +except IOError: + test.skip_test('%s does not exist; skipping test.\n' % MANIFEST) +else: + files = string.split(fp.read()) + files = filter(lambda f: f[-3:] == '.py', files) # some regexps to parse the python files tryexc_pat = re.compile( diff --git a/src/test_pychecker.py b/src/test_pychecker.py index 368520f..0032559 100644 --- a/src/test_pychecker.py +++ b/src/test_pychecker.py @@ -72,10 +72,6 @@ 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 = {} diff --git a/src/test_strings.py b/src/test_strings.py index a3ed6e8..0d6e6ac 100644 --- a/src/test_strings.py +++ b/src/test_strings.py @@ -124,12 +124,13 @@ check_list = [ 'src', search_list = [ '*.py' ], remove_list = [ + 'engine/SCons/compat/_scons_optparse.py', 'engine/SCons/compat/_scons_sets.py', 'engine/SCons/compat/_scons_sets15.py', 'engine/SCons/compat/_scons_subprocess.py', + 'engine/SCons/compat/_scons_textwrap.py', 'engine/SCons/Conftest.py', 'engine/SCons/dblite.py', - 'engine/SCons/Optik', ], ), @@ -147,31 +148,34 @@ check_list = [ 'debian', 'dist', 'gentoo', + 'engine/SCons/compat/_scons_optparse.py', 'engine/SCons/compat/_scons_sets.py', 'engine/SCons/compat/_scons_sets15.py', 'engine/SCons/compat/_scons_subprocess.py', + 'engine/SCons/compat/_scons_textwrap.py', 'engine/SCons/Conftest.py', 'engine/SCons/dblite.py', - 'engine/SCons/Optik', 'MANIFEST', 'os_spawnv_fix.diff', 'setup.cfg', ], # We run epydoc on the *.py files, which generates *.pyc files. remove_patterns = [ - '*.pyc' + '*.pyc', ] ), CheckExpandedCopyright( build_local, remove_list = [ + 'SCons/compat/_scons_optparse.py', 'SCons/compat/_scons_sets.py', 'SCons/compat/_scons_sets15.py', 'SCons/compat/_scons_subprocess.py', + 'SCons/compat/_scons_textwrap.py', 'SCons/Conftest.py', 'SCons/dblite.py', - 'SCons/Optik', + 'scons-%s.egg-info' % scons_version, ], ), @@ -194,6 +198,7 @@ check_list = [ 'doc/user/SCons-win32-install-2.jpg', 'doc/user/SCons-win32-install-3.jpg', 'doc/user/SCons-win32-install-4.jpg', + 'examples', 'gentoo', 'QMTest/classes.qmc', 'QMTest/configuration', @@ -206,12 +211,13 @@ check_list = [ 'src/engine/MANIFEST.in', 'src/engine/MANIFEST-xml.in', 'src/engine/setup.cfg', + 'src/engine/SCons/compat/_scons_optparse.py', 'src/engine/SCons/compat/_scons_sets.py', 'src/engine/SCons/compat/_scons_sets15.py', 'src/engine/SCons/compat/_scons_subprocess.py', + 'src/engine/SCons/compat/_scons_textwrap.py', 'src/engine/SCons/Conftest.py', 'src/engine/SCons/dblite.py', - 'src/engine/SCons/Optik', 'src/script/MANIFEST.in', 'src/script/setup.cfg', ], diff --git a/test/AS/ml.py b/test/AS/ml.py index 0cb0e9e..633befd 100644 --- a/test/AS/ml.py +++ b/test/AS/ml.py @@ -39,7 +39,7 @@ _exe = TestSCons._exe test = TestSCons.TestSCons() -if sys.platform == 'win32': +if sys.platform != 'win32': test.skip_test("Skipping ml test on non-win32 platform '%s'\n" % sys.platform) ml = test.where_is('ml') diff --git a/test/AS/nasm.py b/test/AS/nasm.py index f96db7e..f1b99ee 100644 --- a/test/AS/nasm.py +++ b/test/AS/nasm.py @@ -45,7 +45,7 @@ if not nasm: test.skip_test('nasm not found; skipping test\n') if string.find(sys.platform, 'linux') == -1: - test.skip_test("skipping test on non-Linux platform '%s'\n" % sys.platfrom) + test.skip_test("skipping test on non-Linux platform '%s'\n" % sys.platform) try: import popen2 diff --git a/test/Actions/addpost-link.py b/test/Actions/addpost-link.py new file mode 100644 index 0000000..f97d4a3 --- /dev/null +++ b/test/Actions/addpost-link.py @@ -0,0 +1,76 @@ +#!/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 AddPostAction() on a program target doesn't interfere with +linking. + +This is a test for fix of Issue 1004, reported by Matt Doar and +packaged by Gary Oberbrunner. +""" + +import TestSCons + + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +env = Environment() + +mylib = env.StaticLibrary('mytest', 'test_lib.c') + +myprog = env.Program('test1.c', + LIBPATH = ['.'], + LIBS = ['mytest']) +if ARGUMENTS['case']=='2': + AddPostAction(myprog, Action('strip ' + myprog[0].abspath)) +""") + +test.write('test1.c', """\ +extern void test_lib_fn(); +int main(int argc, char **argv) { + test_lib_fn(); + return 0; +} +""") + +test.write('test_lib.c', r"""\ +#include + +void test_lib_fn() { + printf("Hello world\n"); +} +""") + +test.run(arguments="-Q case=1", stderr=None) + +test.run(arguments="-Q -c case=1") + +test.must_not_exist('test1.o') + +test.run(arguments="-Q case=2", stderr=None) + +test.pass_test() diff --git a/test/Actions/pre-post.py b/test/Actions/pre-post.py index 7566f2f..674c628 100644 --- a/test/Actions/pre-post.py +++ b/test/Actions/pre-post.py @@ -49,9 +49,11 @@ import stat env = Environment(XXX='bar%(_exe)s') def before(env, target, source): - f=open(str(target[0]), "wb") + a=str(target[0]) + f=open(a, "wb") f.write("Foo\\n") f.close() + os.chmod(a, os.stat(a)[stat.ST_MODE] | stat.S_IXUSR) f=open("before.txt", "ab") f.write(os.path.splitext(str(target[0]))[0] + "\\n") f.close() diff --git a/test/AddOption/optional-arg.py b/test/AddOption/optional-arg.py new file mode 100644 index 0000000..f7a0337 --- /dev/null +++ b/test/AddOption/optional-arg.py @@ -0,0 +1,102 @@ +#!/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 nargs='?' keyword argument to specify a long +command-line option with an optional argument value. +""" + +import string + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +AddOption('--install', + nargs='?', + dest='install', + default='/default/directory', + const='/called/default/directory', + action='store', + type='string', + metavar='DIR', + help='installation directory') +print GetOption('install') +""") + +test.run('-Q -q', + stdout="/default/directory\n") + +test.run('-Q -q next-arg', + stdout="/default/directory\n", + status=1) + +test.run('-Q -q . --install', + stdout="/called/default/directory\n") + +test.run('-Q -q . --install next-arg', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . first-arg --install', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . first-arg --install next-arg', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . --install=/command/line/directory', + stdout="/command/line/directory\n") + +test.run('-Q -q . --install=/command/line/directory next-arg', + stdout="/command/line/directory\n", + status=1) + +test.run('-Q -q . first-arg --install=/command/line/directory', + stdout="/command/line/directory\n", + status=1) + +test.run('-Q -q . first-arg --install=/command/line/directory next-arg', + stdout="/command/line/directory\n", + status=1) + + +test.write('SConstruct', """\ +AddOption('-X', nargs='?') +""") + +expect = r""" +scons: \*\*\* option -X: nargs='\?' is incompatible with short options +File "[^"]+", line \d+, in \S+ +""" + +test.run(status=2, stderr=expect, match=TestSCons.match_re) + + + +test.pass_test() diff --git a/test/BuildDir/CPPPATH-subdir.py b/test/BuildDir/CPPPATH-subdir.py index 7254279..9d3bb98 100644 --- a/test/BuildDir/CPPPATH-subdir.py +++ b/test/BuildDir/CPPPATH-subdir.py @@ -58,6 +58,7 @@ env.Library('foo', 'foo.c') test.write(['src', 'glscry', 'foo.c'], """\ #include +int foo(void) { return 0; } """) diff --git a/test/CPPPATH/match-dir.py b/test/CPPPATH/match-dir.py new file mode 100644 index 0000000..f8501c6 --- /dev/null +++ b/test/CPPPATH/match-dir.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 we don't blow up if there's a directory name within +$CPPPATH that matches a #include file name. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir(['src'], + ['src', 'inc'], + ['src', 'inc', 'inc2']) + +test.write('SConstruct', """\ +SConscript('src/SConscript', build_dir = 'build', duplicate = 0) +""") + +test.write(['src', 'SConscript'], """\ +env = Environment(CPPPATH = ['#build/inc', '#build/inc/inc2']) +env.Object('foo.c') +""") + +test.write(['src', 'foo.c'], """\ +#include "inc1" +""") + +test.subdir(['src', 'inc', 'inc1']) + +test.write(['src', 'inc', 'inc2', 'inc1'], "\n") + +test.run(arguments = '.') + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/CacheDir/BuildDir.py b/test/CacheDir/BuildDir.py index d03bfa6..a41d397 100644 --- a/test/CacheDir/BuildDir.py +++ b/test/CacheDir/BuildDir.py @@ -39,11 +39,6 @@ test.subdir('cache', 'src') cache = test.workpath('cache') cat_out = test.workpath('cat.out') -test.write(['src', 'SConstruct'], """\ -CacheDir(r'%(cache)s') -SConscript('SConscript') -""" % locals()) - test.write(['src', 'SConscript'], """\ def cat(env, source, target): target = str(target[0]) @@ -72,7 +67,7 @@ test.write(['src', 'ccc.in'], "ccc.in\n") # test.write('SConstruct', """\ env = Environment(TWO = '2') -env.CacheDir(r'%s') +CacheDir(r'%s') BuildDir('build', 'src', duplicate=0) SConscript('build/SConscript') """ % test.workpath('cache${TWO}')) diff --git a/test/CacheDir/environment.py b/test/CacheDir/environment.py new file mode 100644 index 0000000..391fb94 --- /dev/null +++ b/test/CacheDir/environment.py @@ -0,0 +1,163 @@ +#!/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 whether or not a target gets retrieved from a CacheDir +is configurable by construction environment. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +cache = test.workpath('cache') + +src_aaa_out = test.workpath('src', 'aaa.out') +src_bbb_out = test.workpath('src', 'bbb.out') +src_ccc_out = test.workpath('src', 'ccc.out') +src_cat_out = test.workpath('src', 'cat.out') +src_all = test.workpath('src', 'all') + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """\ +CacheDir(r'%(cache)s') +SConscript('SConscript') +""" % locals()) + +test.write(['src', 'SConscript'], """\ +def cat(env, source, target): + target = str(target[0]) + open('cat.out', 'ab').write(target + "\\n") + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() +env_cache = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env_nocache = env_cache.Clone() +env_nocache.CacheDir(None) +env_cache.Cat('aaa.out', 'aaa.in') +env_nocache.Cat('bbb.out', 'bbb.in') +env_cache.Cat('ccc.out', 'ccc.in') +env_nocache.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that building with -n and an empty cache reports that proper +# build operations would be taken, but that nothing is actually built +# and that the cache is still empty. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +cat(["bbb.out"], ["bbb.in"]) +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) +test.fail_test(len(os.listdir(cache))) + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") +test.must_match(src_cat_out, "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(src_cat_out, "bbb.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that rebuilding with -n reports that files were retrieved +# from the cache, but that nothing really was. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) + +# Verify that rebuilding with -s retrieves everything from the cache +# even though it doesn't report anything. +test.run(chdir = 'src', arguments = '-s .', stdout = "") + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") + +test.must_match(src_cat_out, "bbb.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that updating one input file builds its derived file and +# dependency but that the other files are retrieved from cache. +test.write(['src', 'bbb.in'], "bbb.in 2\n") + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n") +test.must_match(src_cat_out, "bbb.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + + +test.pass_test() diff --git a/test/CacheDir/option--cd.py b/test/CacheDir/option--cd.py new file mode 100644 index 0000000..9aa9402 --- /dev/null +++ b/test/CacheDir/option--cd.py @@ -0,0 +1,129 @@ +#!/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 the --cache-disable option when retrieving derived files from a +CacheDir. +""" + +import os.path +import shutil + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """ +def cat(env, source, target): + target = str(target[0]) + open('cat.out', 'ab').write(target + "\\n") + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() +env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +CacheDir(r'%s') +""" % test.workpath('cache')) + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") +test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") +test.must_not_exist(test.workpath('src', 'cat.out')) + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') + +# Verify that when run with --cache-disable, we rebuild the files even +# though they're available from the cache. Then clean up. +test.run(chdir = 'src', + arguments = '--cache-disable .', + stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +cat(["bbb.out"], ["bbb.in"]) +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") +test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that when run with --no-cache, we rebuild the files even +# though they're available from the cache. Then clean up. +test.run(chdir = 'src', + arguments = '--no-cache .', + stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +cat(["bbb.out"], ["bbb.in"]) +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") +test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# All done. +test.pass_test() diff --git a/test/CacheDir/option--cf.py b/test/CacheDir/option--cf.py new file mode 100644 index 0000000..47847d4 --- /dev/null +++ b/test/CacheDir/option--cf.py @@ -0,0 +1,123 @@ +#!/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 populating a CacheDir with the --cache-force option. +""" + +import os.path +import shutil + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """ +def cat(env, source, target): + target = str(target[0]) + open('cat.out', 'ab').write(target + "\\n") + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() +env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +CacheDir(r'%s') +""" % test.workpath('cache')) + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in\nccc.in\n") +test.fail_test(test.read(['src', 'cat.out']) != "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. DO NOT CLEAN UP. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +test.up_to_date(chdir = 'src', arguments = '.') + +# Blow away and recreate the CacheDir, then verify that --cache-force +# repopulates the cache with the local built targets. DO NOT CLEAN UP. +shutil.rmtree(test.workpath('cache')) +test.subdir('cache') + +test.run(chdir = 'src', arguments = '--cache-force .') + +test.run(chdir = 'src', arguments = '-c .') + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +# Blow away and recreate the CacheDir, then verify that --cache-populate +# repopulates the cache with the local built targets. DO NOT CLEAN UP. +shutil.rmtree(test.workpath('cache')) +test.subdir('cache') + +test.run(chdir = 'src', arguments = '--cache-populate .') + +test.run(chdir = 'src', arguments = '-c .') + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +# All done. +test.pass_test() diff --git a/test/CacheDir/option--cs.py b/test/CacheDir/option--cs.py new file mode 100644 index 0000000..1e62c49 --- /dev/null +++ b/test/CacheDir/option--cs.py @@ -0,0 +1,194 @@ +#!/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 printing build actions when using the --cache-show option and +retrieving derived files from a CacheDir. +""" + +import os.path +import shutil + +import TestSCons + +_python_ = TestSCons._python_ +_exe = TestSCons._exe +_obj = TestSCons._obj + +test = TestSCons.TestSCons() + +test.subdir('cache', 'src1', 'src2') + +test.write(['src1', 'build.py'], r""" +import sys +open('cat.out', 'ab').write(sys.argv[1] + "\n") +file = open(sys.argv[1], 'wb') +for src in sys.argv[2:]: + file.write(open(src, 'rb').read()) +file.close() +""") + +cache = test.workpath('cache') + +test.write(['src1', 'SConstruct'], """ +def cat(env, source, target): + target = str(target[0]) + open('cat.out', 'ab').write(target + "\\n") + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() +env = Environment(BUILDERS={'Internal':Builder(action=cat), + 'External':Builder(action='%(_python_)s build.py $TARGET $SOURCES')}) +env.External('aaa.out', 'aaa.in') +env.External('bbb.out', 'bbb.in') +env.Internal('ccc.out', 'ccc.in') +env.Internal('all', ['aaa.out', 'bbb.out', 'ccc.out']) +CacheDir(r'%(cache)s') +""" % locals()) + +test.write(['src1', 'aaa.in'], "aaa.in\n") +test.write(['src1', 'bbb.in'], "bbb.in\n") +test.write(['src1', 'ccc.in'], "ccc.in\n") + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src1', arguments = '.') + +test.must_match(['src1', 'all'], "aaa.in\nbbb.in\nccc.in\n") + +test.must_match(['src1', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src1', arguments = '.') + +test.run(chdir = 'src1', arguments = '-c .') +test.unlink(['src1', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir = 'src1', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.must_not_exist(test.workpath('src1', 'cat.out')) + +test.up_to_date(chdir = 'src1', arguments = '.') + +test.run(chdir = 'src1', arguments = '-c .') + +# Verify that using --cache-show reports the files as being rebuilt, +# even though we actually fetch them from the cache. Then clean up. +expect = test.wrap_stdout("""\ +%(_python_)s build.py aaa.out aaa.in +%(_python_)s build.py bbb.out bbb.in +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""" % locals()) + +test.run(chdir = 'src1', arguments = '--cache-show .', stdout = expect) + +test.must_not_exist(test.workpath('src1', 'cat.out')) + +test.up_to_date(chdir = 'src1', arguments = '.') + +test.run(chdir = 'src1', arguments = '-c .') + +# Verify that using --cache-show -n reports the files as being rebuilt, +# even though we don't actually fetch them from the cache. No need to +# clean up. +expect = test.wrap_stdout("""\ +%(_python_)s build.py aaa.out aaa.in +%(_python_)s build.py bbb.out bbb.in +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""" % locals()) + +test.run(chdir = 'src1', arguments = '--cache-show -n .', stdout = expect) + +test.must_not_exist(test.workpath('src1', 'cat.out')) + +test.must_not_exist(test.workpath('src1', 'aaa.out')) +test.must_not_exist(test.workpath('src1', 'bbb.out')) +test.must_not_exist(test.workpath('src1', 'ccc.out')) +test.must_not_exist(test.workpath('src1', 'all')) + +# Verify that using --cache-show -s doesn't report anything, even though +# we do fetch the files from the cache. No need to clean up. +test.run(chdir = 'src1', + arguments = '--cache-show -s .', + stdout = "") + +test.must_match(['src1', 'all'], "aaa.in\nbbb.in\nccc.in\n") +test.must_not_exist(test.workpath('src1', 'cat.out')) + +# +hello_exe = 'hello' + _exe +hello_obj = 'hello' + _obj +src2_hello = test.workpath('src2', hello_exe) + +test.write(['src2', 'SConstruct'], """ +env = Environment() +env.Program('hello.c') +CacheDir(r'%s') +""" % (test.workpath('cache'))) + +test.write(['src2', 'hello.c'], r"""\ +#include +#include +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("src2/hello.c\n"); + exit (0); +} +""") + +# Normal build. +test.run(chdir = 'src2', arguments = '.') + +test.run(program = src2_hello, stdout = "src2/hello.c\n") + +test.up_to_date(chdir = 'src2', arguments = '.') + +test.run(chdir = 'src2', arguments = '-c .') + +# Verify that using --cache-show doesn't blow up. +# Don't bother checking the output, since we verified the correct +# behavior above. We just want to make sure the canonical Program() +# invocation works with --cache-show. +test.run(chdir = 'src2', arguments = '--cache-show .') + +test.run(program = src2_hello, stdout = "src2/hello.c\n") + +test.up_to_date(chdir = 'src2', arguments = '.') + +# All done. +test.pass_test() diff --git a/test/Case.py b/test/Case.py index 9b6bb94..663aa40 100644 --- a/test/Case.py +++ b/test/Case.py @@ -65,6 +65,9 @@ void bar() { } """) +if sys.platform == 'darwin': + test.skip_test("Skipping test on Darwin/OSX; it has partial case sensitivity.") + if sys.platform in ['cygwin', 'win32']: sys.stdout.write("Using case-insensitive filesystem, testing for failure\n") sys.stdout.flush() diff --git a/test/Fortran/FORTRANMODDIR.py b/test/Fortran/FORTRANMODDIR.py index f0c500c..a81439a 100644 --- a/test/Fortran/FORTRANMODDIR.py +++ b/test/Fortran/FORTRANMODDIR.py @@ -54,6 +54,8 @@ test.write('SConstruct', """ env = Environment(FORTRANCOM = r'%(_python_)s myfortran.py $FORTRANMODDIR $SOURCE $TARGET', FORTRANMODDIR = 'modules') env.Object(target = 'test1.obj', source = 'test1.f') +env.Object(target = 'sub/test2.obj', source = 'test1.f', + FORTRANMODDIR='${TARGET.dir}') """ % locals()) test.write('test1.f', """\ @@ -86,6 +88,9 @@ test.must_match('test1.obj', "myfortran.py wrote test1.obj\n") test.must_match(['modules', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n") test.must_not_exist(['modules', 'mod_bar.mod']) +test.must_match(['sub', 'test2.obj'], "myfortran.py wrote test2.obj\n") +test.must_match(['sub', 'mod_foo.mod'], "myfortran.py wrote mod_foo.mod\n") + test.up_to_date(arguments = '.') diff --git a/test/Java/JARCHDIR.py b/test/Java/JARCHDIR.py index 9250843..f7d9fca 100644 --- a/test/Java/JARCHDIR.py +++ b/test/Java/JARCHDIR.py @@ -28,8 +28,14 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Test that when JARCHDIR that our command to create .jar files correctly finds all the .class files (by putting -C in front of each class file argument). + +Includes logic to make sure that expansions of $JARCHDIR that include +${TARGET} or ${SOURCE} work. """ +import os +import string + import TestSCons test = TestSCons.TestSCons() @@ -50,6 +56,8 @@ else: if not where_jar: test.skip_test("Could not find Java jar, skipping test(s).\n") + + test.write('SConstruct', """ dir = 'dist' env = Environment(tools = ['javac', 'jar'], @@ -64,9 +72,19 @@ jar = env.Jar(File('c.jar', dir), bin) env = env.Clone(JARCHDIR = '.') inner = env.Jar('inner.jar', 'Inner$$Class.class') +target_env = env.Clone(JARCHDIR = '${TARGET.dir}') +target_env.Jar('out/t.jar', 'in/t.class') + +source_env = env.Clone(JARCHDIR = '${SOURCE.dir}') +source_env.Jar('out/s.jar', 'in/s.class') + Default(bin, jar, inner) """ % locals()) + + +test.subdir('in') + test.write('a.java', """\ package foo.bar; public class a {} @@ -77,8 +95,36 @@ package foo.bar; public class b {} """) +test.write(['in', 's.class'], "s.class\n") + +# Okay, this is bogus, but we're going with it for testing purposes. +# If jar gets a command line like: +# +# jar cf out/t.jar -C out /tmp/tmpXYZZY/in/t.class +# +# Empirically, it doesn't seem to treat the absolute path name +# of the argument class file as an absolute path, but looks for +# "out/tmp/tmpXYZZY/in/t.class". SCons, however, still looks for it in +# the path name specified on the command line. To make this test work, +# we're going to just create the t.class file in both locations, and +# we can revisit this if someone actually tries to use ${TARGET.dir} +# in a real-life expansion. Right now, it at least makes sure things +# don't blow up (i.e., validates that we pass the right arguments to +# env.subst() in the code that handle jar). + +p = test.workpath('out') +for d in string.split(test.workpath('in'), os.sep): + p = p + d + test.subdir(p) + p = p + os.sep + +test.write([p, 't.class'], "t.class\n") +test.write(['in', 't.class'], "t.class\n") + test.write('Inner$Class.class', "Inner$Class.class\n") test.run(arguments = '.') + + test.pass_test() diff --git a/test/Java/JAVACCOM.py b/test/Java/JAVACCOM.py index 7086a2a..b6a30e9 100644 --- a/test/Java/JAVACCOM.py +++ b/test/Java/JAVACCOM.py @@ -60,7 +60,7 @@ test.write(['src', 'file3.java'], "file3.java\n/*javac*/\n") test.run() -test.must_match(['classes', 'src', 'file1.class'], +test.must_match(['classes', 'file1.class'], "file1.java\nfile2.java\nfile3.java\n") diff --git a/test/Java/JAVACCOMSTR.py b/test/Java/JAVACCOMSTR.py index 44b1449..88fa31d 100644 --- a/test/Java/JAVACCOMSTR.py +++ b/test/Java/JAVACCOMSTR.py @@ -62,16 +62,16 @@ test.write(['src', 'file1.java'], "file1.java\n/*javac*/\n") test.write(['src', 'file2.java'], "file2.java\n/*javac*/\n") test.write(['src', 'file3.java'], "file3.java\n/*javac*/\n") -classes_src_file1_class = os.path.join('classes', 'src', 'file1.class') +classes_file1_class = os.path.join('classes', 'file1.class') src_file1_java= os.path.join('src', 'file1.java') src_file2_java= os.path.join('src', 'file2.java') src_file3_java= os.path.join('src', 'file3.java') test.run(stdout = test.wrap_stdout("""\ -Compiling class(es) %(classes_src_file1_class)s from %(src_file1_java)s %(src_file2_java)s %(src_file3_java)s +Compiling class(es) %(classes_file1_class)s from %(src_file1_java)s %(src_file2_java)s %(src_file3_java)s """ % locals())) -test.must_match(['classes', 'src', 'file1.class'], +test.must_match(['classes', 'file1.class'], "file1.java\nfile2.java\nfile3.java\n") diff --git a/test/Java/JAVACLASSPATH.py b/test/Java/JAVACLASSPATH.py new file mode 100644 index 0000000..0ae7dea --- /dev/null +++ b/test/Java/JAVACLASSPATH.py @@ -0,0 +1,104 @@ +#!/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 use of $JAVASOURCEPATH allows finding Java .class +files in alternate locations by adding the -classpath option +to the javac command line. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +ENV = test.java_ENV() + +if test.detect_tool('javac', ENV=ENV): + where_javac = test.detect('JAVAC', 'javac', ENV=ENV) +else: + where_javac = test.where_is('javac') +if not where_javac: + test.skip_test("Could not find Java javac, skipping test(s).\n") + +if test.detect_tool('javah', ENV=ENV): + where_javah = test.detect('JAVAH', 'javah', ENV=ENV) +else: + where_javah = test.where_is('javah') +if not where_javah: + test.skip_test("Could not find Java javah, skipping test(s).\n") + +test.write('SConstruct', """ +env = Environment(tools = ['javac', 'javah'], + JAVAC = r'%(where_javac)s', + JAVAH = r'%(where_javah)s') +j1 = env.Java(target = 'class1', source = 'com1/Example1.java') +j2 = env.Java(target = 'class2', source = 'com2/Example2.java') +env.JavaH(target = 'outdir', source = [j1, j2], JAVACLASSPATH = 'class2') +""" % locals()) + +test.subdir('com1', 'com2') + +test.write(['com1', 'Example1.java'], """\ +package com; + +public class Example1 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com2', 'Example2.java'], """\ +package com; + +public class Example2 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.run(arguments = '.') + +test.must_exist(['class1', 'com', 'Example1.class']) +test.must_exist(['class2', 'com', 'Example2.class']) + +test.must_exist(['outdir', 'com_Example1.h']) +test.must_exist(['outdir', 'com_Example2.h']) + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/Java/JAVASOURCEPATH.py b/test/Java/JAVASOURCEPATH.py new file mode 100644 index 0000000..069e228 --- /dev/null +++ b/test/Java/JAVASOURCEPATH.py @@ -0,0 +1,81 @@ +#!/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 use of $JAVASOURCEPATH allows finding source .java +files in alternate locations by adding the -sourcepath option +to the javac command line. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +ENV = test.java_ENV() + +if test.detect_tool('javac', ENV=ENV): + where_javac = test.detect('JAVAC', 'javac', ENV=ENV) +else: + where_javac = test.where_is('javac') +if not where_javac: + test.skip_test("Could not find Java javac, skipping test(s).\n") + +test.write('SConstruct', """ +env = Environment(tools = ['javac', 'javah'], + JAVAC = r'%(where_javac)s') +bar = env.Java(target = 'bar/classes', + source = 'bar/src/TestBar.java', + JAVASOURCEPATH = ['foo/src']) +""" % locals()) + +test.subdir('foo', + ['foo', 'src'], + ['foo', 'src', 'com'], + ['foo', 'src', 'com', 'foo'], + ['foo', 'src', 'com', 'foo', 'test'], + 'bar', ['bar', 'src']) + +test.write(['foo', 'src', 'com', 'foo', 'test', 'TestFoo.java'], """\ +package com.foo.test; +public class TestFoo {;} +""") + +test.write(['bar', 'src', 'TestBar.java'], """\ +package com.bar.test; +import com.foo.test.TestFoo; +class TestBar extends TestFoo {;} +""") + +test.run(arguments = '.') + +test.must_exist(['bar', 'classes', 'com', 'bar', 'test', 'TestBar.class']) +test.must_exist(['bar', 'classes', 'com', 'foo', 'test', 'TestFoo.class']) + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/Java/Java-1.4.py b/test/Java/Java-1.4.py index 8c3af59..3d3d47a 100644 --- a/test/Java/Java-1.4.py +++ b/test/Java/Java-1.4.py @@ -58,6 +58,7 @@ env.Java(target = 'class2', source = 'com/sub/bar') env.Java(target = 'class3', source = ['src1', 'src2']) env.Java(target = 'class4', source = ['src4']) env.Java(target = 'class5', source = ['src5']) +env.Java(target = 'class6', source = ['src6']) """ % locals()) test.subdir('com', @@ -67,7 +68,8 @@ test.subdir('com', 'src1', 'src2', 'src4', - 'src5') + 'src5', + 'src6') test.write(['com', 'sub', 'foo', 'Example1.java'], """\ package com.sub.foo; @@ -273,23 +275,27 @@ class TestSCons { class Foo { } """) -test.run(arguments = '.') +# Test private inner class instantiation, courtesy Tilo Prutz: +# http://scons.tigris.org/issues/show_bug.cgi?id=1594 +test.write(['src6', 'TestSCons.java'], """\ +class test +{ + test() + { + super(); + new inner(); + } -def get_class_files(dir): - def find_class_files(arg, dirname, fnames): - for fname in fnames: - if fname[-6:] == '.class': - arg.append(os.path.join(dirname, fname)) - result = [] - os.path.walk(dir, find_class_files, result) - result.sort() - return result + static class inner + { + private inner() {} + } +} +""") -classes_1 = get_class_files(test.workpath('class1')) -classes_2 = get_class_files(test.workpath('class2')) -classes_3 = get_class_files(test.workpath('class3')) -classes_4 = get_class_files(test.workpath('class4')) -classes_5 = get_class_files(test.workpath('class5')) + + +test.run(arguments = '.') expect_1 = [ test.workpath('class1', 'com', 'other', 'Example2.class'), @@ -327,9 +333,27 @@ expect_5 = [ test.workpath('class5', 'TestSCons.class'), ] +expect_6 = [ + test.workpath('class6', 'test$1.class'), + test.workpath('class6', 'test$inner.class'), + test.workpath('class6', 'test.class'), +] + failed = None -def classes_must_match(dir, expect, got): +def get_class_files(dir): + def find_class_files(arg, dirname, fnames): + for fname in fnames: + if fname[-6:] == '.class': + arg.append(os.path.join(dirname, fname)) + result = [] + os.path.walk(dir, find_class_files, result) + result.sort() + return result + +def classes_must_match(dir, expect): + global failed + got = get_class_files(test.workpath(dir)) if expect != got: sys.stderr.write("Expected the following class files in '%s':\n" % dir) for c in expect: @@ -339,13 +363,40 @@ def classes_must_match(dir, expect, got): sys.stderr.write(' %s\n' % c) failed = 1 -classes_must_match('class1', expect_1, classes_1) -classes_must_match('class2', expect_2, classes_2) -classes_must_match('class3', expect_3, classes_3) -classes_must_match('class4', expect_4, classes_4) +def classes_must_not_exist(dir, expect): + global failed + present = filter(os.path.exists, expect) + if present: + sys.stderr.write("Found the following unexpected class files in '%s' after cleaning:\n" % dir) + for c in present: + sys.stderr.write(' %s\n' % c) + failed = 1 + +classes_must_match('class1', expect_1) +classes_must_match('class2', expect_2) +classes_must_match('class3', expect_3) +classes_must_match('class4', expect_4) +classes_must_match('class5', expect_5) +classes_must_match('class6', expect_6) test.fail_test(failed) test.up_to_date(options='--debug=explain', arguments = '.') +test.run(arguments = '-c .') + +classes_must_not_exist('class1', expect_1) +classes_must_not_exist('class2', expect_2) +classes_must_not_exist('class3', expect_3) +classes_must_not_exist('class4', expect_4) +classes_must_not_exist('class5', expect_5) +# This test case should pass, but doesn't. +# The expect_6 list contains the class files that the Java compiler +# actually creates, apparently because of the "private" instantiation +# of the "inner" class. Our parser doesn't currently detect this, so +# it doesn't know to remove that generated class file. +#classes_must_not_exist('class6', expect_6) + +test.fail_test(failed) + test.pass_test() diff --git a/test/Java/Java-1.5.py b/test/Java/Java-1.5.py index 0b3ba27..4ac3d96 100644 --- a/test/Java/Java-1.5.py +++ b/test/Java/Java-1.5.py @@ -59,6 +59,7 @@ env.Java(target = 'class2', source = 'com/sub/bar') env.Java(target = 'class3', source = ['src1', 'src2']) env.Java(target = 'class4', source = ['src4']) env.Java(target = 'class5', source = ['src5']) +env.Java(target = 'class6', source = ['src6']) """ % locals()) test.subdir('com', @@ -68,7 +69,8 @@ test.subdir('com', 'src1', 'src2', 'src4', - 'src5') + 'src5', + 'src6') test.write(['com', 'sub', 'foo', 'Example1.java'], """\ package com.sub.foo; @@ -274,23 +276,27 @@ class TestSCons { class Foo { } """) -test.run(arguments = '.') +# Test private inner class instantiation, courtesy Tilo Prutz: +# http://scons.tigris.org/issues/show_bug.cgi?id=1594 +test.write(['src6', 'TestSCons.java'], """\ +class test +{ + test() + { + super(); + new inner(); + } -def get_class_files(dir): - def find_class_files(arg, dirname, fnames): - for fname in fnames: - if fname[-6:] == '.class': - arg.append(os.path.join(dirname, fname)) - result = [] - os.path.walk(dir, find_class_files, result) - result.sort() - return result + static class inner + { + private inner() {} + } +} +""") -classes_1 = get_class_files(test.workpath('class1')) -classes_2 = get_class_files(test.workpath('class2')) -classes_3 = get_class_files(test.workpath('class3')) -classes_4 = get_class_files(test.workpath('class4')) -classes_5 = get_class_files(test.workpath('class5')) + + +test.run(arguments = '.') expect_1 = [ test.workpath('class1', 'com', 'other', 'Example2.class'), @@ -328,9 +334,27 @@ expect_5 = [ test.workpath('class5', 'TestSCons.class'), ] +expect_6 = [ + test.workpath('class6', 'test$1.class'), + test.workpath('class6', 'test$inner.class'), + test.workpath('class6', 'test.class'), +] + failed = None -def classes_must_match(dir, expect, got): +def get_class_files(dir): + def find_class_files(arg, dirname, fnames): + for fname in fnames: + if fname[-6:] == '.class': + arg.append(os.path.join(dirname, fname)) + result = [] + os.path.walk(dir, find_class_files, result) + result.sort() + return result + +def classes_must_match(dir, expect): + global failed + got = get_class_files(test.workpath(dir)) if expect != got: sys.stderr.write("Expected the following class files in '%s':\n" % dir) for c in expect: @@ -340,13 +364,40 @@ def classes_must_match(dir, expect, got): sys.stderr.write(' %s\n' % c) failed = 1 -classes_must_match('class1', expect_1, classes_1) -classes_must_match('class2', expect_2, classes_2) -classes_must_match('class3', expect_3, classes_3) -classes_must_match('class4', expect_4, classes_4) +def classes_must_not_exist(dir, expect): + global failed + present = filter(os.path.exists, expect) + if present: + sys.stderr.write("Found the following unexpected class files in '%s' after cleaning:\n" % dir) + for c in present: + sys.stderr.write(' %s\n' % c) + failed = 1 + +classes_must_match('class1', expect_1) +classes_must_match('class2', expect_2) +classes_must_match('class3', expect_3) +classes_must_match('class4', expect_4) +classes_must_match('class5', expect_5) +classes_must_match('class6', expect_6) test.fail_test(failed) test.up_to_date(options='--debug=explain', arguments = '.') +test.run(arguments = '-c .') + +classes_must_not_exist('class1', expect_1) +classes_must_not_exist('class2', expect_2) +classes_must_not_exist('class3', expect_3) +classes_must_not_exist('class4', expect_4) +classes_must_not_exist('class5', expect_5) +# This test case should pass, but doesn't. +# The expect_6 list contains the class files that the Java compiler +# actually creates, apparently because of the "private" instantiation +# of the "inner" class. Our parser doesn't currently detect this, so +# it doesn't know to remove that generated class file. +#classes_must_not_exist('class6', expect_6) + +test.fail_test(failed) + test.pass_test() diff --git a/test/Java/Java-1.6.py b/test/Java/Java-1.6.py index 0b4ddd7..f2b629a 100644 --- a/test/Java/Java-1.6.py +++ b/test/Java/Java-1.6.py @@ -59,6 +59,7 @@ env.Java(target = 'class2', source = 'com/sub/bar') env.Java(target = 'class3', source = ['src1', 'src2']) env.Java(target = 'class4', source = ['src4']) env.Java(target = 'class5', source = ['src5']) +env.Java(target = 'class6', source = ['src6']) """ % locals()) test.subdir('com', @@ -68,7 +69,8 @@ test.subdir('com', 'src1', 'src2', 'src4', - 'src5') + 'src5', + 'src6') test.write(['com', 'sub', 'foo', 'Example1.java'], """\ package com.sub.foo; @@ -274,23 +276,27 @@ class TestSCons { class Foo { } """) -test.run(arguments = '.') +# Test private inner class instantiation, courtesy Tilo Prutz: +# http://scons.tigris.org/issues/show_bug.cgi?id=1594 +test.write(['src6', 'TestSCons.java'], """\ +class test +{ + test() + { + super(); + new inner(); + } -def get_class_files(dir): - def find_class_files(arg, dirname, fnames): - for fname in fnames: - if fname[-6:] == '.class': - arg.append(os.path.join(dirname, fname)) - result = [] - os.path.walk(dir, find_class_files, result) - result.sort() - return result + static class inner + { + private inner() {} + } +} +""") -classes_1 = get_class_files(test.workpath('class1')) -classes_2 = get_class_files(test.workpath('class2')) -classes_3 = get_class_files(test.workpath('class3')) -classes_4 = get_class_files(test.workpath('class4')) -classes_5 = get_class_files(test.workpath('class5')) + + +test.run(arguments = '.') expect_1 = [ test.workpath('class1', 'com', 'other', 'Example2.class'), @@ -328,9 +334,27 @@ expect_5 = [ test.workpath('class5', 'TestSCons.class'), ] +expect_6 = [ + test.workpath('class6', 'test$1.class'), + test.workpath('class6', 'test$inner.class'), + test.workpath('class6', 'test.class'), +] + failed = None -def classes_must_match(dir, expect, got): +def get_class_files(dir): + def find_class_files(arg, dirname, fnames): + for fname in fnames: + if fname[-6:] == '.class': + arg.append(os.path.join(dirname, fname)) + result = [] + os.path.walk(dir, find_class_files, result) + result.sort() + return result + +def classes_must_match(dir, expect): + global failed + got = get_class_files(test.workpath(dir)) if expect != got: sys.stderr.write("Expected the following class files in '%s':\n" % dir) for c in expect: @@ -340,13 +364,40 @@ def classes_must_match(dir, expect, got): sys.stderr.write(' %s\n' % c) failed = 1 -classes_must_match('class1', expect_1, classes_1) -classes_must_match('class2', expect_2, classes_2) -classes_must_match('class3', expect_3, classes_3) -classes_must_match('class4', expect_4, classes_4) +def classes_must_not_exist(dir, expect): + global failed + present = filter(os.path.exists, expect) + if present: + sys.stderr.write("Found the following unexpected class files in '%s' after cleaning:\n" % dir) + for c in present: + sys.stderr.write(' %s\n' % c) + failed = 1 + +classes_must_match('class1', expect_1) +classes_must_match('class2', expect_2) +classes_must_match('class3', expect_3) +classes_must_match('class4', expect_4) +classes_must_match('class5', expect_5) +classes_must_match('class6', expect_6) test.fail_test(failed) test.up_to_date(options='--debug=explain', arguments = '.') +test.run(arguments = '-c .') + +classes_must_not_exist('class1', expect_1) +classes_must_not_exist('class2', expect_2) +classes_must_not_exist('class3', expect_3) +classes_must_not_exist('class4', expect_4) +classes_must_not_exist('class5', expect_5) +# This test case should pass, but doesn't. +# The expect_6 list contains the class files that the Java compiler +# actually creates, apparently because of the "private" instantiation +# of the "inner" class. Our parser doesn't currently detect this, so +# it doesn't know to remove that generated class file. +#classes_must_not_exist('class6', expect_6) + +test.fail_test(failed) + test.pass_test() diff --git a/test/Java/multi-step.py b/test/Java/multi-step.py new file mode 100644 index 0000000..1b69838 --- /dev/null +++ b/test/Java/multi-step.py @@ -0,0 +1,567 @@ +#!/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__" + +""" +Real-world test (courtesy Leanid Nazdrynau) of the multi-step +capabilities of the various Java Builders. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +# This test requires javac and swig +ENV = test.java_ENV() + +if test.detect_tool('javac', ENV=ENV): + where_javac = test.detect('JAVAC', 'javac', ENV=ENV) +else: + where_javac = test.where_is('javac') +if not where_javac: + test.skip_test("Could not find Java javac, skipping test(s).\n") + +swig = test.where_is('swig') +if not swig: + test.skip_test('Can not find installed "swig", skipping test.\n') + + + +test.subdir(['src'], + ['src', 'HelloApplet'], + ['src', 'HelloApplet', 'com'], + ['src', 'javah'], + ['src', 'jni'], + ['src', 'server'], + ['src', 'server', 'JavaSource'], + ['src', 'server', 'JavaSource', 'com'], + ['src', 'server', 'JavaSource', 'com', 'gnu'], + ['src', 'server', 'JavaSource', 'com', 'gnu', 'scons'], + ['src', 'server', 'JavaSource', 'com', 'gnu', 'scons', 'web'], + ['src', 'server', 'JavaSource', 'com', 'gnu', 'scons', 'web', 'tools'], + ['src', 'server', 'WebContent'], + ['src', 'server', 'WebContent', 'META-INF'], + ['src', 'server', 'WebContent', 'WEB-INF'], + ['src', 'server', 'WebContent', 'WEB-INF', 'conf'], + ['src', 'server', 'WebContent', 'WEB-INF', 'lib'], + ['src', 'server', 'WebContent', 'theme']) + +test.write(['SConstruct'], """\ +import os,sys +env=Environment(tools = ['default', 'javac', 'javah']) +Export('env') +env.PrependENVPath('PATH',os.environ.get('PATH',[])) +env['INCPREFIX']='-I' +env.Append(SWIGFLAGS=['-c++','$_CPPINCFLAGS']) + +#this is for JNI +#env.Append(CCFLAGS=['/IN:/jdk/v1.3.1/include','/IN:/jdk/v1.3.1/include/win32']) + +#this for windows only C++ build +#env.Append(CXXFLAGS='-GX') + +env.Append(CPPPATH='.') + +env.BuildDir('buildout', 'src', duplicate=0) + +if sys.platform=='darwin': + env.Append(CPPPATH=['/System/Library/Frameworks/JavaVM.framework/Headers']) + +#If you do not have swig on your system please remove 'buildout/jni/SConscript' line from next call +env.SConscript(['buildout/server/JavaSource/SConscript', + 'buildout/HelloApplet/SConscript', + 'buildout/jni/SConscript', + 'buildout/javah/SConscript']) +""") + +test.write(['src', 'HelloApplet', 'Hello.html'], """\ + + + Applet Hello + + +
+ + +
+ + + +""") + +test.write(['src', 'HelloApplet', 'SConscript'], """\ +import os +Import ("env") +denv=env.Copy() +classes=denv.Java(target='classes',source=['com']) +#set correct path for jar +denv['JARCHDIR']=os.path.join(denv.Dir('.').get_abspath(),'classes') +denv.Jar('HelloApplet',classes) + + +#To sign applet you have to create keystore before and made a calls like this + +#keystore='/path/to/jarsignkey' +#denv['JARSIGNFLAGS']='-keystore '+keystore+' -storepass pass -keypass passkey' +#denv['JARSIGNALIAS']='ALIAS' +#denv['JARCOM']=[denv['JARCOM'],'$JARSIGNCOM'] + +""") + +test.write(['src', 'HelloApplet', 'com', 'Hello.java'], """\ +package com.Hello; +import java.awt.*; +import java.applet.*; + +public class Hello extends Applet { + public void paint(Graphics g) { + g.drawString("Hello from SCons signed applet",250,150); + } + } + +""") + +test.write(['src', 'javah', 'MyID.cc'], """\ +#include "MyID.h" +int getMyID() +{ + return 0; +} +""") + +test.write(['src', 'javah', 'MyID.java'], """\ +import java.util.*; +import java.io.IOException; +import java.lang.reflect.*; + +public class MyID +{ + static private long current = System.currentTimeMillis(); + static public String get() + { + current++; + return new Long( current ).toString(); + } +} +""") + +test.write(['src', 'javah', 'SConscript'], """\ +Import('env') +denv=env.Copy() +denv['JARCHDIR']=denv.Dir('.').get_abspath() +denv.Jar('myid','MyID.java') +denv.JavaH(denv.Dir('.').get_abspath(),'MyID.java') +denv.SharedLibrary('myid','MyID.cc') + +""") + +test.write(['src', 'jni', 'A.java'], """\ +package web.jni; + +import web.jni.*; + +public class A +{ +class C +{ + void echo2( String text ) + { + System.out.println( text+"aa" ); + + } +} +class B +{ + void echo( String text ) + { + System.out.println( text ); + C c = new C(); + c.echo2("from B callin C"); + } +} + public void main( String[] x) + { + B b = new B(); + b.echo("123"); + C c = new C(); + c.echo2("456"); + } +} +""") + +test.write(['src', 'jni', 'JniWrapper.cc'], """\ +#include +#include + +#include "JniWrapper.h" + + + +JniWrapper::JniWrapper( JNIEnv *pEnv ) + : mpEnv( pEnv ) +{ +} + +JniWrapper::JniWrapper( const JniWrapper& rJniWrapper ) + : mpEnv( rJniWrapper.mpEnv ) +{ +} + +JniWrapper::~JniWrapper() +{ + +} + +JniWrapper& JniWrapper::operator=( const JniWrapper& rJniWrapper ) +{ + if ( this != &rJniWrapper ) + { + mpEnv = rJniWrapper.mpEnv; + } + return *this; +} + +std::string JniWrapper::unmarshalString( jstring value ) +{ + std::string result; + if( value ) + { + const char *pStr = mpEnv->GetStringUTFChars( value, 0 ); + result = pStr; + mpEnv->ReleaseStringUTFChars( value, pStr ); + } + return result; +} + +jobject JniWrapper::marshalDouble( double value ) +{ + jclass classObject = mpEnv->FindClass( "java/lang/Double" ); + jmethodID constructorId = mpEnv->GetMethodID( classObject, "", "(D)V" ); + jobject result = mpEnv->NewObject( classObject, constructorId, value ); + + return result; +} + +jobject JniWrapper::getVectorElement( jobject values, int i ) +{ + jclass vectorClass = mpEnv->FindClass( "java/util/Vector" ); + jmethodID methodID = mpEnv->GetMethodID( vectorClass, + "elementAt", + "(I)Ljava/lang/Object;" ); + jobject result = mpEnv->CallObjectMethod( values, methodID, i ); + + return result; +} + +jobject JniWrapper::newVector() +{ + jclass vectorClass = mpEnv->FindClass( "java/util/Vector" ); + jmethodID constructorID = mpEnv->GetMethodID( vectorClass, "", "()V" ); + jobject result = mpEnv->NewObject( vectorClass, constructorID ); + + return result; +} + +void JniWrapper::addElement( jobject vector, jobject element ) +{ + jclass vectorClass = mpEnv->FindClass( "java/util/Vector" ); + jmethodID addElementMethodID = mpEnv->GetMethodID( vectorClass, + "addElement", + "(Ljava/lang/Object;)V" ); + + mpEnv->CallVoidMethod( vector, addElementMethodID, element ); +} + +jobject JniWrapper::marshalDoubleVector( const std::vector& rVector ) +{ + jobject result = newVector(); + + for ( int i = 0; i < rVector.size(); i++ ) + { + addElement( result, marshalDouble( rVector[i] ) ); + } + + return result; +} + +std::pair JniWrapper::unmarshalPairString( jobject vector ) +{ + std::pair result; + result.first = unmarshalString( (jstring)getVectorElement( vector, 0 ) ); + result.second = unmarshalString( (jstring)getVectorElement( vector, 1 ) ); + + return result; +} +""") + +test.write(['src', 'jni', 'JniWrapper.h'], """\ +#ifndef JniWrapper_h +#define JniWrapper_h + +#include +/** + * Provides routines for dealing with JNI translation etc. + */ + +class JniWrapper +{ +public: + JniWrapper( JNIEnv *pEnv ); + JniWrapper( const JniWrapper& rJniWrapper ); + virtual ~JniWrapper(); + JniWrapper& operator=( const JniWrapper& rJniWrapper ); + + + std::string unmarshalString( jstring value ); + + jstring marshalString( const std::string& value ); + + jbyteArray marshalByteArray( const std::string& value ); + + double unmarshalDouble( jobject value ); + + jobject marshalDouble( double value ); + jobject marshallStringVector( const std::vector& rMap ); + + jobject marshalDoubleVector( const std::vector& rVector ); + std::pair unmarshalPairString( jobject ); + +protected: + JNIEnv *mpEnv; + +private: + JniWrapper(); + jobject newVector(); + void addElement( jobject vector, jobject element ); + jobject getVectorElement( jobject values, int i ); + +}; + +#endif // JniWrapper_h + + +""") + +test.write(['src', 'jni', 'SConscript'], """\ +Import ("env") +denv=env.Copy() + +denv.Append(SWIGFLAGS=['-java']) +denv.SharedLibrary('scons',['JniWrapper.cc','Sample.i']) +denv['JARCHDIR']=denv.Dir('.').get_abspath() +denv.Jar(['Sample.i','A.java']) +""") + +test.write(['src', 'jni', 'Sample.h'], """\ +#include +#include + +class SampleTest +{ +public: + std::vector test1( std::pair param ) + { + std::vector result; + result.push_back(10); + return result; + } +}; + +""") + +test.write(['src', 'jni', 'Sample.i'], """\ +%module Sample + +%{ +#include "Sample.h" +#include "JniWrapper.h" +%} + +%typemap(jni) std::vector, std::vector& "jobject" +%typemap(jtype) std::vector, std::vector& "java.util.Vector" +%typemap(jstype) std::vector, std::vector& "java.util.Vector" + +// return a Vector of Doubles +%typemap(javaout) std::vector { + return $jnicall; +} +%typemap(out) std::vector { + JniWrapper JniWrapper(jenv); + $result = JniWrapper.marshalDoubleVector( $1 ); +} + +/********************************************************************* + * + * Pairs of String (IN/OUT) + * + *********************************************************************/ +%typemap(jni) std::pair, const std::pair& "jobject" +%typemap(jtype) std::pair, const std::pair& "java.util.Vector" +%typemap(jstype) std::pair, const std::pair& "java.util.Vector" +%typemap(javain) std::pair, const std::pair& "$javainput" + +// pass in by reference a Vector of std::string +%typemap(in) const std::pair& { + $1 = new std::pair(); + JniWrapper JniWrapper(jenv); + *($1) = JniWrapper.unmarshalPairString( $input ); +} + +//cleanup +%typemap(freearg) const std::pair& { + delete $1; +} + +%include "Sample.h" + +// generate:Sample.java +// generate:SampleJni.java +// generate:SampleTest.java +""") + +test.write(['src', 'server', 'JavaSource', 'SConscript'], """\ +import os +Import ("env") +classes=env.Java(target='classes',source=['com']) + +HelloApplet=os.path.join('#/buildout/HelloApplet/HelloApplet.jar') +env['WARXFILES']=['SConscript','.cvsignore'] +env['WARXDIRS']=['CVS'] +#env.War('scons',[classes,Dir('../WebContent'),HelloApplet]) +""") + +test.write(['src', 'server', 'JavaSource', 'com', 'gnu', 'scons', 'web', 'tools', 'Bool.java'], """\ +package com.gnu.scons.web.tools; +public class Bool { + boolean flag; + + public Bool() + { + flag = false; + } + + public Bool( boolean aFlag ) + { + flag = aFlag; + } + + public boolean booleanValue() + { + return flag; + } +} +""") + +test.write(['src', 'server', 'JavaSource', 'com', 'gnu', 'scons', 'web', 'tools', 'StringUtils.java'], """\ +package com.gnu.scons.web.tools; + +import java.util.Iterator; +import java.util.Map; + +public class StringUtils +{ + public static String toPercent( String value ) + { + if ( value.equals("") ) + { + return ""; + } + else + { + return value + "%"; + } + } + +} +""") + +test.write(['src', 'server', 'WebContent', 'index.html'], """\ + + + + +index.html + + +

go?action=home

+ + +""") + +test.write(['src', 'server', 'WebContent', 'META-INF', 'MANIFEST.MF'], """\ +Manifest-Version: 1.0 +Class-Path: + +""") + +test.write(['src', 'server', 'WebContent', 'WEB-INF', 'web.xml'], """\ + + + + scons + + WebExample + WebExample + com.gnu.scons.web.tool.WebExample + + + WebExample + /go + + + index.html + index.htm + + +""") + +test.write(['src', 'server', 'WebContent', 'WEB-INF', 'conf', 'app.properties'], """\ +logging = webExample.Example +""") + +test.write(['src', 'server', 'WebContent', 'theme', 'Master.css'], """\ +body +{ + font-family: Helvetica,Sans-Serif; + font-size: 11pt; +} +""") + +test.run(arguments = '.') + +test.must_exist(['buildout', 'javah', 'myid.jar']) +test.must_exist(['buildout', 'javah', 'MyID', 'MyID.class']) + +test.must_exist(['buildout', 'jni', 'Sample.class']) +test.must_exist(['buildout', 'jni', 'Sample.java']) +test.must_exist(['buildout', 'jni', 'SampleJNI.class']) +test.must_exist(['buildout', 'jni', 'SampleJNI.java']) +test.must_exist(['buildout', 'jni', 'SampleTest.class']) +test.must_exist(['buildout', 'jni', 'SampleTest.java']) + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/Java/source-files.py b/test/Java/source-files.py new file mode 100644 index 0000000..8d2506f --- /dev/null +++ b/test/Java/source-files.py @@ -0,0 +1,127 @@ +#!/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 pass the Java() builder explicit lists of .java +files as sources. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +ENV = test.java_ENV() + +if test.detect_tool('javac', ENV=ENV): + where_javac = test.detect('JAVAC', 'javac', ENV=ENV) +else: + where_javac = test.where_is('javac') +if not where_javac: + test.skip_test("Could not find Java javac, skipping test(s).\n") + + +test.write('SConstruct', """ +env = Environment(tools = ['javac', 'javah'], + JAVAC = r'%(where_javac)s') +env.Java(target = 'class1', source = 'com/Example1.java') +env.Java(target = 'class2', source = ['com/Example2.java', 'com/Example3.java']) +""" % locals()) + +test.subdir('com', 'src') + +test.write(['com', 'Example1.java'], """\ +package com; + +public class Example1 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com', 'Example2.java'], """\ +package com; + +public class Example2 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com', 'Example3.java'], """\ +package com; + +public class Example3 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.write(['com', 'Example4.java'], """\ +package com; + +public class Example4 +{ + + public static void main(String[] args) + { + + } + +} +""") + +test.run(arguments = '.') + +test.must_exist (['class1', 'com', 'Example1.class']) +test.must_not_exist(['class1', 'com', 'Example2.class']) +test.must_not_exist(['class1', 'com', 'Example3.class']) +test.must_not_exist(['class1', 'com', 'Example4.class']) + +test.must_not_exist(['class2', 'com', 'Example1.class']) +test.must_exist (['class2', 'com', 'Example2.class']) +test.must_exist (['class2', 'com', 'Example3.class']) +test.must_not_exist(['class2', 'com', 'Example4.class']) + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/Java/swig-dependencies.py b/test/Java/swig-dependencies.py new file mode 100644 index 0000000..c6961f2 --- /dev/null +++ b/test/Java/swig-dependencies.py @@ -0,0 +1,135 @@ +#!/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 dependencies on SWIG-generated .java files work correctly. + +Test case courtesy Jonathan (toolshed@tigris.org). +""" + +import TestSCons + +test = TestSCons.TestSCons() + +ENV = test.java_ENV() +if test.detect_tool('javac', ENV=ENV): + where_javac = test.detect('JAVAC', 'javac', ENV=ENV) +else: + where_javac = test.where_is('javac') +if not where_javac: + test.skip_test("Could not find Java javac, skipping test(s).\n") + +if test.detect_tool('jar', ENV=ENV): + where_jar = test.detect('JAR', 'jar', ENV=ENV) +else: + where_jar = test.where_is('jar') +if not where_jar: + test.skip_test("Could not find Java jar, skipping test(s).\n") + + +test.subdir(['foo'], + ['java'], + ['java', 'build']) + +test.write(['SConstruct'], """\ +import os + +env = Environment(ENV = os.environ) + +env.Append(CPPFLAGS = ' -g -Wall') + +Export('env') + +SConscript('#foo/SConscript') +SConscript('#java/SConscript') +""") + +test.write(['foo', 'SConscript'], """\ +Import('env') + +env.SharedLibrary('foo', 'foo.cpp') +""") + +test.write(['foo', 'foo.cpp'], """\ +#include "foo.h" + +int fooAdd(int a, int b) { + return a + b; +} +""") + +test.write(['foo', 'foo.h'], """\ +int fooAdd(int, int); +""") + +test.write(['java', 'Java_foo_interface.i'], """\ +#include "foo.h" + +%module foopack +""") + +test.write(['java', 'SConscript'], """\ +import os + +Import('env') + +# unnecessary? +env = env.Copy() + +env.Prepend(CPPPATH = ['#foo',]) + +libadd = ['foo',] + +libpath = ['#foo',] + +#swigflags = '-c++ -java -Wall -package foopack -Ifoo' +swigflags = '-c++ -java -Wall -Ifoo' + +Java_foo_interface = env.SharedLibrary( + 'Java_foo_interface', + 'Java_foo_interface.i', + LIBS = libadd, + LIBPATH = libpath, + SWIGFLAGS = swigflags, + SWIGOUTDIR = Dir('build'), + SWIGCXXFILESUFFIX = "_wrap.cpp") + +foopack_jar_javac = env.Java('classes', 'build') + +env['JARCHDIR'] = 'java/classes' +foopack_jar = env.Jar(target = 'foopack.jar', source = 'classes') +""") + +test.run(arguments = '.') + +#test.must_exist(['java', 'classes', 'foopack', 'foopack.class']) +#test.must_exist(['java', 'classes', 'foopack', 'foopackJNI.class']) +test.must_exist(['java', 'classes', 'foopack.class']) +test.must_exist(['java', 'classes', 'foopackJNI.class']) + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/LEX/LEXFLAGS.py b/test/LEX/LEXFLAGS.py index b6a06fa..e72fa9e 100644 --- a/test/LEX/LEXFLAGS.py +++ b/test/LEX/LEXFLAGS.py @@ -35,36 +35,42 @@ _exe = TestSCons._exe test = TestSCons.TestSCons() +test.subdir('in') + test.write('mylex.py', """ import getopt import string import sys -cmd_opts, args = getopt.getopt(sys.argv[1:], 'tx', []) +cmd_opts, args = getopt.getopt(sys.argv[1:], 'I:tx', []) opt_string = '' +i_arguments = '' for opt, arg in cmd_opts: - opt_string = opt_string + ' ' + opt + if opt == '-I': i_arguments = i_arguments + ' ' + arg + else: opt_string = opt_string + ' ' + opt for a in args: contents = open(a, 'rb').read() - sys.stdout.write(string.replace(contents, 'LEXFLAGS', opt_string)) + contents = string.replace(contents, 'LEXFLAGS', opt_string) + contents = string.replace(contents, 'I_ARGS', i_arguments) + sys.stdout.write(contents) sys.exit(0) """) test.write('SConstruct', """ env = Environment(LEX = r'%(_python_)s mylex.py', - LEXFLAGS = '-x', + LEXFLAGS = '-x -I${TARGET.dir} -I${SOURCE.dir}', tools=['default', 'lex']) -env.CFile(target = 'aaa', source = 'aaa.l') +env.CFile(target = 'out/aaa', source = 'in/aaa.l') """ % locals()) -test.write('aaa.l', "aaa.l\nLEXFLAGS\n") +test.write(['in', 'aaa.l'], "aaa.l\nLEXFLAGS\nI_ARGS\n") test.run('.', stderr = None) # Read in with mode='r' because mylex.py implicitley wrote to stdout # with mode='w'. -test.must_match('aaa.c', "aaa.l\n -x -t\n", mode='r') +test.must_match(['out', 'aaa.c'], "aaa.l\n -x -t\n out in\n", mode='r') diff --git a/test/SWIG/SWIG.py b/test/SWIG/SWIG.py index dfee5ef..491cb7c 100644 --- a/test/SWIG/SWIG.py +++ b/test/SWIG/SWIG.py @@ -28,26 +28,15 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Verify that the swig tool generates file names that we expect. """ -import os -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_ - _exe = TestSCons._exe _obj = TestSCons._obj test = TestSCons.TestSCons() +_python_ = test.get_quoted_platform_python() + test.write('myswig.py', r""" diff --git a/test/SWIG/SWIGFLAGS.py b/test/SWIG/SWIGFLAGS.py new file mode 100644 index 0000000..22c311b --- /dev/null +++ b/test/SWIG/SWIGFLAGS.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__" + +""" +Verify that we can use ${SOURCE} expansions in $SWIGFLAGS. +""" + +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +swig = test.where_is('swig') + +if not swig: + test.skip_test('Can not find installed "swig", skipping test.\n') + + + +test.subdir('src') + +test.write(['src', 'foo.i'], """\ +%module foo + +%include bar.i +""") + +test.write(['src', 'bar.i'], """\ +%module bar +""") + +test.write('SConstruct', """ +# Note that setting the -I option in $SWIGFLAGS is not good and the +# documentation says to use $SWIGPATH. This is just for testing. +env = Environment(SWIGFLAGS='-python -I${SOURCE.dir}') +env.CFile(target = 'foo', source = ['src/foo.i']) +""") + +test.run() + +test.up_to_date(arguments = "foo_wrap.c") + + + +test.pass_test() diff --git a/test/SWIG/SWIGOUTDIR.py b/test/SWIG/SWIGOUTDIR.py index 69d535c..b638a3b 100644 --- a/test/SWIG/SWIGOUTDIR.py +++ b/test/SWIG/SWIGOUTDIR.py @@ -29,10 +29,21 @@ Verify that use of the $SWIGOUTDIR variable causes SCons to recognize that Java files are created in the specified output directory. """ +import sys + import TestSCons test = TestSCons.TestSCons() +test = TestSCons.TestSCons() + +swig = test.where_is('swig') + +if not swig: + test.skip_test('Can not find installed "swig", skipping test.\n') + + + test.write(['SConstruct'], """\ env = Environment(tools = ['default', 'swig']) diff --git a/test/SWIG/SWIGPATH.py b/test/SWIG/SWIGPATH.py new file mode 100644 index 0000000..dd2db47 --- /dev/null +++ b/test/SWIG/SWIGPATH.py @@ -0,0 +1,91 @@ +#!/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 use of SWIGPATH finds dependency files in subdirectories. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +swig = test.where_is('swig') + +if not swig: + test.skip_test('Can not find installed "swig", skipping test.\n') + +_python_ = test.get_quoted_platform_python() + + + +test.subdir('inc1', 'inc2') + +test.write(['inc2', 'dependency.i'], """\ +%module dependency +""") + +test.write("dependent.i", """\ +%module dependent + +%include dependency.i +""") + +test.write('SConstruct', """ +foo = Environment(SWIGFLAGS='-python', + SWIGPATH=['inc1', 'inc2']) +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.up_to_date(arguments = "dependent_wrap.c") + +test.write(['inc1', 'dependency.i'], """\ +%module dependency + +extern char *dependency_1(); +""") + +test.not_up_to_date(arguments = "dependent_wrap.c") + +test.write(['inc2', 'dependency.i'], """\ +%module dependency +extern char *dependency_2(); +""") + +test.up_to_date(arguments = "dependent_wrap.c") + +test.unlink(['inc1', 'dependency.i']) + +test.not_up_to_date(arguments = "dependent_wrap.c") + +test.up_to_date(arguments = "dependent_wrap.c") + + + +test.pass_test() diff --git a/test/SWIG/build-dir.py b/test/SWIG/build-dir.py new file mode 100644 index 0000000..593a26a --- /dev/null +++ b/test/SWIG/build-dir.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Make sure SWIG works when a BuildDir (or build_dir) is used. + +Test case courtesy Joe Maruszewski. +""" + +import os.path +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +# swig-python expects specific filenames. +# the platform specific suffix won't necessarily work. +if sys.platform == 'win32': + _dll = '.dll' +else: + _dll = '.so' + +test.subdir(['source']) + +python_include_dir = test.get_python_inc() + +Python_h = os.path.join(python_include_dir, 'Python.h') + +if not os.path.exists(Python_h): + test.skip_test('Can not find %s, skipping test.\n' % Python_h) + +python_frameworks_flags = test.get_python_frameworks_flags() + +test.write(['SConstruct'], """\ +# +# Create the build environment. +# +env = Environment(CPPPATH = [".", r'%(python_include_dir)s'], + CPPDEFINES = "NDEBUG", + SWIGFLAGS = ["-python", "-c++"], + SWIGCXXFILESUFFIX = "_wrap.cpp", + LDMODULEPREFIX='_', + LDMODULESUFFIX='%(_dll)s', + FRAMEWORKSFLAGS='%(python_frameworks_flags)s') + +import sys +if sys.version[0] == '1': + # SWIG requires the -classic flag on pre-2.0 Python versions. + env.Append(SWIGFLAGS = '-classic') + +Export("env") + +# +# Build the libraries. +# +SConscript("source/SConscript", build_dir = "build") +""" % locals()) + +test.write(['source', 'SConscript'], """\ +Import("env") +lib = env.SharedLibrary("_linalg", + "linalg.i", + SHLIBPREFIX = "", + SHLIBSUFFIX = ".pyd") +""") + +test.write(['source', 'Vector.hpp'], """\ +class Vector +{ + public: + Vector(int size = 0) : _size(size) + { + _v = new double[_size]; + for (int i = 0; i < _size; ++i) + _v[i] = 0.0; + } + + ~Vector() { delete [] _v; } + + int size() const { return _size; } + + double& operator[](int key) { return _v[key]; } + double const& operator[](int key) const { return _v[key]; } + + private: + int _size; + double* _v; +}; +""") + +test.write(['source', 'linalg.i'], """\ +%module linalg +%{ +#include +#include "Vector.hpp" +%} + + +class Vector +{ +public: + Vector(int n = 0); + ~Vector(); + + %extend + { + const char* __str__() { return "linalg.Vector()"; } + int __len__() { return $self->size(); } + double __getitem__(int key) { return $self->operator[](key); } + void __setitem__(int key, double value) { $self->operator[](key) = value; } + + %pythoncode %{ + def __iter__(self): + for i in xrange(len(self)): + yield self[i] + %} + } +}; +""") + +test.write(['source', 'test.py'], """\ +#!/usr/bin/env python +import linalg + + +x = linalg.Vector(5) +print x + +x[1] = 99.5 +x[3] = 8.3 +x[4] = 11.1 + + +for i, v in enumerate(x): + print "\tx[%d] = %g" % (i, v) + +""") + +test.run(arguments = '.') + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/SWIG/implicit-dependencies.py b/test/SWIG/implicit-dependencies.py index 81e3cf9..9b3eb8e 100644 --- a/test/SWIG/implicit-dependencies.py +++ b/test/SWIG/implicit-dependencies.py @@ -32,17 +32,6 @@ 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_ - test = TestSCons.TestSCons() swig = test.where_is('swig') @@ -50,10 +39,9 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') +_python_ = test.get_quoted_platform_python() -version = sys.version[:3] # see also sys.prefix documentation - test.write("dependency.i", """\ %module dependency diff --git a/test/SWIG/live.py b/test/SWIG/live.py index d319af7..5070d8e 100644 --- a/test/SWIG/live.py +++ b/test/SWIG/live.py @@ -34,17 +34,6 @@ 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': @@ -59,29 +48,22 @@ swig = test.where_is('swig') if not swig: test.skip_test('Can not find installed "swig", skipping test.\n') +python = test.get_platform_python() +_python_ = test.get_quoted_platform_python() -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 - -python_include_dir = os.path.join(platform_sys_prefix, - 'include', - 'python' + version) +python_include_dir = test.get_python_inc() + Python_h = os.path.join(python_include_dir, 'Python.h') if not os.path.exists(Python_h): test.skip_test('Can not find %s, skipping test.\n' % Python_h) + +python_frameworks_flags = test.get_python_frameworks_flags() test.write("wrapper.py", """import os @@ -96,7 +78,7 @@ foo = Environment(SWIGFLAGS='-python', CPPPATH='%(python_include_dir)s/', LDMODULEPREFIX='%(ldmodule_prefix)s', LDMODULESUFFIX='%(_dll)s', - FRAMEWORKSFLAGS='%(frameworks)s', + FRAMEWORKSFLAGS='%(python_frameworks_flags)s', ) import sys diff --git a/test/SWIG/noproxy.py b/test/SWIG/noproxy.py index c0f6da6..8b0adb0 100644 --- a/test/SWIG/noproxy.py +++ b/test/SWIG/noproxy.py @@ -33,17 +33,6 @@ 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': @@ -58,21 +47,14 @@ 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 +_python_ = test.get_quoted_platform_python() # 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 +python_include_dir = test.get_python_inc() + +python_frameworks_flags = test.get_python_frameworks_flags() test.write("dependency.i", """\ %module dependency @@ -86,10 +68,10 @@ test.write("dependent.i", """\ test.write('SConstruct', """ foo = Environment(SWIGFLAGS=['-python', '-noproxy'], - CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', + CPPPATH='%(python_include_dir)s', LDMODULEPREFIX='%(ldmodule_prefix)s', LDMODULESUFFIX='%(_dll)s', - FRAMEWORKSFLAGS='%(frameworks)s', + FRAMEWORKSFLAGS='%(python_frameworks_flags)s', ) swig = foo.Dictionary('SWIG') diff --git a/test/SWIG/remove-modules.py b/test/SWIG/remove-modules.py index f5d4010..5f15dcf 100644 --- a/test/SWIG/remove-modules.py +++ b/test/SWIG/remove-modules.py @@ -50,27 +50,17 @@ if not 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 - -python_include_dir = os.path.join(platform_sys_prefix, - 'include', - 'python' + version) +python_include_dir = test.get_python_inc() + Python_h = os.path.join(python_include_dir, 'Python.h') if not os.path.exists(Python_h): test.skip_test('Can not find %s, skipping test.\n' % Python_h) + +python_frameworks_flags = test.get_python_frameworks_flags() test.write("module.i", """\ @@ -79,10 +69,10 @@ test.write("module.i", """\ test.write('SConstruct', """ foo = Environment(SWIGFLAGS='-python', - CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/', + CPPPATH='%(python_include_dir)s', LDMODULEPREFIX='%(ldmodule_prefix)s', LDMODULESUFFIX='%(_dll)s', - FRAMEWORKSFLAGS='%(frameworks)s', + FRAMEWORKSFLAGS='%(python_frameworks_flags)s', ) import sys diff --git a/test/Scanner/generated.py b/test/Scanner/generated.py index 99b5787..2dfd322 100644 --- a/test/Scanner/generated.py +++ b/test/Scanner/generated.py @@ -232,12 +232,13 @@ for k in fromdict.keys(): # do env.subst on: # $RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} ... # When $TARGET is None, so $TARGET.attributes would throw an - # exception. + # exception, which SCons would turn into a UserError. They're + # not important for this test, so just catch 'em. f = fromdict[k] - if SCons.Util.is_String(f) and \ - string.find(f, "TARGET") == -1 and \ - string.find(f, "SOURCE") == -1: + try: todict[k] = env.subst(f) + except SCons.Errors.UserError: + pass todict["CFLAGS"] = fromdict["CPPFLAGS"] + " " + \ string.join(map(lambda x: "-I" + x, env["CPPPATH"])) + " " + \ string.join(map(lambda x: "-L" + x, env["LIBPATH"])) diff --git a/test/TEX/auxiliaries.py b/test/TEX/auxiliaries.py index 39984b3..e189d57 100644 --- a/test/TEX/auxiliaries.py +++ b/test/TEX/auxiliaries.py @@ -124,7 +124,7 @@ ps_output_1 = test.read(['build', 'docs', 'test.ps']) # Adding blank lines will cause SCons to re-run the builds, but the # actual contents of the output files should be the same modulo -# the CreationDate header. +# the CreationDate header and some other PDF garp. test.write(['docs', 'test.tex'], tex_input + "\n\n\n") test.run(stderr=None) @@ -137,7 +137,19 @@ ps_output_2 = test.read(['build', 'docs', 'test.ps']) pdf_output_1 = test.normalize_pdf(pdf_output_1) pdf_output_2 = test.normalize_pdf(pdf_output_2) -assert pdf_output_1 == pdf_output_2, test.diff_substr(pdf_output_1, pdf_output_2, 80, 80) +if pdf_output_1 != pdf_output_2: + import sys + test.write(['build', 'docs', 'test.normalized.1.pdf'], pdf_output_1) + test.write(['build', 'docs', 'test.normalized.2.pdf'], pdf_output_2) + sys.stdout.write("***** 1 and 2 are different!\n") + sys.stdout.write(test.diff_substr(pdf_output_1, pdf_output_2, 80, 80) + '\n') + sys.stdout.write("Output from run 1:\n") + sys.stdout.write(test.stdout(-1) + '\n') + sys.stdout.write("Output from run 2:\n") + sys.stdout.write(test.stdout() + '\n') + sys.stdout.flush() + test.fail_test() + assert ps_output_1 == ps_output_2, test.diff_substr(ps_output_1, ps_output_2, 80, 80) diff --git a/test/TEX/bibtex-latex-rerun.py b/test/TEX/bibtex-latex-rerun.py index a0ba5c2..9191713 100644 --- a/test/TEX/bibtex-latex-rerun.py +++ b/test/TEX/bibtex-latex-rerun.py @@ -98,13 +98,24 @@ pdf_output_3 = test.read('bibtest.pdf') # If the PDF file is now different than the second run, modulo the -# creation timestamp and the ID, then something else odd has happened, -# so fail. +# creation timestamp and the ID and some other PDF garp, then something +# else odd has happened, so fail. pdf_output_2 = test.normalize_pdf(pdf_output_2) pdf_output_3 = test.normalize_pdf(pdf_output_3) -assert pdf_output_2 == pdf_output_3, test.diff_substr(pdf_output_2, pdf_output_3, 80, 80) +if pdf_output_2 != pdf_output_3: + import sys + test.write('bibtest.normalized.2.pdf', pdf_output_2) + test.write('bibtest.normalized.3.pdf', pdf_output_3) + sys.stdout.write("***** 2 and 3 are different!\n") + sys.stdout.write(test.diff_substr(pdf_output_2, pdf_output_3, 80, 80) + '\n') + sys.stdout.write("Output from run 2:\n") + sys.stdout.write(test.stdout(-2) + '\n') + sys.stdout.write("Output from run 3:\n") + sys.stdout.write(test.stdout() + '\n') + sys.stdout.flush() + test.fail_test() diff --git a/test/YACC/YACCFLAGS.py b/test/YACC/YACCFLAGS.py index b7e2167..91b391b 100644 --- a/test/YACC/YACCFLAGS.py +++ b/test/YACC/YACCFLAGS.py @@ -42,37 +42,43 @@ else: test = TestSCons.TestSCons() +test.subdir('in') + test.write('myyacc.py', """ import getopt import string import sys -cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:x', []) +cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:I:x', []) output = None opt_string = '' +i_arguments = '' for opt, arg in cmd_opts: if opt == '-o': output = open(arg, 'wb') + elif opt == '-I': i_arguments = i_arguments + ' ' + arg else: opt_string = opt_string + ' ' + opt for a in args: contents = open(a, 'rb').read() - output.write(string.replace(contents, 'YACCFLAGS', opt_string)) + contents = string.replace(contents, 'YACCFLAGS', opt_string) + contents = string.replace(contents, 'I_ARGS', i_arguments) + output.write(contents) output.close() sys.exit(0) """) test.write('SConstruct', """ env = Environment(YACC = r'%(_python_)s myyacc.py', - YACCFLAGS = '-x', + YACCFLAGS = '-x -I${TARGET.dir} -I${SOURCE.dir}', tools=['yacc', '%(linker)s', '%(compiler)s']) -env.CFile(target = 'aaa', source = 'aaa.y') +env.CFile(target = 'out/aaa', source = 'in/aaa.y') """ % locals()) -test.write('aaa.y', "aaa.y\nYACCFLAGS\n") +test.write(['in', 'aaa.y'], "aaa.y\nYACCFLAGS\nI_ARGS\n") test.run('.', stderr = None) -test.must_match('aaa.c', "aaa.y\n -x\n") +test.must_match(['out', 'aaa.c'], "aaa.y\n -x\n out in\n") diff --git a/test/import.py b/test/import.py index a26d745..9c5d3af 100644 --- a/test/import.py +++ b/test/import.py @@ -29,6 +29,7 @@ Verify that we can import and use the contents of Platform and Tool modules directly. """ +import os import re import sys @@ -36,6 +37,14 @@ import TestSCons test = TestSCons.TestSCons() +# Before executing the any of the platform or tool modules, add some +# null entries to the environment $PATH variable to make sure there's +# no code that tries to index elements from the list before making sure +# they're non-null. +# (This was a problem in checkpoint release 0.97.d020070809.) +os.environ['PATH'] = os.pathsep + os.environ['PATH'] + \ + os.pathsep + os.pathsep + '/no/such/dir' + os.pathsep + SConstruct_path = test.workpath('SConstruct') platforms = [ @@ -216,6 +225,7 @@ env = Environment(tools = ['%(tool)s']) import SCons.Tool.%(tool)s env = Environment() +SCons.Tool.%(tool)s.exists(env) SCons.Tool.%(tool)s.generate(env) """ diff --git a/test/long-lines.py b/test/long-lines.py index 1c501e3..340cd9a 100644 --- a/test/long-lines.py +++ b/test/long-lines.py @@ -46,6 +46,13 @@ elif sys.platform == 'cygwin': arflag = 'o' linkflag_init = '-L' + test.workpath() linkflag = ' -L' + test.workpath() +elif sys.platform in ('darwin', 'irix6'): + lib_shared_dll = 'libshared' + TestSCons._dll + lib_static_lib = 'libstatic.a' + arflag_init = 'r' + arflag = 'v' + linkflag_init = '-L' + test.workpath() + linkflag = ' -L' + test.workpath() else: lib_shared_dll = 'libshared.so' lib_static_lib = 'libstatic.a' diff --git a/test/option--cd.py b/test/option--cd.py deleted file mode 100644 index 9aa9402..0000000 --- a/test/option--cd.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test the --cache-disable option when retrieving derived files from a -CacheDir. -""" - -import os.path -import shutil - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'src') - -test.write(['src', 'SConstruct'], """ -def cat(env, source, target): - target = str(target[0]) - open('cat.out', 'ab').write(target + "\\n") - source = map(str, source) - f = open(target, "wb") - for src in source: - f.write(open(src, "rb").read()) - f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -CacheDir(r'%s') -""" % test.workpath('cache')) - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src', arguments = '.') - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") -test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(['src', 'cat.out']) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. Then clean up. -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") -test.must_not_exist(test.workpath('src', 'cat.out')) - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') - -# Verify that when run with --cache-disable, we rebuild the files even -# though they're available from the cache. Then clean up. -test.run(chdir = 'src', - arguments = '--cache-disable .', - stdout = test.wrap_stdout("""\ -cat(["aaa.out"], ["aaa.in"]) -cat(["bbb.out"], ["bbb.in"]) -cat(["ccc.out"], ["ccc.in"]) -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") -test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(['src', 'cat.out']) - -# Verify that when run with --no-cache, we rebuild the files even -# though they're available from the cache. Then clean up. -test.run(chdir = 'src', - arguments = '--no-cache .', - stdout = test.wrap_stdout("""\ -cat(["aaa.out"], ["aaa.in"]) -cat(["bbb.out"], ["bbb.in"]) -cat(["ccc.out"], ["ccc.in"]) -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") -test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(['src', 'cat.out']) - -# All done. -test.pass_test() diff --git a/test/option--cf.py b/test/option--cf.py deleted file mode 100644 index 47847d4..0000000 --- a/test/option--cf.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test populating a CacheDir with the --cache-force option. -""" - -import os.path -import shutil - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'src') - -test.write(['src', 'SConstruct'], """ -def cat(env, source, target): - target = str(target[0]) - open('cat.out', 'ab').write(target + "\\n") - source = map(str, source) - f = open(target, "wb") - for src in source: - f.write(open(src, "rb").read()) - f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -CacheDir(r'%s') -""" % test.workpath('cache')) - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src', arguments = '.') - -test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in\nccc.in\n") -test.fail_test(test.read(['src', 'cat.out']) != "aaa.out\nbbb.out\nccc.out\nall\n") - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(['src', 'cat.out']) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. DO NOT CLEAN UP. -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -test.up_to_date(chdir = 'src', arguments = '.') - -# Blow away and recreate the CacheDir, then verify that --cache-force -# repopulates the cache with the local built targets. DO NOT CLEAN UP. -shutil.rmtree(test.workpath('cache')) -test.subdir('cache') - -test.run(chdir = 'src', arguments = '--cache-force .') - -test.run(chdir = 'src', arguments = '-c .') - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -# Blow away and recreate the CacheDir, then verify that --cache-populate -# repopulates the cache with the local built targets. DO NOT CLEAN UP. -shutil.rmtree(test.workpath('cache')) -test.subdir('cache') - -test.run(chdir = 'src', arguments = '--cache-populate .') - -test.run(chdir = 'src', arguments = '-c .') - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -# All done. -test.pass_test() diff --git a/test/option--cs.py b/test/option--cs.py deleted file mode 100644 index 1e62c49..0000000 --- a/test/option--cs.py +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test printing build actions when using the --cache-show option and -retrieving derived files from a CacheDir. -""" - -import os.path -import shutil - -import TestSCons - -_python_ = TestSCons._python_ -_exe = TestSCons._exe -_obj = TestSCons._obj - -test = TestSCons.TestSCons() - -test.subdir('cache', 'src1', 'src2') - -test.write(['src1', 'build.py'], r""" -import sys -open('cat.out', 'ab').write(sys.argv[1] + "\n") -file = open(sys.argv[1], 'wb') -for src in sys.argv[2:]: - file.write(open(src, 'rb').read()) -file.close() -""") - -cache = test.workpath('cache') - -test.write(['src1', 'SConstruct'], """ -def cat(env, source, target): - target = str(target[0]) - open('cat.out', 'ab').write(target + "\\n") - source = map(str, source) - f = open(target, "wb") - for src in source: - f.write(open(src, "rb").read()) - f.close() -env = Environment(BUILDERS={'Internal':Builder(action=cat), - 'External':Builder(action='%(_python_)s build.py $TARGET $SOURCES')}) -env.External('aaa.out', 'aaa.in') -env.External('bbb.out', 'bbb.in') -env.Internal('ccc.out', 'ccc.in') -env.Internal('all', ['aaa.out', 'bbb.out', 'ccc.out']) -CacheDir(r'%(cache)s') -""" % locals()) - -test.write(['src1', 'aaa.in'], "aaa.in\n") -test.write(['src1', 'bbb.in'], "bbb.in\n") -test.write(['src1', 'ccc.in'], "ccc.in\n") - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src1', arguments = '.') - -test.must_match(['src1', 'all'], "aaa.in\nbbb.in\nccc.in\n") - -test.must_match(['src1', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") - -test.up_to_date(chdir = 'src1', arguments = '.') - -test.run(chdir = 'src1', arguments = '-c .') -test.unlink(['src1', 'cat.out']) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. Then clean up. -test.run(chdir = 'src1', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.must_not_exist(test.workpath('src1', 'cat.out')) - -test.up_to_date(chdir = 'src1', arguments = '.') - -test.run(chdir = 'src1', arguments = '-c .') - -# Verify that using --cache-show reports the files as being rebuilt, -# even though we actually fetch them from the cache. Then clean up. -expect = test.wrap_stdout("""\ -%(_python_)s build.py aaa.out aaa.in -%(_python_)s build.py bbb.out bbb.in -cat(["ccc.out"], ["ccc.in"]) -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""" % locals()) - -test.run(chdir = 'src1', arguments = '--cache-show .', stdout = expect) - -test.must_not_exist(test.workpath('src1', 'cat.out')) - -test.up_to_date(chdir = 'src1', arguments = '.') - -test.run(chdir = 'src1', arguments = '-c .') - -# Verify that using --cache-show -n reports the files as being rebuilt, -# even though we don't actually fetch them from the cache. No need to -# clean up. -expect = test.wrap_stdout("""\ -%(_python_)s build.py aaa.out aaa.in -%(_python_)s build.py bbb.out bbb.in -cat(["ccc.out"], ["ccc.in"]) -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""" % locals()) - -test.run(chdir = 'src1', arguments = '--cache-show -n .', stdout = expect) - -test.must_not_exist(test.workpath('src1', 'cat.out')) - -test.must_not_exist(test.workpath('src1', 'aaa.out')) -test.must_not_exist(test.workpath('src1', 'bbb.out')) -test.must_not_exist(test.workpath('src1', 'ccc.out')) -test.must_not_exist(test.workpath('src1', 'all')) - -# Verify that using --cache-show -s doesn't report anything, even though -# we do fetch the files from the cache. No need to clean up. -test.run(chdir = 'src1', - arguments = '--cache-show -s .', - stdout = "") - -test.must_match(['src1', 'all'], "aaa.in\nbbb.in\nccc.in\n") -test.must_not_exist(test.workpath('src1', 'cat.out')) - -# -hello_exe = 'hello' + _exe -hello_obj = 'hello' + _obj -src2_hello = test.workpath('src2', hello_exe) - -test.write(['src2', 'SConstruct'], """ -env = Environment() -env.Program('hello.c') -CacheDir(r'%s') -""" % (test.workpath('cache'))) - -test.write(['src2', 'hello.c'], r"""\ -#include -#include -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("src2/hello.c\n"); - exit (0); -} -""") - -# Normal build. -test.run(chdir = 'src2', arguments = '.') - -test.run(program = src2_hello, stdout = "src2/hello.c\n") - -test.up_to_date(chdir = 'src2', arguments = '.') - -test.run(chdir = 'src2', arguments = '-c .') - -# Verify that using --cache-show doesn't blow up. -# Don't bother checking the output, since we verified the correct -# behavior above. We just want to make sure the canonical Program() -# invocation works with --cache-show. -test.run(chdir = 'src2', arguments = '--cache-show .') - -test.run(program = src2_hello, stdout = "src2/hello.c\n") - -test.up_to_date(chdir = 'src2', arguments = '.') - -# All done. -test.pass_test() diff --git a/test/option--random.py b/test/option--random.py index 8c7ef1e..eebd34e 100644 --- a/test/option--random.py +++ b/test/option--random.py @@ -64,7 +64,7 @@ test.run(arguments = '-n -Q') non_random_output = test.stdout() tries = 0 -max_tries = 3 +max_tries = 10 while test.stdout() == non_random_output: if tries >= max_tries: print "--random generated the non-random output %s times!" % max_tries @@ -82,7 +82,7 @@ test.run(arguments = '-n -Q') non_random_output = test.stdout() tries = 0 -max_tries = 3 +max_tries = 10 while test.stdout() == non_random_output: if tries >= max_tries: print "--random generated the non-random output %s times!" % max_tries diff --git a/test/option/debug-time.py b/test/option/debug-time.py index bf18b97..e8873cf 100644 --- a/test/option/debug-time.py +++ b/test/option/debug-time.py @@ -117,6 +117,7 @@ scons_time = get_scons_time(stdout) command_time = get_command_time(stdout) failures = [] +warnings = [] if not within_tolerance(expected_command_time, command_time, 0.01): failures.append("""\ @@ -134,15 +135,21 @@ outside of the 1%% tolerance. """ % locals()) if not within_tolerance(total_time, expected_total_time, 0.15): - failures.append("""\ -SCons -j1 reported total build time of %(total_time)s, + # This tolerance check seems empirically to work fine if there's + # a light load on the system, but on a heavily loaded system the + # timings get screwy and it can fail frequently. Some obvious + # attempts to work around the problem didn't, so just treat it as + # a warning for now. + warnings.append("""\ +Warning: SCons -j1 reported total build time of %(total_time)s, but the actual measured build time was %(expected_total_time)s (end-to-end time of %(complete_time)s less Python overhead of %(overhead)s), outside of the 15%% tolerance. """ % locals()) +if failures or warnings: + print string.join([test.stdout()] + failures + warnings, '\n') if failures: - print string.join([test.stdout()] + failures, '\n') test.fail_test(1) test.run(arguments = "--debug=time . SLEEP=0") diff --git a/test/option/tree-all.py b/test/option/tree-all.py index 92b9065..ec7c7d8 100644 --- a/test/option/tree-all.py +++ b/test/option/tree-all.py @@ -122,14 +122,14 @@ tree3 = """ +-. +-Bar.c +-Bar.ooo - | +-[Bar.c] + | +-Bar.c | +-Bar.h | +-Foo.h +-Foo.c +-Foo.ooo - | +-[Foo.c] - | +-[Foo.h] - | +-[Bar.h] + | +-Foo.c + | +-Foo.h + | +-Bar.h +-Foo.xxx | +-[Foo.ooo] | +-[Bar.ooo] diff --git a/test/option/tree-derived.py b/test/option/tree-derived.py index a10faa2..3ccada8 100644 --- a/test/option/tree-derived.py +++ b/test/option/tree-derived.py @@ -98,8 +98,8 @@ dtree3 = """ +-bar.ooo +-foo.ooo +-foo.xxx - +-[foo.ooo] - +-[bar.ooo] + +-foo.ooo + +-bar.ooo """ test.run(arguments = "--tree=derived,prune .") diff --git a/test/packaging/msi/file-placement.py b/test/packaging/msi/file-placement.py index 08b0ba6..c2aaf35 100644 --- a/test/packaging/msi/file-placement.py +++ b/test/packaging/msi/file-placement.py @@ -37,17 +37,19 @@ test = TestSCons.TestSCons() try: from xml.dom.minidom import * except ImportError: - test.skip_test('Canoot import xml.dom.minidom skipping test\n') + test.skip_test('Cannot import xml.dom.minidom; skipping test\n') wix = test.Environment().WhereIs('candle') -if wix: - # - # Test the default directory layout - # - test.write( 'file1.exe', "file1" ) +if not wix: + test.skip_test("No 'candle' found; skipping test\n") - test.write('SConstruct', """ +# +# Test the default directory layout +# +test.write( 'file1.exe', "file1" ) + +test.write('SConstruct', """ env = Environment(tools=['default', 'packaging']) f1 = env.Install( '/bin/' , 'file1.exe' ) @@ -61,28 +63,28 @@ env.Package( NAME = 'foo', ) """) - test.run(arguments='', stderr = None) +test.run(arguments='', stderr = None) - dom = parse( test.workpath( 'foo-1.2.wxs' ) ) - dirs = dom.getElementsByTagName( 'Directory' ) +dom = parse( test.workpath( 'foo-1.2.wxs' ) ) +dirs = dom.getElementsByTagName( 'Directory' ) - test.fail_test( not dirs[0].attributes['Name'].value == 'SourceDir' ) - test.fail_test( not dirs[1].attributes['Name'].value == 'PFiles' ) - test.fail_test( not dirs[2].attributes['Name'].value == 'NANOSOF1' ) - test.fail_test( not dirs[3].attributes['Name'].value == 'FOO-1.2' ) +test.fail_test( not dirs[0].attributes['Name'].value == 'SourceDir' ) +test.fail_test( not dirs[1].attributes['Name'].value == 'PFiles' ) +test.fail_test( not dirs[2].attributes['Name'].value == 'NANOSOF1' ) +test.fail_test( not dirs[3].attributes['Name'].value == 'FOO-1.2' ) - # - # Try to put 7 files into 5 distinct directories of varying depth and overlapping count - # - test.write( 'file1.exe', "file1" ) - test.write( 'file2.exe', "file2" ) - test.write( 'file3.dll', "file3" ) - test.write( 'file4.dll', "file4" ) - test.write( 'file5.class', "file5" ) - test.write( 'file6.class', "file6" ) - test.write( 'file7.class', "file7" ) - - test.write('SConstruct', """ +# +# Try to put 7 files into 5 distinct directories of varying depth and overlapping count +# +test.write( 'file1.exe', "file1" ) +test.write( 'file2.exe', "file2" ) +test.write( 'file3.dll', "file3" ) +test.write( 'file4.dll', "file4" ) +test.write( 'file5.class', "file5" ) +test.write( 'file6.class', "file6" ) +test.write( 'file7.class', "file7" ) + +test.write('SConstruct', """ env = Environment(tools=['default', 'packaging']) f1 = env.Install( '/bin/' , 'file1.exe' ) f2 = env.Install( '/bin/' , 'file2.exe' ) @@ -103,35 +105,35 @@ env.Package( NAME = 'foo', ) """) - test.run(arguments='', stderr = None) +test.run(arguments='', stderr = None) - dom = parse( test.workpath( 'foo-1.2.wxs' ) ) - files = dom.getElementsByTagName( 'File' ) +dom = parse( test.workpath( 'foo-1.2.wxs' ) ) +files = dom.getElementsByTagName( 'File' ) - test.fail_test( not files[0].parentNode.parentNode.attributes['LongName'].value == 'bin' ) - test.fail_test( not files[1].parentNode.parentNode.attributes['LongName'].value == 'bin' ) - test.fail_test( not files[2].parentNode.parentNode.attributes['LongName'].value == 'lib' ) - test.fail_test( not files[3].parentNode.parentNode.attributes['LongName'].value == 'lib' ) - - test.fail_test( not files[4].parentNode.parentNode.attributes['LongName'].value == 'teco' ) - test.fail_test( not files[4].parentNode.parentNode.parentNode.attributes['LongName'].value == 'edu' ) - test.fail_test( not files[4].parentNode.parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) +test.fail_test( not files[0].parentNode.parentNode.attributes['LongName'].value == 'bin' ) +test.fail_test( not files[1].parentNode.parentNode.attributes['LongName'].value == 'bin' ) +test.fail_test( not files[2].parentNode.parentNode.attributes['LongName'].value == 'lib' ) +test.fail_test( not files[3].parentNode.parentNode.attributes['LongName'].value == 'lib' ) + +test.fail_test( not files[4].parentNode.parentNode.attributes['LongName'].value == 'teco' ) +test.fail_test( not files[4].parentNode.parentNode.parentNode.attributes['LongName'].value == 'edu' ) +test.fail_test( not files[4].parentNode.parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) - test.fail_test( not files[5].parentNode.parentNode.attributes['LongName'].value == 'teco' ) - test.fail_test( not files[5].parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) +test.fail_test( not files[5].parentNode.parentNode.attributes['LongName'].value == 'teco' ) +test.fail_test( not files[5].parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) - test.fail_test( not files[6].parentNode.parentNode.attributes['LongName'].value == 'tec' ) - test.fail_test( not files[6].parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) +test.fail_test( not files[6].parentNode.parentNode.attributes['LongName'].value == 'tec' ) +test.fail_test( not files[6].parentNode.parentNode.parentNode.attributes['LongName'].value == 'java' ) - # - # Test distinct directories put into distinct features - # - test.write( 'file1.exe', "file1" ) - test.write( 'file2.exe', "file2" ) - test.write( 'file3.dll', "file3" ) - test.write( 'file3-.dll', "file3" ) +# +# Test distinct directories put into distinct features +# +test.write( 'file1.exe', "file1" ) +test.write( 'file2.exe', "file2" ) +test.write( 'file3.dll', "file3" ) +test.write( 'file3-.dll', "file3" ) - test.write('SConstruct', """ +test.write('SConstruct', """ env = Environment(tools=['default', 'packaging']) f1 = env.Install( '/bin/' , 'file1.exe' ) f2 = env.Install( '/bin/' , 'file2.exe' ) @@ -152,21 +154,19 @@ env.Package( NAME = 'foo', ) """) - test.run(arguments='', stderr = None) - - dom = parse( test.workpath( 'foo-1.2.wxs' ) ) - features = dom.getElementsByTagName( 'Feature' ) +test.run(arguments='', stderr = None) - test.fail_test( not features[1].attributes['Title'].value == 'Core Part' ) - componentrefs = features[1].getElementsByTagName( 'ComponentRef' ) - test.fail_test( not componentrefs[0].attributes['Id'].value == 'file1.exe' ) - test.fail_test( not componentrefs[1].attributes['Id'].value == 'file2.exe' ) - test.fail_test( not componentrefs[2].attributes['Id'].value == 'file3.dll1' ) +dom = parse( test.workpath( 'foo-1.2.wxs' ) ) +features = dom.getElementsByTagName( 'Feature' ) - test.fail_test( not features[2].attributes['Title'].value == 'Java Part' ) - componentrefs = features[2].getElementsByTagName( 'ComponentRef' ) - test.fail_test( not componentrefs[0].attributes['Id'].value == 'file3.dll' ) +test.fail_test( not features[1].attributes['Title'].value == 'Core Part' ) +componentrefs = features[1].getElementsByTagName( 'ComponentRef' ) +test.fail_test( not componentrefs[0].attributes['Id'].value == 'file1.exe' ) +test.fail_test( not componentrefs[1].attributes['Id'].value == 'file2.exe' ) +test.fail_test( not componentrefs[2].attributes['Id'].value == 'file3.dll1' ) -else: - test.no_result() +test.fail_test( not features[2].attributes['Title'].value == 'Java Part' ) +componentrefs = features[2].getElementsByTagName( 'ComponentRef' ) +test.fail_test( not componentrefs[0].attributes['Id'].value == 'file3.dll' ) +test.pass_test() diff --git a/test/packaging/msi/package.py b/test/packaging/msi/package.py index 24bd26d..e6ce668 100644 --- a/test/packaging/msi/package.py +++ b/test/packaging/msi/package.py @@ -42,14 +42,16 @@ except ImportError: wix = test.Environment().WhereIs('candle') -if wix: - # - # build with minimal tag set and test for the given package meta-data - # - test.write( 'file1.exe', "file1" ) - test.write( 'file2.exe', "file2" ) - - test.write('SConstruct', """ +if not wix: + test.skip_test("No 'candle' found; skipping test\n") + +# +# build with minimal tag set and test for the given package meta-data +# +test.write( 'file1.exe', "file1" ) +test.write( 'file2.exe', "file2" ) + +test.write('SConstruct', """ import os env = Environment(tools=['default', 'packaging']) @@ -69,32 +71,32 @@ env.Package( NAME = 'foo', env.Alias( 'install', [ f1, f2 ] ) """) - test.run(arguments='', stderr = None) +test.run(arguments='', stderr = None) - test.must_exist( 'foo-1.2.wxs' ) - test.must_exist( 'foo-1.2.msi' ) +test.must_exist( 'foo-1.2.wxs' ) +test.must_exist( 'foo-1.2.msi' ) - dom = parse( test.workpath( 'foo-1.2.wxs' ) ) - Product = dom.getElementsByTagName( 'Product' )[0] - Package = dom.getElementsByTagName( 'Package' )[0] +dom = parse( test.workpath( 'foo-1.2.wxs' ) ) +Product = dom.getElementsByTagName( 'Product' )[0] +Package = dom.getElementsByTagName( 'Package' )[0] - test.fail_test( not Product.attributes['Manufacturer'].value == 'Nanosoft_2000' ) - test.fail_test( not Product.attributes['Version'].value == '1.2' ) - test.fail_test( not Product.attributes['Name'].value == 'foo' ) +test.fail_test( not Product.attributes['Manufacturer'].value == 'Nanosoft_2000' ) +test.fail_test( not Product.attributes['Version'].value == '1.2' ) +test.fail_test( not Product.attributes['Name'].value == 'foo' ) - test.fail_test( not Package.attributes['Description'].value == 'balalalalal' ) - test.fail_test( not Package.attributes['Comments'].value == 'this should be reallly really long' ) +test.fail_test( not Package.attributes['Description'].value == 'balalalalal' ) +test.fail_test( not Package.attributes['Comments'].value == 'this should be reallly really long' ) - # - # build with file tags resulting in multiple components in the msi installer - # - test.write( 'file1.exe', "file1" ) - test.write( 'file2.exe', "file2" ) - test.write( 'file3.html', "file3" ) - test.write( 'file4.dll', "file4" ) - test.write( 'file5.dll', "file5" ) +# +# build with file tags resulting in multiple components in the msi installer +# +test.write( 'file1.exe', "file1" ) +test.write( 'file2.exe', "file2" ) +test.write( 'file3.html', "file3" ) +test.write( 'file4.dll', "file4" ) +test.write( 'file5.dll', "file5" ) - test.write('SConstruct', """ +test.write('SConstruct', """ import os env = Environment(tools=['default', 'packaging']) f1 = env.Install( '/usr/' , 'file1.exe' ) @@ -121,18 +123,19 @@ env.Package( NAME = 'foo', env.Alias( 'install', [ f1, f2, f3, f4, f5 ] ) """) - test.run(arguments='', stderr = None) +test.run(arguments='', stderr = None) + +test.must_exist( 'foo-1.2.wxs' ) +test.must_exist( 'foo-1.2.msi' ) + +dom = parse( test.workpath( 'foo-1.2.wxs' ) ) +elements = dom.getElementsByTagName( 'Feature' ) +test.fail_test( not elements[1].attributes['Title'].value == 'Main Part' ) +test.fail_test( not elements[2].attributes['Title'].value == 'Documentation' ) +test.fail_test( not elements[3].attributes['Title'].value == 'Another Feature' ) +test.fail_test( not elements[3].attributes['Description'].value == 'with a long description' ) +test.fail_test( not elements[4].attributes['Title'].value == 'Java Part' ) - test.must_exist( 'foo-1.2.wxs' ) - test.must_exist( 'foo-1.2.msi' ) - dom = parse( test.workpath( 'foo-1.2.wxs' ) ) - elements = dom.getElementsByTagName( 'Feature' ) - test.fail_test( not elements[1].attributes['Title'].value == 'Main Part' ) - test.fail_test( not elements[2].attributes['Title'].value == 'Documentation' ) - test.fail_test( not elements[3].attributes['Title'].value == 'Another Feature' ) - test.fail_test( not elements[3].attributes['Description'].value == 'with a long description' ) - test.fail_test( not elements[4].attributes['Title'].value == 'Java Part' ) -else: - test.no_result() +test.pass_test() diff --git a/test/packaging/option--package-type.py b/test/packaging/option--package-type.py index 68a075c..00a569e 100644 --- a/test/packaging/option--package-type.py +++ b/test/packaging/option--package-type.py @@ -35,6 +35,8 @@ _python_ = TestSCons._python_ test = TestSCons.TestSCons() +rpm_build_root = test.workpath('rpm_build_root') + scons = test.program rpm = test.Environment().WhereIs('rpm') @@ -50,6 +52,7 @@ test.write('SConstruct', """ # -*- coding: iso-8859-15 -*- env=Environment(tools=['default', 'packaging']) env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') +env.Append(RPMFLAGS = r' --buildroot %(rpm_build_root)s') prog=env.Install( '/bin', 'main' ) env.Package( NAME = 'foo', VERSION = '1.2.3', diff --git a/test/packaging/rpm/cleanup.py b/test/packaging/rpm/cleanup.py index 26bf79b..5472fbb 100644 --- a/test/packaging/rpm/cleanup.py +++ b/test/packaging/rpm/cleanup.py @@ -45,6 +45,8 @@ rpm = test.Environment().WhereIs('rpm') if not rpm: test.skip_test('rpm not found, skipping test\n') +rpm_build_root = test.workpath('rpm_build_root') + test.subdir('src') test.write( [ 'src', 'main.c' ], r""" @@ -57,7 +59,10 @@ int main( int argc, char* argv[] ) test.write('SConstruct', """ env=Environment(tools=['default', 'packaging']) +env['ENV']['RPM_BUILD_ROOT'] = r'%(rpm_build_root)s/foo-1.2.3' + env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') +env.Append(RPMFLAGS = r' --buildroot %(rpm_build_root)s') prog = env.Install( '/bin/' , Program( 'src/main.c') ) diff --git a/test/packaging/rpm/internationalization.py b/test/packaging/rpm/internationalization.py index 66c2291..af0bc75 100644 --- a/test/packaging/rpm/internationalization.py +++ b/test/packaging/rpm/internationalization.py @@ -47,6 +47,8 @@ rpm = test.Environment().WhereIs('rpm') if not rpm: test.skip_test('rpm not found, skipping test\n') +rpm_build_root = test.workpath('rpm_build_root') + # # test INTERNATIONAL PACKAGE META-DATA # @@ -64,6 +66,7 @@ import os env = Environment(tools=['default', 'packaging']) env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') +env.Append(RPMFLAGS = r' --buildroot %(rpm_build_root)s') prog = env.Install( '/bin', Program( 'main.c' ) ) diff --git a/test/packaging/rpm/package.py b/test/packaging/rpm/package.py index a5f9f0f..2a37265 100644 --- a/test/packaging/rpm/package.py +++ b/test/packaging/rpm/package.py @@ -43,6 +43,8 @@ rpm = test.Environment().WhereIs('rpm') if not rpm: test.skip_test('rpm not found, skipping test\n') +rpm_build_root = test.workpath('rpm_build_root') + test.subdir('src') test.write( [ 'src', 'main.c' ], r""" @@ -58,6 +60,7 @@ import os env=Environment(tools=['default', 'packaging']) env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') +env.Append(RPMFLAGS = r' --buildroot %(rpm_build_root)s') prog = env.Install( '/bin/' , Program( 'src/main.c') ) diff --git a/test/packaging/rpm/tagging.py b/test/packaging/rpm/tagging.py index 198799a..d0fce83 100644 --- a/test/packaging/rpm/tagging.py +++ b/test/packaging/rpm/tagging.py @@ -45,6 +45,8 @@ rpm = test.Environment().WhereIs('rpm') if not rpm: test.skip_test('rpm not found, skipping test\n') +rpm_build_root = test.workpath('rpm_build_root') + # # Test adding an attr tag to the built program. # @@ -61,7 +63,10 @@ test.write('SConstruct', """ import os env = Environment(tools=['default', 'packaging']) + env.Prepend(RPM = 'TAR_OPTIONS=--wildcards ') +env.Append(RPMFLAGS = r' --buildroot %(rpm_build_root)s') + install_dir= os.path.join( ARGUMENTS.get('prefix', '/'), 'bin/' ) prog_install = env.Install( install_dir , Program( 'src/main.c' ) ) env.Tag( prog_install, UNIX_ATTR = '(0755, root, users)' ) diff --git a/test/scons-time/run/archive/zip.py b/test/scons-time/run/archive/zip.py index 67cfc3a..b5b122b 100644 --- a/test/scons-time/run/archive/zip.py +++ b/test/scons-time/run/archive/zip.py @@ -29,6 +29,8 @@ Verify basic generation of timing information from an input fake-project .zip file. """ +import sys + import TestSCons_time test = TestSCons_time.TestSCons_time() @@ -37,6 +39,28 @@ test.write_fake_scons_py() test.write_sample_project('foo.zip') +try: + import zipfile + # There's a bug in the Python 2.1 zipfile library that makes it blow + # up on 64-bit architectures, when trying to read normal 32-bit zip + # files. Check for it by trying to read the archive we just created, + # and skipping the test gracefully if there's a problem. + zf = zipfile.ZipFile('foo.zip', 'r') + for name in zf.namelist(): + zf.read(name) +except ImportError: + # This "shouldn't happen" because the early Python versions that + # have no zipfile module don't support the scons-time script, + # so the initialization above should short-circuit this test. + # But just in case... + fmt = "Python %s has no zipfile module. Skipping test.\n" + test.skip_test(fmt % sys.version[:3]) +except zipfile.BadZipfile, e: + if str(e)[:11] == 'Bad CRC-32 ': + fmt = "Python %s zipfile module doesn't work on 64-bit architectures. Skipping test.\n" + test.skip_test(fmt % sys.version[:3]) + raise + test.run(arguments = 'run foo.zip') test.must_exist('foo-000-0.log', diff --git a/test/scons-time/run/option/quiet.py b/test/scons-time/run/option/quiet.py index 5d5d7cf..f5a3d8c 100644 --- a/test/scons-time/run/option/quiet.py +++ b/test/scons-time/run/option/quiet.py @@ -39,7 +39,7 @@ test.diff_function = TestSCons_time.diff_re def tempdir_re(*args): - import os + import os,sys import os.path import string import tempfile @@ -49,6 +49,9 @@ def tempdir_re(*args): x = apply(os.path.join, args) x = re.escape(x) x = string.replace(x, 'time\\-', 'time\\-[^%s]*' % sep) + if sys.platform=='darwin': + # OSX has /tmp in /private/tmp. + x = '(/private)?' + x return x scons_py = re.escape(test.workpath('src', 'script', 'scons.py')) diff --git a/test/scons-time/run/option/verbose.py b/test/scons-time/run/option/verbose.py index cdf5b5a..fb95dab 100644 --- a/test/scons-time/run/option/verbose.py +++ b/test/scons-time/run/option/verbose.py @@ -40,7 +40,7 @@ test.diff_function = TestSCons_time.diff_re def tempdir_re(*args): - import os + import os,sys import os.path import string import tempfile @@ -50,6 +50,9 @@ def tempdir_re(*args): x = apply(os.path.join, args) x = re.escape(x) x = string.replace(x, 'time\\-', 'time\\-[^%s]*' % sep) + if sys.platform=='darwin': + # OSX has /tmp in /private/tmp. + x = '(/private)?' + x return x scons_py = re.escape(test.workpath('src', 'script', 'scons.py')) -- cgit v0.12