From: stevenknight Date: Sat, 9 Aug 2008 18:23:26 +0000 (+0000) Subject: Merged revisions 3057-3059,3061-3264 via svnmerge from X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=012788b7406779535aa84ded8e086c3bda407995;p=scons.git Merged revisions 3057-3059,3061-3264 via svnmerge from http://scons.tigris.org/svn/scons/branches/core ........ r3061 | stevenknight | 2008-06-10 10:06:24 -0700 (Tue, 10 Jun 2008) | 3 lines Issue 2085: Fix man page indentation under Debian systems. Also add escape characters to the beginnings of some necessary lines. ........ r3063 | GregNoel | 2008-06-10 17:50:28 -0700 (Tue, 10 Jun 2008) | 1 line Add David as a member; parametize input file ........ r3064 | stevenknight | 2008-06-11 07:22:23 -0700 (Wed, 11 Jun 2008) | 3 lines Issue 1973: Clarify the man page description of the SConscript(src_dir) argument. ........ r3065 | stevenknight | 2008-06-11 11:25:00 -0700 (Wed, 11 Jun 2008) | 3 lines Issues 1974, 1999: document {Add,Get,Set}Option(). Reorganize the section a little bit, other minor edits. ........ r3066 | garyo | 2008-06-12 19:53:51 -0700 (Thu, 12 Jun 2008) | 1 line Document GetBuildFailures in Users Guide. Closes issue #1989. ........ r3067 | stevenknight | 2008-06-14 06:42:07 -0700 (Sat, 14 Jun 2008) | 2 lines Fix build errors from undefined &GetBuildFailures; entity in doc. ........ r3073 | garyo | 2008-06-14 18:40:20 -0700 (Sat, 14 Jun 2008) | 1 line Fix issue #1538 (Handling of string argument in Action.Action). Includes test. ........ r3074 | garyo | 2008-06-14 19:11:30 -0700 (Sat, 14 Jun 2008) | 1 line doc: Trivial man page typo fix. ........ r3075 | stevenknight | 2008-06-15 14:35:13 -0700 (Sun, 15 Jun 2008) | 3 lines Add a test to make sure the env.RCS() applied to an individual file works correctly when the checked-out copy is changed. ........ r3076 | stevenknight | 2008-06-15 23:07:17 -0700 (Sun, 15 Jun 2008) | 2 lines Issue 2025: User's Guide updates from Randall Spangler's review. ........ r3078 | stevenknight | 2008-06-16 06:26:13 -0700 (Mon, 16 Jun 2008) | 2 lines Issue 1990: Document the Requires() function in the Users's Guide. ........ r3082 | stevenknight | 2008-06-16 19:31:00 -0700 (Mon, 16 Jun 2008) | 3 lines Fix the second example in the AddMethod() chapter. Show the Windows output generating a resource file. Other copyedits. ........ r3083 | stevenknight | 2008-06-17 06:44:02 -0700 (Tue, 17 Jun 2008) | 2 lines Fix misspelled entity: &Builders; => &Builder;s. ........ r3084 | stevenknight | 2008-06-17 08:30:13 -0700 (Tue, 17 Jun 2008) | 3 lines Issue 1588: - Document suggested use of the Viual C/C++ /FC option to fix the ability to double-click on file names in compilation error messages. ........ r3085 | stevenknight | 2008-06-17 08:50:33 -0700 (Tue, 17 Jun 2008) | 3 lines Issue 1687: Document the need to use Clean() for any SideEffect() files that must be explicitly removed when their targets are removed. ........ r3086 | stevenknight | 2008-06-17 10:59:28 -0700 (Tue, 17 Jun 2008) | 2 lines Typo fix in the new SideEffect() paragraph. (Gary Oberbrunner) ........ r3088 | cournape | 2008-06-17 19:55:27 -0700 (Tue, 17 Jun 2008) | 3 lines Initialized merge tracking via "svnmerge" with revisions "1-3087" from http://scons.tigris.org/svn/scons/branches/vs_revamp ........ r3094 | stevenknight | 2008-06-19 09:52:16 -0700 (Thu, 19 Jun 2008) | 3 lines Change the User's Guide to use the new Variables object and its associated function for controlling command-line build variables. ........ r3115 | stevenknight | 2008-06-25 06:46:36 -0700 (Wed, 25 Jun 2008) | 2 lines Issue 2072: end indentation after generated Builder text. ........ r3116 | stevenknight | 2008-06-25 19:07:15 -0700 (Wed, 25 Jun 2008) | 2 lines Reorganize the command-line arguments chapter. ........ r3117 | stevenknight | 2008-06-25 19:13:58 -0700 (Wed, 25 Jun 2008) | 2 lines Editing pass for formatting in the Glob() sections. ........ r3118 | stevenknight | 2008-06-25 19:23:09 -0700 (Wed, 25 Jun 2008) | 3 lines Wording changing: Preventing => Controlling, because the chapter also talks about how to clean additional targets. ........ r3119 | stevenknight | 2008-06-25 19:50:41 -0700 (Wed, 25 Jun 2008) | 2 lines Fix missing tags, minor wording fix. ........ r3120 | stevenknight | 2008-06-25 19:58:34 -0700 (Wed, 25 Jun 2008) | 2 lines Add "the Default Function" to the appropriate subsection title. ........ r3121 | stevenknight | 2008-06-26 08:33:43 -0700 (Thu, 26 Jun 2008) | 2 lines Issue 1988: Document the Variables.UnknownVariables() method. ........ r3122 | stevenknight | 2008-06-26 08:35:51 -0700 (Thu, 26 Jun 2008) | 3 lines Remove comments listing some of the variables that have been documented recently. ........ r3123 | stevenknight | 2008-06-26 12:42:53 -0700 (Thu, 26 Jun 2008) | 2 lines Issue 2118: Fix incorrectly swapped man page text. (Alexey Zezukin) ........ r3124 | bdbaddog | 2008-06-26 21:23:46 -0700 (Thu, 26 Jun 2008) | 2 lines Fix bug 2108 - duplicate text in description of interactive mode ........ r3125 | stevenknight | 2008-06-27 21:54:56 -0700 (Fri, 27 Jun 2008) | 3 lines Issue 1993: Document the $*COMSTR variables, the Progress() function, and create a common "Controlling Build Output" chapter. ........ r3126 | garyo | 2008-06-28 05:46:44 -0700 (Sat, 28 Jun 2008) | 1 line Fix issue 2105; temporarily omit doc saying that SetOption can override user-created Options (until that is implemented). ........ r3127 | stevenknight | 2008-06-28 07:29:18 -0700 (Sat, 28 Jun 2008) | 2 lines Issue 1747: Explicitly document use of Node lists as input to Depends(). ........ r3128 | stevenknight | 2008-06-28 07:48:32 -0700 (Sat, 28 Jun 2008) | 6 lines White space change: indent the construction environment sections further to make way for combining this chapter with others to make one big "Controlling Environments" chapter. Also, get rid of some now-unnecessary doc from the old Cons classic POD, that was taking up space here waiting to be documented. ........ r3129 | cournape | 2008-06-29 01:56:30 -0700 (Sun, 29 Jun 2008) | 3 lines Initialized merge tracking via "svnmerge" with revisions "1-3128" from http://scons.tigris.org/svn/scons/branches/trygrep ........ r3143 | stevenknight | 2008-07-02 10:29:39 -0700 (Wed, 02 Jul 2008) | 3 lines Initialized merge tracking via "svnmerge" with revisions "1-3142" from http://scons.tigris.org/svn/scons/branches/sgk_lookup ........ r3181 | stevenknight | 2008-07-08 07:17:27 -0700 (Tue, 08 Jul 2008) | 4 lines Reorganize the discussion of different environments into one chapter. Document the SetDefault(), PrependUnique(), AppendUnique(), PrependENVPath() and AppendENVPath() functions. ........ r3182 | stevenknight | 2008-07-08 08:47:55 -0700 (Tue, 08 Jul 2008) | 2 lines Issue 1998: Docment the ARGLIST variable in the User's Guide. ........ r3194 | GregNoel | 2008-07-09 23:16:51 -0700 (Wed, 09 Jul 2008) | 1 line remove unnecessary trailing spaces on lines ........ r3204 | stevenknight | 2008-07-11 08:29:18 -0700 (Fri, 11 Jul 2008) | 2 lines Issue 1853: Remove referenc to SCons.Util.CLVar from a doc example. ........ r3206 | GregNoel | 2008-07-12 02:08:19 -0700 (Sat, 12 Jul 2008) | 1 line Another go at describing VariantDir() ........ r3213 | stevenknight | 2008-07-13 08:57:57 -0700 (Sun, 13 Jul 2008) | 3 lines Set /branches/comments in svnmerge-integrated property to allow merging changes in both directions. ........ r3217 | stevenknight | 2008-07-16 06:52:44 -0700 (Wed, 16 Jul 2008) | 2 lines Update the copyright year in the User's Guide. ........ r3218 | stevenknight | 2008-07-16 07:08:52 -0700 (Wed, 16 Jul 2008) | 3 lines Issue 1881: Add man page text clarifying the behavior of Add{Pre,Post}Action() when multiple targets are specified. ........ r3223 | stevenknight | 2008-07-18 08:18:45 -0700 (Fri, 18 Jul 2008) | 3 lines Initialized merge tracking via "svnmerge" with revisions "1-3222" from http://scons.tigris.org/svn/scons/branches/sgk_subst ........ r3231 | stevenknight | 2008-07-22 01:58:11 -0700 (Tue, 22 Jul 2008) | 4 lines Enhance MSVSProject() tests so they're runnable on any system, regardless of whether Visual Studio is installed, or if it's even a Windows system at all. ........ r3237 | GregNoel | 2008-07-26 00:07:49 -0700 (Sat, 26 Jul 2008) | 1 line Issue 1983: Document ParseConfig, MergeFlags, and ParseFlags for the Users' Guide ........ r3238 | stevenknight | 2008-07-26 08:38:18 -0700 (Sat, 26 Jul 2008) | 3 lines Follow-ons for building the User's Guide with Greg's recent additions for MergeFlags() and ParseFlags(). ........ r3239 | stevenknight | 2008-07-26 09:52:40 -0700 (Sat, 26 Jul 2008) | 3 lines Re-arrange some sections talking about creating construction environments and fetching/expanding variables. ........ r3240 | stevenknight | 2008-07-26 12:16:11 -0700 (Sat, 26 Jul 2008) | 2 lines Stylistic editing of new {Merge,Parse}{Config,Flags} sections. ........ r3241 | GregNoel | 2008-07-26 12:42:42 -0700 (Sat, 26 Jul 2008) | 1 line Issue 1987: Document SideEffect for Users' Guide (incomplete) ........ r3242 | stevenknight | 2008-07-26 13:27:56 -0700 (Sat, 26 Jul 2008) | 2 lines Correct dumb XML mistakes in my last checkin. ........ r3243 | stevenknight | 2008-07-26 13:34:05 -0700 (Sat, 26 Jul 2008) | 2 lines One-character typo. Gah. ........ r3244 | stevenknight | 2008-07-26 13:44:14 -0700 (Sat, 26 Jul 2008) | 2 lines Issue 1977,1980: document the Exit() and Flatten() functions. ........ r3245 | stevenknight | 2008-07-27 10:24:12 -0700 (Sun, 27 Jul 2008) | 14 lines Updates to the new SideEffect section (kudos to Greg). While working on this, Greg discovered a bug (issue #2154) that prevents a SideEffect() file from being used as input to another builder call; it makes the builder target not get build when run in paralle (e.g. -j2)... :-( So this patch comments out that section of Greg's section. This also contains my usual editing pass. In this case I changed some of the examples and added a bunch of text to try to help clarify some things that seemed important. I also added a closing paragraph warning that SideEffect() really shouldn't be used as an alternative to specifying multiple target files in a Builder call when a command builds more than one file that you care about. ........ r3246 | stevenknight | 2008-07-27 10:31:17 -0700 (Sun, 27 Jul 2008) | 2 lines Proofreading edits of the MergeFlags() section. (Greg Noel) ........ r3247 | stevenknight | 2008-07-27 11:17:27 -0700 (Sun, 27 Jul 2008) | 2 lines Issue 1976: document ensure{Python,SCons}Version() in the User's Guide. ........ r3249 | GregNoel | 2008-07-28 15:57:00 -0700 (Mon, 28 Jul 2008) | 1 line Add svn-bisect script ........ git-svn-id: http://scons.tigris.org/svn/scons/trunk@3266 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/bin/sconsoutput.py b/bin/sconsoutput.py index 6340bc18..536bcea3 100644 --- a/bin/sconsoutput.py +++ b/bin/sconsoutput.py @@ -364,7 +364,7 @@ ToolList = { ('jar', 'JARCOM', JarCom, []), ('rmic', 'RMICCOM', Cat, []), ], - 'win32' : [('msvc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']), + 'win32' : [('msvc', ['CCCOM', 'SHCCCOM', 'RCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']), ('mslink', ['LINKCOM', 'SHLINKCOM'], Cat, []), ('mslib', 'ARCOM', Cat, []), ('tar', 'TARCOM', Null, []), diff --git a/bin/svn-bisect.py b/bin/svn-bisect.py new file mode 100755 index 00000000..3be32a00 --- /dev/null +++ b/bin/svn-bisect.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- Python -*- + +import sys +from math import log, ceil +from optparse import OptionParser +import subprocess + +# crack the command line +parser = OptionParser( + usage="%prog lower-revision upper-revision test_script [arg1 ...]", + description="Binary search for a bug in a SVN checkout") +(options,script_args) = parser.parse_args() + +# make sure we have sufficient parameters +if len(script_args) < 1: + parser.error("Need a lower revision") +elif len(script_args) < 2: + parser.error("Need an upper revision") +elif len(script_args) < 3: + parser.error("Need a script to run") + +# extract our starting values +lower = int(script_args[0]) +upper = int(script_args[1]) +script = script_args[2:] + +# print an error message and quit +def error(s): + print >>sys.stderr, "******", s, "******" + sys.exit(1) + +# update to the specified version and run test +def testfail(revision): + "Return true if test fails" + print "Updating to revision", revision + if subprocess.call(["svn","up","-qr",str(revision)]) != 0: + m = "SVN did not update properly to revision %d" + raise RuntimeError(m % revision) + return subprocess.call(script,shell=False) != 0 + +# confirm that the endpoints are different +print "****** Checking upper bracket", upper +upperfails = testfail(upper) +print "****** Checking lower bracket", lower +lowerfails = testfail(lower) +if upperfails == lowerfails: + error("Upper and lower revisions must bracket the failure") + +# binary search for transition +msg = "****** max %d revisions to test (bug bracketed by [%d,%d])" +while upper-lower > 1: + print msg % (ceil(log(upper-lower,2)), lower, upper) + + mid = int((lower + upper)/2) + midfails = testfail(mid) + if midfails == lowerfails: + lower = mid + lowerfails = midfails + else: + upper = mid + upperfails = midfails + +# show which revision was first to fail +if upperfails != lowerfails: lower = upper +print "The error was caused by revision", lower diff --git a/bin/xmlagenda.py b/bin/xmlagenda.py index 7f63d795..405c6989 100755 --- a/bin/xmlagenda.py +++ b/bin/xmlagenda.py @@ -4,8 +4,8 @@ # 'issues.xml'. Run this script to translate 'issues.xml' into a CSV # file named 'editlist.csv'. Upload the CSV into a Google spreadsheet. -# In the spreadsheet, select the last column and delete it (added by the -# upload to allow for expansion; we don't need it). +# In the spreadsheet, select the last column and pick "delete-->column" (it +# was added by the upload to allow for expansion and we don't need it). # Select all the columns and pick "align-->top" # Select the ID and votes columns and pick "align-->right" # Select the priority column and pick "align-->center" @@ -17,7 +17,7 @@ # The team members # FIXME: These names really should be external to this script -team = 'Bill Greg Steven Gary Ken Brandon Sohail Jim'.split() +team = 'Bill Greg Steven Gary Ken Brandon Sohail Jim David'.split() team.sort() # The elements to be picked out of the issue @@ -37,9 +37,11 @@ def Value(element): return v.nodeValue # Parse the XML issues file and produce a DOM for it -# FIXME: parameterize the input file name +import sys +if len(sys.argv) > 1: xml = sys.argv[1] +else: xml = 'issues.xml' from xml.dom.minidom import parse -xml = parse('issues.xml') +xml = parse(xml) # Go through the issues in the DOM, pick out the elements we want, # and put them in our list of issues. diff --git a/doc/man/scons.1 b/doc/man/scons.1 index a4e470f1..6cdf1e07 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -21,17 +21,19 @@ .\" .\" __FILE__ __REVISION__ __DATE__ __DEVELOPER__ .\" +.TH SCONS 1 "__MONTH_YEAR__" .\" ES - Example Start - indents and turns off line fill +.rm ES .de ES .RS .nf .. .\" EE - Example End - ends indent and turns line fill back on +.rm EE .de EE .fi .RE .. -.TH SCONS 1 "__MONTH_YEAR__" .SH NAME scons \- a software construction tool .SH SYNOPSIS @@ -47,15 +49,15 @@ scons \- a software construction tool ] .SH DESCRIPTION -The -.B scons +The +.B scons utility builds software (or other files) by determining which component pieces must be rebuilt and executing the necessary commands to rebuild them. -By default, -.B scons -searches for a file named +By default, +.B scons +searches for a file named .IR SConstruct , .IR Sconstruct , or @@ -63,7 +65,7 @@ or (in that order) in the current directory and reads its configuration from the first file found. An alternate file name may be -specified via the +specified via the .B -f option. @@ -195,7 +197,7 @@ but it may be more convenient for many configurations. can scan known input files automatically for dependency information (for example, #include statements in C or C++ files) and will rebuild dependent files appropriately -whenever any "included" input file changes. +whenever any "included" input file changes. .B scons supports the ability to define new scanners for unknown input file types. @@ -265,7 +267,7 @@ in which case only the specified targets will be built (along with any derived files on which they depend). Specifying "cleanup" targets in SConscript files is not usually necessary. -The +The .B -c flag removes all files necessary to build the specified target: @@ -292,7 +294,7 @@ can be prevented from being removed by using the function. A subset of a hierarchical tree may be built by -remaining at the top-level directory (where the +remaining at the top-level directory (where the .I SConstruct file lives) and specifying the subdirectory as the target to be built: @@ -304,7 +306,7 @@ scons src/subdir or by changing directory and invoking scons with the .B -u option, which traverses up the directory -hierarchy until it finds the +hierarchy until it finds the .I SConstruct file, and then builds targets relatively to the current subdirectory: @@ -329,13 +331,13 @@ builds four targets in parallel, for example. .B scons can maintain a cache of target (derived) files that can be shared between multiple builds. When caching is enabled in a -SConscript file, any target files built by +SConscript file, any target files built by .B scons will be copied to the cache. If an up-to-date target file is found in the cache, it will be retrieved from the cache instead of being rebuilt locally. Caching behavior may be disabled and controlled in other ways by the -.BR --cache-force , +.BR --cache-force , .BR --cache-disable , and .B --cache-show @@ -395,7 +397,7 @@ the Intel compiler tools, and the PharLap ETS compiler. On OS/2 systems, .B scons -searches in order for the +searches in order for the OS/2 compiler, the GCC tool chain, and the Microsoft Visual C++ tools, @@ -416,11 +418,11 @@ by appropriate configuration of Environment construction variables. .SH OPTIONS -In general, -.B scons +In general, +.B scons supports the same command-line options as GNU -.BR make , -and many of those supported by +.BR make , +and many of those supported by .BR cons . .TP @@ -534,27 +536,27 @@ if --config=cache is specified and a necessary test does not yet have any results in the cache. -.TP +.TP .RI "-C" " directory" ", --directory=" directory -Change to the specified +Change to the specified .I directory -before searching for the +before searching for the .IR SConstruct , .IR Sconstruct , or .I sconstruct file, or doing anything -else. Multiple +else. Multiple .B -C options are interpreted relative to the previous one, and the right-most .B -C option wins. (This option is nearly -equivalent to +equivalent to .BR "-f directory/SConstruct" , except that it will search for .IR SConstruct , -.IR Sconstruct , +.IR Sconstruct , or .I sconstruct in the specified directory.) @@ -623,7 +625,7 @@ and about the actual libraries it finds. .TP --debug=includes -Print the include tree after each top-level target is built. +Print the include tree after each top-level target is built. This is generally used to find out what files are included by the sources of a given derived file: @@ -671,7 +673,7 @@ Output looks something like this: $ scons --debug=presub Building myprog.o with action(s): $SHCC $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES -... +\&... .EE .TP @@ -774,11 +776,11 @@ where the SCons configuration expects a directory). .TP .RI --duplicate= ORDER There are three ways to duplicate files in a build tree: hard links, -soft (symbolic) links and copies. The default behaviour of SCons is to +soft (symbolic) links and copies. The default behaviour of SCons is to prefer hard links to soft links to copies. You can specify different behaviours with this option. -.IR ORDER -must be one of +.IR ORDER +must be one of .IR hard-soft-copy (the default), .IR soft-hard-copy , @@ -796,14 +798,14 @@ the mechanisms in the specified order. .TP .RI -f " file" ", --file=" file ", --makefile=" file ", --sconstruct=" file -Use -.I file +Use +.I file as the initial SConscript file. -.TP +.TP -h, --help Print a local help message for this build, if one is defined in -the SConscript file(s), plus a line that describes the +the SConscript file(s), plus a line that describes the .B -H option for command-line option help. If no local help message is defined, prints the standard help message about command-line @@ -818,12 +820,12 @@ exit. -i, --ignore-errors Ignore all errors from commands executed to rebuild files. -.TP +.TP .RI -I " directory" ", --include-dir=" directory -Specifies a +Specifies a .I directory to search for -imported Python modules. If several +imported Python modules. If several .B -I options are used, the directories are searched in the order specified. @@ -864,7 +866,7 @@ implicit dependencies to be rescanned and recached. This implies --implicit-deps-unchanged Force SCons to ignore changes in the implicit dependencies. This causes cached implicit dependencies to always be used. -This implies +This implies .BR --implicit-cache . .TP @@ -909,7 +911,6 @@ command: -n, --no-exec, --just-print, --dry-run, --recon -Q -s, --silent, --quiet --s, --silent, --quiet --taskmastertrace=FILE --tree=OPTIONS .EE @@ -997,14 +998,14 @@ scons>>> exit .TP .RI -j " N" ", --jobs=" N Specifies the number of jobs (commands) to run simultaneously. -If there is more than one -.B -j +If there is more than one +.B -j option, the last one is effective. -.\" ??? If the -.\" .B -j +.\" ??? If the +.\" .B -j .\" option .\" is specified without an argument, -.\" .B scons +.\" .B scons .\" will not limit the number of .\" simultaneous jobs. @@ -1018,7 +1019,7 @@ targets specified on the command line will still be processed. .\" .RI -l " N" ", --load-average=" N ", --max-load=" N .\" No new jobs (commands) will be started if .\" there are other jobs running and the system load -.\" average is at least +.\" average is at least .\" .I N .\" (a floating-point number). @@ -1051,7 +1052,7 @@ Ignored for compatibility with non-GNU versions of .TP .RI --max-drift= SECONDS -Set the maximum expected drift in the modification time of files to +Set the maximum expected drift in the modification time of files to .IR SECONDS . This value determines how long a file must be unmodified before its cached content signature @@ -1071,7 +1072,7 @@ no matter how old the file is. No execute. Print the commands that would be executed to build any out-of-date target files, but do not execute the commands. -.TP +.TP .RI --no-site-dir Prevents the automatic addition of the standard .I site_scons @@ -1085,15 +1086,15 @@ to the toolpath. .\" .TP .\" .RI -o " file" ", --old-file=" file ", --assume-old=" file -.\" Do not rebuild +.\" Do not rebuild .\" .IR file , .\" and do .\" not rebuild anything due to changes in the contents of .\" .IR file . -.\" .TP +.\" .TP .\" .RI --override " file" .\" Read values to override specific build environment variables -.\" from the specified +.\" from the specified .\" .IR file . .\" .TP .\" -p @@ -1103,7 +1104,7 @@ to the toolpath. .\" After printing, a normal build is performed .\" as usual, as specified by other command-line options. .\" This also prints version information -.\" printed by the +.\" printed by the .\" .B -v .\" option. .\" @@ -1155,10 +1156,10 @@ Also suppresses SCons status messages. .TP -S, --no-keep-going, --stop -Ignored for compatibility with GNU +Ignored for compatibility with GNU .BR make . -.TP +.TP .RI --site-dir= dir Uses the named dir as the site dir rather than the default .I site_scons @@ -1173,7 +1174,7 @@ will get added to the default toolpath. .TP .RI --stack-size= KILOBYTES Set the size stack used to run threads to -.IR KILOBYTES . +.IR KILOBYTES . This value determines the stack size of the threads used to run jobs. These are the threads that execute the actions of the builders for the nodes that are out-of-date. @@ -1193,9 +1194,9 @@ unless you encounter stack overflow errors. .TP -t, --touch Ignored for compatibility with GNU -.BR make . +.BR make . (Touching a file to make it -appear up-to-date is unnecessary when using +appear up-to-date is unnecessary when using .BR scons .) .TP @@ -1260,10 +1261,10 @@ scons --tree=all,prune,status target .TP -u, --up, --search-up -Walks up the directory structure until an +Walks up the directory structure until an .I SConstruct , .I Sconstruct -or +or .I sconstruct file is found, and uses that as the top of the directory tree. @@ -1283,7 +1284,7 @@ up in. .TP -v, --version -Print the +Print the .B scons version, copyright information, list of authors, and any other relevant information. @@ -1356,7 +1357,10 @@ method. .TP --warn=duplicate-environment, --warn=no-duplicate-environment -Enables or disables warnings about missing SConscript files. +Enables or disables warnings about attempts to specify a build +of a target with two different construction environments +that use the same action. +These warnings are enabled by default. .TP --warn=fortran-cxx-mix, --warn=no-fortran-cxx-mix @@ -1385,9 +1389,7 @@ These warnings are enabled by default. .TP --warn=missing-sconscript, --warn=no-missing-sconscript -Enables or disables warnings about attempts to specify a build -of a target with two different construction environments -that use the same action. +Enables or disables warnings about missing SConscript files. These warnings are enabled by default. .TP @@ -1452,14 +1454,14 @@ These warnings are enabled by default. .\" .\" .TP .\" .RI -W " file" ", --what-if=" file ", --new-file=" file ", --assume-new=" file -.\" Pretend that the target -.\" .I file +.\" Pretend that the target +.\" .I file .\" has been -.\" modified. When used with the +.\" modified. When used with the .\" .B -n .\" option, this .\" show you what would be rebuilt if you were to modify that file. -.\" Without +.\" Without .\" .B -n .\" ... what? XXX .\" @@ -1467,7 +1469,7 @@ These warnings are enabled by default. .\" --warn-undefined-variables .\" Warn when an undefined variable is referenced. -.TP +.TP .RI -Y " repository" ", --repository=" repository ", --srcdir=" repository Search the specified repository for any input and target files not found in the local directory hierarchy. Multiple @@ -1480,10 +1482,10 @@ repositories are searched in the order specified. .\" XXX Adding this in the future would be a help. .SS Construction Environments A construction environment is the basic means by which the SConscript -files communicate build information to +files communicate build information to .BR scons . -A new construction environment is created using the -.B Environment +A new construction environment is created using the +.B Environment function: .ES @@ -1494,7 +1496,7 @@ Variables, called .I construction .IR variables , may be set in a construction environment -either by specifyng them as keywords when the object is created +either by specifying them as keywords when the object is created or by assigning them a value after the object is created: .ES @@ -1557,7 +1559,7 @@ dictionary. This is so that any executed commands that use sockets to connect with other systems (such as fetching source files from -external CVS repository specifications like +external CVS repository specifications like .BR :pserver:anonymous@cvs.sourceforge.net:/cvsroot/scons ) will work on Windows systems. @@ -1815,7 +1817,7 @@ that are not absolute path names (that is, do not begin with .B / on POSIX systems -or +or .B \\ on Windows systems, with or without @@ -1874,7 +1876,7 @@ The following examples all build the executable program .B bar (on POSIX systems) -or +or .B bar.exe (on Windows systems) from the bar.c source file: @@ -2174,11 +2176,12 @@ provides the following builder methods: '\" END GENERATED BUILDER DESCRIPTIONS '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.P All targets of builder methods automatically depend on their sources. An explicit dependency can -be specified using the -.B Depends +be specified using the +.B Depends method of a construction environment (see below). In addition, @@ -2261,7 +2264,7 @@ from SCons.Script import * Except where otherwise noted, the same-named construction environment method -and global function +and global function provide the exact same functionality. The only difference is that, where appropriate, @@ -2299,7 +2302,7 @@ and global functions supported by include: '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI Action( action ", [" strfunction ", " varlist ]) .TP .RI env.Action( action ", [" strfunction ", " varlist ]) @@ -2309,7 +2312,7 @@ the specified See the section "Action Objects," below, for a complete explanation of the arguments and behavior. -Note that the +Note that the .BR env.Action () form of the invocation will expand construction variables in any arguments strings, @@ -2328,7 +2331,7 @@ form delays all variable expansion until the Action object is actually used. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI AddMethod( object, function ", [" name ]) .TP .RI env.AddMethod( function ", [" name ]) @@ -2415,7 +2418,7 @@ keyword value to to indicate that the specified long option(s) take(s) an .I optional argument. -When +When .B "nargs = '?'" is passed to the .BR AddOption () @@ -2440,17 +2443,19 @@ the option value may be accessed using .BR GetOption () or .BR env.GetOption (). -The value may also be set, using -.BR SetOption () -or -.BR env.SetOption (), -if conditions in a -.B SConscript -require overriding any default value. -Note, however, that a -value specified on the command line will -.I always -override a value set by any SConscript file. +\" NOTE: in SCons 1.x or 2.0, user options will be settable, but not yet. +\" Uncomment this when that works. See tigris issue 2105. +\" The value may also be set, using +\" .BR SetOption () +\" or +\" .BR env.SetOption (), +\" if conditions in a +\" .B SConscript +\" require overriding any default value. +\" Note, however, that a +\" value specified on the command line will +\" .I always +\" override a value set by any SConscript file. Any specified .B help= @@ -2486,7 +2491,7 @@ env = Environment(PREFIX = GetOption('prefix')) .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI AddPostAction( target ", " action ) .TP .RI env.AddPostAction( target ", " action ) @@ -2501,8 +2506,13 @@ an Action object, or anything that can be converted into an Action object (see below). +When multiple targets are supplied, +the action may be called multiple times, +once after each action that generates +one or more targets in the list. + '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI AddPreAction( target ", " action ) .TP .RI env.AddPreAction( target ", " action ) @@ -2517,6 +2527,36 @@ an Action object, or anything that can be converted into an Action object (see below). +When multiple targets are specified, +the action(s) may be called multiple times, +once before each action that generates +one or more targets in the list. + +Note that if any of the targets are built in multiple steps, +the action will be invoked just +before the "final" action that specifically +generates the specified target(s). +For example, when building an executable program +from a specified source +.B .c +file via an intermediate object file: + +.ES +foo = Program('foo.c') +AddPreAction(foo, 'pre_action') +.EE + +The specified +.B pre_action +would be executed before +.B scons +calls the link command that actually +generates the executable program binary +.BR foo , +not before compiling the +.B foo.c +file into an object file. + '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP .RI Alias( alias ", [" targets ", [" action ]]) @@ -2723,9 +2763,9 @@ or (This will be officially deprecated some day.) '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI Builder( action ", [" arguments ]) -.TP +.TP .RI env.Builder( action ", [" arguments ]) Creates a Builder object for the specified @@ -2733,7 +2773,7 @@ the specified See the section "Builder Objects," below, for a complete explanation of the arguments and behavior. -Note that the +Note that the .BR env.Builder () form of the invocation will expand construction variables in any arguments strings, @@ -2752,9 +2792,9 @@ form delays all variable expansion until after the Builder object is actually called. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI CacheDir( cache_dir ) -.TP +.TP .RI env.CacheDir( cache_dir ) Specifies that .B scons @@ -2859,9 +2899,9 @@ useful if inputs and/or outputs of some tool are impossible to predict or prohibitively large. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI Clean( targets ", " files_or_dirs ) -.TP +.TP .RI env.Clean( targets ", " files_or_dirs ) This specifies a list of files or directories which should be removed whenever the targets are specified with the @@ -2885,7 +2925,7 @@ Builder methods. Examples: The related -.BR NoClean () +.BR NoClean () function overrides calling .BR Clean () for the same target, @@ -3089,7 +3129,7 @@ env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo/bar')) .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI Decider( function ) .TP .RI env.Decider( function ) @@ -3252,7 +3292,7 @@ env.Decider(my_decider) .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI Default( targets ) .TP .RI env.Default( targets ) @@ -3315,8 +3355,21 @@ from source code management systems. .TP .RI env.Depends( target ", " dependency ) Specifies an explicit dependency; -the target file(s) will be rebuilt -whenever the dependency file(s) has changed. +the +.I target +will be rebuilt +whenever the +.I dependency +has changed. +Both the specified +.I target +and +.I dependency +can be a string +(usually the path name of a file or directory) +or Node objects, +or a list of strings or Node objects +(such as returned by a Builder call). This should only be necessary for cases where the dependency is not caught by a Scanner @@ -3326,6 +3379,18 @@ Example: .ES env.Depends('foo', 'other-input-file-for-foo') + +mylib = env.Library('mylib.c') +installed_lib = env.Install('lib', mylib) +bar = env.Program('bar.c') + +# Arrange for the library to be copied into the installation +# directory before trying to build the "bar" program. +# (Note that this is for example only. A "real" library +# dependency would normally be configured through the $LIBS +# and $LIBPATH variables, not using an env.Depends() call.) + +env.Depends(bar, installed_lib) .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -3352,16 +3417,16 @@ cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM') .RI env.Dir( name ", [" directory ]) This returns a Directory Node, an object that represents the specified directory -.IR name . +.IR name . .I name -can be a relative or absolute path. +can be a relative or absolute path. .I directory -is an optional directory that will be used as the parent directory. +is an optional directory that will be used as the parent directory. If no .I directory is specified, the current script's directory is used as the parent. -If +If .I name is a list, SCons returns a list of Dir nodes. Construction variables are expanded in @@ -3391,7 +3456,7 @@ print env.Dump('CCCOM') .IP will print: .ES -'$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' +\&'$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' .EE .ES @@ -3415,8 +3480,8 @@ will print: .RI EnsurePythonVersion( major ", " minor ) .TP .RI env.EnsurePythonVersion( major ", " minor ) -Ensure that the Python version is at least -.IR major . minor . +Ensure that the Python version is at least +.IR major . minor . This function will print out an error message and exit SCons with a non-zero exit code if the actual Python version is not late enough. @@ -3432,10 +3497,10 @@ EnsurePythonVersion(2,2) .RI EnsureSConsVersion( major ", " minor ", [" revision ]) .TP .RI env.EnsureSConsVersion( major ", " minor ", [" revision ]) -Ensure that the SCons version is at least +Ensure that the SCons version is at least .IR major.minor , or -.IR major.minor.revision . +.IR major.minor.revision . if .I revision is specified. @@ -3462,7 +3527,7 @@ initialized with the specified pairs. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI Execute( action ", [" strfunction ", " varlist ]) .TP .RI env.Execute( action ", [" strfunction ", " varlist ]) @@ -3502,14 +3567,14 @@ is used if no value is specified. .RI Export( vars ) .TP .RI env.Export( vars ) -This tells +This tells .B scons to export a list of variables from the current SConscript file to all other SConscript files. The exported variables are kept in a global collection, so subsequent calls to .BR Export () -will over-write previous exports that have the same name. +will over-write previous exports that have the same name. Multiple variable names can be passed to .BR Export () as separate arguments or as a list. A dictionary can be used to map @@ -3546,20 +3611,20 @@ See the description of the function, below. '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI File( name ", [" directory ]) -.TP +.TP .RI env.File( name ", [" directory ]) This returns a File Node, an object that represents the specified file -.IR name . +.IR name . .I name -can be a relative or absolute path. +can be a relative or absolute path. .I directory -is an optional directory that will be used as the parent directory. +is an optional directory that will be used as the parent directory. -If +If .I name is a list, SCons returns a list of File nodes. Construction variables are expanded in @@ -3577,9 +3642,9 @@ see "File and Directory Nodes," below. .RI FindFile( file ", " dirs ) .TP .RI env.FindFile( file ", " dirs ) -Search for -.I file -in the path specified by +Search for +.I file +in the path specified by .IR dirs . .I file may be a list of file names or a single file name. In addition to searching @@ -3748,7 +3813,7 @@ for object in Flatten(objects): .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI GetBuildFailures() Returns a list of exceptions for the actions that failed while @@ -3896,7 +3961,7 @@ file is found. This function provides a way to query the value of SCons options set on scons command line (or set using the -.IR SetOption () +.IR SetOption () function). The options supported are: @@ -4129,8 +4194,8 @@ Program('foo', Glob('*.c')) .RI Help( text ) .TP .RI env.Help( text ) -This specifies help text to be printed if the -.B -h +This specifies help text to be printed if the +.B -h argument is given to .BR scons . If @@ -4157,23 +4222,23 @@ env.Ignore('bar', ['bar1.h', 'bar2.h']) .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI Import( vars ) -.TP +.TP .RI env.Import( vars ) -This tells +This tells .B scons to import a list of variables into the current SConscript file. This will import variables that were exported with .BR Export () -or in the +or in the .I exports -argument to +argument to .BR SConscript (). -Variables exported by +Variables exported by .BR SConscript () have precedence. -Multiple variable names can be passed to +Multiple variable names can be passed to .BR Import () as separate arguments or as a list. The variable "*" can be used to import all variables. @@ -4230,7 +4295,7 @@ be passed in as a list, not as separate arguments to .BR env.MergeFlags (). -By default, +By default, duplicate values are eliminated; you can, however, specify .B unique=0 @@ -4268,7 +4333,7 @@ env.MergeFlags(['-O3', .RI env.NoCache( target ", ...)" Specifies a list of files which should .I not -be cached whenever the +be cached whenever the .BR CacheDir () method has been activated. The specified targets may be a list @@ -4324,7 +4389,7 @@ will also accept the return value of any of the construction environment Builder methods. Calling -.BR NoClean () +.BR NoClean () for a target overrides calling .BR Clean () for the same target, @@ -4358,7 +4423,7 @@ which expects the output of a typical .BR gtk-config ) and adds the options to the appropriate construction variables. -By default, +By default, duplicate values are not added to any construction variables; you can specify @@ -4488,7 +4553,7 @@ and added to the following construction variables: Any other strings not associated with options are assumed to be the names of libraries and added to the -.B LIBS +.B LIBS construction variable. Examples (all of which produce the same result): @@ -4571,7 +4636,7 @@ dictionary. This is so that any executed commands that use sockets to connect with other systems (such as fetching source files from -external CVS repository specifications like +external CVS repository specifications like .BR :pserver:anonymous@cvs.sourceforge.net:/cvsroot/scons ) will work on Windows systems. @@ -4942,9 +5007,9 @@ Return('val1 val2') .EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.TP +.TP .RI Scanner( function ", [" argument ", " keys ", " path_function ", " node_class ", " node_factory ", " scan_check ", " recursive ]) -.TP +.TP .RI env.Scanner( function ", [" argument ", " keys ", " path_function ", " node_class ", " node_factory ", " scan_check ", " recursive ]) Creates a Scanner object for the specified @@ -5030,7 +5095,7 @@ by supplying an optional .RI name= script keyword argument. -The optional +The optional .I exports argument provides a list of variable names or a dictionary of named values to export to the @@ -5047,57 +5112,41 @@ must use the .BR Import () function to import the variables. -In effect, the optional +If the optional .I variant_dir -argument causes the files (and subdirectories) in the directory where -.I script -resides to be copied to -.I variant_dir -and the build performed in -.IR variant_dir . -Thus, all of the targets (for example, object files and executables) -that would normally be built in (or underneath) the directory containing -.I script -would actually be built in (or underneath) -.IR variant_dir . -See the description of the -.BR VariantDir () -function below for the details and restrictions. +argument is present, it causes an effect equivalent to +.BR VariantDir ( +.IR variant_dir , +.IR src_dir , +.IR duplicate ) +to be executed prior to reading the +.IR script (s). +(If .I variant_dir -is interpreted relative to the directory -of the calling SConscript file. - -Normally, the source for the variant build is the directory containing -.IR script . -If the sources are not in -.IR script 's -directory, the optional +is not present, the .I src_dir -argument provides the location of the sources. +and +.I duplicate +arguments are ignored.) +The +.I variant_dir +and .I src_dir -is interpreted relative to the directory +arguments are interpreted relative to the directory of the calling SConscript file. - -By default, -.B scons -will link or copy (depending on the platform) -all the source files into the variant directory tree. -This behavior may be disabled by setting the optional -.I duplicate -argument to 0 (it is set to 1 by default), in which case -.B scons -will refer directly to the source files in their source directory -when building target files. -See the description for +If +.I src_dir +is not specified, the directory of the calling SConscript file is assumed. +See the description of the .BR VariantDir () -below for the details and restrictions. +function below for additional details and restrictions. -Any variables returned by -.I script -using +Any variables returned by +.I script +using .BR Return () will be returned by the call to -.BR SConscript (). +.BR SConscript (). Examples: @@ -5105,12 +5154,21 @@ Examples: SConscript('subdir/SConscript') foo = SConscript('sub/SConscript', exports='env') SConscript('dir/SConscript', exports=['env', 'variable']) -SConscript('src/SConscript', variant_dir='build', duplicate=0) -SConscript('bld/SConscript', src_dir='src', exports='env variable') +SConscript('dir/SConscript', exports='env variable') SConscript(dirs=['sub1', 'sub2']) SConscript(dirs=['sub3', 'sub4'], name='MySConscript') .EE +.ES +SConscript('bld/SConscript', variant_dir='bld', duplicate=0) +.EE +which is equivalent to +.ES +VariantDir('bld', '.', duplicate=0) +SConscript('bld/SConscript') +.EE +'\"TODO: SConscript('bld/SConscript', src_dir='src', exports='env variable') + '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP .RI SConscriptChdir( value ) @@ -5291,17 +5349,19 @@ SetOption('max_drift', 1) Declares .I side_effect as a side effect of building -.IR target . -Both -.I side_effect +.IR target . +Both +.I side_effect and .I target can be a list, a file name, or a node. -A side effect is a target that is created +A side effect is a target file that is created or updated as a side effect of building other targets. For example, a Windows PDB file is created as a side effect of building the .obj -files for a static library. +files for a static library, +and various log files are created updated +as side effects of various TeX commands. If a target is a side effect of multiple build commands, .B scons will ensure that only one set of commands @@ -5310,6 +5370,34 @@ Consequently, you only need to use this method for side-effect targets that are built as a result of multiple build commands. +Because multiple build commands may update +the same side effect file, +by default the +.I side_effect +target is +.I not +automatically removed +when the +.I target +is removed by the +.B -c +option. +(Note, however, that the +.I side_effect +might be removed as part of +cleaning the directory in which it lives.) +If you want to make sure the +.I side_effect +is cleaned whenever a specific +.I target +is cleaned, +you must specify this explicitly +with the +.BR Clean () +or +.BR env.Clean () +function. + '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP .RI SourceCode( entries ", " builder ) @@ -5852,7 +5940,7 @@ method that can be used to "build" a Value Node by setting a new value. The optional .I built_value -argument can be specified +argument can be specified when the Value Node is created to indicate the Node should already be considered "built." @@ -5871,7 +5959,7 @@ def create(target, source, env): # $TARGET. f = open(str(target[0]), 'wb') f.write('prefix=' + source[0].get_contents()) - + # Fetch the prefix= argument, if any, from the command # line, and use /usr/local as the default. prefix = ARGUMENTS.get('prefix', '/usr/local') @@ -5901,31 +5989,42 @@ env.UpdateValue(target = Value(output), source = Value(input)) .RI VariantDir( variant_dir ", " src_dir ", [" duplicate ]) .TP .RI env.VariantDir( variant_dir ", " src_dir ", [" duplicate ]) -In effect, the -.I src_dir -directory tree is copied to -.I variant_dir -so a build can be performed there. +Use the +.BR VariantDir () +function to create a copy of your sources in another location: +if a name under +.IR variant_dir +is not found but exists under +.IR src_dir , +the file or directory is copied to +.IR variant_dir . +Target files can be built in a different directory than the original sources +by simply refering to the sources (and targets) within the variant tree. + .BR VariantDir () can be called multiple times with the same .I src_dir to set up multiple builds with different options .RI ( variants ). +The .I src_dir -must be in or underneath the SConstruct file's directory, and +location must be in or underneath the SConstruct file's directory, and .I variant_dir -may not be underneath the -.I src_dir . +may not be underneath +.IR src_dir . +'\"TODO: Can the above restrictions be clarified or relaxed? +'\"TODO: The latter restriction is clearly not completely right; +'\"TODO: src_dir = '.' works fine with a build dir under it. The default behavior is for .B scons -to duplicate the source files in the variant tree -and then build the derived files within the variant tree. -This guarantees correct builds regardless of -whether intermediate source files are generated during the build, -whether preprocessors or other scanners search for included files +to physically duplicate the source files in the variant tree. +Thus, a build performed in the variant tree is guaranteed to be identical +to a build performed in the source tree even if +intermediate source files are generated during the build, +or preprocessors or other scanners search for included files relative to the source file, -or whether individual compilers or other invoked tools are hard-coded +or individual compilers or other invoked tools are hard-coded to put derived files in the same directory as source files. If possible on the platform, @@ -5937,9 +6036,9 @@ Moreover, only the files needed for the build are duplicated; files and directories that are not used are not present in .IR variant_dir . -Duplicating the source tree may be disabled by setting +Duplicating the source tree may be disabled by setting the .I duplicate -to 0. +argument to 0. This will cause .B scons to invoke Builders using the path names of source files in @@ -5957,18 +6056,18 @@ works most naturally with a subsidiary SConscript file. However, you would then call the subsidiary SConscript file not in the source directory, but in the .I variant_dir , -regardless of the value of +regardless of the value of .IR duplicate . This is how you tell .B scons -which variant of a source tree to build. -For example: +which variant of a source tree to build: .ES -VariantDir('build-variant1', 'src') -SConscript('build-variant1/SConscript') -VariantDir('build-variant2', 'src') -SConscript('build-variant2/SConscript') +# run src/SConscript in two variant directories +VariantDir('build/variant1', 'src') +SConscript('build/variant1/SConscript') +VariantDir('build/variant2', 'src') +SConscript('build/variant2/SConscript') .EE .IP @@ -5978,6 +6077,40 @@ function, described above, for another way to specify a variant directory in conjunction with calling a subsidiary SConscript file. +Examples: + +.ES +# use names in the build directory, not the source directory +VariantDir('build', 'src', duplicate=0) +Program('build/prog', 'build/source.c') +.EE + +.ES +# this variant builds both the source and docs +VariantDir('build', '.', duplicate=0) +SConscript(dirs=['build/src','build/doc']) +.EE +Or, equivalently: +.ES +SConscript(dirs=['build/src','build/doc'], + variant_dir = 'build', duplicate = 0) +.EE + +.ES +SConscript('build/SConscript', variant_dir = 'build', duplicate = 0) +.EE +Note that in the last example, the +.I src_dir +is not given, so the current directory is assumed, and the +.B SConstruct +and the +.B SConscript +are actually in the same directory, even though the +.B SConscript +is treated as if it were in the +.B build +subdirectory. + '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP .RI WhereIs( program ", [" path ", " pathext ", " reject ]) @@ -6208,7 +6341,7 @@ default target before it's actually been added to the list. .\" CC The C compiler .\" Example: env["CC"] = "c68x" .\" Default: env["CC"] = "cc" -.\" +.\" .\" CCCOM The command line ... .\" Example: .\" To generate the compiler line c68x -ps -qq -mr -o $TARGET $SOURCES @@ -6261,8 +6394,8 @@ defined construction variables: '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .LP -Construction variables can be retrieved and set using the -.B Dictionary +Construction variables can be retrieved and set using the +.B Dictionary method of the construction environment: .ES @@ -6283,8 +6416,8 @@ constructor: env = Environment(CC="cc") .EE -or when copying a construction environment using the -.B Clone +or when copying a construction environment using the +.B Clone method: .ES @@ -6304,7 +6437,7 @@ In contrast to autoconf, .B scons does not maintain an explicit cache of the tested values, but uses its normal dependency tracking to keep the checked values -up to date. However, users may override this behaviour with the +up to date. However, users may override this behaviour with the .B --config command line option. @@ -6320,7 +6453,7 @@ specifies the environment for building the tests. This environment may be modified when performing checks. .I custom_tests is a dictionary containing custom tests. -See also the section about custom tests below. +See also the section about custom tests below. By default, no custom tests are added to the configure context. .I conf_dir specifies a directory where the test cases are built. @@ -6337,8 +6470,8 @@ If you are using the method, you may want to specify a subdirectory under your variant directory. .I config_h -specifies a C header file where the results of tests -will be written, e.g. #define HAVE_STDIO_H, #define HAVE_LIBM, etc. +specifies a C header file where the results of tests +will be written, e.g. #define HAVE_STDIO_H, #define HAVE_LIBM, etc. The default is to not write a .B config.h file. @@ -6390,15 +6523,15 @@ A created .B Configure instance has the following associated methods: -.TP +.TP .RI Configure.Finish( self ) This method should be called after configuration is done. It returns the environment as modified by the configuration checks performed. After this method is called, no further checks can be performed with this configuration context. -However, you can create a new -.RI Configure +However, you can create a new +.RI Configure context to perform additional checks. Only one context should be active at a time. @@ -6408,7 +6541,7 @@ goes by and developers contribute new useful tests.) .TP .RI Configure.CheckHeader( self ", " header ", [" include_quotes ", " language ]) -Checks if +Checks if .I header is usable in the specified language. .I header @@ -6420,8 +6553,8 @@ header files whose .B #include lines should precede the header line being checked for. -The optional argument -.I include_quotes +The optional argument +.I include_quotes must be a two character string, where the first character denotes the opening quote and the second character denotes the closing quote. @@ -6439,7 +6572,7 @@ Returns 1 on success and 0 on failure. .RI Configure.CheckCHeader( self ", " header ", [" include_quotes ]) This is a wrapper around .B Configure.CheckHeader -which checks if +which checks if .I header is usable in the C language. .I header @@ -6451,8 +6584,8 @@ header files whose .B #include lines should precede the header line being checked for. -The optional argument -.I include_quotes +The optional argument +.I include_quotes must be a two character string, where the first character denotes the opening quote and the second character denotes the closing quote (both default @@ -6463,7 +6596,7 @@ Returns 1 on success and 0 on failure. .RI Configure.CheckCXXHeader( self ", " header ", [" include_quotes ]) This is a wrapper around .B Configure.CheckHeader -which checks if +which checks if .I header is usable in the C++ language. .I header @@ -6475,13 +6608,13 @@ header files whose .B #include lines should precede the header line being checked for. -The optional argument -.I include_quotes +The optional argument +.I include_quotes must be a two character string, where the first character denotes the opening quote and the second character denotes the closing quote (both default to \N'34'). -Returns 1 on success and 0 on failure. +Returns 1 on success and 0 on failure. .TP .RI Configure.CheckFunc( self ", " function_name ", [" header ", " language ]) @@ -6513,27 +6646,27 @@ or and selects the compiler to be used for the check; the default is "C". -.TP +.TP .RI Configure.CheckLib( self ", [" library ", " symbol ", " header ", " language ", " autoadd=1 ]) -Checks if -.I library -provides +Checks if +.I library +provides .IR symbol . If the value of .I autoadd is 1 and the library provides the specified .IR symbol , appends the library to the LIBS construction environment variable. -.I library +.I library may also be None (the default), -in which case -.I symbol +in which case +.I symbol is checked with the current LIBS variable, or a list of library names, in which case each library in the list will be checked for .IR symbol . -If +If .I symbol is not set or is .BR None , @@ -6555,15 +6688,15 @@ The default value for is 1. This method returns 1 on success and 0 on error. -.TP +.TP .RI Configure.CheckLibWithHeader( self ", " library ", " header ", " language ", [" call ", " autoadd ]) -In contrast to the -.RI Configure.CheckLib +In contrast to the +.RI Configure.CheckLib call, this call provides a more sophisticated way to check against libraries. -Again, +Again, .I library -specifies the library or a list of libraries to check. +specifies the library or a list of libraries to check. .I header specifies a header to check for. .I header @@ -6586,7 +6719,7 @@ the default simply checks that you can link against the specified .IR library . .I autoadd -specifies whether to add the library to the environment (only if the check +specifies whether to add the library to the environment (only if the check succeeds). This method returns 1 on success and 0 on error. .TP @@ -6621,7 +6754,7 @@ if not conf.CheckCHeader( 'math.h' ): if conf.CheckLibWithHeader( 'qt', 'qapp.h', 'c++', 'QApplication qapp(0,0);' ): # do stuff for qt - usage, e.g. conf.env.Append( CPPFLAGS = '-DWITH_QT' ) -env = conf.Finish() +env = conf.Finish() .EE .TP @@ -6737,52 +6870,52 @@ conf.Define('A_SYMBOL', 1, 'Set to 1 if you have a symbol') .EE .EE -You can define your own custom checks. +You can define your own custom checks. in addition to the predefined checks. These are passed in a dictionary to the Configure function. This dictionary maps the names of the checks -to user defined Python callables +to user defined Python callables (either Python functions or class instances implementing the .I __call__ method). -The first argument of the call is always a +The first argument of the call is always a .I CheckContext instance followed by the arguments, which must be supplied by the user of the check. These CheckContext instances define the following methods: -.TP +.TP .RI CheckContext.Message( self ", " text ) -Usually called before the check is started. +Usually called before the check is started. .I text will be displayed to the user, e.g. 'Checking for library X...' .TP .RI CheckContext.Result( self, ", " res ) -Usually called after the check is done. +Usually called after the check is done. .I res -can be either an integer or a string. In the former case, 'ok' (res != 0) -or 'failed' (res == 0) is displayed to the user, in the latter case the +can be either an integer or a string. In the former case, 'ok' (res != 0) +or 'failed' (res == 0) is displayed to the user, in the latter case the given string is displayed. .TP .RI CheckContext.TryCompile( self ", " text ", " extension ) -Checks if a file with the specified +Checks if a file with the specified .I extension -(e.g. '.c') containing -.I text +(e.g. '.c') containing +.I text can be compiled using the environment's -.B Object +.B Object builder. Returns 1 on success and 0 on failure. -.TP +.TP .RI CheckContext.TryLink( self ", " text ", " extension ) Checks, if a file with the specified .I extension -(e.g. '.c') containing -.I text +(e.g. '.c') containing +.I text can be compiled using the environment's .B Program builder. Returns 1 on success and 0 on failure. @@ -6791,8 +6924,8 @@ builder. Returns 1 on success and 0 on failure. .RI CheckContext.TryRun( self ", " text ", " extension ) Checks, if a file with the specified .I extension -(e.g. '.c') containing -.I text +(e.g. '.c') containing +.I text can be compiled using the environment's .B Program builder. On success, the program is run. If the program @@ -6811,15 +6944,15 @@ then (0, '') is returned. .TP .RI CheckContext.TryAction( self ", " action ", [" text ", " extension ]) Checks if the specified -.I action +.I action with an optional source file (contents .I text -, extension +, extension .I extension = '' -) can be executed. -.I action -may be anything which can be converted to a +) can be executed. +.I action +may be anything which can be converted to a .B scons .RI Action. On success, @@ -6837,12 +6970,12 @@ Low level implementation for testing specific builds; the methods above are based on this method. Given the Builder instance .I builder -and the optional +and the optional .I text of a source file with optional .IR extension , -this method returns 1 on success and 0 on failure. In addition, -.I self.lastTarget +this method returns 1 on success and 0 on failure. In addition, +.I self.lastTarget is set to the build target node, if the build was successful. .EE @@ -6857,7 +6990,7 @@ def CheckQt(context, qtdir): context.env.Append(LIBS = 'qt', LIBPATH = qtdir + '/lib', CPPPATH = qtdir + '/include' ) ret = context.TryLink(""" #include -int main(int argc, char **argv) { +int main(int argc, char **argv) { QApplication qapp(argc, argv); return 0; } @@ -6872,7 +7005,7 @@ conf = Configure( env, custom_tests = { 'CheckQt' : CheckQt } ) if not conf.CheckQt('/usr/lib/qt'): print 'We really need qt!' Exit(1) -env = conf.Finish() +env = conf.Finish() .EE .SS Command-Line Construction Variables @@ -6881,7 +7014,7 @@ Often when building software, some variables must be specified at build time. For example, libraries needed for the build may be in non-standard locations, or site-specific compiler options may need to be passed to the -compiler. +compiler. .B scons provides a .B Variables @@ -6924,12 +7057,12 @@ Variables objects have the following methods: .TP .RI Add( key ", [" help ", " default ", " validator ", " converter ]) -This adds a customizable construction variable to the Variables object. +This adds a customizable construction variable to the Variables object. .I key -is the name of the variable. -.I help +is the name of the variable. +.I help is the help text for the variable. -.I default +.I default is the default value of the variable; if the default value is .B None @@ -7037,7 +7170,7 @@ for key, value in vars.UnknownVariables(): .TP .RI Save( filename ", " env ) -This saves the currently set variables into a script file named +This saves the currently set variables into a script file named .I filename that can be used on the next invocation to automatically load the current settings. This method combined with the Variables method can be used to @@ -7054,10 +7187,10 @@ vars.Save('variables.cache', env) .TP .RI GenerateHelpText( env ", [" sort ]) This generates help text documenting the customizable construction -variables suitable to passing in to the Help() function. +variables suitable to passing in to the Help() function. .I env is the construction environment that will be used to get the actual values -of customizable variables. Calling with +of customizable variables. Calling with an optional .I sort function @@ -7230,7 +7363,7 @@ Return a tuple of arguments to set up an option whose value is a path name of a package that may be -enabled, disabled or +enabled, disabled or given an explicit path name. The option will use the specified name @@ -7385,7 +7518,7 @@ path of the given .I File or .IR Dir . -The +The .ES # Get the current build dir's path, relative to top. @@ -7512,14 +7645,14 @@ that sets the appropriate construction variables Builder objects are created using the -.B Builder +.B Builder function. The .B Builder function accepts the following arguments: .IP action -The command line string used to build the target from the source. +The command line string used to build the target from the source. .B action can also be: a list of strings representing the command @@ -7536,33 +7669,33 @@ or a list of any of the above. An action function takes three arguments: -.I source -- a list of source nodes, +.I source +- a list of source nodes, .I target - a list of target nodes, .I env - the construction environment. -.IP prefix +.IP prefix The prefix that will be prepended to the target file name. This may be specified as a: .RS 10 .HP 6 -* +* .IR string , .HP 6 -* +* .I callable object - a function or other callable that takes two arguments (a construction environment and a list of sources) and returns a prefix, .HP 6 -* +* .I dictionary -- specifies a mapping from a specific source suffix (of the first +- specifies a mapping from a specific source suffix (of the first source specified) to a corresponding target prefix. Both the source suffix and target prefix specifications may use environment variable substitution, and the target prefix (the 'value' entries in the @@ -7610,7 +7743,7 @@ b = Builder("build_it < $SOURCE > $TARGET", .EE .IP ensure_suffix -When set to any true value, causes +When set to any true value, causes .B scons to add the target suffix specified by the .I suffix @@ -7649,7 +7782,7 @@ for Scanner objects that find implicit dependencies based only on the target file and the construction environment, -.I not +.I not for implicit (See the section "Scanner Objects," below, for information about creating Scanner objects.) @@ -7748,8 +7881,8 @@ from an emitter dictionary.) An emitter function takes three arguments: -.I source -- a list of source nodes, +.I source +- a list of source nodes, .I target - a list of target nodes, .I env @@ -7803,7 +7936,7 @@ can not be called multiple times for the same target file(s). Calling a builder multiple times for the same target simply adds additional source files to the target; it is not allowed to change the environment associated with the target, specify addition environment overrides, or associate a different -builder with the target. +builder with the target. .IP env A construction environment that can be used @@ -7824,8 +7957,8 @@ can be converted into an Action object The generator function takes four arguments: -.I source -- a list of source nodes, +.I source +- a list of source nodes, .I target - a list of target nodes, .I env @@ -7839,13 +7972,13 @@ Example: .ES def g(source, target, env, for_signature): - return [["gcc", "-c", "-o"] + target + source] + return [["gcc", "-c", "-o"] + target + source] b = Builder(generator=g) .EE .IP -The +The .I generator and .I action @@ -7859,12 +7992,12 @@ multi-stage builder. .IP single_source Specifies that this builder expects exactly one source file per call. Giving more than one source files without target files results in implicitely calling -the builder multiple times (once for each source given). Giving multiple +the builder multiple times (once for each source given). Giving multiple source files together with target files results in a UserError exception. .RE .IP -The +The .I generator and .I action @@ -7897,7 +8030,7 @@ In the following example, the setting of .B source_ext_match prevents -.B scons +.B scons from exiting with an error due to the mismatched suffixes of .B foo.in @@ -7997,7 +8130,7 @@ when a Builder object is created will be set in the executing construction environment when the Builder object is called. The canonical example here would be -to set a construction variable to +to set a construction variable to the repository of a source code system. Any additional keyword arguments supplied @@ -8143,7 +8276,7 @@ to indicate an unsuccessful build. def build_it(target = None, source = None, env = None): # build the target from the source return 0 - + a = Action(build_it) .EE @@ -8311,7 +8444,7 @@ supplies a number of functions that arrange for various common file and directory manipulations to be performed. -These are similar in concept to "tasks" in the +These are similar in concept to "tasks" in the Ant build tool, although the implementation is slightly different. These functions do not actually @@ -8357,7 +8490,7 @@ sequences of file manipulation without relying on platform-specific external commands: -that +that .ES env = Environment(TMPBUILD = '/tmp/builddir') env.Command('foo.out', 'foo.in', @@ -8497,7 +8630,7 @@ Besides construction variables, scons provides the following variables for each command execution: .IP TARGET -The file name of the target being built, or the file name of the first +The file name of the target being built, or the file name of the first target if multiple targets are being built. .IP TARGETS @@ -8513,7 +8646,7 @@ The file names of the sources of the build command. (Note that the above variables are reserved and may not be set in a construction environment.) -.LP +.LP For example, given the construction variable CC='cc', targets=['foo'], and sources=['foo.c', 'bar.c']: @@ -8648,8 +8781,8 @@ The function should take four arguments: .I target - a list of target nodes, -.I source -- a list of source nodes, +.I source +- a list of source nodes, .I env - the construction environment, .I for_signature @@ -8782,7 +8915,7 @@ command lines: .IP String When the value is a string it is interpreted as a space delimited list of -command line arguments. +command line arguments. .IP List When the value is a list it is interpreted as a list of command line @@ -8885,7 +9018,7 @@ this argument will be a list of suffixes for the different file types that this Scanner knows how to scan. If the argument is a string, -then it will be expanded +then it will be expanded into a list by the current environment. .IP path_function @@ -8964,7 +9097,7 @@ object that is used by the .BR Object (), .BR SharedObject (), -and +and .BR StaticObject () builders to decide which scanner should be used @@ -9004,12 +9137,12 @@ depending on the capabilities of the underlying system. On a case-sensitive system such as Linux or UNIX, -SCons treats a file with a +SCons treats a file with a .B .C suffix as a C++ source file. On a case-insensitive system such as Windows, -SCons treats a file with a +SCons treats a file with a .B .C suffix as a C source file. .SS .F file suffix @@ -9020,14 +9153,14 @@ depending on the capabilities of the underlying system. On a case-sensitive system such as Linux or UNIX, -SCons treats a file with a +SCons treats a file with a .B .F suffix as a Fortran source file that is to be first run through the standard C preprocessor. On a case-insensitive system such as Windows, -SCons treats a file with a +SCons treats a file with a .B .F suffix as a Fortran source file that should .I not @@ -9106,7 +9239,7 @@ Python interpreter, SCons will prefer the MinGW tools over the Cygwin tools, if they are both installed, regardless of the order of the bin directories in the PATH variable. If you have both MSVC and MinGW installed and you want to use MinGW instead of MSVC, -then you must explictly tell SCons to use MinGW by passing +then you must explictly tell SCons to use MinGW by passing .ES tools=['mingw'] @@ -9290,7 +9423,7 @@ def my_scan(node, env, path, arg): for inc in includes: for dir in path: file = dir + os.sep + inc - if os.path.exists(file): + if os.path.exists(file): results.append(file) break return results @@ -9431,7 +9564,7 @@ libA/SConscript: Import('env') env.Library('a', Split('a1.c a2.c a3.c')) -libB/SConscript: +libB/SConscript: Import('env') env.Library('b', Split('b1.c b2.c b3.c')) @@ -9451,12 +9584,12 @@ Specifying only 'a' and 'b' for the library names allows SCons to append the appropriate library prefix and suffix for the current platform (for example, 'liba.a' on POSIX systems, -'a.lib' on Windows). +\&'a.lib' on Windows). .SS Customizing construction variables from the command line. The following would allow the C compiler to be specified on the command -line or in the file custom.py. +line or in the file custom.py. .ES vars = Variables('custom.py') @@ -9543,7 +9676,7 @@ Since including debugging information in programs and shared libraries can cause their size to increase significantly, Microsoft provides a mechanism for including the debugging information in an external file called a PDB file. SCons supports PDB files through the PDB construction -variable. +variable. SConstruct: .ES diff --git a/doc/scons.mod b/doc/scons.mod index ad20b7b8..deeee332 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -145,8 +145,10 @@ --> Add"> +AddMethod"> AddPostAction"> AddPreAction"> +AddOption"> AddOptions"> AddVariables"> Alias"> @@ -167,6 +169,7 @@ Copy"> Decider"> Default"> +DefaultEnvironment"> DefaultRules"> Delete"> Depends"> @@ -175,15 +178,21 @@ Entry"> EnumOption"> EnumVariable"> +EnsurePythonVersion"> +EnsureSConsVersion"> Environment"> Execute"> +Exit"> Export"> File"> FindFile"> FindInstalledFiles"> Finish"> +Flatten"> GenerateHelpText"> +GetBuildFailures"> GetOption"> +Glob"> Help"> Ignore"> Import"> @@ -193,6 +202,7 @@ ListOption"> ListVariable"> Local"> +MergeFlags"> Mkdir"> Module"> Move"> @@ -204,6 +214,7 @@ PackageOption"> PackageVariable"> ParseConfig"> +ParseFlags"> PathOption"> PathOption.PathAccept"> PathOption.PathExists"> @@ -220,13 +231,16 @@ Prepend"> PrependENVPath"> PrependUnique"> +Progress"> Replace"> Repository"> +Requires"> Return"> RuleSet"> Salt"> SetBuildSignatureType"> SetContentSignatureType"> +SetDefault"> SetOption"> SideEffect"> SourceSignature"> @@ -236,6 +250,8 @@ TargetSignatures"> Task"> Touch"> +UnknownOptions"> +UnknownVariables"> subst"> @@ -272,6 +288,7 @@ --> +ARGLIST"> ARGUMENTS"> BUILD_TARGETS"> COMMAND_LINE_TARGETS"> @@ -289,6 +306,7 @@ COLOR"> COLORS"> CONFIG"> +CPPDEFINES"> RELEASE"> RELEASE_BUILD"> SCANNERMAP"> @@ -323,6 +341,7 @@ exports"> source"> target"> +variables"> variant_dir"> diff --git a/doc/user/ENV.in b/doc/user/ENV.in deleted file mode 100644 index 3d8bc4b3..00000000 --- a/doc/user/ENV.in +++ /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, - if 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 deleted file mode 100644 index 9fa2ec13..00000000 --- a/doc/user/ENV.xml +++ /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, - if 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 b3106af7..ef273d37 100644 --- a/doc/user/MANIFEST +++ b/doc/user/MANIFEST @@ -12,13 +12,11 @@ command-line.xml cons.pl 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 @@ -26,8 +24,12 @@ libraries.xml less-simple.xml main.xml make.xml +mergeflags.xml +misc.xml nodes.xml +output.xml parseconfig.xml +parseflags.xml preface.xml python.xml repositories.xml diff --git a/doc/user/README b/doc/user/README index 7bca314f..bdccf519 100644 --- a/doc/user/README +++ b/doc/user/README @@ -4,6 +4,8 @@ When adding a new file, add it to main.xml and MANIFEST. To build the .xml files from the .in files: scons -D . BUILDDOC=1 +To build the whole PDF doc from this dir, for testing: + scons -D ../../build/doc/PDF/scons-user.pdf Writing examples: here's a simple template. diff --git a/doc/user/add-method.in b/doc/user/add-method.in index 8997efb8..7efd9235 100644 --- a/doc/user/add-method.in +++ b/doc/user/add-method.in @@ -25,12 +25,17 @@ - The env.AddMethod(function, [name]) function is used to add a method - to an environment. It's typically used to add a "pseudo-builder" or - wrap up a call to multiple builders. In the first example, we want - to install the program into the standard bin dir, but also copy it - into a local install/bin dir that might be used to build a package - from. + The &AddMethod; function is used to add a method + to an environment. It's typically used to add a "pseudo-builder," + a function that looks like a &Builder; but + wraps up calls to multiple other &Builder;s + or otherwise processes its arguments + before calling one or more &Builder;s. + In the following example, + we want to install the program into the standard + /usr/bin directory hierarchy, + but also copy it into a local install/bin + directory from which a package might be built: @@ -40,8 +45,8 @@ """Install source in both bin dirs""" i1 = env.Install("$BIN", source) i2 = env.Install("$LOCALBIN", source) - return [i1[0], i2][0] # Return a list, like a normal builder - env = Environment(BIN='/usr/bin', LOCALBIN='#install/bin') + return [i1[0], i2[0]] # Return a list, like a normal builder + env = Environment(BIN='__ROOT__/usr/bin', LOCALBIN='#install/bin') env.AddMethod(install_in_bin_dirs, "InstallInBinDirs") env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs @@ -60,43 +65,46 @@ - It also gives more flexibility in parsing arguments than you can get - with a builder. The next example shows a pseudo-builder with a + As mentioned, a psuedo-builder also provides more flexibility + in parsing arguments than you can get with a &Builder;. + The next example shows a pseudo-builder with a named argument that modifies the filename, and a separate argument for the resource file (rather than having the builder figure it out - by file extension). Also this example demonstrates using the global - AddMethod function to add a method to the global Environment class, + by file extension). This example also demonstrates using the global + &AddMethod; function to add a method to the global Environment class, so it will be used in all subsequently created environments. - import sys def BuildTestProg(env, testfile, resourcefile, testdir="tests"): """Build the test program; - prepends "test_" to src and target, and puts target into testdir.""" - srcfile="test_%s.c"%testfile - if sys.platform=='win32': - target="%s/test_%s$EXESUFFIX"%(testdir,[testfile, resourcefile]) + prepends "test_" to src and target, + and puts target into testdir.""" + srcfile = "test_%s.c" % testfile + target = "%s/test_%s" % (testdir, testfile) + if env['PLATFORM'] == 'win32': + resfile = env.RES(resourcefile) + p = env.Program(target, [srcfile, resfile]) else: - target="%s/test_%s$EXESUFFIX"%(testdir,testfile) - p = env.Program(target, srcfile) + p = env.Program(target, srcfile) return p AddMethod(Environment, BuildTestProg) - # Now use it - env=Environment() + env = Environment() env.BuildTestProg('stuff', resourcefile='res.rc') int main() { printf("Hello, world!\n"); } + + res.rc + - This produces the following (on Linux, anyway; Windows would include the - resource file): + This produces the following on Linux: @@ -104,8 +112,16 @@ - Using AddMethod is better than just adding an instance method to an - Environment because it gets called as a proper method, and AddMethod - provides for copying the method to any copies of the Environment - instance. + And the following on Windows: + + + + scons -Q + + + + Using &AddMethod; is better than just adding an instance method + to a &consenv; because it gets called as a proper method, + and because &AddMethod; provides for copying the method + to any clones of the &consenv; instance. diff --git a/doc/user/add-method.xml b/doc/user/add-method.xml index b6a597b7..e6f2a670 100644 --- a/doc/user/add-method.xml +++ b/doc/user/add-method.xml @@ -25,12 +25,17 @@ - The env.AddMethod(function, [name]) function is used to add a method - to an environment. It's typically used to add a "pseudo-builder" or - wrap up a call to multiple builders. In the first example, we want - to install the program into the standard bin dir, but also copy it - into a local install/bin dir that might be used to build a package - from. + The &AddMethod; function is used to add a method + to an environment. It's typically used to add a "pseudo-builder," + a function that looks like a &Builder; but + wraps up calls to multiple other &Builder;s + or otherwise processes its arguments + before calling one or more &Builder;s. + In the following example, + we want to install the program into the standard + /usr/bin directory hierarchy, + but also copy it into a local install/bin + directory from which a package might be built: @@ -39,7 +44,7 @@ """Install source in both bin dirs""" i1 = env.Install("$BIN", source) i2 = env.Install("$LOCALBIN", source) - return [i1[0], i2][0] # Return a list, like a normal builder + return [i1[0], i2[0]] # Return a list, like a normal builder env = Environment(BIN='/usr/bin', LOCALBIN='#install/bin') env.AddMethod(install_in_bin_dirs, "InstallInBinDirs") env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs @@ -53,44 +58,44 @@ % scons -Q / cc -o hello.o -c hello.c cc -o hello hello.o - Install file: "hello" as "install/bin/hello" Install file: "hello" as "/usr/bin/hello" + Install file: "hello" as "install/bin/hello" - It also gives more flexibility in parsing arguments than you can get - with a builder. The next example shows a pseudo-builder with a + As mentioned, a psuedo-builder also provides more flexibility + in parsing arguments than you can get with a &Builder;. + The next example shows a pseudo-builder with a named argument that modifies the filename, and a separate argument for the resource file (rather than having the builder figure it out - by file extension). Also this example demonstrates using the global - AddMethod function to add a method to the global Environment class, + by file extension). This example also demonstrates using the global + &AddMethod; function to add a method to the global Environment class, so it will be used in all subsequently created environments. - import sys def BuildTestProg(env, testfile, resourcefile, testdir="tests"): """Build the test program; - prepends "test_" to src and target, and puts target into testdir.""" - srcfile="test_%s.c"%testfile - if sys.platform=='win32': - target="%s/test_%s$EXESUFFIX"%(testdir,[testfile, resourcefile]) + prepends "test_" to src and target, + and puts target into testdir.""" + srcfile = "test_%s.c" % testfile + target = "%s/test_%s" % (testdir, testfile) + if env['PLATFORM'] == 'win32': + resfile = env.RES(resourcefile) + p = env.Program(target, [srcfile, resfile]) else: - target="%s/test_%s$EXESUFFIX"%(testdir,testfile) - p = env.Program(target, srcfile) + p = env.Program(target, srcfile) return p AddMethod(Environment, BuildTestProg) - # Now use it - env=Environment() + env = Environment() env.BuildTestProg('stuff', resourcefile='res.rc') - This produces the following (on Linux, anyway; Windows would include the - resource file): + This produces the following on Linux: @@ -100,8 +105,19 @@ - Using AddMethod is better than just adding an instance method to an - Environment because it gets called as a proper method, and AddMethod - provides for copying the method to any copies of the Environment - instance. + And the following on Windows: + + + + C:\>scons -Q + rc /fores.res res.rc + cl /nologo /c test_stuff.c /Fotest_stuff.obj + link /nologo /OUT:tests\test_stuff.exe test_stuff.obj res.res + + + + Using &AddMethod; is better than just adding an instance method + to a &consenv; because it gets called as a proper method, + and because &AddMethod; provides for copying the method + to any clones of the &consenv; instance. diff --git a/doc/user/build-install.in b/doc/user/build-install.in index 763c13e0..547154c2 100644 --- a/doc/user/build-install.in +++ b/doc/user/build-install.in @@ -67,29 +67,21 @@ Because &SCons; is written in Python, you must obviously have Python installed on your system - to use &SCons; + 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 + python -V + (capital 'V') + or + python --version 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 + $ python -V + Python 2.5.1 @@ -99,25 +91,12 @@ - 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 + C:\>python -V + Python 2.5.1 - 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" @@ -132,6 +111,17 @@ + (Note that the option + was added to Python version 2.0, + so if your system only has an earlier version available + you may see an + "Unknown option: -V" + error message.) + + + + + The standard location for information about downloading and installing Python is http://www.python.org/download/. @@ -140,6 +130,16 @@ + + + &SCons; will work with any version of Python from 1.5.2 or later. + If you need to install Python and have a choice, + we recommend using the most recent Python 2.5 version available. + Python 2.5 has significant improvements + the help speed up the performance of &SCons;'. + + +
@@ -402,11 +402,11 @@ install the scons script in the default system scripts directory (/usr/local/bin or - C:\Python2.2\Scripts), + C:\Python25\Scripts), and will install the &SCons; build engine in an appropriate stand-alone library directory (/usr/local/lib/scons or - C:\Python2.2\scons). + C:\Python25\scons). Because these are system directories, you may need root (on Linux or UNIX) or Administrator (on Windows) privileges to install &SCons; like this. @@ -462,7 +462,7 @@ in the /usr/lib/scons-__VERSION__ or - C:\Python2.2\scons-__VERSION__ + C:\Python25\scons-__VERSION__ directory, for example. diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml index 763c13e0..547154c2 100644 --- a/doc/user/build-install.xml +++ b/doc/user/build-install.xml @@ -67,29 +67,21 @@ Because &SCons; is written in Python, you must obviously have Python installed on your system - to use &SCons; + 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 + python -V + (capital 'V') + or + python --version 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 + $ python -V + Python 2.5.1 @@ -99,25 +91,12 @@ - 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 + C:\>python -V + Python 2.5.1 - 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" @@ -132,6 +111,17 @@ + (Note that the option + was added to Python version 2.0, + so if your system only has an earlier version available + you may see an + "Unknown option: -V" + error message.) + + + + + The standard location for information about downloading and installing Python is http://www.python.org/download/. @@ -140,6 +130,16 @@ + + + &SCons; will work with any version of Python from 1.5.2 or later. + If you need to install Python and have a choice, + we recommend using the most recent Python 2.5 version available. + Python 2.5 has significant improvements + the help speed up the performance of &SCons;'. + + +
@@ -402,11 +402,11 @@ install the scons script in the default system scripts directory (/usr/local/bin or - C:\Python2.2\Scripts), + C:\Python25\Scripts), and will install the &SCons; build engine in an appropriate stand-alone library directory (/usr/local/lib/scons or - C:\Python2.2\scons). + C:\Python25\scons). Because these are system directories, you may need root (on Linux or UNIX) or Administrator (on Windows) privileges to install &SCons; like this. @@ -462,7 +462,7 @@ in the /usr/lib/scons-__VERSION__ or - C:\Python2.2\scons-__VERSION__ + C:\Python25\scons-__VERSION__ directory, for example. diff --git a/doc/user/builders-writing.in b/doc/user/builders-writing.in index eefbb31f..b9539756 100644 --- a/doc/user/builders-writing.in +++ b/doc/user/builders-writing.in @@ -696,6 +696,28 @@ This functionality could be invoked as in the following example: &SCons; supports the ability for a Builder to modify the lists of target(s) from the specified source(s). + You do this by defining an &emitter; function + that takes as its arguments + the list of the targets passed to the builder, + the list of the sources passed to the builder, + and the construction environment. + The emitter function should return the modified + lists of targets that should be built + and sources from which the targets will be built. + + + + + + For example, suppose you want to define a Builder + that always calls a foobuild program, + and you want to automatically add + a new target file named + new_target + and a new source file named + new_source + whenever it's called. + The &SConstruct; file might look like this: @@ -738,26 +760,102 @@ This functionality could be invoked as in the following example: env.Foo('file') + + + And would yield the following output: + + + scons -Q - - 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') - + + + One very flexible thing that you can is specify + use a construction variable to specify + different emitter functions for different + construction variable. + To do this, specify a string + containing a construction variable + expansion as the emitter when you call + the &Builder; function, + and set that construction variable to + the desired emitter function + in different construction environments: + + + + + + + bld = Builder(action = 'my_command $SOURCES > $TARGET', + suffix = '.foo', + src_suffix = '.input', + emitter = '$MY_EMITTER') + def modify1(target, source, env): + return target, source + ['modify1.in'] + def modify2(target, source, env): + return target, source + ['modify2.in'] + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + import os + env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() + env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() + + + file1.input + + + file2.input + + + modify1.input + + + modify2.input + + + cat + + + + + + + bld = Builder(action = 'my_command $SOURCES > $TARGET', + suffix = '.foo', + src_suffix = '.input', + emitter = '$MY_EMITTER') + def modify1(target, source, env): + return target, source + ['modify1.in'] + def modify2(target, source, env): + return target, source + ['modify2.in'] + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + + + + + + In this example, the modify1.in + and modify2.in files + get added to the source lists + of the different commands: + + + + + scons -Q +
diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index 6a5a7c15..c45af8a0 100644 --- a/doc/user/builders-writing.xml +++ b/doc/user/builders-writing.xml @@ -617,6 +617,28 @@ This functionality could be invoked as in the following example: &SCons; supports the ability for a Builder to modify the lists of target(s) from the specified source(s). + You do this by defining an &emitter; function + that takes as its arguments + the list of the targets passed to the builder, + the list of the sources passed to the builder, + and the construction environment. + The emitter function should return the modified + lists of targets that should be built + and sources from which the targets will be built. + +
+ + + + For example, suppose you want to define a Builder + that always calls a foobuild program, + and you want to automatically add + a new target file named + new_target + and a new source file named + new_source + whenever it's called. + The &SConstruct; file might look like this: @@ -635,28 +657,88 @@ This functionality could be invoked as in the following example: env.Foo('file') + + + And would yield the following output: + + + % scons -Q foobuild file.foo new_target - file.input new_source + + + One very flexible thing that you can is specify + use a construction variable to specify + different emitter functions for different + construction variable. + To do this, specify a string + containing a construction variable + expansion as the emitter when you call + the &Builder; function, + and set that construction variable to + the desired emitter function + in different construction environments: + + + - 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') + bld = Builder(action = 'my_command $SOURCES > $TARGET', + suffix = '.foo', + src_suffix = '.input', + emitter = '$MY_EMITTER') + def modify1(target, source, env): + return target, source + ['modify1.in'] + def modify2(target, source, env): + return target, source + ['modify2.in'] + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + import os + env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() + env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd() + + + + bld = Builder(action = 'my_command $SOURCES > $TARGET', + suffix = '.foo', + src_suffix = '.input', + emitter = '$MY_EMITTER') + def modify1(target, source, env): + return target, source + ['modify1.in'] + def modify2(target, source, env): + return target, source + ['modify2.in'] + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + + + + + + In this example, the modify1.in + and modify2.in files + get added to the source lists + of the different commands: + + + + + % scons -Q + my_command file1.input modify1.in > file1.foo + my_command file2.input modify2.in > file2.foo + +
diff --git a/doc/user/caching.in b/doc/user/caching.in index 186ece6b..cb8521b1 100644 --- a/doc/user/caching.in +++ b/doc/user/caching.in @@ -104,6 +104,20 @@ scons -Q + + + Note that the &CacheDir; feature still calculates + MD5 build sigantures for the shared cache file names + even if you configure &SCons; to use timestamps + to decide if files are up to date. + (See the + chapter for information about the &Decider; function.) + Consequently, using &CacheDir; may reduce or eliminate any + potential performance improvements + from using timestamps for up-to-date decisions. + + +
diff --git a/doc/user/caching.xml b/doc/user/caching.xml index 51b30aef..2348d329 100644 --- a/doc/user/caching.xml +++ b/doc/user/caching.xml @@ -98,6 +98,20 @@ Retrieved `hello' from cache + + + Note that the &CacheDir; feature still calculates + MD5 build sigantures for the shared cache file names + even if you configure &SCons; to use timestamps + to decide if files are up to date. + (See the + chapter for information about the &Decider; function.) + Consequently, using &CacheDir; may reduce or eliminate any + potential performance improvements + from using timestamps for up-to-date decisions. + + +
diff --git a/doc/user/command-line.in b/doc/user/command-line.in index dbbddaf7..70d19416 100644 --- a/doc/user/command-line.in +++ b/doc/user/command-line.in @@ -25,527 +25,643 @@ - &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. + &SCons; provides a number of ways + for the writer of the &SConscript; files + to give the users who will run &SCons; + a great deal of control over the build execution. + The arguments that the user can specify on + the command line are broken down into three types: -
- 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, - 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: - - - - - - def b(target, source, env): - pass - def s(target, source, env): - return " ... [build output] ..." - a = Action(b, strfunction = s) - env = Environment(BUILDERS = {'A' : Builder(action=a)}) - env.A('foo.out', 'foo.in') - - - foo.in - - - - - scons - export SCONSFLAGS="-Q" - scons - + + Options + - Users of &csh;-style shells on POSIX systems - can set the &SCONSFLAGS; environment as follows: + Command-line options always begin with + one or two - (hyphen) characters. + &SCons; provides ways for you to examind + and set options values from within your &SConscript; files, + as well as the ability to define your own + custom options. + See , below. + + - - $ setenv SCONSFLAGS "-Q" - + + Variables + - Windows users may typically want to set the - &SCONSFLAGS; in the appropriate tab of the - System Properties window. + Any command-line argument containing an = + (equal sign) is considered a variable setting with the form + variable=value + &SCons; provides direct access to + all of the command-line variable settings, + the ability to apply command-line variable settings + to construction environments, + and functions for configuring + specific types of variables + (Boolean values, path names, etc.) + with automatic validation of the user's specified values. + See , below. + + -
- -
- Getting at Command-Line Targets + + 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: + Any command-line argument that is not an option + or a variable setting + (does not begin with a hyphen + and does not contain an equal sign) + is considered a target that the user + (presumably) wants &SCons; to build. + A list of Node objects representing + the target or targets to build. + &SCons; provides access to the list of specified targets, + as well as ways to set the default list of targets + from within the &SConscript; files. + See , below. + + - - - if 'bar' in COMMAND_LINE_TARGETS: - print "Don't forget to copy `bar' to the archive!" - Default(Program('foo.c')) - Program('bar.c') - - - foo.c - - - foo.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 - scons -Q bar - +
+ Command-Line Options - 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. + &SCons; has many command-line options + that control its behavior. + A &SCons; command-line option + always begins with one or two - (hyphen) + characters. -
- -
- Controlling the Default Targets - - +
+ Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable - 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: + - + Users may find themselves supplying + the same command-line options every time + they run &SCons;. + For example, you might find it saves time + to specify a value of -j 2 + to have &SCons; run up to two build commands 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. - - - env = Environment() - hello = env.Program('hello.c') - env.Program('goodbye.c') - Default(hello) - - - hello.c - - - goodbye.c - - + - + - This &SConstruct; file knows how to build two programs, - &hello; and &goodbye;, - but only builds the - &hello; program by default: + If, for example, + 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 -Q - scons -Q - scons -Q goodbye - + - + + + def b(target, source, env): + pass + def s(target, source, env): + return " ... [build output] ..." + a = Action(b, strfunction = s) + env = Environment(BUILDERS = {'A' : Builder(action=a)}) + env.A('foo.out', 'foo.in') + + + foo.in + + - 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 + export SCONSFLAGS="-Q" + scons + - + - - scons -Q . - + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: - + - 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: + + $ setenv SCONSFLAGS "-Q" + - + - - - env = Environment() - prog1 = env.Program('prog1.c') - Default(prog1) - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog3) - - - prog1.c - - - prog2.c - - - prog3.c - - + Windows users may typically want to set the + &SCONSFLAGS; in the appropriate tab of the + System Properties window. - + - Or you can specify more than one target - in a single call to the &Default; function: +
-
+
+ Getting Values Set by Command-Line Options: the &GetOption; Function - - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog1, prog3) - + - + &SCons; provides the &GetOption; function + to get the values set by the various command-line options. + One common use of this is to check whether or not + the -h or --help option + has been specified. + Normally, &SCons; does not print its help text + until after it has read all of the &SConscript; files, + because it's possible that help text has been added + by some subsidiary &SConscript; file deep in the + source tree hierarchy. + Of course, reading all of the &SConscript; files + takes extra time. - Either of these last two examples - will build only the - prog1 - and - prog3 - programs by default: + - + - - scons -Q - scons -Q . - + If you know that your configuration does not define + any additional help text in subsidiary &SConscript; files, + you can speed up the command-line help available to users + by using the &GetOption; function to load the + subsidiary &SConscript; files only if the + the user has not specified + the -h or --help option, + like so: - + - 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') - - - - - int main() { printf("prog1/main.c\n"); } - - - int foo() { printf("prog1/foo.c\n"); } - - - int main() { printf("prog2/main.c\n"); } - - - int bar() { printf("prog2/bar.c\n"); } - - + In general, the string that you pass to the + &GetOption; function to fetch the value of a command-line + option setting is the same as the "most common" long option name + (beginning with two hyphen characters), + although there are some exceptions. + The list of &SCons; command-line options + and the &GetOption; strings for fetching them, + are available in the + section, + below. - + - In which case only the target(s) in that - directory will be built by default: +
- +
+ Setting Values of Command-Line Options: the &SetOption; Function - - scons -Q - scons -Q - scons -Q . - + - + You can also set the values of &SCons; + command-line options from within the &SConscript; files + by using the &SetOption; function. + The strings that you use to set the values of &SCons; + command-line options are available in the + section, + below. - 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) - - - prog1.c - - - prog2.c - - + One use of the &SetOption; function is to + specify a value for the -j + or --jobs option, + so that users get the improved performance + of a parallel build without having to specify the option by hand. + A complicating factor is that a good value + for the -j option is + somewhat system-dependent. + One rough guideline is that the more processors + your system has, + the higher you want to set the + -j value, + in order to take advantage of the number of CPUs. - + - Which would produce build output like: + - + For example, suppose the administrators + of your development systems + have standardized on setting a + NUM_CPU environment variable + to the number of processors on each system. + A little bit of Python code + to access the environment variable + and the &SetOption; function + provide the right level of flexibility: - - scons -Q - scons -Q . - + -
- Getting at the List of Default Targets + + + import os + num_cpu = int(os.environ.get('NUM_CPU', 2)) + SetOption('num_jobs', num_cpu) + print "running with -j", GetOption('num_jobs') + + + foo.in + + - &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: + The above snippet of code + sets the value of the --jobs option + to the value specified in the + $NUM_CPU environment variable. + (This is one of the exception cases + where the string is spelled differently from + the from command-line option. + The string for fetching or setting the --jobs + value is num_jobs + for historical reasons.) + The code in this example prints the num_jobs + value for illustrative purposes. + It uses a default value of 2 + to provide some minimal parallelism even on + single-processor systems: - - - prog1 = Program('prog1.c') - Default(prog1) - print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) - - - prog1.c - - + + scons -Q + - (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;:) + But if the $NUM_CPU + environment variable is set, + then we use that for the default number of jobs: - - scons + + export NUM_CPU="4" + scons -Q - 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: + But any explicit + -j or --jobs + value the user specifies an the command line is used first, + regardless of whether or not + the $NUM_CPU environment + variable is set: - - - 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) - - - prog1.c - - - prog2.c - - + + scons -Q -j 7 + export NUM_CPU="4" + scons -Q -j 3 + + +
+ +
+ Strings for Getting or Setting Values of &SCons; Command-Line Options - Which yields the output: + The strings that you can pass to the &GetOption; + and &SetOption; functions usually correspond to the + first long-form option name + (beginning with two hyphen characters: --), + after replacing any remaining hyphen characters + with underscores. - - scons - - - 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. + The full list of strings and the variables they + correspond to is as follows: + + + + + + + String for &GetOption; and &SetOption; + Command-Line Option(s) + + + + + + + + cache_debug + + + + + cache_disable + + + + + cache_force + + + + + cache_show + + + + + clean + , + , + + + + + config + + + + + directory + , + + + + + diskcheck + + + + + duplicate + + + + + file + , + , + , + + + + + help + , + + + + + ignore_errors + + + + + implicit_cache + + + + + implicit_deps_changed + + + + + implicit_deps_unchanged + + + + + interactive + , + + + + + keep_going + , + + + + + max_drift + + + + + no_exec + , + , + , + , + + + + + no_site_dir + + + + + num_jobs + , + + + + + profile_file + + + + + question + , + + + + + random + + + + + repository + , + , + + + + + silent + , + , + + + + + site_dir + + + + + stack_size + + + + + taskmastertrace_file + + + + + warn + + + + + + + +
-
+
+ Adding Custom Command-Line Options: the &AddOption; Function -
- Getting at the List of Build Targets, Regardless of Origin + - + &SCons; also allows you to define your own + command-line options with the &AddOption; function. + The &AddOption; function takes the same arguments + as the optparse.add_option function + from the standard Python library. + + + The &AddOption; function is, + in fact, implemented using a subclass + of the optparse.OptionParser. + + + Once you have added a custom command-line option + with the &AddOption; function, + the value of the option (if any) is immediately available + using the standard &GetOption; function. + (The value can also be set using &SetOption;, + although that's not very useful in practice + because a default value can be specified in + directly in the &AddOption; call.) - 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 - + One useful example of using this functionality + is to provide a for users: - + - &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. + + + AddOption('--prefix', + dest='prefix', + type='string', + nargs=1, + action='store', + metavar='DIR', + help='installation prefix') + + env = Environment(PREFIX = GetOption('prefix')) + + installed_foo = env.Install('$PREFIX/usr/bin', 'foo.in') + Default(installed_foo) + + + foo.in + + - + - + The above code uses the &GetOption; function + to set the $PREFIX + construction variable to any + value that the user specifies with a command-line + option of --prefix. + Because $PREFIX + will expand to a null string if it's not initialized, + running &SCons; without the + option of --prefix + will install the file in the + /usr/bin/ directory: - 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: + - + + scons -Q -n + - - - prog1 = Program('prog1.c') - Program('prog2.c') - Default(prog1) - print "BUILD_TARGETS is", map(str, BUILD_TARGETS) - - - prog1.c - - - prog2.c - - + - + But specifying --prefix=/tmp/install + on the command line causes the file to be installed in the + /tmp/install/usr/bin/ directory: - Notice how the value of &BUILD_TARGETS; - changes depending on whether a target is - specified on the command line: + - + + scons -Q -n --prefix=/tmp/install + - - scons -Q - scons -Q prog2 - scons -Q -c . - +
-
- Command-Line <varname>variable</varname>=<varname>value</varname> Build Options +
+ Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables @@ -575,7 +691,7 @@ to specifications on the command line. (Note that unless you want to require that users always - specify an option, + specify a variable, you probably want to use the Python ARGUMENTS.get() function, @@ -633,456 +749,1082 @@ -
- -
- 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. + The &ARGUMENTS; dictionary has two minor drawbacks. + First, because it is a dictionary, + it can only store one value for each specified keyword, + and thus only "remembers" the last setting + for each keyword on the command line. + This makes the &ARGUMENTS; dictionary + inappropriate if users should be able to + specify multiple values + on the command line for a given keyword. + Second, it does not preserve + the order in which the variable settings + were specified, + which is a problem if + you want the configuration to + behave differently in response + to the order in which the build + variable settings were specified on the command line. - 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: + To accomodate these requirements, + &SCons; provides an &ARGLIST; variable + that gives you direct access to + variable=value + settings on the command line, + in the exact order they were specified, + and without removing any duplicate settings. + Each element in the &ARGLIST; variable + is itself a two-element list + containing the keyword and the value + of the setting, + and you must loop through, + or otherwise select from, + the elements of &ARGLIST; to + process the specific settings you want + in whatever way is appropriate for your configuration. + For example, + the following code to let the user + add to the &CPPDEFINES; construction variable + by specifying multiple + define= + settings on the command line: - - - 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']) - - - foo.c - - - bar.c - + + + cppdefines = [] + for key, value in ARGLIST: + if key == 'define': + cppdefines.append(value) + env = Environment(CPPDEFINES = cppdefines) + env.Object('prog.c') + + + prog.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. + Yields the followig output: + + scons -Q define=FOO + scons -Q define=FOO define=BAR + + - 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: + Note that the &ARGLIST; and &ARGUMENTS; + variables do not interfere with each other, + but merely provide slightly different views + into how the user specified + variable=value + settings on the command line. + You can use both variables in the same + &SCons; configuration. + In general, the &ARGUMENTS; dictionary + is more convenient to use, + (since you can just fetch variable + settings through a dictionary access), + and the &ARGLIST; list + is more flexible + (since you can examine the + specific order in which + the user's command-line variabe settings). - - scons -Q RELEASE=1 - - -
+
+ Controlling Command-Line Build Variables -
- Providing Help for Command-Line Build Options + - + Being able to use a command-line build variable like + debug=1 is handy, + but it can be a chore to write specific Python code + to recognize each such variable, + check for errors and provide appropriate messages, + and apply the values to a construction variable. + To help with this, + &SCons; supports a class to + define such build variables easily, + and a mechanism to apply the + build variables to a construction environment. + This allows you to control how the build variables affect + construction environments. - 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)) - - + 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: - + - &SCons; will now display some useful text - when the -h option is used: + + + vars = Variables() + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + + + foo.c + + + bar.c + + - + - - scons -Q -h - + This &SConstruct; file first creates a &Variables; object + (the vars = Variables() call), + and then uses the object's &Add; + method to indicate that the &RELEASE; + variable can be set on the command line, + and that its 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. - + - Notice that the help output shows the default value, - and the current actual value of the build option. + - + We then pass the created &Variables; + object as a &variables; keyword argument + to the &Environment; call + used to create the construction environment. + This then allows a user to set the + &RELEASE; build variable on the command line + and have the variable show up in + the command line used to build each object from + a C source file: -
+ -
- Reading Build Options From a File + + scons -Q RELEASE=1 + - + - 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: + NOTE: Before &SCons; release 0.98.1, these build variables + were known as "command-line build options." + The class was actually named the &Options; class, + and in the sections below, + the various functions were named + &BoolOption;, &EnumOption;, &ListOption;, + &PathOption;, &PackageOption; and &AddOptions;. + These older names still work, + and you may encounter them in older + &SConscript; fles, + but their use is discouraged + and will be officially deprecated some day. - + - - - 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)) - - - foo.c - - - bar.c - - - RELEASE = 1 - - +
- +
+ Providing Help for Command-Line Build Variables - This then allows us to control the &RELEASE; - variable by setting it in the &custom_py; file: + - + To make command-line build variables most useful, + you ideally want to provide + some help text that will describe + the available variables + when the user runs scons -h. + You could write this text by hand, + but &SCons; provides an easier way. + &Variables; objects support a + &GenerateHelpText; method + that will, as its name suggests, + generate text that describes + the various variables that + have been added to it. + You then pass the output from this method to + the &Help; function: - + - + + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars) + Help(vars.GenerateHelpText(env)) + + - Note that this file is actually executed - like a Python script. - Now when we run &SCons;: + - + &SCons; will now display some useful text + when the -h option is used: - - scons -Q - + - + + scons -Q -h + - And if we change the contents of &custom_py; to: + - + Notice that the help output shows the default value, + and the current actual value of the build variable. - - - 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)) - - - foo.c - - - bar.c - - - RELEASE = 0 - - + - +
- The object files are rebuilt appropriately - with the new option: +
+ Reading Build Variables From a File - + - - scons -Q - + Giving the user a way to specify the + value of a build variable on the command line + is useful, + but can still be tedious + if users must specify the variable + every time they run &SCons;. + We can let users provide customized build variable settings + in a local file by providing a + file name when we create the + &Variables; object: -
+
-
- Canned Build Options + + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + Help(vars.GenerateHelpText(env)) + + + foo.c + + + bar.c + + + RELEASE = 1 + + - + - &SCons; provides a number of functions - that provide ready-made behaviors - for various types of command-line build options. + This then allows the user to control the &RELEASE; + variable by setting it in the &custom_py; file: - + -
- 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;. + Note that this file is actually executed + like a Python script. + Now when we run &SCons;: + + scons -Q + + - 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: + And if we change the contents of &custom_py; to: - - - opts = Options('custom.py') - opts.Add(BoolOption('RELEASE', 'Set to build for release', 0)) - env = Environment(options = opts, + + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) - env.Program('foo.c') + env.Program(['foo.c', 'bar.c']) + Help(vars.GenerateHelpText(env)) foo.c + + bar.c + + + RELEASE = 0 + - With this build option, - the &RELEASE; variable can now be enabled by - setting it to the value yes - or t: + The object files are rebuilt appropriately + with the new variable: - - scons -Q RELEASE=yes foo.o + + scons -Q - - scons -Q RELEASE=t foo.o - - - - - Other values that equate to &true; include - y, - 1, - on - and - all. +
- +
+ Pre-Defined Build Variable Functions - Conversely, &RELEASE; may now be given a &false; - value by setting it to - no - or - f: + &SCons; provides a number of functions + that provide ready-made behaviors + for various types of command-line build variables. - - scons -Q RELEASE=no foo.o - +
+ True/False Values: the &BoolVariable; Build Variable Function - - scons -Q RELEASE=f foo.o - + - + It's often handy to be able to specify a + variable 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 &BoolVariable; function + makes it easy to accomodate these + common representations of + &true; or &false;. - Other values that equate to &false; include - n, - 0, - off - and - none. + - + - + The &BoolVariable; function takes three arguments: + the name of the build variable, + the default value of the build variable, + and the help string for the variable. + It then returns appropriate information for + passing to the &Add; method of a &Variables; object, like so: - Lastly, if a user tries to specify - any other value, - &SCons; supplies an appropriate error message: + - + + + vars = Variables('custom.py') + vars.Add(BoolVariable('RELEASE', 'Set to build for release', 0)) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program('foo.c') + + + foo.c + + + + + + With this build variable, + the &RELEASE; variable can now be enabled by + setting it to the value yes + or t: + + + + + scons -Q RELEASE=yes foo.o + + + + scons -Q RELEASE=t foo.o + + + + + Other values that equate to &true; include + y, + 1, + on + and + all. + + - - scons -Q RELEASE=bad_value foo.o - + + + Conversely, &RELEASE; may now be given a &false; + value by setting it to + no + or + f: + + + + + scons -Q RELEASE=no foo.o + + + + scons -Q RELEASE=f foo.o + + + + + 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 + + +
+ +
+ Single Value From a List: the &EnumVariable; Build Variable Function + + + + Suppose that we want a user to be able to + set a &COLOR; variable + 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 &EnumVariable;, + which takes a list of &allowed_values + in addition to the variable name, + default value, + and help text arguments: + + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'))) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + foo.c + + + + + + The user can now explicity set the &COLOR; build variable + to any of the specified allowed values: + + + + + scons -Q COLOR=red foo.o + scons -Q COLOR=blue foo.o + scons -Q COLOR=green foo.o + + + + + 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 + + + + + The &EnumVariable; 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: + + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'})) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + 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; + variable to build a target: + + + + + scons -Q COLOR=navy foo.o + + + + + By default, when using the &EnumVariable; function, + arguments that differ + from the legal values + only in case + are treated as illegal values: + + + + + scons -Q COLOR=Red foo.o + scons -Q COLOR=BLUE foo.o + scons -Q COLOR=nAvY foo.o + + + + + The &EnumVariable; function can take an additional + &ignorecase; keyword argument that, + when set to 1, + tells &SCons; to allow case differences + when the values are specified: + + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=1)) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + foo.c + + + + + + Which yields the output: + + + + + scons -Q COLOR=Red foo.o + scons -Q COLOR=BLUE foo.o + scons -Q COLOR=nAvY foo.o + scons -Q COLOR=green foo.o + + + + + 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: + + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=2)) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + 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 + scons -Q COLOR=nAvY foo.o + scons -Q COLOR=GREEN foo.o + + +
+ +
+ Multiple Values From a List: the &ListVariable; Build Variable Function + + + + Another way in which you might want to allow users + to control a build variable is to + specify a list of one or more legal values. + &SCons; supports this through the &ListVariable; function. + If, for example, we want a user to be able to set a + &COLORS; variable to one or more of the legal list of values: + + + + + + vars = Variables('custom.py') + vars.Add(ListVariable('COLORS', 'List of colors', 0, + ['red', 'green', 'blue'])) + env = Environment(variables = vars, + CPPDEFINES={'COLORS' : '"${COLORS}"'}) + env.Program('foo.c') + + + 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 + scons -Q COLORS=blue,green,red foo.o + + + + + In addition, the &ListVariable; 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 + scons -Q COLORS=none foo.o + + + + + And, of course, an illegal value + still generates an error message: + + + + + scons -Q COLORS=magenta foo.o + + +
+ +
+ Path Names: the &PathVariable; Build Variable Function + + + + &SCons; supports a &PathVariable; function + to make it easy to create a build variable + to control an expected path name. + If, for example, you need to + define a variable in the preprocessor + that controls the location of a + configuration file: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('CONFIG', + 'Path to configuration file', + '__ROOT__/etc/my_config')) + env = Environment(variables = vars, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + /opt/location + + + + + + This then allows the user to + override the &CONFIG; build variable + on the command line as necessary: + + + + + scons -Q foo.o + scons -Q CONFIG=__ROOT__/usr/local/etc/other_config foo.o + + + + + By default, &PathVariable; checks to make sure + that the specified path exists and generates an error if it + doesn't: + + + + + scons -Q CONFIG=__ROOT__/does/not/exist foo.o + + + + + &PathVariable; 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 &PathVariable_PathIsFile; method: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('CONFIG', + 'Path to configuration file', + '__ROOT__/etc/my_config', + PathVariable.PathIsFile)) + env = Environment(variables = vars, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + + + + Conversely, to ensure that any specified paths are + directories and not files, + use the &PathVariable_PathIsDir; method: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('DBDIR', + 'Path to database directory', + '__ROOT__/var/my_dbdir', + PathVariable.PathIsDir)) + env = Environment(variables = vars, + CPPDEFINES={'DBDIR' : '"$DBDIR"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + + + + 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 &PathVariable_PathIsDirCreate; method: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('DBDIR', + 'Path to database directory', + '__ROOT__/var/my_dbdir', + PathVariable.PathIsDirCreate)) + env = Environment(variables = vars, + CPPDEFINES={'DBDIR' : '"$DBDIR"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + + + + Lastly, if you don't care whether the path exists, + is a file, or a directory, + use the &PathVariable_PathAccept; method + to accept any path that the user supplies: + + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('OUTPUT', + 'Path to output file or directory', + None, + PathVariable.PathAccept)) + env = Environment(variables = vars, + CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) + env.Program('foo.c') + + + foo.c + + + +
+ +
+ Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function + + + + 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 &PackageVariable; + function to support this: + + + + + + vars = Variables('custom.py') + vars.Add(PackageVariable('PACKAGE', + 'Location package', + '__ROOT__/opt/location')) + env = Environment(variables = vars, + CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) + env.Program('foo.c') + + + foo.c + + + /opt/location + + + /opt/location + + + + + + When the &SConscript; file uses the &PackageVariable; 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 + scons -Q PACKAGE=__ROOT__/usr/local/location foo.o + scons -Q PACKAGE=yes foo.o + scons -Q PACKAGE=no foo.o + + +
- Single Value From a List: the &EnumOption; Build Option + Adding Multiple Command-Line Build Variables at Once - 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: + Lastly, &SCons; provides a way to add + multiple build variables to a &Variables; object at once. + Instead of having to call the &Add; method + multiple times, + you can call the &AddVariables; + method with a list of build variables + to be added to the object. + Each build variable 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 pre-defined + functions for pre-packaged command-line build variables. + in any order: - + - 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') - - - foo.c + vars = Variables() + vars.AddVariables( + ('RELEASE', 'Set to 1 to build for release', 0), + ('CONFIG', 'Configuration file', '/etc/my_config'), + BoolVariable('warnings', 'compilation with -Wall and similiar', 1), + EnumVariable('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=0), # case sensitive + ListVariable('shared', + 'libraries to build as shared libraries', + 'all', + names = list_of_libs), + PackageVariable('x11', + 'use X11 installed here (yes = search some places)', + 'yes'), + PathVariable('qtdir', 'where the root of Qt is installed', qtdir), + ) - - The user can now explicity set the &COLOR; build option - to any of the specified allowed values: - - - scons -Q COLOR=red foo.o - scons -Q COLOR=blue foo.o - scons -Q COLOR=green foo.o - +
+ +
+ Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function - But, almost more importantly, - an attempt to set &COLOR; - to a value that's not in the list - generates an error message: + Users may, of course, + occasionally misspell variable names in their command-line settings. + &SCons; does not generate an error or warning + for any unknown variables the users specifies on the command line. + (This is in no small part because you may be + processing the arguments directly using the &ARGUMENTS; dictionary, + and therefore &SCons; can't know in the general case + whether a given "misspelled" variable is + really unknown and a potential problem, + or something that your &SConscript; file + will handle directly with some Python code.) - - scons -Q COLOR=magenta foo.o - - - 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: + If, however, you're using a &Variables; object to + define a specific set of command-line build variables + that you expect users to be able to set, + you may want to provide an error + message or warning of your own + if the user supplies a variable setting + that is not among + the defined list of variable names known to the &Variables; object. + You can do this by calling the &UnknownVariables; + method of the &Variables; object: - + - 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}"'}) + vars = Variables(None) + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + unknown = vars.UnknownVariables() + if unknown: + print "Unknown variables:", unknown.keys() + Exit(1) env.Program('foo.c') @@ -1092,474 +1834,494 @@ - 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: + The &UnknownVariables; method returns a dictionary + containing the keywords and values + of any variables the user specified on the command line + that are not + among the variables known to the &Variables; object + (from having been specified using + the &Variables; object's&Add; method). + In the examble above, + we check for whether the dictionary + returned by the &UnknownVariables; is non-empty, + and if so print the Python list + containing the names of the unknwown variables + and then call the &Exit; function + to terminate &SCons;: - - scons -Q COLOR=navy foo.o + + scons -Q NOT_KNOWN=foo - By default, when using the &EnumOption; function, - arguments that differ - from the legal values - only in case - are treated as illegal values: + Of course, you can process the items in the + dictionary returned by the &UnknownVariables; function + in any way appropriate to your bulid configuration, + including just printing a warning message + but not exiting, + logging an error somewhere, + etc. - - scons -Q COLOR=Red foo.o - scons -Q COLOR=BLUE foo.o - scons -Q COLOR=nAvY foo.o - - - 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: + Note that you must delay the call of &UnknownVariables; + until after you have applied the &Variables; object + to a construction environment + with the variables= + keyword argument of an &Environment; call. - - - 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') - - - foo.c - - - - +
- Which yields the output: +
- +
+ Command-Line Targets - - scons -Q COLOR=Red foo.o - scons -Q COLOR=BLUE foo.o - scons -Q COLOR=nAvY foo.o - scons -Q COLOR=green foo.o - +
+ Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable - 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: + &SCons; supports a &COMMAND_LINE_TARGETS; variable + that lets you fetch 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: - + - 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') + if 'bar' in COMMAND_LINE_TARGETS: + print "Don't forget to copy `bar' to the archive!" + Default(Program('foo.c')) + Program('bar.c') foo.c + + foo.c + - Now &SCons; will use values of - red, - green or - blue - regardless of how the user spells - those values on the command line: + 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 COLOR=Red foo.o - scons -Q COLOR=nAvY foo.o - scons -Q COLOR=GREEN foo.o + + scons -Q + scons -Q bar + + + 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. + + +
- Multiple Values From a List: the &ListOption; Build Option + Controlling the Default Targets: the &Default; Function - 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: + 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: - - - 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') - - - foo.c - + + + env = Environment() + hello = env.Program('hello.c') + env.Program('goodbye.c') + Default(hello) + + + hello.c + + + goodbye.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: + This &SConstruct; file knows how to build two programs, + &hello; and &goodbye;, + but only builds the + &hello; program by default: - - scons -Q COLORS=red,blue foo.o - scons -Q COLORS=blue,green,red foo.o + + scons -Q + scons -Q + scons -Q goodbye - 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: + 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 COLORS=all foo.o - scons -Q COLORS=none foo.o + + scons -Q . - And, of course, an illegal value - still generates an error message: + 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: - - scons -Q COLORS=magenta foo.o - - -
- -
- Path Names: the &PathOption; Build Option + + + env = Environment() + prog1 = env.Program('prog1.c') + Default(prog1) + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog3) + + + prog1.c + + + prog2.c + + + prog3.c + + - &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 controls the location of a - configuration file: + Or you can specify more than one target + in a single call to the &Default; function: - - - opts = Options('custom.py') - opts.Add(PathOption('CONFIG', - 'Path to configuration file', - '__ROOT__/etc/my_config')) - env = Environment(options = opts, - CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) - env.Program('foo.c') - - - foo.c - - - /opt/location - - - /opt/location - - + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog1, prog3) + - This then allows the user to - override the &CONFIG; build option - on the command line as necessary: + Either of these last two examples + will build only the + prog1 + and + prog3 + programs by default: - - scons -Q foo.o - scons -Q CONFIG=__ROOT__/usr/local/etc/other_config foo.o + + scons -Q + scons -Q . - By default, &PathOption; checks to make sure - that the specified path exists and generates an error if it - doesn't: + You can list a directory as + an argument to &Default;: - - scons -Q CONFIG=__ROOT__/does/not/exist foo.o - + + + env = Environment() + env.Program(['prog1/main.c', 'prog1/foo.c']) + env.Program(['prog2/main.c', 'prog2/bar.c']) + Default('prog1') + + + + + int main() { printf("prog1/main.c\n"); } + + + int foo() { printf("prog1/foo.c\n"); } + + + int main() { printf("prog2/main.c\n"); } + + + int bar() { printf("prog2/bar.c\n"); } + + - &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: + In which case only the target(s) in that + directory will be built by default: - - - opts = Options('custom.py') - opts.Add(PathOption('CONFIG', - 'Path to configuration file', - '__ROOT__/etc/my_config', - PathOption.PathIsFile)) - env = Environment(options = opts, - CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) - env.Program('foo.c') - - - foo.c - - - /opt/location - - + + scons -Q + scons -Q + scons -Q . + - Conversely, to ensure that any specified paths are - directories and not files, - use the &PathOption_PathIsDir; method: + Lastly, if for some reason you don't want + any targets built by default, + you can use the Python None + variable: - - - opts = Options('custom.py') - opts.Add(PathOption('DBDIR', - 'Path to database directory', - '__ROOT__/var/my_dbdir', - PathOption.PathIsDir)) - env = Environment(options = opts, - CPPDEFINES={'DBDIR' : '"$DBDIR"'}) - env.Program('foo.c') - - - foo.c - - - /opt/location - + + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + Default(None) + + + prog1.c + + + prog2.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: + Which would produce build output like: - - - opts = Options('custom.py') - opts.Add(PathOption('DBDIR', - 'Path to database directory', - '__ROOT__/var/my_dbdir', - PathOption.PathIsDirCreate)) - env = Environment(options = opts, - CPPDEFINES={'DBDIR' : '"$DBDIR"'}) - env.Program('foo.c') - - - foo.c - - - /opt/location - - + + scons -Q + scons -Q . + + +
+ Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable + + + + &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) + + + prog1.c + + + + + + (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 + + + + + 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) + + + prog1.c + + + prog2.c + + + + + + Which yields the output: + + + + + scons + + + + + 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. + + + +
+ +
+ +
+ Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable - 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: + 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: - - - 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') - - - foo.c - - + + if COMMAND_LINE_TARGETS: + targets = COMMAND_LINE_TARGETS + else: + targets = DEFAULT_TARGETS + -
+ -
- Enabled/Disabled Path Names: the &PackageOption; Build Option + &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. + + - 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: + 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: - + - opts = Options('custom.py') - opts.Add(PackageOption('PACKAGE', - 'Location package', - '__ROOT__/opt/location')) - env = Environment(options = opts, - CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) - env.Program('foo.c') - - - foo.c + prog1 = Program('prog1.c') + Program('prog2.c') + Default(prog1) + print "BUILD_TARGETS is", map(str, BUILD_TARGETS) - - /opt/location + + prog1.c - - /opt/location + + prog2.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: + Notice how the value of &BUILD_TARGETS; + changes depending on whether a target is + specified on the command line: - - scons -Q foo.o - scons -Q PACKAGE=__ROOT__/usr/local/location foo.o - scons -Q PACKAGE=yes foo.o - scons -Q PACKAGE=no foo.o + + scons -Q + scons -Q prog2 + scons -Q -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 index 8675c191..175dd5ce 100644 --- a/doc/user/command-line.xml +++ b/doc/user/command-line.xml @@ -25,508 +25,633 @@ - &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. + &SCons; provides a number of ways + for the writer of the &SConscript; files + to give the users who will run &SCons; + a great deal of control over the build execution. + The arguments that the user can specify on + the command line are broken down into three types: -
- 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, - 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" - + + Options + - Windows users may typically want to set the - &SCONSFLAGS; in the appropriate tab of the - System Properties window. + Command-line options always begin with + one or two - (hyphen) characters. + &SCons; provides ways for you to examind + and set options values from within your &SConscript; files, + as well as the ability to define your own + custom options. + See , below. + + -
- -
- Getting at Command-Line Targets + + Variables + - &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: + Any command-line argument containing an = + (equal sign) is considered a variable setting with the form + variable=value + &SCons; provides direct access to + all of the command-line variable settings, + the ability to apply command-line variable settings + to construction environments, + and functions for configuring + specific types of variables + (Boolean values, path names, etc.) + with automatic validation of the user's specified values. + See , below. + + - - % 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 - + + Targets + - 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. + Any command-line argument that is not an option + or a variable setting + (does not begin with a hyphen + and does not contain an equal sign) + is considered a target that the user + (presumably) wants &SCons; to build. + A list of Node objects representing + the target or targets to build. + &SCons; provides access to the list of specified targets, + as well as ways to set the default list of targets + from within the &SConscript; files. + See , below. + + -
+ -
- Controlling the Default Targets +
+ Command-Line Options - 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: + &SCons; has many command-line options + that control its behavior. + A &SCons; command-line option + always begins with one or two - (hyphen) + characters. - - 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: +
+ Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable - + - - % 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 - + Users may find themselves supplying + the same command-line options every time + they run &SCons;. + For example, you might find it saves time + to specify a value of -j 2 + to have &SCons; run up to two build commands 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. - + - 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: + - + If, for example, + 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 -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: + + % 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] ... + - + - - env = Environment() - prog1 = env.Program('prog1.c') - Default(prog1) - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog3) - + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: - + - Or you can specify more than one target - in a single call to the &Default; function: + + $ setenv SCONSFLAGS "-Q" + - + - - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog1, prog3) - + Windows users may typically want to set the + &SCONSFLAGS; in the appropriate tab of the + System Properties window. - + - Either of these last two examples - will build only the - prog1 - and - prog3 - programs by default: +
-
+
+ Getting Values Set by Command-Line Options: the &GetOption; Function - - % 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 - + - + &SCons; provides the &GetOption; function + to get the values set by the various command-line options. + One common use of this is to check whether or not + the -h or --help option + has been specified. + Normally, &SCons; does not print its help text + until after it has read all of the &SConscript; files, + because it's possible that help text has been added + by some subsidiary &SConscript; file deep in the + source tree hierarchy. + Of course, reading all of the &SConscript; files + takes extra time. - 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') - + If you know that your configuration does not define + any additional help text in subsidiary &SConscript; files, + you can speed up the command-line help available to users + by using the &GetOption; function to load the + subsidiary &SConscript; files only if the + the user has not specified + the -h or --help option, + like so: - + - 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 - + In general, the string that you pass to the + &GetOption; function to fetch the value of a command-line + option setting is the same as the "most common" long option name + (beginning with two hyphen characters), + although there are some exceptions. + The list of &SCons; command-line options + and the &GetOption; strings for fetching them, + are available in the + section, + below. - + - Lastly, if for some reason you don't want - any targets built by default, - you can use the Python None - variable: +
- +
+ Setting Values of Command-Line Options: the &SetOption; Function - - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - Default(None) - + - + You can also set the values of &SCons; + command-line options from within the &SConscript; files + by using the &SetOption; function. + The strings that you use to set the values of &SCons; + command-line options are available in the + section, + below. - 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 - + One use of the &SetOption; function is to + specify a value for the -j + or --jobs option, + so that users get the improved performance + of a parallel build without having to specify the option by hand. + A complicating factor is that a good value + for the -j option is + somewhat system-dependent. + One rough guideline is that the more processors + your system has, + the higher you want to set the + -j value, + in order to take advantage of the number of CPUs. -
- 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: + For example, suppose the administrators + of your development systems + have standardized on setting a + NUM_CPU environment variable + to the number of processors on each system. + A little bit of Python code + to access the environment variable + and the &SetOption; function + provide the right level of flexibility: - prog1 = Program('prog1.c') - Default(prog1) - print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) + import os + num_cpu = int(os.environ.get('NUM_CPU', 2)) + SetOption('num_jobs', num_cpu) + print "running with -j", GetOption('num_jobs') - (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;:) + The above snippet of code + sets the value of the --jobs option + to the value specified in the + $NUM_CPU environment variable. + (This is one of the exception cases + where the string is spelled differently from + the from command-line option. + The string for fetching or setting the --jobs + value is num_jobs + for historical reasons.) + The code in this example prints the num_jobs + value for illustrative purposes. + It uses a default value of 2 + to provide some minimal parallelism even on + single-processor systems: - % 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. + % scons -Q + running with -j 2 + scons: `.' is up to date. - 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: + But if the $NUM_CPU + environment variable is set, + then we use that for the default number of jobs: - - 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) - + + % export NUM_CPU="4" + % scons -Q + running with -j 4 + scons: `.' is up to date. + - Which yields the output: + But any explicit + -j or --jobs + value the user specifies an the command line is used first, + regardless of whether or not + the $NUM_CPU environment + variable is set: - % 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. + % scons -Q -j 7 + running with -j 7 + scons: `.' is up to date. + % export NUM_CPU="4" + % scons -Q -j 3 + running with -j 3 + scons: `.' is up to date. +
+ +
+ Strings for Getting or Setting Values of &SCons; Command-Line Options + - 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. + The strings that you can pass to the &GetOption; + and &SetOption; functions usually correspond to the + first long-form option name + (beginning with two hyphen characters: --), + after replacing any remaining hyphen characters + with underscores. + + + The full list of strings and the variables they + correspond to is as follows: + + + + + + + + + + String for &GetOption; and &SetOption; + Command-Line Option(s) + + + + + + + + cache_debug + + + + + cache_disable + + + + + cache_force + + + + + cache_show + + + + + clean + , + , + + + + + config + + + + + directory + , + + + + + diskcheck + + + + + duplicate + + + + + file + , + , + , + + + + + help + , + + + + + ignore_errors + + + + + implicit_cache + + + + + implicit_deps_changed + + + + + implicit_deps_unchanged + + + + + interactive + , + + + + + keep_going + , + + + + + max_drift + + + + + no_exec + , + , + , + , + + + + + no_site_dir + + + + + num_jobs + , + + + + + profile_file + + + + + question + , + + + + + random + + + + + repository + , + , + + + + + silent + , + , + + + + + site_dir + + + + + stack_size + + + + + taskmastertrace_file + + + + + warn + + + + + + + +
-
+
+ Adding Custom Command-Line Options: the &AddOption; Function -
- Getting at the List of Build Targets, Regardless of Origin + - + &SCons; also allows you to define your own + command-line options with the &AddOption; function. + The &AddOption; function takes the same arguments + as the optparse.add_option function + from the standard Python library. + + + The &AddOption; function is, + in fact, implemented using a subclass + of the optparse.OptionParser. + + + Once you have added a custom command-line option + with the &AddOption; function, + the value of the option (if any) is immediately available + using the standard &GetOption; function. + (The value can also be set using &SetOption;, + although that's not very useful in practice + because a default value can be specified in + directly in the &AddOption; call.) - 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 - + One useful example of using this functionality + is to provide a for users: - + - &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. + + AddOption('--prefix', + dest='prefix', + type='string', + nargs=1, + action='store', + metavar='DIR', + help='installation prefix') + + env = Environment(PREFIX = GetOption('prefix')) + + installed_foo = env.Install('$PREFIX/usr/bin', 'foo.in') + Default(installed_foo) + - + - + The above code uses the &GetOption; function + to set the $PREFIX + construction variable to any + value that the user specifies with a command-line + option of --prefix. + Because $PREFIX + will expand to a null string if it's not initialized, + running &SCons; without the + option of --prefix + will install the file in the + /usr/bin/ directory: - 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: + - + + % scons -Q -n + Install file: "foo.in" as "/usr/bin/foo.in" + - - prog1 = Program('prog1.c') - Program('prog2.c') - Default(prog1) - print "BUILD_TARGETS is", map(str, BUILD_TARGETS) - + - + But specifying --prefix=/tmp/install + on the command line causes the file to be installed in the + /tmp/install/usr/bin/ directory: - Notice how the value of &BUILD_TARGETS; - changes depending on whether a target is - specified on the command line: + - + + % scons -Q -n --prefix=/tmp/install + Install file: "foo.in" as "/tmp/install/usr/bin/foo.in" + - - % 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 +
+ Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables @@ -556,7 +681,7 @@ to specifications on the command line. (Note that unless you want to require that users always - specify an option, + specify a variable, you probably want to use the Python ARGUMENTS.get() function, @@ -615,878 +740,1505 @@ -
- -
- 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. + The &ARGUMENTS; dictionary has two minor drawbacks. + First, because it is a dictionary, + it can only store one value for each specified keyword, + and thus only "remembers" the last setting + for each keyword on the command line. + This makes the &ARGUMENTS; dictionary + inappropriate if users should be able to + specify multiple values + on the command line for a given keyword. + Second, it does not preserve + the order in which the variable settings + were specified, + which is a problem if + you want the configuration to + behave differently in response + to the order in which the build + variable settings were specified on the command line. - 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: + To accomodate these requirements, + &SCons; provides an &ARGLIST; variable + that gives you direct access to + variable=value + settings on the command line, + in the exact order they were specified, + and without removing any duplicate settings. + Each element in the &ARGLIST; variable + is itself a two-element list + containing the keyword and the value + of the setting, + and you must loop through, + or otherwise select from, + the elements of &ARGLIST; to + process the specific settings you want + in whatever way is appropriate for your configuration. + For example, + the following code to let the user + add to the &CPPDEFINES; construction variable + by specifying multiple + define= + settings on the command line: - 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']) + cppdefines = [] + for key, value in ARGLIST: + if key == 'define': + cppdefines.append(value) + env = Environment(CPPDEFINES = cppdefines) + env.Object('prog.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: + Yields the followig output: - % 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 + % scons -Q define=FOO + cc -o prog.o -c -DFOO prog.c + % scons -Q define=FOO define=BAR + cc -o prog.o -c -DFOO -DBAR prog.c -
- -
- 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: + Note that the &ARGLIST; and &ARGUMENTS; + variables do not interfere with each other, + but merely provide slightly different views + into how the user specified + variable=value + settings on the command line. + You can use both variables in the same + &SCons; configuration. + In general, the &ARGUMENTS; dictionary + is more convenient to use, + (since you can just fetch variable + settings through a dictionary access), + and the &ARGLIST; list + is more flexible + (since you can examine the + specific order in which + the user's command-line variabe settings). - - 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: - - +
+ Controlling Command-Line Build Variables - - % scons -Q -h - - RELEASE: Set to 1 to build for release - default: 0 - actual: 0 - - Use scons -H for help about command-line options. - + - + Being able to use a command-line build variable like + debug=1 is handy, + but it can be a chore to write specific Python code + to recognize each such variable, + check for errors and provide appropriate messages, + and apply the values to a construction variable. + To help with this, + &SCons; supports a class to + define such build variables easily, + and a mechanism to apply the + build variables to a construction environment. + This allows you to control how the build variables affect + construction environments. - Notice that the help output shows the default value, - and the current actual value of the build option. + - + -
+ 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: -
- Reading Build Options From a File + - + + vars = Variables() + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + - 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: + - + This &SConstruct; file first creates a &Variables; object + (the vars = Variables() call), + and then uses the object's &Add; + method to indicate that the &RELEASE; + variable can be set on the command line, + and that its 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. - - 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: + We then pass the created &Variables; + object as a &variables; keyword argument + to the &Environment; call + used to create the construction environment. + This then allows a user to set the + &RELEASE; build variable on the command line + and have the variable show up in + the command line used to build each object from + a C source file: - + - - RELEASE = 1 - + + % 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 + - + - Note that this file is actually executed - like a Python script. - Now when we run &SCons;: + NOTE: Before &SCons; release 0.98.1, these build variables + were known as "command-line build options." + The class was actually named the &Options; class, + and in the sections below, + the various functions were named + &BoolOption;, &EnumOption;, &ListOption;, + &PathOption;, &PackageOption; and &AddOptions;. + These older names still work, + and you may encounter them in older + &SConscript; fles, + but their use is discouraged + and will be officially deprecated some day. - + - - % 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 - +
- +
+ Providing Help for Command-Line Build Variables - And if we change the contents of &custom_py; to: + - + To make command-line build variables most useful, + you ideally want to provide + some help text that will describe + the available variables + when the user runs scons -h. + You could write this text by hand, + but &SCons; provides an easier way. + &Variables; objects support a + &GenerateHelpText; method + that will, as its name suggests, + generate text that describes + the various variables that + have been added to it. + You then pass the output from this method to + the &Help; function: - - RELEASE = 0 - + - + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars) + Help(vars.GenerateHelpText(env)) + - The object files are rebuilt appropriately - with the new option: + - + &SCons; will now display some useful text + when the -h option is used: - - % 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 - + -
+ + % scons -Q -h + + RELEASE: Set to 1 to build for release + default: 0 + actual: 0 + + Use scons -H for help about command-line options. + -
- Canned Build Options + - + Notice that the help output shows the default value, + and the current actual value of the build variable. - &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 + Reading Build Variables From a File - 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;. + Giving the user a way to specify the + value of a build variable on the command line + is useful, + but can still be tedious + if users must specify the variable + every time they run &SCons;. + We can let users provide customized build variable settings + in a local file by providing a + file name when we create the + &Variables; object: + + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program(['foo.c', 'bar.c']) + Help(vars.GenerateHelpText(env)) + + - 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: + This then allows the user to control the &RELEASE; + variable by setting it in the &custom_py; file: - 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') - + RELEASE = 1 + - With this build option, - the &RELEASE; variable can now be enabled by - setting it to the value yes - or t: + Note that this file is actually executed + like a Python script. + Now when we run &SCons;: - % 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 + % 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 - Other values that equate to &true; include - y, - 1, - on - and - all. + And if we change the contents of &custom_py; to: + + RELEASE = 0 + + - Conversely, &RELEASE; may now be given a &false; - value by setting it to - no - or - f: + The object files are rebuilt appropriately + with the new variable: - % scons -Q RELEASE=no foo.o - cc -o foo.o -c -DRELEASE_BUILD=False foo.c + % 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 - - % scons -Q RELEASE=f foo.o - cc -o foo.o -c -DRELEASE_BUILD=False foo.c - +
+ +
+ Pre-Defined Build Variable Functions - Other values that equate to &false; include - n, - 0, - off - and - none. + &SCons; provides a number of functions + that provide ready-made behaviors + for various types of command-line build variables. - +
+ True/False Values: the &BoolVariable; Build Variable Function - Lastly, if a user tries to specify - any other value, - &SCons; supplies an appropriate error message: + - + It's often handy to be able to specify a + variable 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 &BoolVariable; function + makes it easy to accomodate these + common representations of + &true; or &false;. - - % 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> - + + + + + The &BoolVariable; function takes three arguments: + the name of the build variable, + the default value of the build variable, + and the help string for the variable. + It then returns appropriate information for + passing to the &Add; method of a &Variables; object, like so: + + + + + vars = Variables('custom.py') + vars.Add(BoolVariable('RELEASE', 'Set to build for release', 0)) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + env.Program('foo.c') + + + + + With this build variable, + 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 &EnumVariable; Build Variable Function + + + + Suppose that we want a user to be able to + set a &COLOR; variable + 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 &EnumVariable;, + which takes a list of &allowed_values + in addition to the variable name, + default value, + and help text arguments: + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'))) + env = Environment(variables = vars, + CPPDEFINES={'COLOR' : '"${COLOR}"'}) + env.Program('foo.c') + + + + + The user can now explicity set the &COLOR; build variable + 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 &EnumVariable; 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: + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'})) + env = Environment(variables = vars, + 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; + variable to build a target: + + + + + % scons -Q COLOR=navy foo.o + cc -o foo.o -c -DCOLOR="blue" foo.c + + + + + By default, when using the &EnumVariable; 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 &EnumVariable; function can take an additional + &ignorecase; keyword argument that, + when set to 1, + tells &SCons; to allow case differences + when the values are specified: + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=1)) + env = Environment(variables = vars, + 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: + + + + + vars = Variables('custom.py') + vars.Add(EnumVariable('COLOR', 'Set background color', 'red', + allowed_values=('red', 'green', 'blue'), + map={'navy':'blue'}, + ignorecase=2)) + env = Environment(variables = vars, + 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 &ListVariable; Build Variable Function + + + + Another way in which you might want to allow users + to control a build variable is to + specify a list of one or more legal values. + &SCons; supports this through the &ListVariable; function. + If, for example, we want a user to be able to set a + &COLORS; variable to one or more of the legal list of values: + + + + + vars = Variables('custom.py') + vars.Add(ListVariable('COLORS', 'List of colors', 0, + ['red', 'green', 'blue'])) + env = Environment(variables = vars, + 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 &ListVariable; 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 &PathVariable; Build Variable Function + + + + &SCons; supports a &PathVariable; function + to make it easy to create a build variable + to control an expected path name. + If, for example, you need to + define a variable in the preprocessor + that controls the location of a + configuration file: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('CONFIG', + 'Path to configuration file', + '/etc/my_config')) + env = Environment(variables = vars, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + + + This then allows the user to + override the &CONFIG; build variable + 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, &PathVariable; 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> + + + + + &PathVariable; 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 &PathVariable_PathIsFile; method: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('CONFIG', + 'Path to configuration file', + '/etc/my_config', + PathVariable.PathIsFile)) + env = Environment(variables = vars, + CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) + env.Program('foo.c') + + + + + Conversely, to ensure that any specified paths are + directories and not files, + use the &PathVariable_PathIsDir; method: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('DBDIR', + 'Path to database directory', + '/var/my_dbdir', + PathVariable.PathIsDir)) + env = Environment(variables = vars, + 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 &PathVariable_PathIsDirCreate; method: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('DBDIR', + 'Path to database directory', + '/var/my_dbdir', + PathVariable.PathIsDirCreate)) + env = Environment(variables = vars, + 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 &PathVariable_PathAccept; method + to accept any path that the user supplies: + + + + + vars = Variables('custom.py') + vars.Add(PathVariable('OUTPUT', + 'Path to output file or directory', + None, + PathVariable.PathAccept)) + env = Environment(variables = vars, + CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) + env.Program('foo.c') + + +
+ +
+ Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function + + + + 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 &PackageVariable; + function to support this: + + + + + vars = Variables('custom.py') + vars.Add(PackageVariable('PACKAGE', + 'Location package', + '/opt/location')) + env = Environment(variables = vars, + CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) + env.Program('foo.c') + + + + + When the &SConscript; file uses the &PackageVariable; 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 + + +
- Single Value From a List: the &EnumOption; Build Option + Adding Multiple Command-Line Build Variables at Once - 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: + Lastly, &SCons; provides a way to add + multiple build variables to a &Variables; object at once. + Instead of having to call the &Add; method + multiple times, + you can call the &AddVariables; + method with a list of build variables + to be added to the object. + Each build variable 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 pre-defined + functions for pre-packaged command-line build variables. + in any order: - 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') + vars = Variables() + vars.AddVariables( + ('RELEASE', 'Set to 1 to build for release', 0), + ('CONFIG', 'Configuration file', '/etc/my_config'), + BoolVariable('warnings', 'compilation with -Wall and similiar', 1), + EnumVariable('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=0), # case sensitive + ListVariable('shared', + 'libraries to build as shared libraries', + 'all', + names = list_of_libs), + PackageVariable('x11', + 'use X11 installed here (yes = search some places)', + 'yes'), + PathVariable('qtdir', 'where the root of Qt is installed', qtdir), + ) - - 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 - +
+ +
+ Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function - But, almost more importantly, - an attempt to set &COLOR; - to a value that's not in the list - generates an error message: + Users may, of course, + occasionally misspell variable names in their command-line settings. + &SCons; does not generate an error or warning + for any unknown variables the users specifies on the command line. + (This is in no small part because you may be + processing the arguments directly using the &ARGUMENTS; dictionary, + and therefore &SCons; can't know in the general case + whether a given "misspelled" variable is + really unknown and a potential problem, + or something that your &SConscript; file + will handle directly with some Python code.) - - % 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: + If, however, you're using a &Variables; object to + define a specific set of command-line build variables + that you expect users to be able to set, + you may want to provide an error + message or warning of your own + if the user supplies a variable setting + that is not among + the defined list of variable names known to the &Variables; object. + You can do this by calling the &UnknownVariables; + method of the &Variables; object: - 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}"'}) + vars = Variables(None) + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars, + CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) + unknown = vars.UnknownVariables() + if unknown: + print "Unknown variables:", unknown.keys() + Exit(1) 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: + The &UnknownVariables; method returns a dictionary + containing the keywords and values + of any variables the user specified on the command line + that are not + among the variables known to the &Variables; object + (from having been specified using + the &Variables; object's&Add; method). + In the examble above, + we check for whether the dictionary + returned by the &UnknownVariables; is non-empty, + and if so print the Python list + containing the names of the unknwown variables + and then call the &Exit; function + to terminate &SCons;: - % scons -Q COLOR=navy foo.o - cc -o foo.o -c -DCOLOR="blue" foo.c + % scons -Q NOT_KNOWN=foo + Unknown variables: ['NOT_KNOWN'] - By default, when using the &EnumOption; function, - arguments that differ - from the legal values - only in case - are treated as illegal values: + Of course, you can process the items in the + dictionary returned by the &UnknownVariables; function + in any way appropriate to your bulid configuration, + including just printing a warning message + but not exiting, + logging an error somewhere, + etc. - - % 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: + Note that you must delay the call of &UnknownVariables; + until after you have applied the &Variables; object + to a construction environment + with the variables= + keyword argument of an &Environment; call. - - 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: +
- +
+ Command-Line Targets - - % 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 - +
+ Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable - 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: + &SCons; supports a &COMMAND_LINE_TARGETS; variable + that lets you fetch 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: - 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') + if 'bar' in COMMAND_LINE_TARGETS: + print "Don't forget to copy `bar' to the archive!" + Default(Program('foo.c')) + Program('bar.c') - Now &SCons; will use values of - red, - green or - blue - regardless of how the user spells - those values on the command line: + 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 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 + % 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 -
- -
- 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: + 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. - - 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') - +
+ +
+ Controlling the Default Targets: the &Default; Function - 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: + 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: - - % 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 - + + env = Environment() + hello = env.Program('hello.c') + env.Program('goodbye.c') + Default(hello) + - 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: + This &SConstruct; file knows how to build two programs, + &hello; and &goodbye;, + but only builds the + &hello; program by default: - % 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 + % 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 - And, of course, an illegal value - still generates an error message: + 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 COLORS=magenta foo.o - - scons: *** Error converting option: COLORS - Invalid value(s) for option: magenta - File "/home/my/project/SConstruct", line 5, in <module> + % 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 -
- -
- 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 controls the location of a - configuration file: + 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: - 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') + env = Environment() + prog1 = env.Program('prog1.c') + Default(prog1) + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog3) - This then allows the user to - override the &CONFIG; build option - on the command line as necessary: + Or you can specify more than one target + in a single call to the &Default; function: - - % 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. - + + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog1, prog3) + - By default, &PathOption; checks to make sure - that the specified path exists and generates an error if it - doesn't: + Either of these last two examples + will build only the + prog1 + and + prog3 + programs by default: - % 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> + % 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 - &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: + You can list a directory as + an argument to &Default;: - 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') + env = Environment() + env.Program(['prog1/main.c', 'prog1/foo.c']) + env.Program(['prog2/main.c', 'prog2/bar.c']) + Default('prog1') - Conversely, to ensure that any specified paths are - directories and not files, - use the &PathOption_PathIsDir; method: + In which case only the target(s) in that + directory will be built by default: - - 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') - + + % 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 + - 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: + Lastly, if for some reason you don't want + any targets built by default, + you can use the Python None + variable: - 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') + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + Default(None) - 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: + Which would produce build output like: - - 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') - + + % 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 + + +
+ Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable + + + + &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. + + + +
- Enabled/Disabled Path Names: the &PackageOption; Build Option + Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable - 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: + 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: - opts = Options('custom.py') - opts.Add(PackageOption('PACKAGE', - 'Location package', - '/opt/location')) - env = Environment(options = opts, - CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) - env.Program('foo.c') + if COMMAND_LINE_TARGETS: + targets = COMMAND_LINE_TARGETS + else: + targets = DEFAULT_TARGETS - 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;, 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. - - % 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 - + -
+ 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: -
+ -
- Adding Multiple Command-Line Build Options at Once + + prog1 = Program('prog1.c') + Program('prog2.c') + Default(prog1) + print "BUILD_TARGETS is", map(str, BUILD_TARGETS) + - + - 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: + Notice how the value of &BUILD_TARGETS; + changes depending on whether a target is + specified on the command line: - + - - 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), - ) - + + % 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 + - - +
- - diff --git a/doc/user/depends.in b/doc/user/depends.in index 7c15c522..e861cbe3 100644 --- a/doc/user/depends.in +++ b/doc/user/depends.in @@ -391,6 +391,13 @@ + + + + % 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. + % edit hello.c + [CHANGE THE CONTENTS OF hello.c] + % scons -Q hello + cc -o hello.o -c hello.c + cc -o hello hello.o + + However, the second call to &SCons; in the above output, @@ -489,21 +512,75 @@ Note that in the function definition, - the dependency + the dependency (input file) is the first argument, - and then the target. + and then the ⌖. Both of these are passed to the functions as SCons &Node; objects, which we convert to strings using the Python str(). - The third argument, prev_ni, + + + + + + The third argument, prev_ni, is an object that holds the signature or timestamp information that was recorded about the dependency the last time the target was built. + A prev_ni object can hold + different information, + depending on the type of thing that the + dependency argument represents. + For normal files, + the prev_ni object + has the following attributes: + + + + .csig + + + + The content signature, + or MD5 checksum, of the contents of the + dependency + file the list time the ⌖ was built. + + + + + + + .size + + + + The size in bytes of the dependency + file the list time the target was built. + + + + + + + .timestamp + + + + The modification time of the dependency + file the list time the ⌖ was built. + + + + + + + Note that ignoring some of the arguments @@ -1157,6 +1234,36 @@ cc -o hello hello.o + + + Note that the dependency + (the second argument to &Depends;) + may also be a list of Node objects + (for example, as returned by a call to a Builder): + + + + + hello = Program('hello.c') + goodbye = Program('goodbye.c') + Depends(hello, goodbye) + + + + + in which case the dependency or dependencies + will be built before the target(s): + + + + + % scons -Q hello + cc -c goodbye.c -o goodbye.o + cc -o goodbye goodbye.o + cc -c hello.c -o hello.o + cc -o hello hello.o + +
@@ -1187,7 +1294,7 @@ - + + + + % scons -Q + gcc -o hello.o -c hello.c + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % scons -Q + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % scons -Q + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + + + + + One solution is to use the &Requires; function + to specify that the version.o + must be rebuilt before it is used by the link step, + but that changes to version.o + should not actually cause the hello + executable to be re-linked: + + + + + + import time + + version_c_text = """ + char *date = "%s"; + """ % time.ctime(time.time()) + open('version.c', 'w').write(version_c_text) + + version_obj = Object('version.c') + + hello = Program('hello.c', + LINKFLAGS = str(version_obj[0])) + + Requires(hello, version_obj) + + + extern char *date; + int main() { printf("Hello, %s! I was built: %s\n", date); } + + + + + + Notice that because we can no longer list version.c + as one of the sources for the hello program, + we have to find some other way to get it into the link command line. + For this example, we're cheating a bit and stuffing the + object file name (extracted from version_obj + list returned by the &b-Object; call) + into the &cv-link-LINKFLAGS; variable, + because &cv-LINKFLAGS; is already included + in the &cv-link-LINKCOM; command line. + + + + + + With these changes, + we get the desired behavior of + re-building the version.o file, + and therefore re-linking the hello executable, + only when the hello.c has changed: + + + + + scons -Q + scons -Q + edit hello.c + scons -Q + scons -Q + + +
+
The &AlwaysBuild; Function diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 936e4b2a..b19b4b29 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -396,6 +396,23 @@ + + % scons -Q hello cc -o hello.o -c hello.c @@ -406,7 +423,8 @@ % edit hello.c [CHANGE THE CONTENTS OF hello.c] % scons -Q hello - scons: `hello' is up to date. + cc -o hello.o -c hello.c + cc -o hello hello.o @@ -494,21 +512,75 @@ Note that in the function definition, - the dependency + the dependency (input file) is the first argument, - and then the target. + and then the ⌖. Both of these are passed to the functions as SCons &Node; objects, which we convert to strings using the Python str(). - The third argument, prev_ni, + + + + + + The third argument, prev_ni, is an object that holds the signature or timestamp information that was recorded about the dependency the last time the target was built. + A prev_ni object can hold + different information, + depending on the type of thing that the + dependency argument represents. + For normal files, + the prev_ni object + has the following attributes: + + + + .csig + + + + The content signature, + or MD5 checksum, of the contents of the + dependency + file the list time the ⌖ was built. + + + + + + + .size + + + + The size in bytes of the dependency + file the list time the target was built. + + + + + + + .timestamp + + + + The modification time of the dependency + file the list time the ⌖ was built. + + + + + + + Note that ignoring some of the arguments @@ -1165,6 +1237,36 @@ cc -o hello hello.o + + + Note that the dependency + (the second argument to &Depends;) + may also be a list of Node objects + (for example, as returned by a call to a Builder): + + + + + hello = Program('hello.c') + goodbye = Program('goodbye.c') + Depends(hello, goodbye) + + + + + in which case the dependency or dependencies + will be built before the target(s): + + + + + % scons -Q hello + cc -c goodbye.c -o goodbye.o + cc -o goodbye goodbye.o + cc -c hello.c -o hello.o + cc -o hello hello.o + +
@@ -1186,7 +1288,7 @@ Ignore(hello, 'hello.h') - + + + + % scons -Q + gcc -o hello.o -c hello.c + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % scons -Q + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % scons -Q + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + + + + + One solution is to use the &Requires; function + to specify that the version.o + must be rebuilt before it is used by the link step, + but that changes to version.o + should not actually cause the hello + executable to be re-linked: + + + + + import time + + version_c_text = """ + char *date = "%s"; + """ % time.ctime(time.time()) + open('version.c', 'w').write(version_c_text) + + version_obj = Object('version.c') + + hello = Program('hello.c', + LINKFLAGS = str(version_obj[0])) + + Requires(hello, version_obj) + + + + + Notice that because we can no longer list version.c + as one of the sources for the hello program, + we have to find some other way to get it into the link command line. + For this example, we're cheating a bit and stuffing the + object file name (extracted from version_obj + list returned by the &b-Object; call) + into the &cv-link-LINKFLAGS; variable, + because &cv-LINKFLAGS; is already included + in the &cv-link-LINKCOM; command line. + + + + + + With these changes, + we get the desired behavior of + re-building the version.o file, + and therefore re-linking the hello executable, + only when the hello.c has changed: + + + + + % scons -Q + cc -o hello.o -c hello.c + cc -o version.o -c version.c + cc -o hello version.o hello.o + % scons -Q + scons: `.' is up to date. + % edit hello.c + [CHANGE THE CONTENTS OF hello.c] + % scons -Q + cc -o hello.o -c hello.c + cc -o hello version.o hello.o + % scons -Q + scons: `.' is up to date. + + +
+
The &AlwaysBuild; Function diff --git a/doc/user/environments.in b/doc/user/environments.in index 3fcec02f..1181e55d 100644 --- a/doc/user/environments.in +++ b/doc/user/environments.in @@ -1,25 +1,25 @@ @@ -38,120 +38,120 @@ build behavior. Construction variables from a construction environment are expanded by preceding the keyword with a C<%> (percent sign): - Construction variables: + Construction variables: XYZZY => 'abracadabra', - The string: "The magic word is: %XYZZY!" - expands to: "The magic word is: abracadabra!" + The string: "The magic word is: %XYZZY!" + expands to: "The magic word is: abracadabra!" A construction variable name may be surrounded by C<{> and C<}> (curly braces), which are stripped as part of the expansion. This can sometimes be necessary to separate a variable expansion from trailing alphanumeric characters: - Construction variables: + Construction variables: OPT => 'value1', OPTION => 'value2', - The string: "%OPT %{OPT}ION %OPTION %{OPTION}" - expands to: "value1 value1ION value2 value2" + The string: "%OPT %{OPT}ION %OPTION %{OPTION}" + expands to: "value1 value1ION value2 value2" Construction variable expansion is recursive, that is, a string containing C<%->expansions after substitution will be re-expanded until no further substitutions can be made: - Construction variables: + Construction variables: STRING => 'The result is: %FOO', FOO => '%BAR', BAR => 'final value', - The string: "The string says: %STRING" - expands to: "The string says: The result is: final value" + The string: "The string says: %STRING" + expands to: "The string says: The result is: final value" If a construction variable is not defined in an environment, then the null string is substituted: - Construction variables: + Construction variables: FOO => 'value1', BAR => 'value2', - The string: "%FOO <%NO_VARIABLE> %BAR" - expands to: "value1 <> value2" + The string: "%FOO <%NO_VARIABLE> %BAR" + expands to: "value1 <> value2" A doubled C<%%> will be replaced by a single C<%>: - The string: "Here is a percent sign: %%" - expands to: "Here is a percent sign: %" + The string: "Here is a percent sign: %%" + expands to: "Here is a percent sign: %" =head2 Default construction variables When you specify no arguments when creating a new construction environment: - $env = new cons(); + $env = new cons(); Cons creates a reference to a new, default construction environment. This contains a number of construction variables and some methods. At the present writing, the default construction variables on a UNIX system are: - CC => 'cc', - CFLAGS => '', - CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>', - CXX => '%CC', - CXXFLAGS => '%CFLAGS', - CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>', - INCDIRPREFIX => '-I', - INCDIRSUFFIX => '', - LINK => '%CXX', - LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS', - LINKMODULECOM => '%LD -r -o %> %<', - LIBDIRPREFIX => '-L', - LIBDIRSUFFIX => '', - AR => 'ar', - ARFLAGS => 'r', - ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'], - RANLIB => 'ranlib', - AS => 'as', - ASFLAGS => '', - ASCOM => '%AS %ASFLAGS %< -o %>', - LD => 'ld', - LDFLAGS => '', - PREFLIB => 'lib', - SUFLIB => '.a', - SUFLIBS => '.so:.a', - SUFOBJ => '.o', - SIGNATURE => [ '*' => 'build' ], - ENV => { 'PATH' => '/bin:/usr/bin' }, + CC => 'cc', + CFLAGS => '', + CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>', + CXX => '%CC', + CXXFLAGS => '%CFLAGS', + CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>', + INCDIRPREFIX => '-I', + INCDIRSUFFIX => '', + LINK => '%CXX', + LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS', + LINKMODULECOM => '%LD -r -o %> %<', + LIBDIRPREFIX => '-L', + LIBDIRSUFFIX => '', + AR => 'ar', + ARFLAGS => 'r', + ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'], + RANLIB => 'ranlib', + AS => 'as', + ASFLAGS => '', + ASCOM => '%AS %ASFLAGS %< -o %>', + LD => 'ld', + LDFLAGS => '', + PREFLIB => 'lib', + SUFLIB => '.a', + SUFLIBS => '.so:.a', + SUFOBJ => '.o', + SIGNATURE => [ '*' => 'build' ], + ENV => { 'PATH' => '/bin:/usr/bin' }, And on a Windows system (Windows NT), the default construction variables are (unless the default rule style is set using the B method): - CC => 'cl', - CFLAGS => '/nologo', - CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>', - CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>', - INCDIRPREFIX => '/I', - INCDIRSUFFIX => '', - LINK => 'link', - LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS', - LINKMODULECOM => '%LD /r /o %> %<', - LIBDIRPREFIX => '/LIBPATH:', - LIBDIRSUFFIX => '', - AR => 'lib', - ARFLAGS => '/nologo ', - ARCOM => "%AR %ARFLAGS /out:%> %<", - RANLIB => '', - LD => 'link', - LDFLAGS => '/nologo ', - PREFLIB => '', - SUFEXE => '.exe', - SUFLIB => '.lib', - SUFLIBS => '.dll:.lib', - SUFOBJ => '.obj', - SIGNATURE => [ '*' => 'build' ], + CC => 'cl', + CFLAGS => '/nologo', + CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>', + CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>', + INCDIRPREFIX => '/I', + INCDIRSUFFIX => '', + LINK => 'link', + LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS', + LINKMODULECOM => '%LD /r /o %> %<', + LIBDIRPREFIX => '/LIBPATH:', + LIBDIRSUFFIX => '', + AR => 'lib', + ARFLAGS => '/nologo ', + ARCOM => "%AR %ARFLAGS /out:%> %<", + RANLIB => '', + LD => 'link', + LDFLAGS => '/nologo ', + PREFLIB => '', + SUFEXE => '.exe', + SUFLIB => '.lib', + SUFLIBS => '.dll:.lib', + SUFOBJ => '.obj', + SIGNATURE => [ '*' => 'build' ], These variables are used by the various methods associated with the environment. In particular, any method that ultimately invokes an external @@ -160,7 +160,7 @@ appropriate. For example, the C method takes a number of source files and arranges to derive, if necessary, the corresponding object files: - Objects $env 'foo.c', 'bar.c'; + Objects $env 'foo.c', 'bar.c'; This will arrange to produce, if necessary, F and F. The command invoked is simply C<%CCCOM>, which expands, through substitution, @@ -234,32 +234,32 @@ anywhere else in the current command line (via C<%1>, C<%2>, etc.), then those will be deleted from the list provided by C<%E>. Consider the following command found in a F file in the F directory: - Command $env 'tgt', qw(foo bar baz), qq( + Command $env 'tgt', qw(foo bar baz), qq( echo %< -i %1 > %> echo %< -i %2 >> %> echo %< -i %3 >> %> - ); + ); If F needed to be updated, then this would result in the execution of the following commands, assuming that no remapping has been established for the F directory: - echo test/bar test/baz -i test/foo > test/tgt - echo test/foo test/baz -i test/bar >> test/tgt - echo test/foo test/bar -i test/baz >> test/tgt + echo test/bar test/baz -i test/foo > test/tgt + echo test/foo test/baz -i test/bar >> test/tgt + echo test/foo test/bar -i test/baz >> test/tgt =back Any of the above pseudo-variables may be followed immediately by one of the following suffixes to select a portion of the expanded path name: - :a the absolute path to the file name - :b the directory plus the file name stripped of any suffix - :d the directory - :f the file name - :s the file name suffix - :F the file name stripped of any suffix - :S the absolute path path to a Linked source file + :a the absolute path to the file name + :b the directory plus the file name stripped of any suffix + :d the directory + :f the file name + :s the file name suffix + :F the file name stripped of any suffix + :S the absolute path path to a Linked source file Continuing with the above example, C<%E:f> would expand to C, and C<%E:d> would expand to C. @@ -277,17 +277,17 @@ as a Perl code reference; the results of this call will be used to replace the contents of the brackets in the command line. For example, given an existing input file named F: - @keywords = qw(foo bar baz); - $env = new cons(X_COMMA => sub { join(",", @_) }); - Command $env 'tgt', 'tgt.in', qq( + @keywords = qw(foo bar baz); + $env = new cons(X_COMMA => sub { join(",", @_) }); + Command $env 'tgt', 'tgt.in', qq( echo '# Keywords: %[X_COMMA @keywords %]' > %> cat %< >> %> - ); + ); This will execute: - echo '# Keywords: foo,bar,baz' > tgt - cat tgt.in >> tgt + echo '# Keywords: foo,bar,baz' > tgt + cat tgt.in >> tgt =item %( %) @@ -313,843 +313,1366 @@ Cons expands construction variables in the source and target file names passed to the various construction methods according to the expansion rules described above: - $env = new cons( + $env = new cons( DESTDIR => 'programs', SRCDIR => 'src', - ); - Program $env '%DESTDIR/hello', '%SRCDIR/hello.c'; + ); + Program $env '%DESTDIR/hello', '%SRCDIR/hello.c'; This allows for flexible configuration, through the construction environment, of directory names, suffixes, etc. -=head1 Default construction methods +--> -The list of default construction methods includes the following: + + + An environment + is a collection of values that + can affect how a program executes. + &SCons; distinguishes between three + different types of environments + that can affect the behavior of &SCons; itself + (subject to the configuration in the &SConscript; files), + as well as the compilers and other tools it executes: + + + + + + + External Environment + + + + + The external environment + is the set of variables in the user's environment + at the time the user runs &SCons. + These variables are available within the &SConscript; files + through the Python os.environ dictionary. + See , below. + + + + + + + &ConsEnv; + + + + + A &consenv; + is a distinct object creating within + a &SConscript; file and + and which contains values that + affect how &SCons; decides + what action to use to build a target, + and even to define which targets + should be built from which sources. + One of the most powerful features of &SCons; + is the ability to create multiple &consenvs;, + including the ability to clone a new, customized + &consenv; from an existing &consenv;. + See , below. + + + + + + + Execution Environment + + + + + An execution environment + is the values that &SCons; sets + when executing an external + command (such as a compiler or linker) + to build one or more targets. + Note that this is not the same as + the external environment + (see above). + See , below. + + + + + + + + + + Unlike &Make;, &SCons; does not automatically + copy or import values between different environments + (with the exception of explicit clones of &consenvs, + which inherit values from their parent). + This is a deliberate design choice + to make sure that builds are, + by default, repeatable regardless of + the values in the user's external environment. + This avoids a whole class of problems with builds + where a developer's local build works + because a custom variable setting + causes a different comiler or build option to be used, + but the checked-in change breaks the official build + because it uses different environment variable settings. + + + + + + Note that the &SConscript; writer can + easily arrange for variables to be + copied or imported between environments, + and this is often very useful + (or even downright necessary) + to make it easy for developers + to customize the build in appropriate ways. + The point is not + that copying variables between different environments + is evil and must always be avoided. + Instead, it should be up to the + implementer of the build system + to make conscious choices + about how and when to import + a variable from one environment to another, + making informed decisions about + striking the right balance + between making the build + repeatable on the one hand + and convenient to use on the other. + + + +
+ Using Values From the External Environment + + + + The external environment + variable settings that + the user has in force + when executing &SCons; + are available through the normal Python + os.environ + dictionary. + This means that you must add an + import os statuement + to any &SConscript; file + in which you want to use + values from the user's external environment. + + + + + + import os + + + int main() { } + + + + + More usefully, you can use the + os.environ + dictionary in your &SConscript; + files to initialize &consenvs; + with values from the user's external environment. + See the next section, + , + for information on how to do this. + + + +
+ +
+ Construction Environments + + + + 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; accommodates these different build + requirements by allowing you to create and + configure multiple &consenvs; + that control how the software is built. + 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.) + + + +
+ Creating a &ConsEnv;: the &Environment; Function -=head2 The C constructor + + + A &consenv; is created by the &Environment; method: -The C method is a Perl object constructor. That is, it is not invoked -via a reference to an existing construction environment B, but, -rather statically, using the name of the Perl B where the -constructor is defined. The method is invoked like this: + - $env = new cons(); + + env = Environment() + + + -The environment you get back is blessed into the package C, which -means that it will have associated with it the default methods described -below. Individual construction variables can be overridden by providing -name/value pairs in an override list. Note that to override any command -environment variable (i.e. anything under C), you will have to override -all of them. You can get around this difficulty by using the C method -on an existing construction environment. + By default, &SCons; initializes 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: + -=head2 The C method + + + env = Environment(CC = 'gcc', + CCFLAGS = '-O2') -The C method creates a clone of an existing construction environment, -and can be called as in the following example: + env.Program('foo.c') + + + int main() { } + + - $env2 = $env1->clone(); + -You can provide overrides in the usual manner to create a different -environment from the original. If you just want a new name for the same -environment (which may be helpful when exporting environments to existing -components), you can just use simple assignment. + 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: + -=head2 The C method + + scons -Q + -The C method extracts the externally defined construction variables -from an environment and returns them as a list of name/value -pairs. Overrides can also be provided, in which case, the overridden values -will be returned, as appropriate. The returned list can be assigned to a -hash, as shown in the prototype, below, but it can also be manipulated in -other ways: +
- %env = $env1->copy(); +
+ Fetching Values From a &ConsEnv; + + + + You can fetch individual construction variables + using the normal syntax + for accessing individual named items in a Python dictionary: -The value of C, which is itself a hash, is also copied to a new hash, -so this may be changed without fear of affecting the original -environment. So, for example, if you really want to override just the -C variable in the default environment, you could do the following: + - %cons = new cons()->copy(); - $cons{ENV}{PATH} = ""; - $cons = new cons(%cons); + + + env = Environment() + print "CC is:", env['CC'] + + -This will leave anything else that might be in the default execution -environment undisturbed. + ---> + 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: - - - 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; accommodates 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; initializes 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') - - - int main() { } - - - - - - 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 - - -
- 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') + + scons -Q + - dbg.Program('bar', 'bar.c') - - - int main() { } - - - int main() { } - - + - - scons -Q - + 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: - + - 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: + + + env = Environment(FOO = 'foo', BAR = 'bar') + dict = env.Dictionary() + for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: + print "key = %s, value = %s" % (key, dict[key]) + + - + - - - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') + This &SConstruct; file + will print the specified dictionary items for us on POSIX + systems as follows: - opt.Program('foo', 'foo.c') + - dbg.Program('foo', 'foo.c') - - - int main() { } - - + + scons -Q + - + - Then &SCons; generates the following error: + And on Windows: - + - - scons -Q - - - - - 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') + + scons -Q + - o = opt.Object('foo-opt', 'foo.c') - opt.Program(o) + - d = dbg.Object('foo-dbg', 'foo.c') - dbg.Program(d) - - - int main() { } - - + If you want to loop 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: - + - 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: + + env = Environment() + dict = env.Dictionary() + keys = dict.keys() + keys.sort() + for key in keys: + print "construction variable = '%s', value = '%s'" % (key, dict[key]) + - +
- - scons -Q - +
+ Expanding Values From a &ConsEnv;: the &subst; Method -
+ -
- Copying &ConsEnvs; + 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: - + - 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. + + env = Environment() + print "CC is:", env.subst('$CC') + - + - + One 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;: - 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(CCFLAGS = '-DFOO') + print "CCCOM is:", env['CCCOM'] + - - - env = Environment(CC = 'gcc') - opt = env.Clone(CCFLAGS = '-O2') - dbg = env.Clone(CCFLAGS = '-g') + - env.Program('foo', 'foo.c') + Will print the unexpanded value of &cv-CCCOM;, + showing us the construction + variables that still need to be expanded: - o = opt.Object('foo-opt', 'foo.c') - opt.Program(o) + - d = dbg.Object('foo-dbg', 'foo.c') - dbg.Program(d) - - - int main() { } - - + + % scons -Q + CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES + scons: `.' is up to date. + - + - Then our output would look like: + Calling the &subst; method on $CCOM, + however: - + - - scons -Q - + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env.subst('$CCCOM') + -
+ -
- Fetching Values From a &ConsEnv; + Will recursively expand all of + the construction variables prefixed + with $ (dollar signs), + showing us the final output: - + - You can fetch individual construction variables - using the normal syntax - for accessing individual named items in a Python dictionary: + + % scons -Q + CCCOM is: gcc -DFOO -c -o + scons: `.' is up to date. + - + - - - env = Environment() - print "CC is:", env['CC'] - - + 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. - + - 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: +
-
+
+ Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function - - scons -Q - + - + All of the &Builder; functions that we've introduced so far, + like &Program; and &Library;, + actually use a default &consenv; + that contains settings + for the various compilers + and other tools that + &SCons; configures by default, + or otherwise knows about + and has discovered on your system. + The goal of the default construction environment + is to make many configurations to "just work" + to build software using + readily available tools + with a minimum of configuration changes. - 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]) - - + You can, however, control the settings + in the default contstruction environment + by using the &DefaultEnvironment; function + to initialize various settings: - + - This &SConstruct; file - will print the specified dictionary items for us on POSIX - systems as follows: + - + DefaultEnvironment(CC = '/usr/local/bin/gcc') - - scons -Q - + - + - And on Windows: + When configured as above, + all calls to the &Program; + or &Object; Builder + will build object files with the + /usr/local/bin/gcc + compiler. - + - - scons -Q - + - + Note that the &DefaultEnvironment; function + returns the initialized + default construction environment object, + which can then be manipulated like any + other construction environment. + So the following + would be equivalent to the + previous example, + setting the &cv-CC; + variable to /usr/local/bin/gcc + but as a separate step after + the default construction environment has been initialized: - 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]) - + env = DefaultEnvironment() + env['CC'] = '/usr/local/bin/gcc' -
+ -
- Expanding Values From a &ConsEnv; + - + One very common use of the &DefaultEnvironment; function + is to speed up &SCons; initialization. + As part of trying to make most default + configurations "just work," + &SCons; will actually + search the local system for installed + compilers and other utilities. + This search can take time, + especially on systems with + slow or networked file systems. + If you know which compiler(s) and/or + other utilities you want to configure, + you can control the search + that &SCons; performs + by specifying some specific + tool modules with which to + initialize the default construction environment: - 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') - + env = DefaultEnvironment(tools = ['gcc', 'gnulink'], + CC = '/usr/local/bin/gcc') + + + + + + So the above example would tell &SCons; + to explicitly configure the default environment + to use its normal GNU Compiler and GNU Linker settings + (without having to search for them, + or any other utilities for that matter), + and specifically to use the compiler found at + /usr/local/bin/gcc. + + + +
+ +
+ Multiple &ConsEnvs; - + - 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;: + 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') + + + int main() { } + + + int main() { } + + + + + scons -Q + + + + + 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') + + + int main() { } + + + + + + Then &SCons; generates the following error: + + + + + scons -Q + + + + + 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) + + + int main() { } + + + + + + 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 + + +
+ +
+ Making Copies of &ConsEnvs;: the &Clone; Method - + - - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env['CCCOM'] - + 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: - Will print the unexpanded value of &cv-CCCOM;, - showing us the construction - variables that still need to be expanded: + - + + + 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) + + + int main() { } + + + + + + Then our output would look like: + + + + + scons -Q + + +
+ +
+ Replacing Values: the &Replace; Method + + + + You can replace existing construction variable values + using the &Replace; method: + + + + + + env = Environment(CCFLAGS = '-DDEFINE1') + env.Replace(CCFLAGS = '-DDEFINE2') + env.Program('foo.c') + + + int main() { } + + + + + + The replacing value + (-DDEFINE2 in the above example) + completely replaces the value in the + construction environment: - - % scons -Q - CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES - scons: `.' is up to date. - + - + + scons -Q + - Calling the &subst; method on $CCOM, - however: + - + You can safely call &Replace; + for construction variables that + don't exist in the construction environment: + + - - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env.subst('$CCCOM') - + + + 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 + - Will recursively expand all of - the $-prefixed construction variables, - showing us the final output: + - + 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: - - % scons -Q - CCCOM is: gcc -DFOO -c -o - scons: `.' is up to date. - + - + + + env = Environment(CCFLAGS = '-DDEFINE1') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('foo.c') - (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.) + env.Replace(CCFLAGS = '-DDEFINE2') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('bar.c') + + + int main() { } + + + int main() { } + + - + -
+ 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: -
- Modifying a &ConsEnv; + + + + scons + + + - + 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. - &SCons; provides various methods that - support modifying existing values in a construction environment. + - +
-
- Replacing Values in a &ConsEnv; +
+ Setting Values Only If They're Not Already Defined: the &SetDefault; Method - + - You can replace existing construction variable values - using the &Replace; method: + Sometimes it's useful to be able to specify + that a construction variable should be + set to a value only if the construction environment + does not already have that variable defined + You can do this with the &SetDefault; method, + which behaves similarly to the set_default + method of Python dictionary objects: - + - - - env = Environment(CCFLAGS = '-DDEFINE1') - env.Replace(CCFLAGS = '-DDEFINE2') - env.Program('foo.c') - - - int main() { } - - + + env.SetDefault(SPECIAL_FLAG = '-extra-option') + - + - The replacing value - (-DDEFINE2 in the above example) - completely replaces the value in the - construction environment: + This is especially useful + when writing your own Tool modules + to apply variables to construction environments. + - + - - scons -Q - +
- +
+ Appending to the End of Values: the &Append; Method - You can safely call &Replace; - for construction variables that - don't exist in the construction environment: + - + You can append a value to + an existing construction variable + using the &Append; method: - - - env = Environment() - env.Replace(NEW_VARIABLE = 'xyzzy') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - - + - + + + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Append(CCFLAGS = ['-DLAST']) + env.Program('foo.c') + + + int main() { } + + - In this case, - the construction variable simply - gets added to the construction environment: + - + &SCons; then supplies both the -DMY_VALUE and + -DLAST flags when compiling the object file: - - scons -Q - + - + + scons -Q + - 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: + - + If the construction variable doesn't already exist, + the &Append; method will create it: - - - env = Environment(CCFLAGS = '-DDEFINE1') - print "CCFLAGS =", env['CCFLAGS'] - env.Program('foo.c') + - env.Replace(CCFLAGS = '-DDEFINE2') - print "CCFLAGS =", env['CCFLAGS'] - env.Program('bar.c') - - - int main() { } - - - int main() { } - - + + + env = Environment() + env.Append(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + - + - 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: + Which yields: - + - - scons - + + scons -Q + - + - 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. + Note that the &Append; function tries to be "smart" + about how the new value is appended to the old value. + If both are strings, the previous and new strings + are simply concatenated. + Similarly, if both are lists, + the lists are concatenated. + If, however, one is a string and the other is a list, + the string is added as a new element to the list. - + -
+
- + In the above example, + the -g would be added + only if the &cv-CCFLAGS; variable + does not already contain a -g value. -
- Appending to the End of Values in a &ConsEnv; + - +
- You can append a value to - an existing construction variable - using the &Append; method: +
+ Appending to the Beginning of Values: the &Prepend; Method - + - - - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Append(CCFLAGS = ' -DLAST') - env.Program('foo.c') - - - int main() { } - - + You can append a value to the beginning of + an existing construction variable + using the &Prepend; method: - + - &SCons; then supplies both the -DMY_VALUE and - -DLAST flags when compiling the object file: + + + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Prepend(CCFLAGS = ['-DFIRST']) + env.Program('foo.c') + + + int main() { } + + - + - - scons -Q - + &SCons; then supplies both the -DFIRST and + -DMY_VALUE flags when compiling the object file: - + - If the construction variable doesn't already exist, - the &Append; method will create it: + + scons -Q + - + - - - env = Environment() - env.Append(NEW_VARIABLE = 'added') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - - + If the construction variable doesn't already exist, + the &Prepend; method will create it: - + - Which yields: + + + env = Environment() + env.Prepend(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + + - + - - scons -Q - + Which yields: - + -
+ Like the &Append; function, + the &Prepend; function tries to be "smart" + about how the new value is appended to the old value. + If both are strings, the previous and new strings + are simply concatenated. + Similarly, if both are lists, + the lists are concatenated. + If, however, one is a string and the other is a list, + the string is added as a new element to the list. -
- 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: +
+ Prepending Unique Values: the &PrependUnique; Method - + - - - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Prepend(CCFLAGS = '-DFIRST ') - env.Program('foo.c') - - - int main() { } - - + Some times it's useful to add a new value + to the beginning of a construction variable + only if the existing value + doesn't already contain the to-be-added value. + This can be done using the &PrependUnique; method: - + - &SCons; then supplies both the -DFIRST and - -DMY_VALUE flags when compiling the object file: + + env.PrependUnique(CCFLAGS=['-g']) + - + - - scons -Q - + In the above example, + the -g would be added + only if the &cv-CCFLAGS; variable + does not already contain a -g value. - + - 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'] - - +
+ Controlling the Execution Environment for Issued Commands + + + + 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. - + - Which yields: + - + 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. - - scons -Q - + - + -
+ - + -
+ 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, + if 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. + + + +
+ +
+ Adding to <varname>PATH</varname> Values in the Execution Environment + + + + One of the most common requirements + for manipulating a variable in the execution environment + is to add one or more custom directories to a search + like the $PATH variable on Linux or POSIX systems, + or the %PATH% variable on Windows, + so that a locally-installed compiler or other utility + can be found when &SCons; tries to execute it to update a target. + &SCons; provides &PrependENVPath; and &AppendENVPath; functions + to make adding things to execution variables convenient. + You call these functions by specifying the variable + to which you want the value added, + and then value itself. + So to add some /usr/local directories + to the $PATH and $LIB variables, + you might: + + + + + env = Environment(ENV = os.environ) + env.PrependENVPath('PATH', '/usr/local/bin') + env.AppendENVPath('LIB', '/usr/local/lib') + + + + + Note that the added values are strings, + and if you want to add multiple directories to + a variable like $PATH, + you must include the path separate character + (: on Linux or POSIX, + ; on Windows) + in the string. + + + +
+ +
diff --git a/doc/user/environments.xml b/doc/user/environments.xml index 01ae2ed5..0aac0d81 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -1,25 +1,25 @@ @@ -38,120 +38,120 @@ build behavior. Construction variables from a construction environment are expanded by preceding the keyword with a C<%> (percent sign): - Construction variables: + Construction variables: XYZZY => 'abracadabra', - The string: "The magic word is: %XYZZY!" - expands to: "The magic word is: abracadabra!" + The string: "The magic word is: %XYZZY!" + expands to: "The magic word is: abracadabra!" A construction variable name may be surrounded by C<{> and C<}> (curly braces), which are stripped as part of the expansion. This can sometimes be necessary to separate a variable expansion from trailing alphanumeric characters: - Construction variables: + Construction variables: OPT => 'value1', OPTION => 'value2', - The string: "%OPT %{OPT}ION %OPTION %{OPTION}" - expands to: "value1 value1ION value2 value2" + The string: "%OPT %{OPT}ION %OPTION %{OPTION}" + expands to: "value1 value1ION value2 value2" Construction variable expansion is recursive, that is, a string containing C<%->expansions after substitution will be re-expanded until no further substitutions can be made: - Construction variables: + Construction variables: STRING => 'The result is: %FOO', FOO => '%BAR', BAR => 'final value', - The string: "The string says: %STRING" - expands to: "The string says: The result is: final value" + The string: "The string says: %STRING" + expands to: "The string says: The result is: final value" If a construction variable is not defined in an environment, then the null string is substituted: - Construction variables: + Construction variables: FOO => 'value1', BAR => 'value2', - The string: "%FOO <%NO_VARIABLE> %BAR" - expands to: "value1 <> value2" + The string: "%FOO <%NO_VARIABLE> %BAR" + expands to: "value1 <> value2" A doubled C<%%> will be replaced by a single C<%>: - The string: "Here is a percent sign: %%" - expands to: "Here is a percent sign: %" + The string: "Here is a percent sign: %%" + expands to: "Here is a percent sign: %" =head2 Default construction variables When you specify no arguments when creating a new construction environment: - $env = new cons(); + $env = new cons(); Cons creates a reference to a new, default construction environment. This contains a number of construction variables and some methods. At the present writing, the default construction variables on a UNIX system are: - CC => 'cc', - CFLAGS => '', - CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>', - CXX => '%CC', - CXXFLAGS => '%CFLAGS', - CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>', - INCDIRPREFIX => '-I', - INCDIRSUFFIX => '', - LINK => '%CXX', - LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS', - LINKMODULECOM => '%LD -r -o %> %<', - LIBDIRPREFIX => '-L', - LIBDIRSUFFIX => '', - AR => 'ar', - ARFLAGS => 'r', - ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'], - RANLIB => 'ranlib', - AS => 'as', - ASFLAGS => '', - ASCOM => '%AS %ASFLAGS %< -o %>', - LD => 'ld', - LDFLAGS => '', - PREFLIB => 'lib', - SUFLIB => '.a', - SUFLIBS => '.so:.a', - SUFOBJ => '.o', - SIGNATURE => [ '*' => 'build' ], - ENV => { 'PATH' => '/bin:/usr/bin' }, + CC => 'cc', + CFLAGS => '', + CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>', + CXX => '%CC', + CXXFLAGS => '%CFLAGS', + CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>', + INCDIRPREFIX => '-I', + INCDIRSUFFIX => '', + LINK => '%CXX', + LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS', + LINKMODULECOM => '%LD -r -o %> %<', + LIBDIRPREFIX => '-L', + LIBDIRSUFFIX => '', + AR => 'ar', + ARFLAGS => 'r', + ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'], + RANLIB => 'ranlib', + AS => 'as', + ASFLAGS => '', + ASCOM => '%AS %ASFLAGS %< -o %>', + LD => 'ld', + LDFLAGS => '', + PREFLIB => 'lib', + SUFLIB => '.a', + SUFLIBS => '.so:.a', + SUFOBJ => '.o', + SIGNATURE => [ '*' => 'build' ], + ENV => { 'PATH' => '/bin:/usr/bin' }, And on a Windows system (Windows NT), the default construction variables are (unless the default rule style is set using the B method): - CC => 'cl', - CFLAGS => '/nologo', - CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>', - CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>', - INCDIRPREFIX => '/I', - INCDIRSUFFIX => '', - LINK => 'link', - LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS', - LINKMODULECOM => '%LD /r /o %> %<', - LIBDIRPREFIX => '/LIBPATH:', - LIBDIRSUFFIX => '', - AR => 'lib', - ARFLAGS => '/nologo ', - ARCOM => "%AR %ARFLAGS /out:%> %<", - RANLIB => '', - LD => 'link', - LDFLAGS => '/nologo ', - PREFLIB => '', - SUFEXE => '.exe', - SUFLIB => '.lib', - SUFLIBS => '.dll:.lib', - SUFOBJ => '.obj', - SIGNATURE => [ '*' => 'build' ], + CC => 'cl', + CFLAGS => '/nologo', + CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>', + CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>', + INCDIRPREFIX => '/I', + INCDIRSUFFIX => '', + LINK => 'link', + LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS', + LINKMODULECOM => '%LD /r /o %> %<', + LIBDIRPREFIX => '/LIBPATH:', + LIBDIRSUFFIX => '', + AR => 'lib', + ARFLAGS => '/nologo ', + ARCOM => "%AR %ARFLAGS /out:%> %<", + RANLIB => '', + LD => 'link', + LDFLAGS => '/nologo ', + PREFLIB => '', + SUFEXE => '.exe', + SUFLIB => '.lib', + SUFLIBS => '.dll:.lib', + SUFOBJ => '.obj', + SIGNATURE => [ '*' => 'build' ], These variables are used by the various methods associated with the environment. In particular, any method that ultimately invokes an external @@ -160,7 +160,7 @@ appropriate. For example, the C method takes a number of source files and arranges to derive, if necessary, the corresponding object files: - Objects $env 'foo.c', 'bar.c'; + Objects $env 'foo.c', 'bar.c'; This will arrange to produce, if necessary, F and F. The command invoked is simply C<%CCCOM>, which expands, through substitution, @@ -234,32 +234,32 @@ anywhere else in the current command line (via C<%1>, C<%2>, etc.), then those will be deleted from the list provided by C<%E>. Consider the following command found in a F file in the F directory: - Command $env 'tgt', qw(foo bar baz), qq( + Command $env 'tgt', qw(foo bar baz), qq( echo %< -i %1 > %> echo %< -i %2 >> %> echo %< -i %3 >> %> - ); + ); If F needed to be updated, then this would result in the execution of the following commands, assuming that no remapping has been established for the F directory: - echo test/bar test/baz -i test/foo > test/tgt - echo test/foo test/baz -i test/bar >> test/tgt - echo test/foo test/bar -i test/baz >> test/tgt + echo test/bar test/baz -i test/foo > test/tgt + echo test/foo test/baz -i test/bar >> test/tgt + echo test/foo test/bar -i test/baz >> test/tgt =back Any of the above pseudo-variables may be followed immediately by one of the following suffixes to select a portion of the expanded path name: - :a the absolute path to the file name - :b the directory plus the file name stripped of any suffix - :d the directory - :f the file name - :s the file name suffix - :F the file name stripped of any suffix - :S the absolute path path to a Linked source file + :a the absolute path to the file name + :b the directory plus the file name stripped of any suffix + :d the directory + :f the file name + :s the file name suffix + :F the file name stripped of any suffix + :S the absolute path path to a Linked source file Continuing with the above example, C<%E:f> would expand to C, and C<%E:d> would expand to C. @@ -277,17 +277,17 @@ as a Perl code reference; the results of this call will be used to replace the contents of the brackets in the command line. For example, given an existing input file named F: - @keywords = qw(foo bar baz); - $env = new cons(X_COMMA => sub { join(",", @_) }); - Command $env 'tgt', 'tgt.in', qq( + @keywords = qw(foo bar baz); + $env = new cons(X_COMMA => sub { join(",", @_) }); + Command $env 'tgt', 'tgt.in', qq( echo '# Keywords: %[X_COMMA @keywords %]' > %> cat %< >> %> - ); + ); This will execute: - echo '# Keywords: foo,bar,baz' > tgt - cat tgt.in >> tgt + echo '# Keywords: foo,bar,baz' > tgt + cat tgt.in >> tgt =item %( %) @@ -313,833 +313,1353 @@ Cons expands construction variables in the source and target file names passed to the various construction methods according to the expansion rules described above: - $env = new cons( + $env = new cons( DESTDIR => 'programs', SRCDIR => 'src', - ); - Program $env '%DESTDIR/hello', '%SRCDIR/hello.c'; + ); + Program $env '%DESTDIR/hello', '%SRCDIR/hello.c'; This allows for flexible configuration, through the construction environment, of directory names, suffixes, etc. -=head1 Default construction methods +--> -The list of default construction methods includes the following: + + + An environment + is a collection of values that + can affect how a program executes. + &SCons; distinguishes between three + different types of environments + that can affect the behavior of &SCons; itself + (subject to the configuration in the &SConscript; files), + as well as the compilers and other tools it executes: + + + + + + + External Environment + + + + + The external environment + is the set of variables in the user's environment + at the time the user runs &SCons;. + These variables are available within the &SConscript; files + through the Python os.environ dictionary. + See , below. + + + + + + + &ConsEnv; + + + + + A &consenv; + is a distinct object creating within + a &SConscript; file and + and which contains values that + affect how &SCons; decides + what action to use to build a target, + and even to define which targets + should be built from which sources. + One of the most powerful features of &SCons; + is the ability to create multiple &consenvs;, + including the ability to clone a new, customized + &consenv; from an existing &consenv;. + See , below. + + + + + + + Execution Environment + + + + + An execution environment + is the values that &SCons; sets + when executing an external + command (such as a compiler or linker) + to build one or more targets. + Note that this is not the same as + the external environment + (see above). + See , below. + + + + + + + + + + Unlike &Make;, &SCons; does not automatically + copy or import values between different environments + (with the exception of explicit clones of &consenvs;, + which inherit values from their parent). + This is a deliberate design choice + to make sure that builds are, + by default, repeatable regardless of + the values in the user's external environment. + This avoids a whole class of problems with builds + where a developer's local build works + because a custom variable setting + causes a different comiler or build option to be used, + but the checked-in change breaks the official build + because it uses different environment variable settings. + + + + + + Note that the &SConscript; writer can + easily arrange for variables to be + copied or imported between environments, + and this is often very useful + (or even downright necessary) + to make it easy for developers + to customize the build in appropriate ways. + The point is not + that copying variables between different environments + is evil and must always be avoided. + Instead, it should be up to the + implementer of the build system + to make conscious choices + about how and when to import + a variable from one environment to another, + making informed decisions about + striking the right balance + between making the build + repeatable on the one hand + and convenient to use on the other. + + + +
+ Using Values From the External Environment + + + + The external environment + variable settings that + the user has in force + when executing &SCons; + are available through the normal Python + os.environ + dictionary. + This means that you must add an + import os statuement + to any &SConscript; file + in which you want to use + values from the user's external environment. + + + + import os + -=head2 The C constructor + + + More usefully, you can use the + os.environ + dictionary in your &SConscript; + files to initialize &consenvs; + with values from the user's external environment. + See the next section, + , + for information on how to do this. + + + +
+ +
+ Construction Environments -The C method is a Perl object constructor. That is, it is not invoked -via a reference to an existing construction environment B, but, -rather statically, using the name of the Perl B where the -constructor is defined. The method is invoked like this: + - $env = new cons(); + 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; accommodates these different build + requirements by allowing you to create and + configure multiple &consenvs; + that control how the software is built. + 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.) -The environment you get back is blessed into the package C, which -means that it will have associated with it the default methods described -below. Individual construction variables can be overridden by providing -name/value pairs in an override list. Note that to override any command -environment variable (i.e. anything under C), you will have to override -all of them. You can get around this difficulty by using the C method -on an existing construction environment. + +
+ Creating a &ConsEnv;: the &Environment; Function -=head2 The C method + -The C method creates a clone of an existing construction environment, -and can be called as in the following example: + A &consenv; is created by the &Environment; method: - $env2 = $env1->clone(); + -You can provide overrides in the usual manner to create a different -environment from the original. If you just want a new name for the same -environment (which may be helpful when exporting environments to existing -components), you can just use simple assignment. + + env = Environment() + + -=head2 The C method + By default, &SCons; initializes 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. -The C method extracts the externally defined construction variables -from an environment and returns them as a list of name/value -pairs. Overrides can also be provided, in which case, the overridden values -will be returned, as appropriate. The returned list can be assigned to a -hash, as shown in the prototype, below, but it can also be manipulated in -other ways: + - %env = $env1->copy(); + -The value of C, which is itself a hash, is also copied to a new hash, -so this may be changed without fear of affecting the original -environment. So, for example, if you really want to override just the -C variable in the default environment, you could do the following: + 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: - %cons = new cons()->copy(); - $cons{ENV}{PATH} = ""; - $cons = new cons(%cons); + -This will leave anything else that might be in the default execution -environment undisturbed. + + import os ---> + env = Environment(CC = 'gcc', + CCFLAGS = '-O2') - - - 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; accommodates 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; initializes 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: - - + env.Program('foo.c') + - - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') + - opt.Program('foo', '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: - 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 - + + % scons -Q + gcc -o foo.o -c -O2 foo.c + gcc -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: +
+ Fetching Values From a &ConsEnv; - + - - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') + 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'] + - opt.Program('foo', 'foo.c') + - dbg.Program('foo', 'foo.c') - + 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: - + - Then &SCons; generates the following error: + + % scons -Q + CC is: cc + scons: `.' is up to date. + - + - - % 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> - + 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 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: + - + This &SConstruct; file + will print the specified dictionary items for us on POSIX + systems as follows: - - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') + - o = opt.Object('foo-opt', 'foo.c') - opt.Program(o) + + % scons -Q + key = OBJSUFFIX, value = .o + key = LIBSUFFIX, value = .a + key = PROGSUFFIX, value = + scons: `.' is up to date. + - 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: - - + And on Windows: - - env = Environment(CC = 'gcc') - opt = env.Clone(CCFLAGS = '-O2') - dbg = env.Clone(CCFLAGS = '-g') + - env.Program('foo', 'foo.c') + + C:\>scons -Q + key = OBJSUFFIX, value = .obj + key = LIBSUFFIX, value = .lib + key = PROGSUFFIX, value = .exe + scons: `.' is up to date. + + + - o = opt.Object('foo-opt', 'foo.c') - opt.Program(o) + If you want to loop 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: - d = dbg.Object('foo-dbg', 'foo.c') - dbg.Program(d) - + - + + env = Environment() + dict = env.Dictionary() + keys = dict.keys() + keys.sort() + for key in keys: + print "construction variable = '%s', value = '%s'" % (key, dict[key]) + - Then our output would look like: +
- +
+ Expanding Values From a &ConsEnv;: the &subst; Method - - % 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 - + -
+ 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: -
- Fetching Values From a &ConsEnv; + - + + env = Environment() + print "CC is:", env.subst('$CC') + - You can fetch individual construction variables - using the normal syntax - for accessing individual named items in a Python dictionary: + - + One 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() - print "CC is:", env['CC'] - + - + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env['CCCOM'] + - 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: + - + Will print the unexpanded value of &cv-CCCOM;, + showing us the construction + variables that still need to be expanded: - - % scons -Q - CC is: cc - scons: `.' is up to date. - + - + + % scons -Q + CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES + 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: + - + Calling the &subst; method on $CCOM, + however: - - env = Environment(FOO = 'foo', BAR = 'bar') - dict = env.Dictionary() - for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: - print "key = %s, value = %s" % (key, dict[key]) - + - + + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env.subst('$CCCOM') + - This &SConstruct; file - will print the specified dictionary items for us on POSIX - systems as follows: + - + Will recursively expand all of + the construction variables prefixed + with $ (dollar signs), + showing us the final output: - - % scons -Q - key = OBJSUFFIX, value = .o - key = LIBSUFFIX, value = .a - key = PROGSUFFIX, value = - scons: `.' is up to date. - + - + + % scons -Q + CCCOM is: gcc -DFOO -c -o + scons: `.' is up to date. + - And on Windows: + + + 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. - + - - C:\>scons -Q - key = OBJSUFFIX, value = .obj - key = LIBSUFFIX, value = .lib - key = PROGSUFFIX, value = .exe - scons: `.' is up to date. - +
- +
+ Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function - 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: + - + All of the &Builder; functions that we've introduced so far, + like &Program; and &Library;, + actually use a default &consenv; + that contains settings + for the various compilers + and other tools that + &SCons; configures by default, + or otherwise knows about + and has discovered on your system. + The goal of the default construction environment + is to make many configurations to "just work" + to build software using + readily available tools + with a minimum of configuration changes. - - 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; + You can, however, control the settings + in the default contstruction environment + by using the &DefaultEnvironment; function + to initialize various settings: - + - 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: + - + DefaultEnvironment(CC = '/usr/local/bin/gcc') - - 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;: + When configured as above, + all calls to the &Program; + or &Object; Builder + will build object files with the + /usr/local/bin/gcc + compiler. - + - - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env['CCCOM'] - + - + Note that the &DefaultEnvironment; function + returns the initialized + default construction environment object, + which can then be manipulated like any + other construction environment. + So the following + would be equivalent to the + previous example, + setting the &cv-CC; + variable to /usr/local/bin/gcc + but as a separate step after + the default construction environment has been initialized: - 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. - + env = DefaultEnvironment() + env['CC'] = '/usr/local/bin/gcc' - + - Calling the &subst; method on $CCOM, - however: + - + One very common use of the &DefaultEnvironment; function + is to speed up &SCons; initialization. + As part of trying to make most default + configurations "just work," + &SCons; will actually + search the local system for installed + compilers and other utilities. + This search can take time, + especially on systems with + slow or networked file systems. + If you know which compiler(s) and/or + other utilities you want to configure, + you can control the search + that &SCons; performs + by specifying some specific + tool modules with which to + initialize the default construction environment: - - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env.subst('$CCCOM') - + - + - Will recursively expand all of - the $-prefixed construction variables, - showing us the final output: + env = DefaultEnvironment(tools = ['gcc', 'gnulink'], + CC = '/usr/local/bin/gcc') + + + + - + So the above example would tell &SCons; + to explicitly configure the default environment + to use its normal GNU Compiler and GNU Linker settings + (without having to search for them, + or any other utilities for that matter), + and specifically to use the compiler found at + /usr/local/bin/gcc. - - % 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.) +
+ 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: -
- Modifying a &ConsEnv; + + + + 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) - &SCons; provides various methods that - support modifying existing values in a construction environment. + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + - + -
- Replacing Values in a &ConsEnv; + 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: - + - You can replace existing construction variable values - using the &Replace; method: + + % 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 + - +
- - env = Environment(CCFLAGS = '-DDEFINE1') - env.Replace(CCFLAGS = '-DDEFINE2') - env.Program('foo.c') - +
+ Making Copies of &ConsEnvs;: the &Clone; Method - + - The replacing value - (-DDEFINE2 in the above example) - completely replaces the value in the - construction environment: + 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. - + - - % scons -Q - cc -o foo.o -c -DDEFINE2 foo.c - cc -o foo foo.o - + - + 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: - You can safely call &Replace; - for construction variables that - don't exist in the construction environment: + + + + 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 + - +
- - env = Environment() - env.Replace(NEW_VARIABLE = 'xyzzy') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - +
+ Replacing Values: the &Replace; Method + + + + 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 + - + - In this case, - the construction variable simply - gets added to the construction environment: + 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. + + + - - % 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: - + - 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') + - - 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: - + - 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. + - + - - % 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. - + - 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. +
-
+
+ Setting Values Only If They're Not Already Defined: the &SetDefault; Method -
+ - -
+
- --> +
-
- Appending to the End of Values in a &ConsEnv; +
+ Appending to the End of Values: the &Append; Method - + - You can append a value to - an existing construction variable - using the &Append; method: + 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') - + + 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; 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 - + + % 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: + 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'] - + + env = Environment() + env.Append(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + - + - Which yields: + Which yields: - + - - % scons -Q - NEW_VARIABLE = added - scons: `.' is up to date. - + + % scons -Q + NEW_VARIABLE = added + scons: `.' is up to date. + - + -
+
-
- Appending to the Beginning of Values in a &ConsEnv; +
+ Appending Unique Values: the &AppendUnique; Method - + - You can append a value to the beginning of - an existing construction variable - using the &Prepend; method: + Some times it's useful to add a new value + only if the existing construction variable + doesn't already contain the value. + This can be done using the &AppendUnique; method: - + - - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Prepend(CCFLAGS = '-DFIRST ') - env.Program('foo.c') - + + env.AppendUnique(CCFLAGS=['-g']) + - + - &SCons; then supplies both the -DFIRST and - -DMY_VALUE flags when compiling the object file: + In the above example, + the -g would be added + only if the &cv-CCFLAGS; variable + does not already contain a -g value. - + - - % scons -Q - cc -o foo.o -c -DFIRST -DMY_VALUE foo.c - cc -o foo foo.o - +
- +
+ Appending to the Beginning of Values: the &Prepend; Method - If the construction variable doesn't already exist, - the &Prepend; method will create it: + - + You can append a value to the beginning of + an existing construction variable + using the &Prepend; method: - - env = Environment() - env.Prepend(NEW_VARIABLE = 'added') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - + - + + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Prepend(CCFLAGS = ['-DFIRST']) + env.Program('foo.c') + - Which yields: + - + &SCons; then supplies both the -DFIRST and + -DMY_VALUE flags when compiling the object file: - - % scons -Q - NEW_VARIABLE = added - scons: `.' is up to date. - + + + + % 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. + + + + + Like the &Append; function, + the &Prepend; function tries to be "smart" + about how the new value is appended to the old value. + If both are strings, the previous and new strings + are simply concatenated. + Similarly, if both are lists, + the lists are concatenated. + If, however, one is a string and the other is a list, + the string is added as a new element to the list. + + + +
+ +
+ Prepending Unique Values: the &PrependUnique; Method + + + + Some times it's useful to add a new value + to the beginning of a construction variable + only if the existing value + doesn't already contain the to-be-added value. + This can be done using the &PrependUnique; method: + + + + + env.PrependUnique(CCFLAGS=['-g']) + + + - +
-
+
- + -
+ 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, + if 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. + + + +
+ +
+ Adding to <varname>PATH</varname> Values in the Execution Environment + + + + One of the most common requirements + for manipulating a variable in the execution environment + is to add one or more custom directories to a search + like the $PATH variable on Linux or POSIX systems, + or the %PATH% variable on Windows, + so that a locally-installed compiler or other utility + can be found when &SCons; tries to execute it to update a target. + &SCons; provides &PrependENVPath; and &AppendENVPath; functions + to make adding things to execution variables convenient. + You call these functions by specifying the variable + to which you want the value added, + and then value itself. + So to add some /usr/local directories + to the $PATH and $LIB variables, + you might: + + + + + env = Environment(ENV = os.environ) + env.PrependENVPath('PATH', '/usr/local/bin') + env.AppendENVPath('LIB', '/usr/local/lib') + + + + + Note that the added values are strings, + and if you want to add multiple directories to + a variable like $PATH, + you must include the path separate character + (: on Linux or POSIX, + ; on Windows) + in the string. + + + +
+ +
diff --git a/doc/user/factories.in b/doc/user/factories.in index 222f02a2..94af6a39 100644 --- a/doc/user/factories.in +++ b/doc/user/factories.in @@ -44,8 +44,17 @@ 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. + and don't have a suitable pre-existing builder. + + + Unfortunately, in the early days of SCons design, + we used the name &Copy; for the function that + returns a copy of the environment, + otherwise that would be the logical choice for + a Builder that copies a file or directory tree + to a target location. + + One way would be to use the &Copy; action factory in conjunction with the &Command; builder: @@ -229,13 +238,23 @@ - (Note, however, that you typically don't need to + 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. + + + One word of caution about using the &Delete; factory: + it has the same variable expansions available + as any other factory, including the &cv-SOURCE; variable. + Specifying Delete("$SOURCE") + is not something you usually want to do! + + +
diff --git a/doc/user/factories.xml b/doc/user/factories.xml index 6d0de02d..ae6e9d03 100644 --- a/doc/user/factories.xml +++ b/doc/user/factories.xml @@ -44,8 +44,17 @@ 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. + and don't have a suitable pre-existing builder. + + + Unfortunately, in the early days of SCons design, + we used the name &Copy; for the function that + returns a copy of the environment, + otherwise that would be the logical choice for + a Builder that copies a file or directory tree + to a target location. + + One way would be to use the &Copy; action factory in conjunction with the &Command; builder: @@ -207,13 +216,23 @@ - (Note, however, that you typically don't need to + 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. + + + One word of caution about using the &Delete; factory: + it has the same variable expansions available + as any other factory, including the &cv-SOURCE; variable. + Specifying Delete("$SOURCE") + is not something you usually want to do! + + +
diff --git a/doc/user/help.in b/doc/user/help.in deleted file mode 100644 index 5ab7e83b..00000000 --- a/doc/user/help.in +++ /dev/null @@ -1,136 +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 - - - - - 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 complete help text on Windows: - - - - - scons -h - - - - - But only show the relevant option on a Linux or UNIX system: - - - - - scons -h - - - - - 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 deleted file mode 100644 index ed2b5ded..00000000 --- a/doc/user/help.xml +++ /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 complete 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/less-simple.in b/doc/user/less-simple.in index cf593194..23b585c2 100644 --- a/doc/user/less-simple.in +++ b/doc/user/less-simple.in @@ -209,15 +209,19 @@
- Making a list of files with Glob() + Making a list of files with &Glob; - You can also use the Glob() function to find all files matching a - certain template, using standard the shell pattern matching - characters *, ?, and [abc] to match any of a, b, or c. [!abc] is - also supported, to match any character except - a, b, or c. This makes many multi-source-file builds quite easy: + You can also use the &Glob; function to find all files matching a + certain template, using the standard shell pattern matching + characters *, ? + and [abc] to match any of + a, b or c. + [!abc] is also supported, + to match any character except + a, b or c. + This makes many multi-source-file builds quite easy: @@ -227,8 +231,8 @@ - The SCons man page has more details on using Glob() with Variant - dirs and Repositories and returning strings rather than Nodes. + The SCons man page has more details on using &Glob; with Variant + directories and Repositories, and returning strings rather than Nodes. @@ -388,8 +392,8 @@ - list = Split('main.c file1.c file2.c') - Program('program', list) + src_files = Split('main.c file1.c file2.c') + Program('program', src_files) @@ -404,10 +408,10 @@ - list = Split("""main.c - file1.c - file2.c""") - Program('program', list) + src_files = Split("""main.c + file1.c + file2.c""") + Program('program', src_files) @@ -440,8 +444,8 @@ - list = Split('main.c file1.c file2.c') - Program(target = 'program', source = list) + src_files = Split('main.c file1.c file2.c') + Program(target = 'program', source = src_files) @@ -453,8 +457,8 @@ - list = Split('main.c file1.c file2.c') - Program(source = list, target = 'program') + src_files = Split('main.c file1.c file2.c') + Program(source = src_files, target = 'program') diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml index 53925189..35b6b608 100644 --- a/doc/user/less-simple.xml +++ b/doc/user/less-simple.xml @@ -198,15 +198,19 @@
- Making a list of files with Glob() + Making a list of files with &Glob; - You can also use the Glob() function to find all files matching a - certain template, using standard the shell pattern matching - characters *, ?, and [abc] to match any of a, b, or c. [!abc] is - also supported, to match any character except - a, b, or c. This makes many multi-source-file builds quite easy: + You can also use the &Glob; function to find all files matching a + certain template, using the standard shell pattern matching + characters *, ? + and [abc] to match any of + a, b or c. + [!abc] is also supported, + to match any character except + a, b or c. + This makes many multi-source-file builds quite easy: @@ -216,8 +220,8 @@ - The SCons man page has more details on using Glob() with Variant - dirs and Repositories and returning strings rather than Nodes. + The SCons man page has more details on using &Glob; with Variant + directories and Repositories, and returning strings rather than Nodes. @@ -377,8 +381,8 @@ - list = Split('main.c file1.c file2.c') - Program('program', list) + src_files = Split('main.c file1.c file2.c') + Program('program', src_files) @@ -393,10 +397,10 @@ - list = Split("""main.c - file1.c - file2.c""") - Program('program', list) + src_files = Split("""main.c + file1.c + file2.c""") + Program('program', src_files) @@ -429,8 +433,8 @@ - list = Split('main.c file1.c file2.c') - Program(target = 'program', source = list) + src_files = Split('main.c file1.c file2.c') + Program(target = 'program', source = src_files) @@ -442,8 +446,8 @@ - list = Split('main.c file1.c file2.c') - Program(source = list, target = 'program') + src_files = Split('main.c file1.c file2.c') + Program(source = src_files, target = 'program') diff --git a/doc/user/main.in b/doc/user/main.in index 0dbadfc8..4c2c1b84 100644 --- a/doc/user/main.in +++ b/doc/user/main.in @@ -56,21 +56,23 @@ - - + + + + @@ -83,7 +85,7 @@ - + @@ -94,59 +96,26 @@ @@ -160,10 +129,10 @@ Revision &buildrevision; (&builddate;) - 2004, 2005, 2006, 2007 + 2004, 2005, 2006, 2007, 2008 - 2004, 2005, 2006, 2007 + 2004, 2005, 2006, 2007, 2008 Steven Knight @@ -211,22 +180,27 @@ - Construction Environments + Environments &environments; - + + Merging Options into the Environment: the &MergeFlags; Function + &mergeflags; + + + Separating Compile Arguments into their Variables: the &ParseFlags; Function + &parseflags; + Finding Installed Library Information: the &ParseConfig; Function &parseconfig; - --> - - - Controlling the External Environment Used to Execute Build Commands - &ENV_file; + + Controlling Build Output + &output; @@ -234,11 +208,6 @@ &command-line; - - Providing Build Help: the &Help; Function - &help; - - Installing Files in Other Directories: the &Install; Builder &install; @@ -250,7 +219,7 @@ - Preventing Removal of Targets + Controlling Removal of Targets &file-removal; @@ -354,6 +323,11 @@ --> + + Miscellaneous Functionality + &misc; + + Troubleshooting &troubleshoot; @@ -361,7 +335,7 @@ Construction Variables - &variables; + &variables-xml; diff --git a/doc/user/main.xml b/doc/user/main.xml index 0dbadfc8..181b2a8d 100644 --- a/doc/user/main.xml +++ b/doc/user/main.xml @@ -56,21 +56,23 @@ - - + + + + @@ -83,7 +85,7 @@ - + @@ -94,59 +96,27 @@ @@ -160,10 +130,10 @@ Revision &buildrevision; (&builddate;) - 2004, 2005, 2006, 2007 + 2004, 2005, 2006, 2007, 2008 - 2004, 2005, 2006, 2007 + 2004, 2005, 2006, 2007, 2008 Steven Knight @@ -211,22 +181,27 @@ - Construction Environments + Environments &environments; - + + Merging Options into the Environment: the &MergeFlags; Function + &mergeflags; + + + Separating Compile Arguments into their Variables: the &ParseFlags; Function + &parseflags; + Finding Installed Library Information: the &ParseConfig; Function &parseconfig; - --> - - - Controlling the External Environment Used to Execute Build Commands - &ENV_file; + + Controlling Build Output + &output; @@ -234,11 +209,6 @@ &command-line; - - Providing Build Help: the &Help; Function - &help; - - Installing Files in Other Directories: the &Install; Builder &install; @@ -250,7 +220,7 @@ - Preventing Removal of Targets + Controlling Removal of Targets &file-removal; @@ -354,6 +324,11 @@ --> + + Miscellaneous Functionality + &misc; + + Troubleshooting &troubleshoot; @@ -361,7 +336,7 @@ Construction Variables - &variables; + &variables-xml; diff --git a/doc/user/mergeflags.in b/doc/user/mergeflags.in new file mode 100644 index 00000000..a1484097 --- /dev/null +++ b/doc/user/mergeflags.in @@ -0,0 +1,137 @@ + + + + + &SCons; construction environments have a &MergeFlags; method + that merges a dictionary of values into the construction environment. + &MergeFlags; treats each value in the dictionary + as a list of options such as one might pass to a command + (such as a compiler or linker). + &MergeFlags; will not duplicate an option + if it already exists in the construction environment variable. + + + + + + &MergeFlags; tries to be intelligent about merging options. + When merging options to any variable + whose name ends in PATH, + &MergeFlags; keeps the leftmost occurrence of the option, + because in typical lists of directory paths, + the first occurrence "wins." + When merging options to any other variable name, + &MergeFlags; keeps the rightmost occurrence of the option, + because in a list of typical command-line options, + the last occurrence "wins." + + + + + + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + flags = { 'CCFLAGS' : '-whatever -O3' } + env.MergeFlags(flags) + print env['CCFLAGS'] + + + + + scons -Q + + + + + Note that the default value for &cv-link-CCFLAGS; + + is an internal &SCons; object + which automatically converts + the options we specified as a string into a list. + + + + + + env = Environment() + env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) + flags = { 'CPPPATH' : ['/usr/opt/include', '/usr/local/include'] } + env.MergeFlags(flags) + print env['CPPPATH'] + + + + + scons -Q + + + + + Note that the default value for &cv-link-CPPPATH; + + is a normal Python list, + so we must specify its values as a list + in the dictionary we pass to the &MergeFlags; function. + + + + + + If &MergeFlags; is passed anything other than a dictionary, + it calls the &ParseFlags; method to convert it into a dictionary. + + + + + + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) + env.MergeFlags('-whatever -I/usr/opt/include -O3 -I/usr/local/include') + print env['CCFLAGS'] + print env['CPPPATH'] + + + + + scons -Q + + + + + In the combined example above, + &ParseFlags; has sorted the options into their corresponding variables + and returned a dictionary for &MergeFlags; to apply + to the construction variables + in the specified construction environment. + + diff --git a/doc/user/mergeflags.xml b/doc/user/mergeflags.xml new file mode 100644 index 00000000..723ab74d --- /dev/null +++ b/doc/user/mergeflags.xml @@ -0,0 +1,138 @@ + + + + + &SCons; construction environments have a &MergeFlags; method + that merges a dictionary of values into the construction environment. + &MergeFlags; treats each value in the dictionary + as a list of options such as one might pass to a command + (such as a compiler or linker). + &MergeFlags; will not duplicate an option + if it already exists in the construction environment variable. + + + + + + &MergeFlags; tries to be intelligent about merging options. + When merging options to any variable + whose name ends in PATH, + &MergeFlags; keeps the leftmost occurrence of the option, + because in typical lists of directory paths, + the first occurrence "wins." + When merging options to any other variable name, + &MergeFlags; keeps the rightmost occurrence of the option, + because in a list of typical command-line options, + the last occurrence "wins." + + + + + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + flags = { 'CCFLAGS' : '-whatever -O3' } + env.MergeFlags(flags) + print env['CCFLAGS'] + + + + % scons -Q + ['-option', '-O1', '-whatever', '-O3'] + scons: `.' is up to date. + + + + + Note that the default value for &cv-link-CCFLAGS; + + is an internal &SCons; object + which automatically converts + the options we specified as a string into a list. + + + + + env = Environment() + env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) + flags = { 'CPPPATH' : ['/usr/opt/include', '/usr/local/include'] } + env.MergeFlags(flags) + print env['CPPPATH'] + + + + % scons -Q + ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include'] + scons: `.' is up to date. + + + + + Note that the default value for &cv-link-CPPPATH; + + is a normal Python list, + so we must specify its values as a list + in the dictionary we pass to the &MergeFlags; function. + + + + + + If &MergeFlags; is passed anything other than a dictionary, + it calls the &ParseFlags; method to convert it into a dictionary. + + + + + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + env.Append(CPPPATH = ['/include', '/usr/local/include', '/usr/include']) + env.MergeFlags('-whatever -I/usr/opt/include -O3 -I/usr/local/include') + print env['CCFLAGS'] + print env['CPPPATH'] + + + + % scons -Q + ['-option', '-O1', '-whatever', '-O3'] + ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include'] + scons: `.' is up to date. + + + + + In the combined example above, + &ParseFlags; has sorted the options into their corresponding variables + and returned a dictionary for &MergeFlags; to apply + to the construction variables + in the specified construction environment. + + diff --git a/doc/user/misc.in b/doc/user/misc.in new file mode 100644 index 00000000..21bb0bb7 --- /dev/null +++ b/doc/user/misc.in @@ -0,0 +1,375 @@ + + + + + &SCons; supports a lot of additional functionality + that doesn't readily fit into the other chapters. + + + +
+ Verifying the Python Version: the &EnsurePythonVersion; Function + + + + Although the &SCons; code itself will run + on any Python version 1.5.2 or later, + you are perfectly free to make use of + Python syntax and modules from more modern versions + (for example, Python 2.4 or 2.5) + when writing your &SConscript; files + or your own local modules. + If you do this, it's usually helpful to + configure &SCons; to exit gracefully with an error message + if it's being run with a version of Python + that simply won't work with your code. + This is especially true if you're going to use &SCons; + to build source code that you plan to distribute publicly, + where you can't be sure of the Python version + that an anonymous remote user might use + to try to build your software. + + + + + + &SCons; provides an &EnsurePythonVersion; function for this. + You simply pass it the major and minor versions + numbers of the version of Python you require: + + + + + + + EnsurePythonVersion(2, 5) + + + + + And then &SCons will exit with the following error + message when a user runs it with an unsupported + earlier version of Python: + + + + + + + % scons -Q + Python 2.5 or greater required, but you have Python 2.3.6 + + +
+ +
+ Verifying the SCons Version: the &EnsureSConsVersion; Function + + + + You may, of course, write your &SConscript; files + to use features that were only added in + recent versions of &SCons;. + When you publicly distribute software that is built using &SCons;, + it's helpful to have &SCons; + verify the version being used and + exit gracefully with an error message + if the user's version of &SCons; won't work + with your &SConscript; files. + &SCons; provides an &EnsureSConsVersion; function + that verifies the version of &SCons; + in the same + the &EnsurePythonVersion; function + verifies the version of Python, + by passing in the major and minor versions + numbers of the version of SCons you require: + + + + + + + EnsureSConsVersion(1, 0) + + + + + And then &SCons will exit with the following error + message when a user runs it with an unsupported + earlier version of &SCons;: + + + + + + + % scons -Q + SCons 1.0 or greater required, but you have SCons 0.98.5 + + +
+ +
+ Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function + + + + &SCons; supports an &Exit; function + which can be used to terminate &SCons; + while reading the &SConscript; files, + usually because you've detected a condition + under which it doesn't make sense to proceed: + + + + + + if ARGUMENTS.get('FUTURE'): + print "The FUTURE option is not supported yet!" + Exit(2) + env = Environment() + env.Program('hello.c') + + + hello.c + + + + + scons -Q FUTURE=1 + scons -Q + + + + + The &Exit; function takes as an argument + the (numeric) exit status that you want &SCons; to exit with. + If you don't specify a value, + the default is to exit with 0, + which indicates successful execution. + + + + + + Note that the &Exit; function + is equivalent to calling the Python + sys.exit function + (which the it actually calls), + but because &Exit; is a &SCons; function, + you don't have to import the Python + sys module to use it. + + + +
+ +
+ Handling Nested Lists: the &Flatten; Function + + + + &SCons; supports a &Flatten; function + which takes an input Python sequence + (list or tuple) + and returns a flattened list + containing just the individual elements of + the sequence. + This can be handy when trying to examine + a list composed of the lists + returned by calls to various Builders. + For example, you might collect + object files built in different ways + into one call to the &Program; Builder + by just enclosing them in a list, as follows: + + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + + prog1.c + + + prog2.c + + + + + + Because the Builder calls in &SCons; + flatten their input lists, + this works just fine to build the program: + + + + + scons -Q + + + + + But if you were debugging your build + and wanted to print the absolute path + of each object file in the + objects list, + you might try the following simple approach, + trying to print each Node's + abspath + attribute: + + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in objects: + print object_file.abspath + + + prog1.c + + + prog2.c + + + + + + This does not work as expected + because each call to str + is operating an embedded list returned by + each &Object; call, + not on the underlying Nodes within those lists: + + + + + scons -Q + + + + + The solution is to use the &Flatten; function + so that you can pass each Node to + the str separately: + + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in Flatten(objects): + print object_file.abspath + + + prog1.c + + + prog2.c + + + + + + + % scons -Q + /home/me/project/prog1.o + /home/me/project/prog2.o + cc -o prog1.o -c prog1.c + cc -o prog2.o -c -DFOO prog2.c + cc -o prog1 prog1.o prog2.o + + +
diff --git a/doc/user/misc.xml b/doc/user/misc.xml new file mode 100644 index 00000000..54b021a0 --- /dev/null +++ b/doc/user/misc.xml @@ -0,0 +1,355 @@ + + + + + &SCons; supports a lot of additional functionality + that doesn't readily fit into the other chapters. + + + +
+ Verifying the Python Version: the &EnsurePythonVersion; Function + + + + Although the &SCons; code itself will run + on any Python version 1.5.2 or later, + you are perfectly free to make use of + Python syntax and modules from more modern versions + (for example, Python 2.4 or 2.5) + when writing your &SConscript; files + or your own local modules. + If you do this, it's usually helpful to + configure &SCons; to exit gracefully with an error message + if it's being run with a version of Python + that simply won't work with your code. + This is especially true if you're going to use &SCons; + to build source code that you plan to distribute publicly, + where you can't be sure of the Python version + that an anonymous remote user might use + to try to build your software. + + + + + + &SCons; provides an &EnsurePythonVersion; function for this. + You simply pass it the major and minor versions + numbers of the version of Python you require: + + + + + + + EnsurePythonVersion(2, 5) + + + + + And then &SCons; will exit with the following error + message when a user runs it with an unsupported + earlier version of Python: + + + + + + + % scons -Q + Python 2.5 or greater required, but you have Python 2.3.6 + + +
+ +
+ Verifying the SCons Version: the &EnsureSConsVersion; Function + + + + You may, of course, write your &SConscript; files + to use features that were only added in + recent versions of &SCons;. + When you publicly distribute software that is built using &SCons;, + it's helpful to have &SCons; + verify the version being used and + exit gracefully with an error message + if the user's version of &SCons; won't work + with your &SConscript; files. + &SCons; provides an &EnsureSConsVersion; function + that verifies the version of &SCons; + in the same + the &EnsurePythonVersion; function + verifies the version of Python, + by passing in the major and minor versions + numbers of the version of SCons you require: + + + + + + + EnsureSConsVersion(1, 0) + + + + + And then &SCons; will exit with the following error + message when a user runs it with an unsupported + earlier version of &SCons;: + + + + + + + % scons -Q + SCons 1.0 or greater required, but you have SCons 0.98.5 + + +
+ +
+ Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function + + + + &SCons; supports an &Exit; function + which can be used to terminate &SCons; + while reading the &SConscript; files, + usually because you've detected a condition + under which it doesn't make sense to proceed: + + + + + if ARGUMENTS.get('FUTURE'): + print "The FUTURE option is not supported yet!" + Exit(2) + env = Environment() + env.Program('hello.c') + + + + % scons -Q FUTURE=1 + The FUTURE option is not supported yet! + % scons -Q + cc -o hello.o -c hello.c + cc -o hello hello.o + + + + + The &Exit; function takes as an argument + the (numeric) exit status that you want &SCons; to exit with. + If you don't specify a value, + the default is to exit with 0, + which indicates successful execution. + + + + + + Note that the &Exit; function + is equivalent to calling the Python + sys.exit function + (which the it actually calls), + but because &Exit; is a &SCons; function, + you don't have to import the Python + sys module to use it. + + + +
+ +
+ Handling Nested Lists: the &Flatten; Function + + + + &SCons; supports a &Flatten; function + which takes an input Python sequence + (list or tuple) + and returns a flattened list + containing just the individual elements of + the sequence. + This can be handy when trying to examine + a list composed of the lists + returned by calls to various Builders. + For example, you might collect + object files built in different ways + into one call to the &Program; Builder + by just enclosing them in a list, as follows: + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + + + + Because the Builder calls in &SCons; + flatten their input lists, + this works just fine to build the program: + + + + + % scons -Q + cc -o prog1.o -c prog1.c + cc -o prog2.o -c -DFOO prog2.c + cc -o prog1 prog1.o prog2.o + + + + + But if you were debugging your build + and wanted to print the absolute path + of each object file in the + objects list, + you might try the following simple approach, + trying to print each Node's + abspath + attribute: + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in objects: + print object_file.abspath + + + + + This does not work as expected + because each call to str + is operating an embedded list returned by + each &Object; call, + not on the underlying Nodes within those lists: + + + + + % scons -Q + AttributeError: NodeList instance has no attribute 'abspath': + File "/home/my/project/SConstruct", line 8: + print object_file.abspath + + + + + The solution is to use the &Flatten; function + so that you can pass each Node to + the str separately: + + + + + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in Flatten(objects): + print object_file.abspath + + + + + + % scons -Q + /home/me/project/prog1.o + /home/me/project/prog2.o + cc -o prog1.o -c prog1.c + cc -o prog2.o -c -DFOO prog2.c + cc -o prog1 prog1.o prog2.o + + +
diff --git a/doc/user/output.in b/doc/user/output.in new file mode 100644 index 00000000..02d74844 --- /dev/null +++ b/doc/user/output.in @@ -0,0 +1,681 @@ + + + + + A key aspect of creating a usable build configuration + is providing good output from the build + so its users can readily understand + what the build is doing + and get information about how to control the build. + &SCons; provides several ways of + controlling output from the build configuration + to help make the build + more useful and understandable. + + + +
+ Providing Build Help: the &Help; Function + + + + 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 + + + + + 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 complete help text on Windows: + + + + + scons -h + + + + + But only show the relevant option on a Linux or UNIX system: + + + + + scons -h + + + + + 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. + + + +
+ +
+ Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables + + + + Sometimes the commands executed + to compile object files or link programs + (or build other targets) + can get very long, + long enough to make it difficult for users + to distinguish error messages or + other important build output + from the commands themselves. + All of the default $*COM variables + that specify the command lines + used to build various types of target files + have a corresponding $*COMSTR variable + that can be set to an alternative + string that will be displayed + when the target is built. + + + + + + For example, suppose you want to + have &SCons; display a + "Compiling" + message whenever it's compiling an object file, + and a + "Linking" + when it's linking an executable. + You could write a &SConstruct; file + that looks like: + + + + + + env = Environment(CCCOMSTR = "Compiling $TARGET", + LINKCOMSTR = "Linking $TARGET") + env.Program('foo.c') + + + foo.c + + + + + + Which would then yield the output: + + + + + + + % scons -Q + Compiling foo.o + Linking foo + + + + + &SCons; performs complete variable substitution + on $*COMSTR variables, + so they have access to all of the + standard variables like &cv-TARGET; &cv-SOURCES;, etc., + as well as any construction variables + that happen to be configured in + the construction environment + used to build a specific target. + + + + + + Of course, sometimes it's still important to + be able to see the exact command + that &SCons; will execute to build a target. + For example, you may simply need to verify + that &SCons; is configured to supply + the right options to the compiler, + or a developer may want to + cut-and-paste a comiloe command + to add a few options + for a custom test. + + + + + + One common way to give users + control over whether or not + &SCons; should print the actual command line + or a short, configured summary + is to add support for a + VERBOSE + command-line variable to your &SConstruct; file. + A simple configuration for this might look like: + + + + + + env = Environment() + if ARGUMENTS.get('VERBOSE') != "1': + env['CCCOMSTR'] = "Compiling $TARGET" + env['LINKCOMSTR'] = "Linking $TARGET" + env.Program('foo.c') + + + foo.c + + + + + + + By only setting the appropriate + $*COMSTR variables + if the user specifies + VERBOSE=1 + on the command line, + the user has control + over how &SCons; + displays these particular command lines: + + + + + + + % scons -Q + Compiling foo.o + Linking foo + % scons -Q -c + Removed foo.o + Removed foo + % scons -Q VERBOSE=1 + cc -o foo.o -c foo.c + cc -o foo foo.o + + +
+ +
+ Providing Build Progress Output: the &Progress; Function + + + + Another aspect of providing good build output + is to give the user feedback + about what &SCons; is doing + even when nothing is being built at the moment. + This can be especially true for large builds + when most of the targets are already up-to-date. + Because &SCons; can take a long time + making absolutely sure that every + target is, in fact, up-to-date + with respect to a lot of dependency files, + it can be easy for users to mistakenly + conclude that &SCons; is hung + or that there is some other problem with the build. + + + + + + One way to deal with this perception + is to configure &SCons; to print something to + let the user know what it's "thinking about." + The &Progress; function + allows you to specify a string + that will be printed for every file + that &SCons; is "considering" + while it is traversing the dependency graph + to decide what targets are or are not up-to-date. + + + + + + Progress('Evaluating $TARGET\n') + Program('f1.c') + Program('f2.c') + + + f1.c + + + f2.c + + + + + + Note that the &Progress; function does not + arrange for a newline to be printed automatically + at the end of the string (as does the Python + print statement), + and we must specify the + \n + that we want printed at the end of the configured string. + This configuration, then, + will have &SCons; + print that it is Evaluating + each file that it encounters + in turn as it traverses the dependency graph: + + + + + scons -Q + + + + + Of course, normally you don't want to add + all of these additional lines to your build output, + as that can make it difficult for the user + to find errors or other important messages. + A more useful way to display + this progress might be + to have the file names printed + directly to the user's screen, + not to the same standard output + stream where build output is printed, + and to use a carriage return character + (\r) + so that each file name gets re-printed on the same line. + Such a configuration would look like: + + + + + Progress('$TARGET\r', + file=open('/dev/tty', 'w'), + overwrite=True) + Program('f1.c') + Program('f2.c') + + + + + Note that we also specified the + overwrite=True argument + to the &Progress; function, + which causes &SCons; to + "wipe out" the previous string with space characters + before printing the next &Progress; string. + Without the + overwrite=True argument, + a shorter file name would not overwrite + all of the charactes in a longer file name that + precedes it, + making it difficult to tell what the + actual file name is on the output. + Also note that we opened up the + /dev/tty file + for direct access (on POSIX) to + the user's screen. + On Windows, the equivalent would be to open + the con: file name. + + + + + + Also, it's important to know that although you can use + $TARGET to substitute the name of + the node in the string, + the &Progress; function does not + perform general variable substitution + (because there's not necessarily a construction + environment involved in evaluating a node + like a source file, for example). + + + + + + You can also specify a list of strings + to the &Progress; function, + in which case &SCons; will + display each string in turn. + This can be used to implement a "spinner" + by having &SCons; cycle through a + sequence of strings: + + + + + Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) + Program('f1.c') + Program('f2.c') + + + + + Note that here we have also used the + interval= + keyword argument to have &SCons; + only print a new "spinner" string + once every five evaluated nodes. + Using an interval= count, + even with strings that use $TARGET like + our examples above, + can be a good way to lessen the + work that &SCons; expends printing &Progress; strings, + while still giving the user feedback + that indicates &SCons; is still + working on evaluating the build. + + + + + + Lastly, you can have direct control + over how to print each evaluated node + by passing a Python function + (or other Python callable) + to the &Progress function. + Your function will be called + for each evaluated node, + allowing you to + implement more sophisticated logic + like adding a counter: + + + + + + screen = open('/dev/tty', 'w') + count = 0 + def progress_function(node) + count += 1 + screen.write('Node %4d: %s\r' % (count, node)) + + Progress(progress_function) + + + + + + Of course, if you choose, + you could completely ignore the + node argument to the function, + and just print a count, + or anything else you wish. + + + + + + (Note that there's an obvious follow-on question here: + how would you find the total number of nodes + that will be + evaluated so you can tell the user how + close the build is to finishing? + Unfortunately, in the general case, + there isn't a good way to do that, + short of having &SCons; evaluate its + dependency graph twice, + first to count the total and + the second time to actually build the targets. + This would be necessary because + you can't know in advance which + target(s) the user actually requested + to be built. + The entire build may consist of thousands of Nodes, + for example, + but maybe the user specifically requested + that only a single object file be built.) + + + +
+ +
+ Printing Detailed Build Status: the &GetBuildFailures; Function + + + + SCons, like most build tools, returns zero status to + the shell on success and nonzero status on failure. + Sometimes it's useful to give more information about + the build status at the end of the run, for instance + to print an informative message, send an email, or + page the poor slob who broke the build. + + + + + + SCons provides a &GetBuildFailures; method that + you can use in a python atexit function + to get a list of objects describing the actions that failed + while attempting to build targets. There can be more + than one if you're using -j. Here's a + simple example: + + + + + + import atexit + + def print_build_failures(): + from SCons.Script import GetBuildFailures + for bf in GetBuildFailures(): + print "%s failed: %s" % (bf.node, bf.errstr) + atexit.register(print_build_failures) + + + + + + The atexit.register call + registers print_build_failures + as an atexit callback, to be called + before &SCons; exits. When that function is called, + it calls &GetBuildFailures; to fetch the list of failed objects. + See the man page + for the detailed contents of the returned objects; + some of the more useful attributes are + .node, + .errstr, + .filename, and + .command. + The filename is not necessarily + the same file as the node; the + node is the target that was + being built when the error occurred, while the + filenameis the file or dir that + actually caused the error. + Note: only call &GetBuildFailures; at the end of the + build; calling it at any other time is undefined. + + + + + + Here is a more complete example showing how to + turn each element of &GetBuildFailures; into a string: + + + + + + # Make the build fail if we pass fail=1 on the command line + if ARGUMENTS.get('fail', 0): + Command('target', 'source', ['/bin/false']) + + def bf_to_str(bf): + """Convert an element of GetBuildFailures() to a string + in a useful way.""" + import SCons.Errors + if bf is None: # unknown targets product None in list + return '(unknown tgt)' + elif isinstance(bf, SCons.Errors.StopError): + return str(bf) + elif bf.node: + return str(bf.node) + ': ' + bf.errstr + elif bf.filename: + return bf.filename + ': ' + bf.errstr + return 'unknown failure: ' + bf.errstr + import atexit + + def build_status(): + """Convert the build status to a 2-tuple, (status, msg).""" + from SCons.Script import GetBuildFailures + bf = GetBuildFailures() + if bf: + # bf is normally a list of build failures; if an element is None, + # it's because of a target that scons doesn't know anything about. + status = 'failed' + failures_message = "\n".join(["Failed building %s" % bf_to_str(x) + for x in bf if x is not None]) + else: + # if bf is None, the build completed successfully. + status = 'ok' + failures_message = '' + return (status, failures_message) + + def display_build_status(): + """Display the build status. Called by atexit. + Here you could do all kinds of complicated things.""" + status, failures_message = build_status() + if status == 'failed': + print "FAILED!!!!" # could display alert, ring bell, etc. + elif status == 'ok': + print "Build succeeded." + print failures_message + + atexit.register(display_build_status) + + + + + + When this runs, you'll see the appropriate output: + + + + + scons -Q + scons -Q fail=1 + + +
diff --git a/doc/user/output.xml b/doc/user/output.xml new file mode 100644 index 00000000..d42457a9 --- /dev/null +++ b/doc/user/output.xml @@ -0,0 +1,691 @@ + + + + + A key aspect of creating a usable build configuration + is providing good output from the build + so its users can readily understand + what the build is doing + and get information about how to control the build. + &SCons; provides several ways of + controlling output from the build configuration + to help make the build + more useful and understandable. + + + +
+ Providing Build Help: the &Help; Function + + + + 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 complete 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. + + + +
+ +
+ Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables + + + + Sometimes the commands executed + to compile object files or link programs + (or build other targets) + can get very long, + long enough to make it difficult for users + to distinguish error messages or + other important build output + from the commands themselves. + All of the default $*COM variables + that specify the command lines + used to build various types of target files + have a corresponding $*COMSTR variable + that can be set to an alternative + string that will be displayed + when the target is built. + + + + + + For example, suppose you want to + have &SCons; display a + "Compiling" + message whenever it's compiling an object file, + and a + "Linking" + when it's linking an executable. + You could write a &SConstruct; file + that looks like: + + + + + env = Environment(CCCOMSTR = "Compiling $TARGET", + LINKCOMSTR = "Linking $TARGET") + env.Program('foo.c') + + + + + Which would then yield the output: + + + + + + + % scons -Q + Compiling foo.o + Linking foo + + + + + &SCons; performs complete variable substitution + on $*COMSTR variables, + so they have access to all of the + standard variables like &cv-TARGET; &cv-SOURCES;, etc., + as well as any construction variables + that happen to be configured in + the construction environment + used to build a specific target. + + + + + + Of course, sometimes it's still important to + be able to see the exact command + that &SCons; will execute to build a target. + For example, you may simply need to verify + that &SCons; is configured to supply + the right options to the compiler, + or a developer may want to + cut-and-paste a comiloe command + to add a few options + for a custom test. + + + + + + One common way to give users + control over whether or not + &SCons; should print the actual command line + or a short, configured summary + is to add support for a + VERBOSE + command-line variable to your &SConstruct; file. + A simple configuration for this might look like: + + + + + env = Environment() + if ARGUMENTS.get('VERBOSE') != "1': + env['CCCOMSTR'] = "Compiling $TARGET" + env['LINKCOMSTR'] = "Linking $TARGET" + env.Program('foo.c') + + + + + + By only setting the appropriate + $*COMSTR variables + if the user specifies + VERBOSE=1 + on the command line, + the user has control + over how &SCons; + displays these particular command lines: + + + + + + + % scons -Q + Compiling foo.o + Linking foo + % scons -Q -c + Removed foo.o + Removed foo + % scons -Q VERBOSE=1 + cc -o foo.o -c foo.c + cc -o foo foo.o + + +
+ +
+ Providing Build Progress Output: the &Progress; Function + + + + Another aspect of providing good build output + is to give the user feedback + about what &SCons; is doing + even when nothing is being built at the moment. + This can be especially true for large builds + when most of the targets are already up-to-date. + Because &SCons; can take a long time + making absolutely sure that every + target is, in fact, up-to-date + with respect to a lot of dependency files, + it can be easy for users to mistakenly + conclude that &SCons; is hung + or that there is some other problem with the build. + + + + + + One way to deal with this perception + is to configure &SCons; to print something to + let the user know what it's "thinking about." + The &Progress; function + allows you to specify a string + that will be printed for every file + that &SCons; is "considering" + while it is traversing the dependency graph + to decide what targets are or are not up-to-date. + + + + + Progress('Evaluating $TARGET\n') + Program('f1.c') + Program('f2.c') + + + + + Note that the &Progress; function does not + arrange for a newline to be printed automatically + at the end of the string (as does the Python + print statement), + and we must specify the + \n + that we want printed at the end of the configured string. + This configuration, then, + will have &SCons; + print that it is Evaluating + each file that it encounters + in turn as it traverses the dependency graph: + + + + + % scons -Q + Evaluating SConstruct + Evaluating f1.c + Evaluating f1.o + cc -o f1.o -c f1.c + Evaluating f1 + cc -o f1 f1.o + Evaluating f2.c + Evaluating f2.o + cc -o f2.o -c f2.c + Evaluating f2 + cc -o f2 f2.o + Evaluating . + + + + + Of course, normally you don't want to add + all of these additional lines to your build output, + as that can make it difficult for the user + to find errors or other important messages. + A more useful way to display + this progress might be + to have the file names printed + directly to the user's screen, + not to the same standard output + stream where build output is printed, + and to use a carriage return character + (\r) + so that each file name gets re-printed on the same line. + Such a configuration would look like: + + + + + Progress('$TARGET\r', + file=open('/dev/tty', 'w'), + overwrite=True) + Program('f1.c') + Program('f2.c') + + + + + Note that we also specified the + overwrite=True argument + to the &Progress; function, + which causes &SCons; to + "wipe out" the previous string with space characters + before printing the next &Progress; string. + Without the + overwrite=True argument, + a shorter file name would not overwrite + all of the charactes in a longer file name that + precedes it, + making it difficult to tell what the + actual file name is on the output. + Also note that we opened up the + /dev/tty file + for direct access (on POSIX) to + the user's screen. + On Windows, the equivalent would be to open + the con: file name. + + + + + + Also, it's important to know that although you can use + $TARGET to substitute the name of + the node in the string, + the &Progress; function does not + perform general variable substitution + (because there's not necessarily a construction + environment involved in evaluating a node + like a source file, for example). + + + + + + You can also specify a list of strings + to the &Progress; function, + in which case &SCons; will + display each string in turn. + This can be used to implement a "spinner" + by having &SCons; cycle through a + sequence of strings: + + + + + Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) + Program('f1.c') + Program('f2.c') + + + + + Note that here we have also used the + interval= + keyword argument to have &SCons; + only print a new "spinner" string + once every five evaluated nodes. + Using an interval= count, + even with strings that use $TARGET like + our examples above, + can be a good way to lessen the + work that &SCons; expends printing &Progress; strings, + while still giving the user feedback + that indicates &SCons; is still + working on evaluating the build. + + + + + + Lastly, you can have direct control + over how to print each evaluated node + by passing a Python function + (or other Python callable) + to the &Progress; function. + Your function will be called + for each evaluated node, + allowing you to + implement more sophisticated logic + like adding a counter: + + + + + screen = open('/dev/tty', 'w') + count = 0 + def progress_function(node) + count += 1 + screen.write('Node %4d: %s\r' % (count, node)) + + Progress(progress_function) + + + + + Of course, if you choose, + you could completely ignore the + node argument to the function, + and just print a count, + or anything else you wish. + + + + + + (Note that there's an obvious follow-on question here: + how would you find the total number of nodes + that will be + evaluated so you can tell the user how + close the build is to finishing? + Unfortunately, in the general case, + there isn't a good way to do that, + short of having &SCons; evaluate its + dependency graph twice, + first to count the total and + the second time to actually build the targets. + This would be necessary because + you can't know in advance which + target(s) the user actually requested + to be built. + The entire build may consist of thousands of Nodes, + for example, + but maybe the user specifically requested + that only a single object file be built.) + + + +
+ +
+ Printing Detailed Build Status: the &GetBuildFailures; Function + + + + SCons, like most build tools, returns zero status to + the shell on success and nonzero status on failure. + Sometimes it's useful to give more information about + the build status at the end of the run, for instance + to print an informative message, send an email, or + page the poor slob who broke the build. + + + + + + SCons provides a &GetBuildFailures; method that + you can use in a python atexit function + to get a list of objects describing the actions that failed + while attempting to build targets. There can be more + than one if you're using -j. Here's a + simple example: + + + + + import atexit + + def print_build_failures(): + from SCons.Script import GetBuildFailures + for bf in GetBuildFailures(): + print "%s failed: %s" % (bf.node, bf.errstr) + atexit.register(print_build_failures) + + + + + The atexit.register call + registers print_build_failures + as an atexit callback, to be called + before &SCons; exits. When that function is called, + it calls &GetBuildFailures; to fetch the list of failed objects. + See the man page + for the detailed contents of the returned objects; + some of the more useful attributes are + .node, + .errstr, + .filename, and + .command. + The filename is not necessarily + the same file as the node; the + node is the target that was + being built when the error occurred, while the + filenameis the file or dir that + actually caused the error. + Note: only call &GetBuildFailures; at the end of the + build; calling it at any other time is undefined. + + + + + + Here is a more complete example showing how to + turn each element of &GetBuildFailures; into a string: + + + + + # Make the build fail if we pass fail=1 on the command line + if ARGUMENTS.get('fail', 0): + Command('target', 'source', ['/bin/false']) + + def bf_to_str(bf): + """Convert an element of GetBuildFailures() to a string + in a useful way.""" + import SCons.Errors + if bf is None: # unknown targets product None in list + return '(unknown tgt)' + elif isinstance(bf, SCons.Errors.StopError): + return str(bf) + elif bf.node: + return str(bf.node) + ': ' + bf.errstr + elif bf.filename: + return bf.filename + ': ' + bf.errstr + return 'unknown failure: ' + bf.errstr + import atexit + + def build_status(): + """Convert the build status to a 2-tuple, (status, msg).""" + from SCons.Script import GetBuildFailures + bf = GetBuildFailures() + if bf: + # bf is normally a list of build failures; if an element is None, + # it's because of a target that scons doesn't know anything about. + status = 'failed' + failures_message = "\n".join(["Failed building %s" % bf_to_str(x) + for x in bf if x is not None]) + else: + # if bf is None, the build completed successfully. + status = 'ok' + failures_message = '' + return (status, failures_message) + + def display_build_status(): + """Display the build status. Called by atexit. + Here you could do all kinds of complicated things.""" + status, failures_message = build_status() + if status == 'failed': + print "FAILED!!!!" # could display alert, ring bell, etc. + elif status == 'ok': + print "Build succeeded." + print failures_message + + atexit.register(display_build_status) + + + + + When this runs, you'll see the appropriate output: + + + + + % scons -Q + scons: `.' is up to date. + Build succeeded. + % scons -Q fail=1 + scons: *** Source `source' not found, needed by target `target'. Stop. + FAILED!!!! + Failed building Source `source' not found, needed by target `target'. + + +
diff --git a/doc/user/parseconfig.in b/doc/user/parseconfig.in index 45b58cc8..e5cc5644 100644 --- a/doc/user/parseconfig.in +++ b/doc/user/parseconfig.in @@ -25,48 +25,56 @@ - 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 + Configuring the right options to build programs to work with + libraries--especially shared libraries--that are available + on POSIX systems can be very complicated. + To help this situation, + various utilies with names that end in config + return the command-line options for the GNU Compiler Collection (GCC) + that are needed to use these libraries; + for example, the command-line options + to use a library named lib + would be found by calling a utility named lib-config. + + + + + + A more recent convention is that these options + are available from the generic pkg-config program, + which has common framework, error handling, and the like, + so that all the package creator has to do is provide the set of strings + for his particular package. &SCons; construction environments have a &ParseConfig; method - that executes a utility and configures - the appropriate construction variables + that executes a *config utility + (either pkg-config or a + more specific 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 x11") - - - int f1() { } - - - int f2() { } - - - int f3() { } + env['CPPPATH'] = ['/lib/compat'] + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] - &SCons; will execute the specified command string - and XXX + &SCons; will execute the specified command string, + parse the resultant flags, + and add the flags to the appropriate environment variables. @@ -76,6 +84,31 @@ - XXX + In the example above, &SCons; has added the include directory to + CPPPATH. + (Depending upon what other flags are emitted by the + pkg-config command, + other variables may have been extended as well.) + + + + Note that the options are merged with existing options using + the &MergeFlags; method, + so that each option only occurs once in the construction variable: + + + + + + env = Environment() + env.ParseConfig("pkg-config x11 --cflags --libs") + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] + + + + + scons -Q + diff --git a/doc/user/parseconfig.xml b/doc/user/parseconfig.xml index 25ea12c8..46bd4dc0 100644 --- a/doc/user/parseconfig.xml +++ b/doc/user/parseconfig.xml @@ -25,47 +25,90 @@ - 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 + Configuring the right options to build programs to work with + libraries--especially shared libraries--that are available + on POSIX systems can be very complicated. + To help this situation, + various utilies with names that end in config + return the command-line options for the GNU Compiler Collection (GCC) + that are needed to use these libraries; + for example, the command-line options + to use a library named lib + would be found by calling a utility named lib-config. + + + + + + A more recent convention is that these options + are available from the generic pkg-config program, + which has common framework, error handling, and the like, + so that all the package creator has to do is provide the set of strings + for his particular package. &SCons; construction environments have a &ParseConfig; method - that executes a utility and configures - the appropriate construction variables + that executes a *config utility + (either pkg-config or a + more specific 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 x11") + env['CPPPATH'] = ['/lib/compat'] + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] - &SCons; will execute the specified command string - and XXX + &SCons; will execute the specified command string, + parse the resultant flags, + and add the flags to the appropriate environment variables. % scons -Q + ['/lib/compat', '/usr/X11/include'] scons: `.' is up to date. - XXX + In the example above, &SCons; has added the include directory to + CPPPATH. + (Depending upon what other flags are emitted by the + pkg-config command, + other variables may have been extended as well.) + + + + Note that the options are merged with existing options using + the &MergeFlags; method, + so that each option only occurs once in the construction variable: + + + + + env = Environment() + env.ParseConfig("pkg-config x11 --cflags --libs") + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] + + + + % scons -Q + ['/usr/X11/include'] + scons: `.' is up to date. + diff --git a/doc/user/parseflags.in b/doc/user/parseflags.in new file mode 100644 index 00000000..733ee1d4 --- /dev/null +++ b/doc/user/parseflags.in @@ -0,0 +1,184 @@ + + + + + &SCons; has a bewildering array of construction variables + for different types of options when building programs. + Sometimes you may not know exactly which variable + should be used for a particular option. + + + + + + &SCons; construction environments have a &ParseFlags; method + that takes a set of typical command-line options + and distrbutes them into the appropriate construction variables. + Historically, it was created to support the &ParseConfig; method, + so it focuses on options used by the GNU Compiler Collection (GCC) + for the C and C++ toolchains. + + + + + + &ParseFlags; returns a dictionary containing the options + distributed into their respective construction variables. + Normally, this dictionary would be passed to &MergeFlags; + to merge the options into a &consenv;, + but the dictionary can be edited if desired to provide + additional functionality. + (Note that if the flags are not going to be edited, + calling &MergeFlags; with the options directly + will avoid an additional step.) + + + + + + env = Environment() + d = env.ParseFlags("-I/opt/include -L/opt/lib -lfoo") + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + int main() { return 0; } + + + + + scons -Q + + + + + Note that if the options are limited to generic types + like those above, + they will be correctly translated for other platform types: + + + + + scons -Q + + + + + Since the assumption is that the flags are used for the GCC toolchain, + unrecognized flags are placed in &cv-link-CCFLAGS; + so they will be used for both C and C++ compiles: + + + + + + env = Environment() + d = env.ParseFlags("-whatever") + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + int main() { return 0; } + + + + + scons -Q + + + + + &ParseFlags; will also accept a (recursive) list of strings as input; + the list is flattened before the strings are processed: + + + + + + env = Environment() + d = env.ParseFlags(["-I/opt/include", ["-L/opt/lib", "-lfoo"]]) + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + int main() { return 0; } + + + + + scons -Q + + + + + If a string begins with a "!" (an exclamation mark, often called a bang), + the string is passed to the shell for execution. + The output of the command is then parsed: + + + + + + env = Environment() + d = env.ParseFlags(["!echo -I/opt/include", "!echo -L/opt/lib", "-lfoo"]) + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + int main() { return 0; } + + + + + scons -Q + + + + + &ParseFlags; is regularly updated for new options; + consult the man page for details about those currently recognized. + + diff --git a/doc/user/parseflags.xml b/doc/user/parseflags.xml new file mode 100644 index 00000000..e52bd780 --- /dev/null +++ b/doc/user/parseflags.xml @@ -0,0 +1,187 @@ + + + + + &SCons; has a bewildering array of construction variables + for different types of options when building programs. + Sometimes you may not know exactly which variable + should be used for a particular option. + + + + + + &SCons; construction environments have a &ParseFlags; method + that takes a set of typical command-line options + and distrbutes them into the appropriate construction variables. + Historically, it was created to support the &ParseConfig; method, + so it focuses on options used by the GNU Compiler Collection (GCC) + for the C and C++ toolchains. + + + + + + &ParseFlags; returns a dictionary containing the options + distributed into their respective construction variables. + Normally, this dictionary would be passed to &MergeFlags; + to merge the options into a &consenv;, + but the dictionary can be edited if desired to provide + additional functionality. + (Note that if the flags are not going to be edited, + calling &MergeFlags; with the options directly + will avoid an additional step.) + + + + + env = Environment() + d = env.ParseFlags("-I/opt/include -L/opt/lib -lfoo") + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + + % scons -Q + CPPPATH ['/opt/include'] + LIBPATH ['/opt/lib'] + LIBS ['foo'] + cc -o f1.o -c -I/opt/include f1.c + cc -o f1 f1.o -L/opt/lib -lfoo + + + + + Note that if the options are limited to generic types + like those above, + they will be correctly translated for other platform types: + + + + + C:\>scons -Q + CPPPATH ['/opt/include'] + LIBPATH ['/opt/lib'] + LIBS ['foo'] + cl /nologo /I\opt\include /c f1.c /Fof1.obj + link /nologo /OUT:f1.exe /LIBPATH:\opt\lib foo.lib f1.obj + + + + + Since the assumption is that the flags are used for the GCC toolchain, + unrecognized flags are placed in &cv-link-CCFLAGS; + so they will be used for both C and C++ compiles: + + + + + env = Environment() + d = env.ParseFlags("-whatever") + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + + % scons -Q + CCFLAGS -whatever + cc -o f1.o -c -whatever f1.c + cc -o f1 f1.o + + + + + &ParseFlags; will also accept a (recursive) list of strings as input; + the list is flattened before the strings are processed: + + + + + env = Environment() + d = env.ParseFlags(["-I/opt/include", ["-L/opt/lib", "-lfoo"]]) + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + + % scons -Q + CPPPATH ['/opt/include'] + LIBPATH ['/opt/lib'] + LIBS ['foo'] + cc -o f1.o -c -I/opt/include f1.c + cc -o f1 f1.o -L/opt/lib -lfoo + + + + + If a string begins with a "!" (an exclamation mark, often called a bang), + the string is passed to the shell for execution. + The output of the command is then parsed: + + + + + env = Environment() + d = env.ParseFlags(["!echo -I/opt/include", "!echo -L/opt/lib", "-lfoo"]) + l = d.items() + l.sort() + for k,v in l: + if v: + print k, v + env.MergeFlags(d) + env.Program('f1.c') + + + + % scons -Q + CPPPATH ['/opt/include'] + LIBPATH ['/opt/lib'] + LIBS ['foo'] + cc -o f1.o -c -I/opt/include f1.c + cc -o f1 f1.o -L/opt/lib -lfoo + + + + + &ParseFlags; is regularly updated for new options; + consult the man page for details about those currently recognized. + + diff --git a/doc/user/separate.in b/doc/user/separate.in index dc77af3a..f23111d9 100644 --- a/doc/user/separate.in +++ b/doc/user/separate.in @@ -467,11 +467,11 @@ program using the F path name.
- Using Glob() with &VariantDir; + Using &Glob; with &VariantDir; - The Glob() file name pattern matching function + The &Glob; file name pattern matching function works just as usual when using &VariantDir;. For example, if the src/SConscript @@ -503,8 +503,9 @@ program using the F path name. Then with the same &SConstruct; file as in the previous section, - and source files f1.c and f2.c in src, we would see the following - output: + and source files f1.c + and f2.c in src, + we would see the following output: @@ -516,8 +517,8 @@ program using the F path name. - The Glob function returns Nodes in the build/ tree, as - you'd expect. + The &Glob; function returns Nodes in the + build/ tree, as you'd expect. diff --git a/doc/user/separate.xml b/doc/user/separate.xml index c5e90e0d..01f15eec 100644 --- a/doc/user/separate.xml +++ b/doc/user/separate.xml @@ -458,11 +458,11 @@ program using the F path name.
- Using Glob() with &VariantDir; + Using &Glob; with &VariantDir; - The Glob() file name pattern matching function + The &Glob; file name pattern matching function works just as usual when using &VariantDir;. For example, if the src/SConscript @@ -478,8 +478,9 @@ program using the F path name. Then with the same &SConstruct; file as in the previous section, - and source files f1.c and f2.c in src, we would see the following - output: + and source files f1.c + and f2.c in src, + we would see the following output: @@ -496,8 +497,8 @@ program using the F path name. - The Glob function returns Nodes in the build/ tree, as - you'd expect. + The &Glob; function returns Nodes in the + build/ tree, as you'd expect. diff --git a/doc/user/sideeffect.in b/doc/user/sideeffect.in new file mode 100644 index 00000000..cbeefae9 --- /dev/null +++ b/doc/user/sideeffect.in @@ -0,0 +1,216 @@ + + + + + + + Sometimes a program the you need to call + to build a target file + will also update another file, + such as a log file describing what the program + does while building the target. + For example, we the folowing configuration + would have &SCons; invoke a hypothetical + script named build + (in the local directory) + with command-line arguments that write + log information to a common + logfile.txt file: + + + + + env = Environment() + env.Command('file1.out', 'file.in', + './build --log logfile.txt $SOURCE $TARGET') + env.Command('file2.out', 'file.in', + './build --log logfile.txt $SOURCE $TARGET') + + + + + This can cause problems when running + the build in parallel if + &SCons; decides to update both targets + by running both program invocations at the same time. + The multiple program invocations + may interfere with each other + writing to the common log file, + leading at best to intermixed output in the log file, + and at worst to an actual failed build + (on a system like Windows, for example, + where only one process at a time can open the log file for writing). + + + + + + We can make sure that &SCons; does not + run these build + commands at the same time + by using the &SideEffect; function + to specify that updating + the logfile.txt file + is a side effect of building the specified + file1 + and + file2 + target files: + + + + + + env = Environment() + f1 = env.Command('file1.out', 'file1.in', + './build --log logfile.txt $SOURCE $TARGET') + f2 = env.Command('file2.out', 'file2.in', + './build --log logfile.txt $SOURCE $TARGET') + env.SideEffect('logfile.txt', f1 + f2) + + file1.in + file2.in + + cat + + + + + + + + + + This makes sure the the two + ./build steps are run sequentially, + even withthe --jobs=2 in the command line: + + + + + scons -Q --jobs=2 + + + + + The &SideEffect; function can be called multiple + times for the same side-effect file. + Additionally, the name used as a &SideEffect; does not + even need to actually exist as a file on disk. + &SCons; will still make sure + that the relevant targets + will be executed sequentially, not in parallel: + + + + + + env = Environment() + f1 = env.Command('file1.out', [], 'echo >$TARGET data1') + env.SideEffect('not_really_updated', f1) + f2 = env.Command('file2.out', [], 'echo >$TARGET data2') + env.SideEffect('not_really_updated', f2) + + + + + scons -Q --jobs=2 + + + + + Note that it might be tempting to + use &SideEffect; for additional target files + that a command produces. + For example, versions the Microsoft Visual C/C++ compiler + produce a foo.ilk + alongside compiling foo.obj file. + Specifying foo.ilk as a + side-effect of foo.obj + is not a recommended use of &SideEffect;, + because &SCons; handle side-effect files + slightly differently in its analysis of the dependency graph. + When a command produces multiple output files, + they should be specified as multiple targets of + the call to the relevant builder function, + and the &SideEffect; function itself should really only be used + when it's important to ensure that commands are not executed in parallel, + such as when a "peripheral" file (such as a log file) + may actually updated by more than one command invocation. + + diff --git a/doc/user/sideeffect.xml b/doc/user/sideeffect.xml new file mode 100644 index 00000000..2cb42545 --- /dev/null +++ b/doc/user/sideeffect.xml @@ -0,0 +1,211 @@ + + + + + + + Sometimes a program the you need to call + to build a target file + will also update another file, + such as a log file describing what the program + does while building the target. + For example, we the folowing configuration + would have &SCons; invoke a hypothetical + script named build + (in the local directory) + with command-line arguments that write + log information to a common + logfile.txt file: + + + + + env = Environment() + env.Command('file1.out', 'file.in', + './build --log logfile.txt $SOURCE $TARGET') + env.Command('file2.out', 'file.in', + './build --log logfile.txt $SOURCE $TARGET') + + + + + This can cause problems when running + the build in parallel if + &SCons; decides to update both targets + by running both program invocations at the same time. + The multiple program invocations + may interfere with each other + writing to the common log file, + leading at best to intermixed output in the log file, + and at worst to an actual failed build + (on a system like Windows, for example, + where only one process at a time can open the log file for writing). + + + + + + We can make sure that &SCons; does not + run these build + commands at the same time + by using the &SideEffect; function + to specify that updating + the logfile.txt file + is a side effect of building the specified + file1 + and + file2 + target files: + + + + + env = Environment() + f1 = env.Command('file1.out', 'file1.in', + './build --log logfile.txt $SOURCE $TARGET') + f2 = env.Command('file2.out', 'file2.in', + './build --log logfile.txt $SOURCE $TARGET') + env.SideEffect('logfile.txt', f1 + f2) + + + + + + + + + This makes sure the the two + ./build steps are run sequentially, + even withthe --jobs=2 in the command line: + + + + + % scons -Q --jobs=2 + ./build --log logfile.txt file1.in file1.out + ./build --log logfile.txt file2.in file2.out + + + + + The &SideEffect; function can be called multiple + times for the same side-effect file. + Additionally, the name used as a &SideEffect; does not + even need to actually exist as a file on disk. + &SCons; will still make sure + that the relevant targets + will be executed sequentially, not in parallel: + + + + + env = Environment() + f1 = env.Command('file1.out', [], 'echo >$TARGET data1') + env.SideEffect('not_really_updated', f1) + f2 = env.Command('file2.out', [], 'echo >$TARGET data2') + env.SideEffect('not_really_updated', f2) + + + + % scons -Q --jobs=2 + echo > file1.out data1 + echo > file2.out data2 + + + + + Note that it might be tempting to + use &SideEffect; for additional target files + that a command produces. + For example, versions the Microsoft Visual C/C++ compiler + produce a foo.ilk + alongside compiling foo.obj file. + Specifying foo.ilk as a + side-effect of foo.obj + is not a recommended use of &SideEffect;, + because &SCons; handle side-effect files + slightly differently in its analysis of the dependency graph. + When a command produces multiple output files, + they should be specified as multiple targets of + the call to the relevant builder function, + and the &SideEffect; function itself should really only be used + when it's important to ensure that commands are not executed in parallel, + such as when a "peripheral" file (such as a log file) + may actually updated by more than one command invocation. + + diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml index 510587ad..2d525b9e 100644 --- a/doc/user/troubleshoot.xml +++ b/doc/user/troubleshoot.xml @@ -405,7 +405,7 @@ 'PROGSUFFIX': '.exe', 'PSPAWN': <function piped_spawn at 0x700000>, 'RC': 'rc', - 'RCCOM': '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES', + 'RCCOM': <SCons.Action.FunctionAction instance at 0x700000>, 'RCFLAGS': [], 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0x700000>, 'SCANNERS': [], diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 4430191e..b3c346dc 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,6 +8,73 @@ +RELEASE XXX - XXX + +From Luca Falavigna: + + - Fix SCons man page indentation under Debian's man page macros. + +From Steven Knight: + + - Clarify the man page description of the SConscript(src_dir) argument. + + - User's Guide updates: + + - Document the BUILD_TARGETS, COMMAND_LINE_TARGETS and + DEFAULT_TARGETS variables. + + - Document the AddOption(), GetOption() and SetOption() functions. + + - Document the Requires() function; convert to the Variables + object, its UnknownOptions() method, and its associated + BoolVariable(), EnumVariable(), ListVariable(), PackageVariable() + and PathVariable() functions. + + - Document the Progress() function. + + - Reorganize the chapter and sections describing the different + types of environments and how they interact. Document the + SetDefault() method. Document the PrependENVPath() and + AppendENVPath() functions. + + - Reorganize the command-line arguments chapter. Document the + ARGLIST variable. + + - Collect some miscellaneous sections into a chapter about + configuring build output. + + - Man page updates: + + - Document suggested use of the Visual C/C++ /FC option to fix + the ability to double-click on file names in compilation error + messages. + + - Document the need to use Clean() for any SideEffect() files that + must be explicitly removed when their targets are removed. + + - Explicitly document use of Node lists as input to Dependency(). + +From Greg Noel: + + - Document MergeFlags(), ParseConfig(), ParseFlags() and SideEffect() + in the User's Guide. + +From Gary Oberbrunner: + + - Document use of the GetBuildFailures() function in the User's Guide. + +From Adam Simpkins: + + - Add man page text clarifying the behavior of AddPreAction() and + AddPostAction() when called with multiple targets. + +From Alexey Zezukin: + + - Fix incorrectly swapped man page descriptions of the --warn= options + for duplicate-environment and missing-sconscript. + + + RELEASE 0.98.5 - Sat, 07 Jun 2008 08:20:35 -0700 From Benoit Belley: diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 1bc9baac..b1585741 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -791,6 +791,9 @@ class FunctionAction(_ActionAction): apply(_ActionAction.__init__, (self,)+args, kw) self.varlist = kw.get('varlist', []) + if SCons.Util.is_String(self.varlist): + # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O'] + self.varlist=[self.varlist] self.cmdstr = cmdstr def function_name(self): diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 2fb3b066..8ff53117 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -1523,6 +1523,16 @@ class FunctionActionTestCase(unittest.TestCase): c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo')) assert c in matches_foo, repr(c) + # Make sure a bare string varlist works + a = SCons.Action.FunctionAction(GlobalFunc, varlist='XYZ') + + matches_foo = map(lambda x: x + "foo", func_matches) + + c = a.get_contents(target=[], source=[], env=Environment()) + assert c in func_matches, repr(c) + c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo')) + assert c in matches_foo, repr(c) + class Foo: def get_contents(self, target, source, env): return 'xyzzy' diff --git a/src/engine/SCons/Tool/msvc.xml b/src/engine/SCons/Tool/msvc.xml index b85c7aa0..e099d009 100644 --- a/src/engine/SCons/Tool/msvc.xml +++ b/src/engine/SCons/Tool/msvc.xml @@ -123,8 +123,7 @@ You can generate PDB files with the switch by overriding the default &cv-link-CCPDBFLAGS; variable as follows: -import SCons.Util -env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Zi /Fd%s" % File(PDB)) or ""}']) +env['CCPDBFLAGS'] = ['${(PDB and "/Zi /Fd%s" % File(PDB)) or ""}'] An alternative would be to use the diff --git a/src/engine/SCons/Tool/msvs.xml b/src/engine/SCons/Tool/msvs.xml index 2bf02f8d..91a196b5 100644 --- a/src/engine/SCons/Tool/msvs.xml +++ b/src/engine/SCons/Tool/msvs.xml @@ -60,8 +60,9 @@ by calling the &b-MSVSSolution; Builder (see below). -It takes several lists of filenames to be placed into the project -file. +The &b-MSVSProject; builder +takes several lists of filenames +to be placed into the project file. These are currently limited to srcs, incs, @@ -136,6 +137,21 @@ the default is the same as the specified buildtarget value. +Note that because &SCons; always executes its build commands +from the directory in which the &SConstruct; file is located, +if you generate a project file in a different directory +than the &SConstruct; directory, +users will not be able to double-click +on the file name in compilation error messages +displayed in the Visual Studio console output window. +This can be remedied by adding the +Visual C/C++ +.B /FC +compiler option to the &cv-link-CCFLAGS; variable +so that the compiler will print +the full path name of any +files that cause compilation errors. + Example usage: diff --git a/test/MSVS/vs-6.0-files.py b/test/MSVS/vs-6.0-files.py index 28634280..8153b52d 100644 --- a/test/MSVS/vs-6.0-files.py +++ b/test/MSVS/vs-6.0-files.py @@ -32,14 +32,12 @@ Test that we can generate Visual Studio 6 project (.dsp) and solution import os import sys -import TestCmd import TestSCons test = TestSCons.TestSCons() -if sys.platform != 'win32': - msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform - test.skip_test(msg) +# Make the test infrastructure think we have this version of MSVS installed. +test._msvs_versions = ['6.0'] @@ -184,7 +182,7 @@ Package=<3> SConscript_contents = """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '6.0') +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='6.0') testsrc = ['test.c'] testincs = ['sdk.h'] @@ -278,7 +276,7 @@ The real workspace file is here: test.subdir('work3') test.write(['work3', 'SConstruct'], """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '6.0') +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='6.0') testsrc = ['test.c'] testincs = ['sdk.h'] diff --git a/test/MSVS/vs-7.0-files.py b/test/MSVS/vs-7.0-files.py index 71b16ef9..25355149 100644 --- a/test/MSVS/vs-7.0-files.py +++ b/test/MSVS/vs-7.0-files.py @@ -33,14 +33,14 @@ import os import os.path import sys -import TestCmd import TestSCons test = TestSCons.TestSCons() -if sys.platform != 'win32': - msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform - test.skip_test(msg) +# Make the test infrastructure think we have this version of MSVS installed. +test._msvs_versions = ['7.0'] + + expected_slnfile = """\ Microsoft Visual Studio Solution File, Format Version 7.00 @@ -144,7 +144,7 @@ expected_vcprojfile = """\ SConscript_contents = """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '7.0') +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -230,7 +230,8 @@ test.write(['work2', 'src', 'SConscript'], SConscript_contents) test.run(chdir='work2', arguments=".") vcproj = test.read(['work2', 'src', 'Test.vcproj'], 'r') -expect = test.msvs_substitute(expected_vcprojfile, '7.0', 'work2', 'SConstruct') +expect = test.msvs_substitute(expected_vcprojfile, '7.0', 'work2', 'SConstruct', + python=python) # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) @@ -260,7 +261,7 @@ The real workspace file is here: test.subdir('work3') test.write(['work3', 'SConstruct'], """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '7.0') +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -288,7 +289,8 @@ test.run(chdir='work3', arguments=".") test.must_exist(test.workpath('work3', 'Test.vcproj')) vcproj = test.read(['work3', 'Test.vcproj'], 'r') -expect = test.msvs_substitute(expected_vcprojfile, '7.0', 'work3', 'SConstruct') +expect = test.msvs_substitute(expected_vcprojfile, '7.0', 'work3', 'SConstruct', + python=python) # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) diff --git a/test/MSVS/vs-7.1-files.py b/test/MSVS/vs-7.1-files.py index e6a59263..578943d6 100644 --- a/test/MSVS/vs-7.1-files.py +++ b/test/MSVS/vs-7.1-files.py @@ -33,14 +33,14 @@ import os import os.path import sys -import TestCmd import TestSCons test = TestSCons.TestSCons() -if sys.platform != 'win32': - msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform - test.skip_test(msg) +# Make the test infrastructure think we have this version of MSVS installed. +test._msvs_versions = ['7.1'] + + expected_slnfile = """\ Microsoft Visual Studio Solution File, Format Version 8.00 @@ -146,7 +146,7 @@ expected_vcprojfile = """\ SConscript_contents = """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '7.1') +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -232,7 +232,8 @@ test.write(['work2', 'src', 'SConscript'], SConscript_contents) test.run(chdir='work2', arguments=".") vcproj = test.read(['work2', 'src', 'Test.vcproj'], 'r') -expect = test.msvs_substitute(expected_vcprojfile, '7.0', 'work2', 'SConstruct') +expect = test.msvs_substitute(expected_vcprojfile, '7.0', 'work2', 'SConstruct', + python=python) # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) @@ -262,7 +263,7 @@ The real workspace file is here: test.subdir('work3') test.write(['work3', 'SConstruct'], """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '7.1') +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -290,7 +291,8 @@ test.run(chdir='work3', arguments=".") test.must_exist(test.workpath('work3', 'Test.vcproj')) vcproj = test.read(['work3', 'Test.vcproj'], 'r') -expect = test.msvs_substitute(expected_vcprojfile, '7.1', 'work3', 'SConstruct') +expect = test.msvs_substitute(expected_vcprojfile, '7.1', 'work3', 'SConstruct', + python=python) # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) diff --git a/test/MSVS/vs-8.0-files.py b/test/MSVS/vs-8.0-files.py index 7a4fdaaa..33eefd56 100644 --- a/test/MSVS/vs-8.0-files.py +++ b/test/MSVS/vs-8.0-files.py @@ -33,14 +33,14 @@ import os import os.path import sys -import TestCmd import TestSCons test = TestSCons.TestSCons() -if sys.platform != 'win32': - msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform - test.skip_test(msg) +# Make the test infrastructure think we have this version of MSVS installed. +test._msvs_versions = ['8.0'] + + expected_slnfile = """\ Microsoft Visual Studio Solution File, Format Version 9.00 @@ -153,7 +153,7 @@ expected_vcprojfile = """\ SConscript_contents = """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '8.0') +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -240,16 +240,17 @@ test.run(chdir='work2', arguments=".") vcproj = test.read(['work2', 'src', 'Test.vcproj'], 'r') expect = test.msvs_substitute(expected_vcprojfile, - '7.0', + '8.0', 'work2', 'SConstruct', - project_guid="{25F6CE89-8E22-2910-8B6E-FFE6DC1E2792}") + project_guid="{FC63FE9E-71B3-06CC-11AF-2077D8108DFE}", + python=python) # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) test.must_exist(test.workpath('work2', 'src', 'Test.sln')) sln = test.read(['work2', 'src', 'Test.sln'], 'r') -expect = test.msvs_substitute(expected_slnfile, '7.0', +expect = test.msvs_substitute(expected_slnfile, '8.0', os.path.join('work2', 'src')) # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) @@ -273,7 +274,7 @@ The real workspace file is here: test.subdir('work3') test.write(['work3', 'SConstruct'], """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '8.0') +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -301,7 +302,8 @@ test.run(chdir='work3', arguments=".") test.must_exist(test.workpath('work3', 'Test.vcproj')) vcproj = test.read(['work3', 'Test.vcproj'], 'r') -expect = test.msvs_substitute(expected_vcprojfile, '8.0', 'work3', 'SConstruct') +expect = test.msvs_substitute(expected_vcprojfile, '8.0', 'work3', 'SConstruct', + python=python) # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) diff --git a/test/RCS/changed.py b/test/RCS/changed.py new file mode 100644 index 00000000..6aaeffb9 --- /dev/null +++ b/test/RCS/changed.py @@ -0,0 +1,97 @@ +#!/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 explicit checkouts from local RCS files. +""" + +import os.path + +import TestSCons + +test = TestSCons.TestSCons() + +rcs = test.where_is('rcs') +if not rcs: + test.skip_test("Could not find 'rcs'; skipping test(s).\n") + +ci = test.where_is('ci') +if not ci: + test.skip_test("Could not find `ci' command, skipping test(s).\n") + +co = test.where_is('co') +if not co: + test.skip_test("Could not find `co' command, skipping test(s).\n") + + + +main_cpp_contents = """\ +#include +#include +int +main(int argc, char *argv[]) +{ + printf("main.c %s\\n"); + exit (0); +} +""" + +test.write('main.c', main_cpp_contents % 1) + +test.run(program = ci, arguments = '-f -tmain.c main.c', stderr = None) + + + +test.write('SConstruct', """ +import os +for key in ['LOGNAME', 'USERNAME', 'USER']: + logname = os.environ.get(key) + if logname: break +ENV = {'PATH' : os.environ['PATH'], + 'LOGNAME' : logname} +env = Environment(ENV=ENV, RCS_COFLAGS='-q') +env.SourceCode('main.c', env.RCS()) +env2 = env.Clone() +env2.Program('main.exe', 'main.c') +""") + +test.run() + +test.run(program = test.workpath('main.exe'), stdout = "main.c 1\n") + + + +test.run(program = co, arguments = '-l main.c', stderr = None) + +test.write('main.c', main_cpp_contents % 2) + +test.not_up_to_date(arguments = 'main.exe') + +test.run(program = test.workpath('main.exe'), stdout = "main.c 2\n") + + + +test.pass_test()