diff options
author | Steven Knight <knight@baldmt.com> | 2008-08-09 18:23:26 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2008-08-09 18:23:26 (GMT) |
commit | d0c5748eb01e8303b6221afad432cb908672c161 (patch) | |
tree | 962a16c94759f57f27387cb315e72a6a028aa443 /doc/user | |
parent | 2f5c0f667910c8c9db5e93842f5f6201409f704e (diff) | |
download | SCons-d0c5748eb01e8303b6221afad432cb908672c161.zip SCons-d0c5748eb01e8303b6221afad432cb908672c161.tar.gz SCons-d0c5748eb01e8303b6221afad432cb908672c161.tar.bz2 |
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 </literal> 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
........
Diffstat (limited to 'doc/user')
41 files changed, 10450 insertions, 4579 deletions
diff --git a/doc/user/ENV.in b/doc/user/ENV.in deleted file mode 100644 index 3d8bc4b..0000000 --- a/doc/user/ENV.in +++ /dev/null @@ -1,208 +0,0 @@ - -<!-- - - __COPYRIGHT__ - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY - KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE - WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---> - - <para> - - 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. - - </para> - - <para> - - 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. - - </para> - - <para> - - The default value of the &PATH; environment variable - on a POSIX system - is <literal>/usr/local/bin:/bin:/usr/bin</literal>. - 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. - - </para> - - <para> - - 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: - - </para> - - <sconstruct> - path = ['/usr/local/bin', '/bin', '/usr/bin'] - env = Environment(ENV = {'PATH' : path}) - </sconstruct> - - <para> - - 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: - - </para> - - <sconstruct> - env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] - </sconstruct> - - <para> - - 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): - - </para> - - <sconstruct> - env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' - </sconstruct> - - <para> - - 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). - - </para> - - <!-- - - <scons_example name="ex1"> - <file name="SConstruct" printme="1"> - env = Environment() - env.Command('foo', [], '__ROOT__/usr/bin/printenv.py') - </file> - <file name="__ROOT__/usr/bin/printenv.py" chmod="0755"> - #!/usr/bin/env python - import os - import sys - if len(sys.argv) > 1: - keys = sys.argv[1:] - else: - keys = os.environ.keys() - keys.sort() - for key in keys: - print " " + key + "=" + os.environ[key] - </file> - </scons_example> - - <para> - - </para> - - <scons_output example="ex1"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - --> - - <section> - <title>Propagating &PATH; From the External Environment</title> - - <para> - - 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 <literal>os.environ</literal> - dictionary, - which is Python's way of letting you - get at the external environment: - - </para> - - <sconstruct> - import os - env = Environment(ENV = {'PATH' : os.environ['PATH']}) - </sconstruct> - - <para> - - 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: - - </para> - - <sconstruct> - import os - env = Environment(ENV = os.environ) - </sconstruct> - - <para> - - 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 <literal>/bin</literal> and - <literal>/usr/local/bin</literal> 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. - - </para> - - </section> diff --git a/doc/user/ENV.xml b/doc/user/ENV.xml deleted file mode 100644 index 9fa2ec1..0000000 --- a/doc/user/ENV.xml +++ /dev/null @@ -1,208 +0,0 @@ - -<!-- - - __COPYRIGHT__ - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY - KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE - WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---> - - <para> - - 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. - - </para> - - <para> - - 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. - - </para> - - <para> - - The default value of the &PATH; environment variable - on a POSIX system - is <literal>/usr/local/bin:/bin:/usr/bin</literal>. - 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. - - </para> - - <para> - - 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: - - </para> - - <programlisting> - path = ['/usr/local/bin', '/bin', '/usr/bin'] - env = Environment(ENV = {'PATH' : path}) - </programlisting> - - <para> - - 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: - - </para> - - <programlisting> - env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] - </programlisting> - - <para> - - 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): - - </para> - - <programlisting> - env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' - </programlisting> - - <para> - - 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). - - </para> - - <!-- - - <scons_example name="ex1"> - <file name="SConstruct" printme="1"> - env = Environment() - env.Command('foo', [], '__ROOT__/usr/bin/printenv.py') - </file> - <file name="__ROOT__/usr/bin/printenv.py" chmod="0755"> - #!/usr/bin/env python - import os - import sys - if len(sys.argv) > 1: - keys = sys.argv[1:] - else: - keys = os.environ.keys() - keys.sort() - for key in keys: - print " " + key + "=" + os.environ[key] - </file> - </scons_example> - - <para> - - </para> - - <scons_output example="ex1"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - --> - - <section> - <title>Propagating &PATH; From the External Environment</title> - - <para> - - 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 <literal>os.environ</literal> - dictionary, - which is Python's way of letting you - get at the external environment: - - </para> - - <programlisting> - import os - env = Environment(ENV = {'PATH' : os.environ['PATH']}) - </programlisting> - - <para> - - 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: - - </para> - - <programlisting> - import os - env = Environment(ENV = os.environ) - </programlisting> - - <para> - - 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 <literal>/bin</literal> and - <literal>/usr/local/bin</literal> 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. - - </para> - - </section> diff --git a/doc/user/MANIFEST b/doc/user/MANIFEST index b3106af..ef273d3 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 7bca314..bdccf51 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 8997efb..7efd923 100644 --- a/doc/user/add-method.in +++ b/doc/user/add-method.in @@ -25,12 +25,17 @@ <para> - 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 + <filename>/usr/bin</filename> directory hierarchy, + but also copy it into a local <filename>install/bin</filename> + directory from which a package might be built: </para> @@ -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 </file> @@ -60,43 +65,46 @@ <para> - 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. </para> <scons_example name="ex2"> <file name="SConstruct" printme="1"> - 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') </file> <file name="test_stuff.c"> int main() { printf("Hello, world!\n"); } </file> + <file name="res.rc"> + res.rc + </file> </scons_example> <para> - This produces the following (on Linux, anyway; Windows would include the - resource file): + This produces the following on Linux: </para> <scons_output example="ex2"> @@ -104,8 +112,16 @@ </scons_output> <para> - 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: + </para> + + <scons_output example="ex2" os="win32"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + 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. </para> diff --git a/doc/user/add-method.xml b/doc/user/add-method.xml index b6a597b..e6f2a67 100644 --- a/doc/user/add-method.xml +++ b/doc/user/add-method.xml @@ -25,12 +25,17 @@ <para> - 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 + <filename>/usr/bin</filename> directory hierarchy, + but also copy it into a local <filename>install/bin</filename> + directory from which a package might be built: </para> @@ -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 @@ % <userinput>scons -Q /</userinput> 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" </screen> <para> - 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. </para> <programlisting> - 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') </programlisting> <para> - This produces the following (on Linux, anyway; Windows would include the - resource file): + This produces the following on Linux: </para> <screen> @@ -100,8 +105,19 @@ </screen> <para> - 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: + </para> + + <screen> + C:\><userinput>scons -Q</userinput> + 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 + </screen> + + <para> + 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. </para> diff --git a/doc/user/build-install.in b/doc/user/build-install.in index 763c13e..547154c 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 - <userinput>python</userinput> + <userinput>python -V</userinput> + (capital 'V') + or + <userinput>python --version</userinput> at your system's command-line prompt. - You should see something like the following - on a UNIX or Linux system that has Python installed: </para> - <!-- - Robert P.J. Day has suggested using "python -V", - but that's not supported in 1.5.2, so we're going - to leave this as is for now. - --> - <screen> - $ <userinput>python</userinput> - 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. - >>> <userinput>^D</userinput> + $ <userinput>python -V</userinput> + Python 2.5.1 </screen> <para> @@ -99,25 +91,12 @@ </para> <screen> - C:\><userinput>python</userinput> - 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. - >>> <userinput>^Z</userinput> + C:\><userinput>python -V</userinput> + Python 2.5.1 </screen> <para> - The <prompt>>>></prompt> is the input prompt - for the Python interpreter. - The <userinput>^D</userinput> and <userinput>^Z</userinput> - 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;. - - </para> - - <para> - If Python is not installed on your system, you will see an error message stating something like "command not found" @@ -132,6 +111,17 @@ <para> + (Note that the <option>-V</option> option + was added to Python version 2.0, + so if your system only has an earlier version available + you may see an + <literal>"Unknown option: -V"</literal> + error message.) + + </para> + + <para> + The standard location for information about downloading and installing Python is <ulink url="http://www.python.org/download/">http://www.python.org/download/</ulink>. @@ -140,6 +130,16 @@ </para> + <para> + + &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;'. + + </para> + </section> <section> @@ -402,11 +402,11 @@ install the <application>scons</application> script in the default system scripts directory (<filename>/usr/local/bin</filename> or - <filename>C:\Python2.2\Scripts</filename>), + <filename>C:\Python25\Scripts</filename>), and will install the &SCons; build engine in an appropriate stand-alone library directory (<filename>/usr/local/lib/scons</filename> or - <filename>C:\Python2.2\scons</filename>). + <filename>C:\Python25\scons</filename>). 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 <filename>/usr/lib/scons-__VERSION__</filename> or - <filename>C:\Python2.2\scons-__VERSION__</filename> + <filename>C:\Python25\scons-__VERSION__</filename> directory, for example. </para> diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml index 763c13e..547154c 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 - <userinput>python</userinput> + <userinput>python -V</userinput> + (capital 'V') + or + <userinput>python --version</userinput> at your system's command-line prompt. - You should see something like the following - on a UNIX or Linux system that has Python installed: </para> - <!-- - Robert P.J. Day has suggested using "python -V", - but that's not supported in 1.5.2, so we're going - to leave this as is for now. - --> - <screen> - $ <userinput>python</userinput> - 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. - >>> <userinput>^D</userinput> + $ <userinput>python -V</userinput> + Python 2.5.1 </screen> <para> @@ -99,25 +91,12 @@ </para> <screen> - C:\><userinput>python</userinput> - 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. - >>> <userinput>^Z</userinput> + C:\><userinput>python -V</userinput> + Python 2.5.1 </screen> <para> - The <prompt>>>></prompt> is the input prompt - for the Python interpreter. - The <userinput>^D</userinput> and <userinput>^Z</userinput> - 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;. - - </para> - - <para> - If Python is not installed on your system, you will see an error message stating something like "command not found" @@ -132,6 +111,17 @@ <para> + (Note that the <option>-V</option> option + was added to Python version 2.0, + so if your system only has an earlier version available + you may see an + <literal>"Unknown option: -V"</literal> + error message.) + + </para> + + <para> + The standard location for information about downloading and installing Python is <ulink url="http://www.python.org/download/">http://www.python.org/download/</ulink>. @@ -140,6 +130,16 @@ </para> + <para> + + &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;'. + + </para> + </section> <section> @@ -402,11 +402,11 @@ install the <application>scons</application> script in the default system scripts directory (<filename>/usr/local/bin</filename> or - <filename>C:\Python2.2\Scripts</filename>), + <filename>C:\Python25\Scripts</filename>), and will install the &SCons; build engine in an appropriate stand-alone library directory (<filename>/usr/local/lib/scons</filename> or - <filename>C:\Python2.2\scons</filename>). + <filename>C:\Python25\scons</filename>). 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 <filename>/usr/lib/scons-__VERSION__</filename> or - <filename>C:\Python2.2\scons-__VERSION__</filename> + <filename>C:\Python25\scons-__VERSION__</filename> directory, for example. </para> diff --git a/doc/user/builders-writing.in b/doc/user/builders-writing.in index eefbb31..b953975 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. + + </para> + + <para> + + For example, suppose you want to define a Builder + that always calls a <filename>foobuild</filename> program, + and you want to automatically add + a new target file named + <filename>new_target</filename> + and a new source file named + <filename>new_source</filename> + whenever it's called. + The &SConstruct; file might look like this: </para> @@ -738,26 +760,102 @@ This functionality could be invoked as in the following example: env.Foo('file') </sconstruct> + <para> + + And would yield the following output: + + </para> + <scons_output example="ex7"> <scons_output_command>scons -Q</scons_output_command> </scons_output> - <programlisting> - 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') - </programlisting> + <para> + + 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: + + </para> + + <scons_example name="MY_EMITTER"> + + <file name="SConstruct" printme="1"> + 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() + </file> + <file name="file1.input"> + file1.input + </file> + <file name="file2.input"> + file2.input + </file> + <file name="modify1.in"> + modify1.input + </file> + <file name="modify2.in"> + modify2.input + </file> + <file name="my_command" chmod="0755"> + cat + </file> + </file> + + </scons_example> + + <sconstruct> + 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') + </file> + </sconstruct> + + <para> + + In this example, the <filename>modify1.in</filename> + and <filename>modify2.in</filename> files + get added to the source lists + of the different commands: + + </para> + + <scons_output example="MY_EMITTER"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> </section> diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index 6a5a7c1..c45af8a 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. + + </para> + + <para> + + For example, suppose you want to define a Builder + that always calls a <filename>foobuild</filename> program, + and you want to automatically add + a new target file named + <filename>new_target</filename> + and a new source file named + <filename>new_source</filename> + whenever it's called. + The &SConstruct; file might look like this: </para> @@ -635,28 +657,88 @@ This functionality could be invoked as in the following example: env.Foo('file') </programlisting> + <para> + + And would yield the following output: + + </para> + <screen> % <userinput>scons -Q</userinput> foobuild file.foo new_target - file.input new_source </screen> + <para> + + 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: + + </para> + <programlisting> - 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() + + </programlisting> + <programlisting> + 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') + + </programlisting> + + <para> + + In this example, the <filename>modify1.in</filename> + and <filename>modify2.in</filename> files + get added to the source lists + of the different commands: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + my_command file1.input modify1.in > file1.foo + my_command file2.input modify2.in > file2.foo + </screen> + </section> <section> diff --git a/doc/user/caching.in b/doc/user/caching.in index 186ece6..cb8521b 100644 --- a/doc/user/caching.in +++ b/doc/user/caching.in @@ -104,6 +104,20 @@ <scons_output_command>scons -Q</scons_output_command> </scons_output> + <para> + + 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 <xref linkend="chap-depends"></xref> + 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. + + </para> + </section> <section> diff --git a/doc/user/caching.xml b/doc/user/caching.xml index 51b30ae..2348d32 100644 --- a/doc/user/caching.xml +++ b/doc/user/caching.xml @@ -98,6 +98,20 @@ Retrieved `hello' from cache </screen> + <para> + + 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 <xref linkend="chap-depends"></xref> + 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. + + </para> + </section> <section> diff --git a/doc/user/command-line.in b/doc/user/command-line.in index dbbddaf..70d1941 100644 --- a/doc/user/command-line.in +++ b/doc/user/command-line.in @@ -25,527 +25,643 @@ <para> - &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: </para> - <section> - <title>Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable</title> + <variablelist> - <para> - - 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 <literal>-j 2</literal> - to run the builds in parallel. - To avoid having to type <literal>-j 2</literal> 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. - - </para> - - <para> - - If, for example, - you're using a POSIX shell that's - compatible with the Bourne shell, - and you always want &SCons; to use the - <literal>-Q</literal> option, - you can set the &SCONSFLAGS; - environment as follows: - - </para> - - <scons_example name="SCONSFLAGS"> - <file name="SConstruct"> - 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') - </file> - <file name="foo.in"> - foo.in - </file> - </scons_example> - - <scons_output example="SCONSFLAGS"> - <scons_output_command>scons</scons_output_command> - <scons_output_command>export SCONSFLAGS="-Q"</scons_output_command> - <scons_output_command environment="SCONSFLAGS=-Q">scons</scons_output_command> - </scons_output> + <varlistentry> + <term>Options</term> + <listitem> <para> - Users of &csh;-style shells on POSIX systems - can set the &SCONSFLAGS; environment as follows: + Command-line options always begin with + one or two <literal>-</literal> (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 <xref linkend="sect-command-line-options"></xref>, below. </para> + </listitem> + </varlistentry> - <screen> - $ <userinput>setenv SCONSFLAGS "-Q"</userinput> - </screen> + <varlistentry> + <term>Variables</term> + <listitem> <para> - Windows users may typically want to set the - &SCONSFLAGS; in the appropriate tab of the - <literal>System Properties</literal> window. + Any command-line argument containing an <literal>=</literal> + (equal sign) is considered a variable setting with the form + <varname>variable</varname>=<varname>value</varname> + &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 <xref linkend="sect-command-line-variables"></xref>, below. </para> + </listitem> + </varlistentry> - </section> - - <section> - <title>Getting at Command-Line Targets</title> + <varlistentry> + <term>Targets</term> + <listitem> <para> - &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 <xref linkend="sect-command-line-targets"></xref>, below. </para> + </listitem> + </varlistentry> - <scons_example name="COMMAND_LINE_TARGETS"> - <file name="SConstruct" printme="1"> - if 'bar' in COMMAND_LINE_TARGETS: - print "Don't forget to copy `bar' to the archive!" - Default(Program('foo.c')) - Program('bar.c') - </file> - <file name="foo.c"> - foo.c - </file> - <file name="bar.c"> - foo.c - </file> - </scons_example> - - <para> - - 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: - - </para> + </variablelist> - <scons_output example="COMMAND_LINE_TARGETS"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q bar</scons_output_command> - </scons_output> + <section id="sect-command-line-options"> + <title>Command-Line Options</title> <para> - 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 <emphasis>command-line options</emphasis> + that control its behavior. + A &SCons; <emphasis>command-line option</emphasis> + always begins with one or two <literal>-</literal> (hyphen) + characters. </para> - </section> - - <section> - <title>Controlling the Default Targets</title> - - <para> + <section> + <title>Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable</title> - 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: + <para> - </para> + 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 <literal>-j 2</literal> + to have &SCons; run up to two build commands in parallel. + To avoid having to type <literal>-j 2</literal> 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. - <scons_example name="Default1"> - <file name="SConstruct" printme="1"> - env = Environment() - hello = env.Program('hello.c') - env.Program('goodbye.c') - Default(hello) - </file> - <file name="hello.c"> - hello.c - </file> - <file name="goodbye.c"> - goodbye.c - </file> - </scons_example> + </para> - <para> + <para> - 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 + <literal>-Q</literal> option, + you can set the &SCONSFLAGS; + environment as follows: - </para> - - <scons_output example="Default1"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q goodbye</scons_output_command> - </scons_output> + </para> - <para> + <scons_example name="SCONSFLAGS"> + <file name="SConstruct"> + 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') + </file> + <file name="foo.in"> + foo.in + </file> + </scons_example> - Note that, even when you use the &Default; - function in your &SConstruct; file, - you can still explicitly specify the current directory - (<literal>.</literal>) on the command line - to tell &SCons; to build - everything in (or below) the current directory: + <scons_output example="SCONSFLAGS"> + <scons_output_command>scons</scons_output_command> + <scons_output_command>export SCONSFLAGS="-Q"</scons_output_command> + <scons_output_command environment="SCONSFLAGS=-Q">scons</scons_output_command> + </scons_output> - </para> + <para> - <scons_output example="Default1"> - <scons_output_command>scons -Q .</scons_output_command> - </scons_output> + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: - <para> + </para> - 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: + <screen> + $ <userinput>setenv SCONSFLAGS "-Q"</userinput> + </screen> - </para> + <para> - <scons_example name="Default2"> - <file name="SConstruct" printme="1"> - env = Environment() - prog1 = env.Program('prog1.c') - Default(prog1) - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog3) - </file> - <file name="prog1.c"> - prog1.c - </file> - <file name="prog2.c"> - prog2.c - </file> - <file name="prog3.c"> - prog3.c - </file> - </scons_example> + Windows users may typically want to set the + &SCONSFLAGS; in the appropriate tab of the + <literal>System Properties</literal> window. - <para> + </para> - Or you can specify more than one target - in a single call to the &Default; function: + </section> - </para> + <section> + <title>Getting Values Set by Command-Line Options: the &GetOption; Function</title> - <programlisting> - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog1, prog3) - </programlisting> + <para> - <para> + &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 <literal>-h</literal> or <literal>--help</literal> 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 - <application>prog1</application> - and - <application>prog3</application> - programs by default: + </para> - </para> + <para> - <scons_output example="Default2"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q .</scons_output_command> - </scons_output> + 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 <emphasis>not</emphasis> specified + the <literal>-h</literal> or <literal>--help</literal> option, + like so: - <para> + </para> - You can list a directory as - an argument to &Default;: + <sconstruct) + if not GetOption('help'): + SConscript('src/SConscript', export='env') + </sconstruct> - </para> + <para> - <scons_example name="Default3"> - <file name="SConstruct" printme="1"> - env = Environment() - env.Program(['prog1/main.c', 'prog1/foo.c']) - env.Program(['prog2/main.c', 'prog2/bar.c']) - Default('prog1') - </file> - <directory name="prog1"></directory> - <directory name="prog2"></directory> - <file name="prog1/main.c"> - int main() { printf("prog1/main.c\n"); } - </file> - <file name="prog1/foo.c"> - int foo() { printf("prog1/foo.c\n"); } - </file> - <file name="prog2/main.c"> - int main() { printf("prog2/main.c\n"); } - </file> - <file name="prog2/bar.c"> - int bar() { printf("prog2/bar.c\n"); } - </file> - </scons_example> + 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 + <xref linkend="sect-command-line-option-strings"></xref> section, + below. - <para> + </para> - In which case only the target(s) in that - directory will be built by default: + </section> - </para> + <section> + <title>Setting Values of Command-Line Options: the &SetOption; Function</title> - <scons_output example="Default3"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q .</scons_output_command> - </scons_output> + <para> - <para> + 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 + <xref linkend="sect-command-line-option-strings"></xref> section, + below. - Lastly, if for some reason you don't want - any targets built by default, - you can use the Python <literal>None</literal> - variable: + </para> - </para> + <para> - <scons_example name="Default4"> - <file name="SConstruct" printme="1"> - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - Default(None) - </file> - <file name="prog1.c"> - prog1.c - </file> - <file name="prog2.c"> - prog2.c - </file> - </scons_example> + One use of the &SetOption; function is to + specify a value for the <literal>-j</literal> + or <literal>--jobs</literal> 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 <literal>-j</literal> option is + somewhat system-dependent. + One rough guideline is that the more processors + your system has, + the higher you want to set the + <literal>-j</literal> value, + in order to take advantage of the number of CPUs. - <para> + </para> - Which would produce build output like: + <para> - </para> + For example, suppose the administrators + of your development systems + have standardized on setting a + <varname>NUM_CPU</varname> 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_output example="Default4"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q .</scons_output_command> - </scons_output> + </para> - <section> - <title>Getting at the List of Default Targets</title> + <scons_example name="SetOption"> + <file name="SConstruct" printme="1"> + import os + num_cpu = int(os.environ.get('NUM_CPU', 2)) + SetOption('num_jobs', num_cpu) + print "running with -j", GetOption('num_jobs') + </file> + <file name="foo.in"> + foo.in + </file> + </scons_example> <para> - &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 <function>map</function> function - to run the list through <function>str</function>: + The above snippet of code + sets the value of the <literal>--jobs</literal> option + to the value specified in the + <varname>$NUM_CPU</varname> 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 <literal>--jobs</literal> + value is <literal>num_jobs</literal> + for historical reasons.) + The code in this example prints the <literal>num_jobs</literal> + value for illustrative purposes. + It uses a default value of <literal>2</literal> + to provide some minimal parallelism even on + single-processor systems: </para> - <scons_example name="DEFAULT_TARGETS_1"> - <file name="SConstruct" printme="1"> - prog1 = Program('prog1.c') - Default(prog1) - print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) - </file> - <file name="prog1.c"> - prog1.c - </file> - </scons_example> + <scons_output example="SetOption"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> <para> - (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 <literal>-Q</literal> flag when we run &SCons;:) + But if the <varname>$NUM_CPU</varname> + environment variable is set, + then we use that for the default number of jobs: </para> - <scons_output example="DEFAULT_TARGETS_1"> - <scons_output_command>scons</scons_output_command> + <scons_output example="SetOption"> + <scons_output_command>export NUM_CPU="4"</scons_output_command> + <scons_output_command environment="NUM_CPU=4">scons -Q</scons_output_command> </scons_output> <para> - 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 + <literal>-j</literal> or <literal>--jobs</literal> + value the user specifies an the command line is used first, + regardless of whether or not + the <varname>$NUM_CPU</varname> environment + variable is set: </para> - <scons_example name="DEFAULT_TARGETS_2"> - <file name="SConstruct" printme="1"> - 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) - </file> - <file name="prog1.c"> - prog1.c - </file> - <file name="prog2.c"> - prog2.c - </file> - </scons_example> + <scons_output example="SetOption"> + <scons_output_command>scons -Q -j 7</scons_output_command> + <scons_output_command>export NUM_CPU="4"</scons_output_command> + <scons_output_command environment="NUM_CPU=4">scons -Q -j 3</scons_output_command> + </scons_output> + + </section> + + <section id="sect-command-line-option-strings"> + <title>Strings for Getting or Setting Values of &SCons; Command-Line Options</title> <para> - 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: <literal>--</literal>), + after replacing any remaining hyphen characters + with underscores. </para> - <scons_output example="DEFAULT_TARGETS_2"> - <scons_output_command>scons</scons_output_command> - </scons_output> - <para> - 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: </para> + <informaltable> + <tgroup cols="2" align="left"> + + <thead> + + <row> + <entry>String for &GetOption; and &SetOption;</entry> + <entry>Command-Line Option(s)</entry> + </row> + + </thead> + + <tbody> + + <row> + <entry><literal>cache_debug</literal></entry> + <entry><option>--cache-debug</option></entry> + </row> + + <row> + <entry><literal>cache_disable</literal></entry> + <entry><option>--cache-disable</option></entry> + </row> + + <row> + <entry><literal>cache_force</literal></entry> + <entry><option>--cache-force</option></entry> + </row> + + <row> + <entry><literal>cache_show</literal></entry> + <entry><option>--cache-show</option></entry> + </row> + + <row> + <entry><literal>clean</literal></entry> + <entry><option>-c</option>, + <option>--clean</option>, + <option>--remove</option></entry> + </row> + + <row> + <entry><literal>config</literal></entry> + <entry><option>--config</option></entry> + </row> + + <row> + <entry><literal>directory</literal></entry> + <entry><option>-C</option>, + <option>--directory</option></entry> + </row> + + <row> + <entry><literal>diskcheck</literal></entry> + <entry><option>--diskcheck</option></entry> + </row> + + <row> + <entry><literal>duplicate</literal></entry> + <entry><option>--duplicate</option></entry> + </row> + + <row> + <entry><literal>file</literal></entry> + <entry><option>-f</option>, + <option>--file</option>, + <option>--makefile </option>, + <option>--sconstruct</option></entry> + </row> + + <row> + <entry><literal>help</literal></entry> + <entry><option>-h</option>, + <option>--help</option></entry> + </row> + + <row> + <entry><literal>ignore_errors</literal></entry> + <entry><option>--ignore-errors</option></entry> + </row> + + <row> + <entry><literal>implicit_cache</literal></entry> + <entry><option>--implicit-cache</option></entry> + </row> + + <row> + <entry><literal>implicit_deps_changed</literal></entry> + <entry><option>--implicit-deps-changed</option></entry> + </row> + + <row> + <entry><literal>implicit_deps_unchanged</literal></entry> + <entry><option>--implicit-deps-unchanged</option></entry> + </row> + + <row> + <entry><literal>interactive</literal></entry> + <entry><option>--interact</option>, + <option>--interactive</option></entry> + </row> + + <row> + <entry><literal>keep_going</literal></entry> + <entry><option>-k</option>, + <option>--keep-going</option></entry> + </row> + + <row> + <entry><literal>max_drift</literal></entry> + <entry><option>--max-drift</option></entry> + </row> + + <row> + <entry><literal>no_exec</literal></entry> + <entry><option>-n</option>, + <option>--no-exec</option>, + <option>--just-print</option>, + <option>--dry-run</option>, + <option>--recon</option></entry> + </row> + + <row> + <entry><literal>no_site_dir</literal></entry> + <entry><option>--no-site-dir</option></entry> + </row> + + <row> + <entry><literal>num_jobs</literal></entry> + <entry><option>-j</option>, + <option>--jobs</option></entry> + </row> + + <row> + <entry><literal>profile_file</literal></entry> + <entry><option>--profile</option></entry> + </row> + + <row> + <entry><literal>question</literal></entry> + <entry><option>-q</option>, + <option>--question</option></entry> + </row> + + <row> + <entry><literal>random</literal></entry> + <entry><option>--random</option></entry> + </row> + + <row> + <entry><literal>repository</literal></entry> + <entry><option>-Y</option>, + <option>--repository</option>, + <option>--srcdir</option></entry> + </row> + + <row> + <entry><literal>silent</literal></entry> + <entry><option>-s</option>, + <option>--silent</option>, + <option>--quiet</option></entry> + </row> + + <row> + <entry><literal>site_dir</literal></entry> + <entry><option>--site-dir</option></entry> + </row> + + <row> + <entry><literal>stack_size</literal></entry> + <entry><option>--stack-size</option></entry> + </row> + + <row> + <entry><literal>taskmastertrace_file</literal></entry> + <entry><option>--taskmastertrace</option></entry> + </row> + + <row> + <entry><literal>warn</literal></entry> + <entry><option>--warn</option> <option>--warning</option></entry> + </row> + + </tbody> + + </tgroup> + </informaltable> + </section> - </section> + <section> + <title>Adding Custom Command-Line Options: the &AddOption; Function</title> - <section> - <title>Getting at the List of Build Targets, Regardless of Origin</title> + <para> - <para> + &SCons; also allows you to define your own + command-line options with the &AddOption; function. + The &AddOption; function takes the same arguments + as the <function>optparse.add_option</function> function + from the standard Python library. + <footnote> + <para> + The &AddOption; function is, + in fact, implemented using a subclass + of the <classname>optparse.OptionParser</classname>. + </para> + </footnote> + 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: + </para> - </para> + <para> - <sconstruct> - if COMMAND_LINE_TARGETS: - targets = COMMAND_LINE_TARGETS - else: - targets = DEFAULT_TARGETS - </sconstruct> + One useful example of using this functionality + is to provide a <option>--prefix</option> for users: - <para> + </para> - &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_example name="AddOption"> + <file name="SConstruct" printme="1"> + 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) + </file> + <file name="foo.in"> + foo.in + </file> + </scons_example> - </para> + <para> - <para> + The above code uses the &GetOption; function + to set the <varname>$PREFIX</varname> + construction variable to any + value that the user specifies with a command-line + option of <literal>--prefix</literal>. + Because <varname>$PREFIX</varname> + will expand to a null string if it's not initialized, + running &SCons; without the + option of <literal>--prefix</literal> + will install the file in the + <filename>/usr/bin/</filename> 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: + </para> - </para> + <scons_output example="AddOption"> + <scons_output_command>scons -Q -n</scons_output_command> + </scons_output> - <scons_example name="BUILD_TARGETS_1"> - <file name="SConstruct" printme="1"> - prog1 = Program('prog1.c') - Program('prog2.c') - Default(prog1) - print "BUILD_TARGETS is", map(str, BUILD_TARGETS) - </file> - <file name="prog1.c"> - prog1.c - </file> - <file name="prog2.c"> - prog2.c - </file> - </scons_example> + <para> - <para> + But specifying <literal>--prefix=/tmp/install</literal> + on the command line causes the file to be installed in the + <filename>/tmp/install/usr/bin/</filename> directory: - Notice how the value of &BUILD_TARGETS; - changes depending on whether a target is - specified on the command line: + </para> - </para> + <scons_output example="AddOption"> + <scons_output_command>scons -Q -n --prefix=/tmp/install</scons_output_command> + </scons_output> - <scons_output example="BUILD_TARGETS_1"> - <scons_output_command>scons -Q</scons_output_command> - <scons_output_command>scons -Q prog2</scons_output_command> - <scons_output_command>scons -Q -c .</scons_output_command> - </scons_output> + </section> </section> - <section> - <title>Command-Line <varname>variable</varname>=<varname>value</varname> Build Options</title> + <section id="sect-command-line-variables"> + <title>Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables</title> <para> @@ -575,7 +691,7 @@ to specifications on the command line. (Note that unless you want to require that users <emphasis>always</emphasis> - specify an option, + specify a variable, you probably want to use the Python <literal>ARGUMENTS.get()</literal> function, @@ -633,456 +749,1082 @@ </para> - </section> - - <section> - <title>Controlling Command-Line Build Options</title> - <para> - Being able to use a command-line build option like - <literal>debug=1</literal> 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. </para> <para> - 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 <literal>-D</literal> 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 + <varname>variable</varname>=<varname>value</varname> + 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 + <varname>define=</varname> + settings on the command line: </para> - <scons_example name="Options1"> - <file name="SConstruct" printme="1"> - 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']) - </file> - <file name="foo.c"> - foo.c - </file> - <file name="bar.c"> - bar.c - </file> + <scons_example name="ARGLIST"> + <file name="SConstruct" printme="1"> + cppdefines = [] + for key, value in ARGLIST: + if key == 'define': + cppdefines.append(value) + env = Environment(CPPDEFINES = cppdefines) + env.Object('prog.c') + </file> + <file name="prog.c"> + prog.c + </file> </scons_example> <para> - This &SConstruct; file first creates an - &Options; object - (the <literal>opts = Options()</literal> 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 <literal>0</literal> - (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: </para> + <scons_output example="ARGLIST"> + <scons_output_command>scons -Q define=FOO</scons_output_command> + <scons_output_command>scons -Q define=FOO define=BAR</scons_output_command> + </scons_output> + <para> - 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 + <varname>variable</varname>=<varname>value</varname> + 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). </para> - <scons_output example="Options1"> - <scons_output_command>scons -Q RELEASE=1</scons_output_command> - </scons_output> - - </section> + <section> + <title>Controlling Command-Line Build Variables</title> - <section> - <title>Providing Help for Command-Line Build Options</title> + <para> - <para> + Being able to use a command-line build variable like + <literal>debug=1</literal> 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 <literal>scons -h</literal>. - 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: + </para> - </para> + <para> - <scons_example name="Options_Help"> - <file name="SConstruct" printme="1"> - opts = Options('custom.py') - opts.Add('RELEASE', 'Set to 1 to build for release', 0) - env = Environment(options = opts) - Help(opts.GenerateHelpText(env)) - </file> - </scons_example> + 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 <literal>-D</literal> 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: - <para> + </para> - &SCons; will now display some useful text - when the <literal>-h</literal> option is used: + <scons_example name="Variables1"> + <file name="SConstruct" printme="1"> + 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']) + </file> + <file name="foo.c"> + foo.c + </file> + <file name="bar.c"> + bar.c + </file> + </scons_example> - </para> + <para> - <scons_output example="Options_Help"> - <scons_output_command>scons -Q -h</scons_output_command> - </scons_output> + This &SConstruct; file first creates a &Variables; object + (the <literal>vars = Variables()</literal> 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 <literal>0</literal> + (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. - <para> + </para> - Notice that the help output shows the default value, - and the current actual value of the build option. + <para> - </para> + 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: - </section> + </para> - <section> - <title>Reading Build Options From a File</title> + <scons_output example="Variables1"> + <scons_output_command>scons -Q RELEASE=1</scons_output_command> + </scons_output> - <para> + <para> - Being able to use a command-line build option like - <literal>debug=1</literal> 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. - </para> + </para> - <scons_example name="Options_custom_py_1"> - <file name="SConstruct" printme="1"> - 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)) - </file> - <file name="foo.c"> - foo.c - </file> - <file name="bar.c"> - bar.c - </file> - <file name="custom.py"> - RELEASE = 1 - </file> - </scons_example> + </section> - <para> + <section> + <title>Providing Help for Command-Line Build Variables</title> - This then allows us to control the &RELEASE; - variable by setting it in the &custom_py; file: + <para> - </para> + 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 <literal>scons -h</literal>. + 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: - <scons_example_file example="Options_custom_py_1" name="custom.py"></scons_example_file> + </para> - <para> + <scons_example name="Variables_Help"> + <file name="SConstruct" printme="1"> + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars) + Help(vars.GenerateHelpText(env)) + </file> + </scons_example> - Note that this file is actually executed - like a Python script. - Now when we run &SCons;: + <para> - </para> + &SCons; will now display some useful text + when the <literal>-h</literal> option is used: - <scons_output example="Options_custom_py_1"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + </para> - <para> + <scons_output example="Variables_Help"> + <scons_output_command>scons -Q -h</scons_output_command> + </scons_output> - And if we change the contents of &custom_py; to: + <para> - </para> + Notice that the help output shows the default value, + and the current actual value of the build variable. - <scons_example name="Options_custom_py_2"> - <file name="SConstruct"> - 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)) - </file> - <file name="foo.c"> - foo.c - </file> - <file name="bar.c"> - bar.c - </file> - <file name="custom.py" printme="1"> - RELEASE = 0 - </file> - </scons_example> + </para> - <para> + </section> - The object files are rebuilt appropriately - with the new option: + <section> + <title>Reading Build Variables From a File</title> - </para> + <para> - <scons_output example="Options_custom_py_2"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + 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: - </section> + </para> - <section> - <title>Canned Build Options</title> + <scons_example name="Variables_custom_py_1"> + <file name="SConstruct" printme="1"> + 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)) + </file> + <file name="foo.c"> + foo.c + </file> + <file name="bar.c"> + bar.c + </file> + <file name="custom.py"> + RELEASE = 1 + </file> + </scons_example> - <para> + <para> - &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: - </para> + </para> - <section> - <title>True/False Values: the &BoolOption; Build Option</title> + <scons_example_file example="Variables_custom_py_1" name="custom.py"></scons_example_file> <para> - 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;: </para> + <scons_output example="Variables_custom_py_1"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + <para> - 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: </para> - <scons_example name="BoolOption"> - <file name="SConstruct" printme="1"> - opts = Options('custom.py') - opts.Add(BoolOption('RELEASE', 'Set to build for release', 0)) - env = Environment(options = opts, + <scons_example name="Variables_custom_py_2"> + <file name="SConstruct"> + 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)) </file> <file name="foo.c"> foo.c </file> + <file name="bar.c"> + bar.c + </file> + <file name="custom.py" printme="1"> + RELEASE = 0 + </file> </scons_example> <para> - With this build option, - the &RELEASE; variable can now be enabled by - setting it to the value <literal>yes</literal> - or <literal>t</literal>: + The object files are rebuilt appropriately + with the new variable: </para> - <scons_output example="BoolOption"> - <scons_output_command>scons -Q RELEASE=yes foo.o</scons_output_command> + <scons_output example="Variables_custom_py_2"> + <scons_output_command>scons -Q</scons_output_command> </scons_output> - <scons_output example="BoolOption"> - <scons_output_command>scons -Q RELEASE=t foo.o</scons_output_command> - </scons_output> - - <para> - - Other values that equate to &true; include - <literal>y</literal>, - <literal>1</literal>, - <literal>on</literal> - and - <literal>all</literal>. + </section> - </para> + <section> + <title>Pre-Defined Build Variable Functions</title> <para> - Conversely, &RELEASE; may now be given a &false; - value by setting it to - <literal>no</literal> - or - <literal>f</literal>: + &SCons; provides a number of functions + that provide ready-made behaviors + for various types of command-line build variables. </para> - <scons_output example="BoolOption"> - <scons_output_command>scons -Q RELEASE=no foo.o</scons_output_command> - </scons_output> + <section> + <title>True/False Values: the &BoolVariable; Build Variable Function</title> - <scons_output example="BoolOption"> - <scons_output_command>scons -Q RELEASE=f foo.o</scons_output_command> - </scons_output> + <para> - <para> + 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 - <literal>n</literal>, - <literal>0</literal>, - <literal>off</literal> - and - <literal>none</literal>. + </para> - </para> + <para> - <para> + 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: + </para> - </para> + <scons_example name="BoolVariable"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + <para> + + With this build variable, + the &RELEASE; variable can now be enabled by + setting it to the value <literal>yes</literal> + or <literal>t</literal>: + + </para> + + <scons_output example="BoolVariable"> + <scons_output_command>scons -Q RELEASE=yes foo.o</scons_output_command> + </scons_output> + + <scons_output example="BoolVariable"> + <scons_output_command>scons -Q RELEASE=t foo.o</scons_output_command> + </scons_output> + + <para> + + Other values that equate to &true; include + <literal>y</literal>, + <literal>1</literal>, + <literal>on</literal> + and + <literal>all</literal>. + + </para> - <scons_output example="BoolOption"> - <scons_output_command>scons -Q RELEASE=bad_value foo.o</scons_output_command> - </scons_output> + <para> + + Conversely, &RELEASE; may now be given a &false; + value by setting it to + <literal>no</literal> + or + <literal>f</literal>: + + </para> + + <scons_output example="BoolVariable"> + <scons_output_command>scons -Q RELEASE=no foo.o</scons_output_command> + </scons_output> + + <scons_output example="BoolVariable"> + <scons_output_command>scons -Q RELEASE=f foo.o</scons_output_command> + </scons_output> + + <para> + + Other values that equate to &false; include + <literal>n</literal>, + <literal>0</literal>, + <literal>off</literal> + and + <literal>none</literal>. + + </para> + + <para> + + Lastly, if a user tries to specify + any other value, + &SCons; supplies an appropriate error message: + + </para> + + <scons_output example="BoolVariable"> + <scons_output_command>scons -Q RELEASE=bad_value foo.o</scons_output_command> + </scons_output> + + </section> + + <section> + <title>Single Value From a List: the &EnumVariable; Build Variable Function</title> + + <para> + + 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: + + </para> + + <scons_example name="EnumVariable"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + <para> + + The user can now explicity set the &COLOR; build variable + to any of the specified allowed values: + + </para> + + <scons_output example="EnumVariable"> + <scons_output_command>scons -Q COLOR=red foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=blue foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=green foo.o</scons_output_command> + </scons_output> + + <para> + + But, almost more importantly, + an attempt to set &COLOR; + to a value that's not in the list + generates an error message: + + </para> + + <scons_output example="EnumVariable"> + <scons_output_command>scons -Q COLOR=magenta foo.o</scons_output_command> + </scons_output> + + <para> + + 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 <literal>navy</literal> as a synonym for + <literal>blue</literal>. + We do this by adding a ↦ dictionary + that will map its key values + to the desired legal value: + + </para> + + <scons_example name="EnumVariable_map"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + <para> + + As desired, the user can then use + <literal>navy</literal> on the command line, + and &SCons; will translate it into <literal>blue</literal> + when it comes time to use the &COLOR; + variable to build a target: + + </para> + + <scons_output example="EnumVariable_map"> + <scons_output_command>scons -Q COLOR=navy foo.o</scons_output_command> + </scons_output> + + <para> + + By default, when using the &EnumVariable; function, + arguments that differ + from the legal values + only in case + are treated as illegal values: + + </para> + + <scons_output example="EnumVariable"> + <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=BLUE foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command> + </scons_output> + + <para> + + The &EnumVariable; function can take an additional + &ignorecase; keyword argument that, + when set to <literal>1</literal>, + tells &SCons; to allow case differences + when the values are specified: + + </para> + + <scons_example name="EnumVariable_ic1"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + <para> + + Which yields the output: + + </para> + + <scons_output example="EnumVariable_ic1"> + <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=BLUE foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=green foo.o</scons_output_command> + </scons_output> + + <para> + + Notice that an &ignorecase; value of <literal>1</literal> + 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 <literal>2</literal>: + + </para> + + <scons_example name="EnumVariable_ic2"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + <para> + + Now &SCons; will use values of + <literal>red</literal>, + <literal>green</literal> or + <literal>blue</literal> + regardless of how the user spells + those values on the command line: + + </para> + + <scons_output example="EnumVariable_ic2"> + <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command> + <scons_output_command>scons -Q COLOR=GREEN foo.o</scons_output_command> + </scons_output> + + </section> + + <section> + <title>Multiple Values From a List: the &ListVariable; Build Variable Function</title> + + <para> + + 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: + + </para> + + <scons_example name="ListVariable"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + <para> + + 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: + + </para> + + <scons_output example="ListVariable"> + <scons_output_command>scons -Q COLORS=red,blue foo.o</scons_output_command> + <scons_output_command>scons -Q COLORS=blue,green,red foo.o</scons_output_command> + </scons_output> + + <para> + + 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: + + </para> + + <scons_output example="ListVariable"> + <scons_output_command>scons -Q COLORS=all foo.o</scons_output_command> + <scons_output_command>scons -Q COLORS=none foo.o</scons_output_command> + </scons_output> + + <para> + + And, of course, an illegal value + still generates an error message: + + </para> + + <scons_output example="ListVariable"> + <scons_output_command>scons -Q COLORS=magenta foo.o</scons_output_command> + </scons_output> + + </section> + + <section> + <title>Path Names: the &PathVariable; Build Variable Function</title> + + <para> + + &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: + + </para> + + <scons_example name="PathVariable"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + <file name="__ROOT__/etc/my_config"> + /opt/location + </file> + <file name="__ROOT__/usr/local/etc/other_config"> + /opt/location + </file> + </scons_example> + + <para> + + This then allows the user to + override the &CONFIG; build variable + on the command line as necessary: + + </para> + + <scons_output example="PathVariable"> + <scons_output_command>scons -Q foo.o</scons_output_command> + <scons_output_command>scons -Q CONFIG=__ROOT__/usr/local/etc/other_config foo.o</scons_output_command> + </scons_output> + + <para> + + By default, &PathVariable; checks to make sure + that the specified path exists and generates an error if it + doesn't: + + </para> + + <scons_output example="PathVariable"> + <scons_output_command>scons -Q CONFIG=__ROOT__/does/not/exist foo.o</scons_output_command> + </scons_output> + + <para> + + &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: + + </para> + + <scons_example name="PathIsFile"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + <file name="__ROOT__/etc/my_config"> + /opt/location + </file> + </scons_example> + + <para> + + Conversely, to ensure that any specified paths are + directories and not files, + use the &PathVariable_PathIsDir; method: + + </para> + + <scons_example name="PathIsDir"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + <file name="__ROOT__/var/my_dbdir"> + /opt/location + </file> + </scons_example> + + <para> + + 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: + + </para> + + <scons_example name="PathIsDirCreate"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + <file name="__ROOT__/var/my_dbdir"> + /opt/location + </file> + </scons_example> + + <para> + + 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: + + </para> + + <scons_example name="PathAccept"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + </section> + + <section> + <title>Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function</title> + + <para> + + 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 <literal>yes</literal> or <literal>no</literal> keywords, + in addition to allow them + to supply an explicit path name. + &SCons; supports the &PackageVariable; + function to support this: + + </para> + + <scons_example name="PackageVariable"> + <file name="SConstruct" printme="1"> + vars = Variables('custom.py') + vars.Add(PackageVariable('PACKAGE', + 'Location package', + '__ROOT__/opt/location')) + env = Environment(variables = vars, + CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) + env.Program('foo.c') + </file> + <file name="foo.c"> + foo.c + </file> + <file name="__ROOT__/opt/location"> + /opt/location + </file> + <file name="__ROOT__/usr/local/location"> + /opt/location + </file> + </scons_example> + + <para> + + 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: + + </para> + + <scons_output example="PackageVariable"> + <scons_output_command>scons -Q foo.o</scons_output_command> + <scons_output_command>scons -Q PACKAGE=__ROOT__/usr/local/location foo.o</scons_output_command> + <scons_output_command>scons -Q PACKAGE=yes foo.o</scons_output_command> + <scons_output_command>scons -Q PACKAGE=no foo.o</scons_output_command> + </scons_output> + + </section> </section> <section> - <title>Single Value From a List: the &EnumOption; Build Option</title> + <title>Adding Multiple Command-Line Build Variables at Once</title> <para> - 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: </para> - <scons_example name="EnumOption"> + <scons_example name="AddVariables_1"> <file name="SConstruct" printme="1"> - 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') - </file> - <file name="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), + ) </file> </scons_example> <para> - - The user can now explicity set the &COLOR; build option - to any of the specified allowed values: - </para> - <scons_output example="EnumOption"> - <scons_output_command>scons -Q COLOR=red foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=blue foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=green foo.o</scons_output_command> - </scons_output> + </section> + + <section> + <title>Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function</title> <para> - 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.) </para> - <scons_output example="EnumOption"> - <scons_output_command>scons -Q COLOR=magenta foo.o</scons_output_command> - </scons_output> - <para> - 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 <literal>navy</literal> as a synonym for - <literal>blue</literal>. - 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 <emphasis>not</emphasis> 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: </para> - <scons_example name="EnumOption_map"> + <scons_example name="UnknownVariables"> <file name="SConstruct" printme="1"> - 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') </file> <file name="foo.c"> @@ -1092,474 +1834,494 @@ <para> - As desired, the user can then use - <literal>navy</literal> on the command line, - and &SCons; will translate it into <literal>blue</literal> - 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 <emphasis>not</emphasis> + 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;: </para> - <scons_output example="EnumOption_map"> - <scons_output_command>scons -Q COLOR=navy foo.o</scons_output_command> + <scons_output example="UnknownVariables"> + <scons_output_command>scons -Q NOT_KNOWN=foo</scons_output_command> </scons_output> <para> - 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. </para> - <scons_output example="EnumOption"> - <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=BLUE foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command> - </scons_output> - <para> - The &EnumOption; function can take an additional - &ignorecase; keyword argument that, - when set to <literal>1</literal>, - 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 <literal>variables=</literal> + keyword argument of an &Environment; call. </para> - <scons_example name="EnumOption_ic1"> - <file name="SConstruct" printme="1"> - 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') - </file> - <file name="foo.c"> - foo.c - </file> - </scons_example> - - <para> + </section> - Which yields the output: + </section> - </para> + <section id="sect-command-line-targets"> + <title>Command-Line Targets</title> - <scons_output example="EnumOption_ic1"> - <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=BLUE foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=green foo.o</scons_output_command> - </scons_output> + <section> + <title>Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable</title> <para> - Notice that an &ignorecase; value of <literal>1</literal> - 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 <literal>2</literal>: + &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: </para> - <scons_example name="EnumOption_ic2"> + <scons_example name="COMMAND_LINE_TARGETS"> <file name="SConstruct" printme="1"> - 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') </file> <file name="foo.c"> foo.c </file> + <file name="bar.c"> + foo.c + </file> </scons_example> <para> - Now &SCons; will use values of - <literal>red</literal>, - <literal>green</literal> or - <literal>blue</literal> - 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: </para> - <scons_output example="EnumOption_ic2"> - <scons_output_command>scons -Q COLOR=Red foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=nAvY foo.o</scons_output_command> - <scons_output_command>scons -Q COLOR=GREEN foo.o</scons_output_command> + <scons_output example="COMMAND_LINE_TARGETS"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q bar</scons_output_command> </scons_output> + <para> + + 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. + + </para> + </section> <section> - <title>Multiple Values From a List: the &ListOption; Build Option</title> + <title>Controlling the Default Targets: the &Default; Function</title> <para> - 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: </para> - <scons_example name="ListOption"> - <file name="SConstruct" printme="1"> - 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') - </file> - <file name="foo.c"> - foo.c - </file> + <scons_example name="Default1"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + env.Program('goodbye.c') + Default(hello) + </file> + <file name="hello.c"> + hello.c + </file> + <file name="goodbye.c"> + goodbye.c + </file> </scons_example> <para> - 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: </para> - <scons_output example="ListOption"> - <scons_output_command>scons -Q COLORS=red,blue foo.o</scons_output_command> - <scons_output_command>scons -Q COLORS=blue,green,red foo.o</scons_output_command> + <scons_output example="Default1"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q goodbye</scons_output_command> </scons_output> <para> - 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 + (<literal>.</literal>) on the command line + to tell &SCons; to build + everything in (or below) the current directory: </para> - <scons_output example="ListOption"> - <scons_output_command>scons -Q COLORS=all foo.o</scons_output_command> - <scons_output_command>scons -Q COLORS=none foo.o</scons_output_command> + <scons_output example="Default1"> + <scons_output_command>scons -Q .</scons_output_command> </scons_output> <para> - 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: </para> - <scons_output example="ListOption"> - <scons_output_command>scons -Q COLORS=magenta foo.o</scons_output_command> - </scons_output> - - </section> - - <section> - <title>Path Names: the &PathOption; Build Option</title> + <scons_example name="Default2"> + <file name="SConstruct" printme="1"> + env = Environment() + prog1 = env.Program('prog1.c') + Default(prog1) + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog3) + </file> + <file name="prog1.c"> + prog1.c + </file> + <file name="prog2.c"> + prog2.c + </file> + <file name="prog3.c"> + prog3.c + </file> + </scons_example> <para> - &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: </para> - <scons_example name="PathOption"> - <file name="SConstruct" printme="1"> - 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') - </file> - <file name="foo.c"> - foo.c - </file> - <file name="__ROOT__/etc/my_config"> - /opt/location - </file> - <file name="__ROOT__/usr/local/etc/other_config"> - /opt/location - </file> - </scons_example> + <programlisting> + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog1, prog3) + </programlisting> <para> - 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 + <application>prog1</application> + and + <application>prog3</application> + programs by default: </para> - <scons_output example="PathOption"> - <scons_output_command>scons -Q foo.o</scons_output_command> - <scons_output_command>scons -Q CONFIG=__ROOT__/usr/local/etc/other_config foo.o</scons_output_command> + <scons_output example="Default2"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q .</scons_output_command> </scons_output> <para> - 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;: </para> - <scons_output example="PathOption"> - <scons_output_command>scons -Q CONFIG=__ROOT__/does/not/exist foo.o</scons_output_command> - </scons_output> + <scons_example name="Default3"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program(['prog1/main.c', 'prog1/foo.c']) + env.Program(['prog2/main.c', 'prog2/bar.c']) + Default('prog1') + </file> + <directory name="prog1"></directory> + <directory name="prog2"></directory> + <file name="prog1/main.c"> + int main() { printf("prog1/main.c\n"); } + </file> + <file name="prog1/foo.c"> + int foo() { printf("prog1/foo.c\n"); } + </file> + <file name="prog2/main.c"> + int main() { printf("prog2/main.c\n"); } + </file> + <file name="prog2/bar.c"> + int bar() { printf("prog2/bar.c\n"); } + </file> + </scons_example> <para> - &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: </para> - <scons_example name="PathIsFile"> - <file name="SConstruct" printme="1"> - 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') - </file> - <file name="foo.c"> - foo.c - </file> - <file name="__ROOT__/etc/my_config"> - /opt/location - </file> - </scons_example> + <scons_output example="Default3"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q .</scons_output_command> + </scons_output> <para> - 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 <literal>None</literal> + variable: </para> - <scons_example name="PathIsDir"> - <file name="SConstruct" printme="1"> - 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') - </file> - <file name="foo.c"> - foo.c - </file> - <file name="__ROOT__/var/my_dbdir"> - /opt/location - </file> + <scons_example name="Default4"> + <file name="SConstruct" printme="1"> + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + Default(None) + </file> + <file name="prog1.c"> + prog1.c + </file> + <file name="prog2.c"> + prog2.c + </file> </scons_example> <para> - 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: </para> - <scons_example name="PathIsDirCreate"> - <file name="SConstruct" printme="1"> - 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') - </file> - <file name="foo.c"> - foo.c - </file> - <file name="__ROOT__/var/my_dbdir"> - /opt/location - </file> - </scons_example> + <scons_output example="Default4"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q .</scons_output_command> + </scons_output> + + <section> + <title>Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable</title> + + <para> + + &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 <function>map</function> function + to run the list through <function>str</function>: + + </para> + + <scons_example name="DEFAULT_TARGETS_1"> + <file name="SConstruct" printme="1"> + prog1 = Program('prog1.c') + Default(prog1) + print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) + </file> + <file name="prog1.c"> + prog1.c + </file> + </scons_example> + + <para> + + (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 <literal>-Q</literal> flag when we run &SCons;:) + + </para> + + <scons_output example="DEFAULT_TARGETS_1"> + <scons_output_command>scons</scons_output_command> + </scons_output> + + <para> + + 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: + + </para> + + <scons_example name="DEFAULT_TARGETS_2"> + <file name="SConstruct" printme="1"> + 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) + </file> + <file name="prog1.c"> + prog1.c + </file> + <file name="prog2.c"> + prog2.c + </file> + </scons_example> + + <para> + + Which yields the output: + + </para> + + <scons_output example="DEFAULT_TARGETS_2"> + <scons_output_command>scons</scons_output_command> + </scons_output> + + <para> + + 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. + + </para> + + </section> + + </section> + + <section> + <title>Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable</title> <para> - 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: </para> - <scons_example name="PathAccept"> - <file name="SConstruct" printme="1"> - 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') - </file> - <file name="foo.c"> - foo.c - </file> - </scons_example> + <sconstruct> + if COMMAND_LINE_TARGETS: + targets = COMMAND_LINE_TARGETS + else: + targets = DEFAULT_TARGETS + </sconstruct> - </section> + <para> - <section> - <title>Enabled/Disabled Path Names: the &PackageOption; Build Option</title> + &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. + + </para> <para> - 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 <literal>yes</literal> or <literal>no</literal> 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: </para> - <scons_example name="PackageOption"> + <scons_example name="BUILD_TARGETS_1"> <file name="SConstruct" printme="1"> - opts = Options('custom.py') - opts.Add(PackageOption('PACKAGE', - 'Location package', - '__ROOT__/opt/location')) - env = Environment(options = opts, - CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) - env.Program('foo.c') - </file> - <file name="foo.c"> - foo.c + prog1 = Program('prog1.c') + Program('prog2.c') + Default(prog1) + print "BUILD_TARGETS is", map(str, BUILD_TARGETS) </file> - <file name="__ROOT__/opt/location"> - /opt/location + <file name="prog1.c"> + prog1.c </file> - <file name="__ROOT__/usr/local/location"> - /opt/location + <file name="prog2.c"> + prog2.c </file> </scons_example> <para> - 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: </para> - <scons_output example="PackageOption"> - <scons_output_command>scons -Q foo.o</scons_output_command> - <scons_output_command>scons -Q PACKAGE=__ROOT__/usr/local/location foo.o</scons_output_command> - <scons_output_command>scons -Q PACKAGE=yes foo.o</scons_output_command> - <scons_output_command>scons -Q PACKAGE=no foo.o</scons_output_command> + <scons_output example="BUILD_TARGETS_1"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q prog2</scons_output_command> + <scons_output_command>scons -Q -c .</scons_output_command> </scons_output> </section> </section> - - <section> - <title>Adding Multiple Command-Line Build Options at Once</title> - - <para> - - 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: - - </para> - - <scons_example name="AddOptions_1"> - <file name="SConstruct" printme="1"> - 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), - ) - </file> - </scons_example> - - <para> - </para> - - </section> - - <!-- - - AddOption() function for things like - -prefix=, - -force - - --> diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml index 8675c19..175dd5c 100644 --- a/doc/user/command-line.xml +++ b/doc/user/command-line.xml @@ -25,508 +25,633 @@ <para> - &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: </para> - <section> - <title>Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable</title> + <variablelist> - <para> - - 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 <literal>-j 2</literal> - to run the builds in parallel. - To avoid having to type <literal>-j 2</literal> 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. - - </para> - - <para> - - If, for example, - you're using a POSIX shell that's - compatible with the Bourne shell, - and you always want &SCons; to use the - <literal>-Q</literal> option, - you can set the &SCONSFLAGS; - environment as follows: - - </para> - - - - <screen> - % <userinput>scons</userinput> - scons: Reading SConscript files ... - scons: done reading SConscript files. - scons: Building targets ... - ... [build output] ... - scons: done building targets. - % <userinput>export SCONSFLAGS="-Q"</userinput> - % <userinput>scons</userinput> - ... [build output] ... - </screen> - - <para> - - Users of &csh;-style shells on POSIX systems - can set the &SCONSFLAGS; environment as follows: - - </para> - - <screen> - $ <userinput>setenv SCONSFLAGS "-Q"</userinput> - </screen> + <varlistentry> + <term>Options</term> + <listitem> <para> - Windows users may typically want to set the - &SCONSFLAGS; in the appropriate tab of the - <literal>System Properties</literal> window. + Command-line options always begin with + one or two <literal>-</literal> (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 <xref linkend="sect-command-line-options"></xref>, below. </para> + </listitem> + </varlistentry> - </section> - - <section> - <title>Getting at Command-Line Targets</title> + <varlistentry> + <term>Variables</term> + <listitem> <para> - &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: - - </para> - - <programlisting> - if 'bar' in COMMAND_LINE_TARGETS: - print "Don't forget to copy `bar' to the archive!" - Default(Program('foo.c')) - Program('bar.c') - </programlisting> - - <para> - - 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 <literal>=</literal> + (equal sign) is considered a variable setting with the form + <varname>variable</varname>=<varname>value</varname> + &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 <xref linkend="sect-command-line-variables"></xref>, below. </para> + </listitem> + </varlistentry> - <screen> - % <userinput>scons -Q</userinput> - cc -o foo.o -c foo.c - cc -o foo foo.o - % <userinput>scons -Q bar</userinput> - Don't forget to copy `bar' to the archive! - cc -o bar.o -c bar.c - cc -o bar bar.o - </screen> + <varlistentry> + <term>Targets</term> + <listitem> <para> - 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 <xref linkend="sect-command-line-targets"></xref>, below. </para> + </listitem> + </varlistentry> - </section> + </variablelist> - <section> - <title>Controlling the Default Targets</title> + <section id="sect-command-line-options"> + <title>Command-Line Options</title> <para> - 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 <emphasis>command-line options</emphasis> + that control its behavior. + A &SCons; <emphasis>command-line option</emphasis> + always begins with one or two <literal>-</literal> (hyphen) + characters. </para> - <programlisting> - env = Environment() - hello = env.Program('hello.c') - env.Program('goodbye.c') - Default(hello) - </programlisting> - - <para> - - This &SConstruct; file knows how to build two programs, - &hello; and &goodbye;, - but only builds the - &hello; program by default: + <section> + <title>Not Having to Specify Command-Line Options Each Time: the &SCONSFLAGS; Environment Variable</title> - </para> + <para> - <screen> - % <userinput>scons -Q</userinput> - cc -o hello.o -c hello.c - cc -o hello hello.o - % <userinput>scons -Q</userinput> - scons: `hello' is up to date. - % <userinput>scons -Q goodbye</userinput> - cc -o goodbye.o -c goodbye.c - cc -o goodbye goodbye.o - </screen> + 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 <literal>-j 2</literal> + to have &SCons; run up to two build commands in parallel. + To avoid having to type <literal>-j 2</literal> 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. - <para> + </para> - Note that, even when you use the &Default; - function in your &SConstruct; file, - you can still explicitly specify the current directory - (<literal>.</literal>) on the command line - to tell &SCons; to build - everything in (or below) the current directory: + <para> - </para> + If, for example, + you're using a POSIX shell that's + compatible with the Bourne shell, + and you always want &SCons; to use the + <literal>-Q</literal> option, + you can set the &SCONSFLAGS; + environment as follows: - <screen> - % <userinput>scons -Q .</userinput> - cc -o goodbye.o -c goodbye.c - cc -o goodbye goodbye.o - cc -o hello.o -c hello.c - cc -o hello hello.o - </screen> + </para> - <para> + - 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: + <screen> + % <userinput>scons</userinput> + scons: Reading SConscript files ... + scons: done reading SConscript files. + scons: Building targets ... + ... [build output] ... + scons: done building targets. + % <userinput>export SCONSFLAGS="-Q"</userinput> + % <userinput>scons</userinput> + ... [build output] ... + </screen> - </para> + <para> - <programlisting> - env = Environment() - prog1 = env.Program('prog1.c') - Default(prog1) - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog3) - </programlisting> + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: - <para> + </para> - Or you can specify more than one target - in a single call to the &Default; function: + <screen> + $ <userinput>setenv SCONSFLAGS "-Q"</userinput> + </screen> - </para> + <para> - <programlisting> - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - prog3 = env.Program('prog3.c') - Default(prog1, prog3) - </programlisting> + Windows users may typically want to set the + &SCONSFLAGS; in the appropriate tab of the + <literal>System Properties</literal> window. - <para> + </para> - Either of these last two examples - will build only the - <application>prog1</application> - and - <application>prog3</application> - programs by default: + </section> - </para> + <section> + <title>Getting Values Set by Command-Line Options: the &GetOption; Function</title> - <screen> - % <userinput>scons -Q</userinput> - cc -o prog1.o -c prog1.c - cc -o prog1 prog1.o - cc -o prog3.o -c prog3.c - cc -o prog3 prog3.o - % <userinput>scons -Q .</userinput> - cc -o prog2.o -c prog2.c - cc -o prog2 prog2.o - </screen> + <para> - <para> + &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 <literal>-h</literal> or <literal>--help</literal> 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;: + </para> - </para> + <para> - <programlisting> - env = Environment() - env.Program(['prog1/main.c', 'prog1/foo.c']) - env.Program(['prog2/main.c', 'prog2/bar.c']) - Default('prog1') - </programlisting> + 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 <emphasis>not</emphasis> specified + the <literal>-h</literal> or <literal>--help</literal> option, + like so: - <para> + </para> - In which case only the target(s) in that - directory will be built by default: + <programlisting></programlisting> - </para> + <para> - <screen> - % <userinput>scons -Q</userinput> - 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 - % <userinput>scons -Q</userinput> - scons: `prog1' is up to date. - % <userinput>scons -Q .</userinput> - 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 - </screen> + 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 + <xref linkend="sect-command-line-option-strings"></xref> section, + below. - <para> + </para> - Lastly, if for some reason you don't want - any targets built by default, - you can use the Python <literal>None</literal> - variable: + </section> - </para> + <section> + <title>Setting Values of Command-Line Options: the &SetOption; Function</title> - <programlisting> - env = Environment() - prog1 = env.Program('prog1.c') - prog2 = env.Program('prog2.c') - Default(None) - </programlisting> + <para> - <para> + 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 + <xref linkend="sect-command-line-option-strings"></xref> section, + below. - Which would produce build output like: + </para> - </para> + <para> - <screen> - % <userinput>scons -Q</userinput> - scons: *** No targets specified and no Default() targets found. Stop. - % <userinput>scons -Q .</userinput> - cc -o prog1.o -c prog1.c - cc -o prog1 prog1.o - cc -o prog2.o -c prog2.c - cc -o prog2 prog2.o - </screen> + One use of the &SetOption; function is to + specify a value for the <literal>-j</literal> + or <literal>--jobs</literal> 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 <literal>-j</literal> option is + somewhat system-dependent. + One rough guideline is that the more processors + your system has, + the higher you want to set the + <literal>-j</literal> value, + in order to take advantage of the number of CPUs. - <section> - <title>Getting at the List of Default Targets</title> + </para> <para> - &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 <function>map</function> function - to run the list through <function>str</function>: + For example, suppose the administrators + of your development systems + have standardized on setting a + <varname>NUM_CPU</varname> 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: </para> <programlisting> - 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') </programlisting> <para> - (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 <literal>-Q</literal> flag when we run &SCons;:) + The above snippet of code + sets the value of the <literal>--jobs</literal> option + to the value specified in the + <varname>$NUM_CPU</varname> 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 <literal>--jobs</literal> + value is <literal>num_jobs</literal> + for historical reasons.) + The code in this example prints the <literal>num_jobs</literal> + value for illustrative purposes. + It uses a default value of <literal>2</literal> + to provide some minimal parallelism even on + single-processor systems: </para> <screen> - % <userinput>scons</userinput> - 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. + % <userinput>scons -Q</userinput> + running with -j 2 + scons: `.' is up to date. </screen> <para> - 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 <varname>$NUM_CPU</varname> + environment variable is set, + then we use that for the default number of jobs: </para> - <programlisting> - 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) - </programlisting> + <screen> + % <userinput>export NUM_CPU="4"</userinput> + % <userinput>scons -Q</userinput> + running with -j 4 + scons: `.' is up to date. + </screen> <para> - Which yields the output: + But any explicit + <literal>-j</literal> or <literal>--jobs</literal> + value the user specifies an the command line is used first, + regardless of whether or not + the <varname>$NUM_CPU</varname> environment + variable is set: </para> <screen> - % <userinput>scons</userinput> - 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. + % <userinput>scons -Q -j 7</userinput> + running with -j 7 + scons: `.' is up to date. + % <userinput>export NUM_CPU="4"</userinput> + % <userinput>scons -Q -j 3</userinput> + running with -j 3 + scons: `.' is up to date. </screen> + </section> + + <section id="sect-command-line-option-strings"> + <title>Strings for Getting or Setting Values of &SCons; Command-Line Options</title> + <para> - 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: <literal>--</literal>), + after replacing any remaining hyphen characters + with underscores. </para> + <para> + + The full list of strings and the variables they + correspond to is as follows: + + </para> + + <informaltable> + <tgroup cols="2" align="left"> + + <thead> + + <row> + <entry>String for &GetOption; and &SetOption;</entry> + <entry>Command-Line Option(s)</entry> + </row> + + </thead> + + <tbody> + + <row> + <entry><literal>cache_debug</literal></entry> + <entry><option>--cache-debug</option></entry> + </row> + + <row> + <entry><literal>cache_disable</literal></entry> + <entry><option>--cache-disable</option></entry> + </row> + + <row> + <entry><literal>cache_force</literal></entry> + <entry><option>--cache-force</option></entry> + </row> + + <row> + <entry><literal>cache_show</literal></entry> + <entry><option>--cache-show</option></entry> + </row> + + <row> + <entry><literal>clean</literal></entry> + <entry><option>-c</option>, + <option>--clean</option>, + <option>--remove</option></entry> + </row> + + <row> + <entry><literal>config</literal></entry> + <entry><option>--config</option></entry> + </row> + + <row> + <entry><literal>directory</literal></entry> + <entry><option>-C</option>, + <option>--directory</option></entry> + </row> + + <row> + <entry><literal>diskcheck</literal></entry> + <entry><option>--diskcheck</option></entry> + </row> + + <row> + <entry><literal>duplicate</literal></entry> + <entry><option>--duplicate</option></entry> + </row> + + <row> + <entry><literal>file</literal></entry> + <entry><option>-f</option>, + <option>--file</option>, + <option>--makefile </option>, + <option>--sconstruct</option></entry> + </row> + + <row> + <entry><literal>help</literal></entry> + <entry><option>-h</option>, + <option>--help</option></entry> + </row> + + <row> + <entry><literal>ignore_errors</literal></entry> + <entry><option>--ignore-errors</option></entry> + </row> + + <row> + <entry><literal>implicit_cache</literal></entry> + <entry><option>--implicit-cache</option></entry> + </row> + + <row> + <entry><literal>implicit_deps_changed</literal></entry> + <entry><option>--implicit-deps-changed</option></entry> + </row> + + <row> + <entry><literal>implicit_deps_unchanged</literal></entry> + <entry><option>--implicit-deps-unchanged</option></entry> + </row> + + <row> + <entry><literal>interactive</literal></entry> + <entry><option>--interact</option>, + <option>--interactive</option></entry> + </row> + + <row> + <entry><literal>keep_going</literal></entry> + <entry><option>-k</option>, + <option>--keep-going</option></entry> + </row> + + <row> + <entry><literal>max_drift</literal></entry> + <entry><option>--max-drift</option></entry> + </row> + + <row> + <entry><literal>no_exec</literal></entry> + <entry><option>-n</option>, + <option>--no-exec</option>, + <option>--just-print</option>, + <option>--dry-run</option>, + <option>--recon</option></entry> + </row> + + <row> + <entry><literal>no_site_dir</literal></entry> + <entry><option>--no-site-dir</option></entry> + </row> + + <row> + <entry><literal>num_jobs</literal></entry> + <entry><option>-j</option>, + <option>--jobs</option></entry> + </row> + + <row> + <entry><literal>profile_file</literal></entry> + <entry><option>--profile</option></entry> + </row> + + <row> + <entry><literal>question</literal></entry> + <entry><option>-q</option>, + <option>--question</option></entry> + </row> + + <row> + <entry><literal>random</literal></entry> + <entry><option>--random</option></entry> + </row> + + <row> + <entry><literal>repository</literal></entry> + <entry><option>-Y</option>, + <option>--repository</option>, + <option>--srcdir</option></entry> + </row> + + <row> + <entry><literal>silent</literal></entry> + <entry><option>-s</option>, + <option>--silent</option>, + <option>--quiet</option></entry> + </row> + + <row> + <entry><literal>site_dir</literal></entry> + <entry><option>--site-dir</option></entry> + </row> + + <row> + <entry><literal>stack_size</literal></entry> + <entry><option>--stack-size</option></entry> + </row> + + <row> + <entry><literal>taskmastertrace_file</literal></entry> + <entry><option>--taskmastertrace</option></entry> + </row> + + <row> + <entry><literal>warn</literal></entry> + <entry><option>--warn</option> <option>--warning</option></entry> + </row> + + </tbody> + + </tgroup> + </informaltable> + </section> - </section> + <section> + <title>Adding Custom Command-Line Options: the &AddOption; Function</title> - <section> - <title>Getting at the List of Build Targets, Regardless of Origin</title> + <para> - <para> + &SCons; also allows you to define your own + command-line options with the &AddOption; function. + The &AddOption; function takes the same arguments + as the <function>optparse.add_option</function> function + from the standard Python library. + <footnote> + <para> + The &AddOption; function is, + in fact, implemented using a subclass + of the <classname>optparse.OptionParser</classname>. + </para> + </footnote> + 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: + </para> - </para> + <para> - <programlisting> - if COMMAND_LINE_TARGETS: - targets = COMMAND_LINE_TARGETS - else: - targets = DEFAULT_TARGETS - </programlisting> + One useful example of using this functionality + is to provide a <option>--prefix</option> for users: - <para> + </para> - &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. + <programlisting> + 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) + </programlisting> - </para> + <para> - <para> + The above code uses the &GetOption; function + to set the <varname>$PREFIX</varname> + construction variable to any + value that the user specifies with a command-line + option of <literal>--prefix</literal>. + Because <varname>$PREFIX</varname> + will expand to a null string if it's not initialized, + running &SCons; without the + option of <literal>--prefix</literal> + will install the file in the + <filename>/usr/bin/</filename> 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: + </para> - </para> + <screen> + % <userinput>scons -Q -n</userinput> + Install file: "foo.in" as "/usr/bin/foo.in" + </screen> - <programlisting> - prog1 = Program('prog1.c') - Program('prog2.c') - Default(prog1) - print "BUILD_TARGETS is", map(str, BUILD_TARGETS) - </programlisting> + <para> - <para> + But specifying <literal>--prefix=/tmp/install</literal> + on the command line causes the file to be installed in the + <filename>/tmp/install/usr/bin/</filename> directory: - Notice how the value of &BUILD_TARGETS; - changes depending on whether a target is - specified on the command line: + </para> - </para> + <screen> + % <userinput>scons -Q -n --prefix=/tmp/install</userinput> + Install file: "foo.in" as "/tmp/install/usr/bin/foo.in" + </screen> - <screen> - % <userinput>scons -Q</userinput> - BUILD_TARGETS is ['prog1'] - cc -o prog1.o -c prog1.c - cc -o prog1 prog1.o - % <userinput>scons -Q prog2</userinput> - BUILD_TARGETS is ['prog2'] - cc -o prog2.o -c prog2.c - cc -o prog2 prog2.o - % <userinput>scons -Q -c .</userinput> - BUILD_TARGETS is ['.'] - Removed prog1.o - Removed prog1 - Removed prog2.o - Removed prog2 - </screen> + </section> </section> - <section> - <title>Command-Line <varname>variable</varname>=<varname>value</varname> Build Options</title> + <section id="sect-command-line-variables"> + <title>Command-Line <varname>variable</varname>=<varname>value</varname> Build Variables</title> <para> @@ -556,7 +681,7 @@ to specifications on the command line. (Note that unless you want to require that users <emphasis>always</emphasis> - specify an option, + specify a variable, you probably want to use the Python <literal>ARGUMENTS.get()</literal> function, @@ -615,878 +740,1505 @@ </para> - </section> - - <section> - <title>Controlling Command-Line Build Options</title> - <para> - Being able to use a command-line build option like - <literal>debug=1</literal> 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. </para> <para> - 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 <literal>-D</literal> 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 + <varname>variable</varname>=<varname>value</varname> + 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 + <varname>define=</varname> + settings on the command line: </para> <programlisting> - 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') </programlisting> <para> - This &SConstruct; file first creates an - &Options; object - (the <literal>opts = Options()</literal> 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 <literal>0</literal> - (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. - - </para> - - <para> - - 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: </para> <screen> - % <userinput>scons -Q RELEASE=1</userinput> - 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 + % <userinput>scons -Q define=FOO</userinput> + cc -o prog.o -c -DFOO prog.c + % <userinput>scons -Q define=FOO define=BAR</userinput> + cc -o prog.o -c -DFOO -DBAR prog.c </screen> - </section> - - <section> - <title>Providing Help for Command-Line Build Options</title> - <para> - 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 <literal>scons -h</literal>. - 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 + <varname>variable</varname>=<varname>value</varname> + 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). </para> - <programlisting> - opts = Options('custom.py') - opts.Add('RELEASE', 'Set to 1 to build for release', 0) - env = Environment(options = opts) - Help(opts.GenerateHelpText(env)) - </programlisting> - - <para> - - &SCons; will now display some useful text - when the <literal>-h</literal> option is used: - - </para> + <section> + <title>Controlling Command-Line Build Variables</title> - <screen> - % <userinput>scons -Q -h</userinput> - - RELEASE: Set to 1 to build for release - default: 0 - actual: 0 - - Use scons -H for help about command-line options. - </screen> + <para> - <para> + Being able to use a command-line build variable like + <literal>debug=1</literal> 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. + </para> - </para> + <para> - </section> + 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 <literal>-D</literal> 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: - <section> - <title>Reading Build Options From a File</title> + </para> - <para> + <programlisting> + 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']) + </programlisting> - Being able to use a command-line build option like - <literal>debug=1</literal> 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: + <para> - </para> + This &SConstruct; file first creates a &Variables; object + (the <literal>vars = Variables()</literal> 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 <literal>0</literal> + (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. - <programlisting> - 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)) - </programlisting> + </para> - <para> + <para> - 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: - </para> + </para> - <programlisting> - RELEASE = 1 - </programlisting> + <screen> + % <userinput>scons -Q RELEASE=1</userinput> + 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 + </screen> - <para> + <para> - 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. - </para> + </para> - <screen> - % <userinput>scons -Q</userinput> - 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 - </screen> + </section> - <para> + <section> + <title>Providing Help for Command-Line Build Variables</title> - And if we change the contents of &custom_py; to: + <para> - </para> + 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 <literal>scons -h</literal>. + 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: - <programlisting> - RELEASE = 0 - </programlisting> + </para> - <para> + <programlisting> + vars = Variables('custom.py') + vars.Add('RELEASE', 'Set to 1 to build for release', 0) + env = Environment(variables = vars) + Help(vars.GenerateHelpText(env)) + </programlisting> - The object files are rebuilt appropriately - with the new option: + <para> - </para> + &SCons; will now display some useful text + when the <literal>-h</literal> option is used: - <screen> - % <userinput>scons -Q</userinput> - 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 - </screen> + </para> - </section> + <screen> + % <userinput>scons -Q -h</userinput> + + RELEASE: Set to 1 to build for release + default: 0 + actual: 0 + + Use scons -H for help about command-line options. + </screen> - <section> - <title>Canned Build Options</title> + <para> - <para> + 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. + </para> - </para> + </section> <section> - <title>True/False Values: the &BoolOption; Build Option</title> + <title>Reading Build Variables From a File</title> <para> - 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: </para> + <programlisting> + 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)) + </programlisting> + <para> - 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: </para> <programlisting> - 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') - </programlisting> + RELEASE = 1 + </programlisting> <para> - With this build option, - the &RELEASE; variable can now be enabled by - setting it to the value <literal>yes</literal> - or <literal>t</literal>: + Note that this file is actually executed + like a Python script. + Now when we run &SCons;: </para> <screen> - % <userinput>scons -Q RELEASE=yes foo.o</userinput> - cc -o foo.o -c -DRELEASE_BUILD=True foo.c - </screen> - - <screen> - % <userinput>scons -Q RELEASE=t foo.o</userinput> - cc -o foo.o -c -DRELEASE_BUILD=True foo.c + % <userinput>scons -Q</userinput> + 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 </screen> <para> - Other values that equate to &true; include - <literal>y</literal>, - <literal>1</literal>, - <literal>on</literal> - and - <literal>all</literal>. + And if we change the contents of &custom_py; to: </para> + <programlisting> + RELEASE = 0 + </programlisting> + <para> - Conversely, &RELEASE; may now be given a &false; - value by setting it to - <literal>no</literal> - or - <literal>f</literal>: + The object files are rebuilt appropriately + with the new variable: </para> <screen> - % <userinput>scons -Q RELEASE=no foo.o</userinput> - cc -o foo.o -c -DRELEASE_BUILD=False foo.c + % <userinput>scons -Q</userinput> + 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 </screen> - <screen> - % <userinput>scons -Q RELEASE=f foo.o</userinput> - cc -o foo.o -c -DRELEASE_BUILD=False foo.c - </screen> + </section> + + <section> + <title>Pre-Defined Build Variable Functions</title> <para> - Other values that equate to &false; include - <literal>n</literal>, - <literal>0</literal>, - <literal>off</literal> - and - <literal>none</literal>. + &SCons; provides a number of functions + that provide ready-made behaviors + for various types of command-line build variables. </para> - <para> + <section> + <title>True/False Values: the &BoolVariable; Build Variable Function</title> - Lastly, if a user tries to specify - any other value, - &SCons; supplies an appropriate error message: + <para> - </para> + 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;. - <screen> - % <userinput>scons -Q RELEASE=bad_value foo.o</userinput> - - scons: *** Error converting option: RELEASE - Invalid value for boolean option: bad_value - File "/home/my/project/SConstruct", line 4, in <module> - </screen> + </para> + + <para> + + 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: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + With this build variable, + the &RELEASE; variable can now be enabled by + setting it to the value <literal>yes</literal> + or <literal>t</literal>: + + </para> + + <screen> + % <userinput>scons -Q RELEASE=yes foo.o</userinput> + cc -o foo.o -c -DRELEASE_BUILD=True foo.c + </screen> + + <screen> + % <userinput>scons -Q RELEASE=t foo.o</userinput> + cc -o foo.o -c -DRELEASE_BUILD=True foo.c + </screen> + + <para> + + Other values that equate to &true; include + <literal>y</literal>, + <literal>1</literal>, + <literal>on</literal> + and + <literal>all</literal>. + + </para> + + <para> + + Conversely, &RELEASE; may now be given a &false; + value by setting it to + <literal>no</literal> + or + <literal>f</literal>: + + </para> + + <screen> + % <userinput>scons -Q RELEASE=no foo.o</userinput> + cc -o foo.o -c -DRELEASE_BUILD=False foo.c + </screen> + + <screen> + % <userinput>scons -Q RELEASE=f foo.o</userinput> + cc -o foo.o -c -DRELEASE_BUILD=False foo.c + </screen> + + <para> + + Other values that equate to &false; include + <literal>n</literal>, + <literal>0</literal>, + <literal>off</literal> + and + <literal>none</literal>. + + </para> + + <para> + + Lastly, if a user tries to specify + any other value, + &SCons; supplies an appropriate error message: + + </para> + + <screen> + % <userinput>scons -Q RELEASE=bad_value foo.o</userinput> + + scons: *** Error converting option: RELEASE + Invalid value for boolean option: bad_value + File "/home/my/project/SConstruct", line 4, in <module> + </screen> + + </section> + + <section> + <title>Single Value From a List: the &EnumVariable; Build Variable Function</title> + + <para> + + 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: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + The user can now explicity set the &COLOR; build variable + to any of the specified allowed values: + + </para> + + <screen> + % <userinput>scons -Q COLOR=red foo.o</userinput> + cc -o foo.o -c -DCOLOR="red" foo.c + % <userinput>scons -Q COLOR=blue foo.o</userinput> + cc -o foo.o -c -DCOLOR="blue" foo.c + % <userinput>scons -Q COLOR=green foo.o</userinput> + cc -o foo.o -c -DCOLOR="green" foo.c + </screen> + + <para> + + But, almost more importantly, + an attempt to set &COLOR; + to a value that's not in the list + generates an error message: + + </para> + + <screen> + % <userinput>scons -Q COLOR=magenta foo.o</userinput> + + scons: *** Invalid value for option COLOR: magenta + File "/home/my/project/SConstruct", line 5, in <module> + </screen> + + <para> + + 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 <literal>navy</literal> as a synonym for + <literal>blue</literal>. + We do this by adding a ↦ dictionary + that will map its key values + to the desired legal value: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + As desired, the user can then use + <literal>navy</literal> on the command line, + and &SCons; will translate it into <literal>blue</literal> + when it comes time to use the &COLOR; + variable to build a target: + + </para> + + <screen> + % <userinput>scons -Q COLOR=navy foo.o</userinput> + cc -o foo.o -c -DCOLOR="blue" foo.c + </screen> + + <para> + + By default, when using the &EnumVariable; function, + arguments that differ + from the legal values + only in case + are treated as illegal values: + + </para> + + <screen> + % <userinput>scons -Q COLOR=Red foo.o</userinput> + + scons: *** Invalid value for option COLOR: Red + File "/home/my/project/SConstruct", line 5, in <module> + % <userinput>scons -Q COLOR=BLUE foo.o</userinput> + + scons: *** Invalid value for option COLOR: BLUE + File "/home/my/project/SConstruct", line 5, in <module> + % <userinput>scons -Q COLOR=nAvY foo.o</userinput> + + scons: *** Invalid value for option COLOR: nAvY + File "/home/my/project/SConstruct", line 5, in <module> + </screen> + + <para> + + The &EnumVariable; function can take an additional + &ignorecase; keyword argument that, + when set to <literal>1</literal>, + tells &SCons; to allow case differences + when the values are specified: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + Which yields the output: + + </para> + + <screen> + % <userinput>scons -Q COLOR=Red foo.o</userinput> + cc -o foo.o -c -DCOLOR="Red" foo.c + % <userinput>scons -Q COLOR=BLUE foo.o</userinput> + cc -o foo.o -c -DCOLOR="BLUE" foo.c + % <userinput>scons -Q COLOR=nAvY foo.o</userinput> + cc -o foo.o -c -DCOLOR="blue" foo.c + % <userinput>scons -Q COLOR=green foo.o</userinput> + cc -o foo.o -c -DCOLOR="green" foo.c + </screen> + + <para> + + Notice that an &ignorecase; value of <literal>1</literal> + 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 <literal>2</literal>: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + Now &SCons; will use values of + <literal>red</literal>, + <literal>green</literal> or + <literal>blue</literal> + regardless of how the user spells + those values on the command line: + + </para> + + <screen> + % <userinput>scons -Q COLOR=Red foo.o</userinput> + cc -o foo.o -c -DCOLOR="red" foo.c + % <userinput>scons -Q COLOR=nAvY foo.o</userinput> + cc -o foo.o -c -DCOLOR="blue" foo.c + % <userinput>scons -Q COLOR=GREEN foo.o</userinput> + cc -o foo.o -c -DCOLOR="green" foo.c + </screen> + + </section> + + <section> + <title>Multiple Values From a List: the &ListVariable; Build Variable Function</title> + + <para> + + 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: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + 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: + + </para> + + <screen> + % <userinput>scons -Q COLORS=red,blue foo.o</userinput> + cc -o foo.o -c -DCOLORS="red blue" foo.c + % <userinput>scons -Q COLORS=blue,green,red foo.o</userinput> + cc -o foo.o -c -DCOLORS="blue green red" foo.c + </screen> + + <para> + + 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: + + </para> + + <screen> + % <userinput>scons -Q COLORS=all foo.o</userinput> + cc -o foo.o -c -DCOLORS="red green blue" foo.c + % <userinput>scons -Q COLORS=none foo.o</userinput> + cc -o foo.o -c -DCOLORS="" foo.c + </screen> + + <para> + + And, of course, an illegal value + still generates an error message: + + </para> + + <screen> + % <userinput>scons -Q COLORS=magenta foo.o</userinput> + + scons: *** Error converting option: COLORS + Invalid value(s) for option: magenta + File "/home/my/project/SConstruct", line 5, in <module> + </screen> + + </section> + + <section> + <title>Path Names: the &PathVariable; Build Variable Function</title> + + <para> + + &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: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + This then allows the user to + override the &CONFIG; build variable + on the command line as necessary: + + </para> + + <screen> + % <userinput>scons -Q foo.o</userinput> + cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c + % <userinput>scons -Q CONFIG=/usr/local/etc/other_config foo.o</userinput> + scons: `foo.o' is up to date. + </screen> + + <para> + + By default, &PathVariable; checks to make sure + that the specified path exists and generates an error if it + doesn't: + + </para> + + <screen> + % <userinput>scons -Q CONFIG=/does/not/exist foo.o</userinput> + + scons: *** Path for option CONFIG does not exist: /does/not/exist + File "/home/my/project/SConstruct", line 6, in <module> + </screen> + + <para> + + &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: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + Conversely, to ensure that any specified paths are + directories and not files, + use the &PathVariable_PathIsDir; method: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + 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: + + </para> + + <programlisting> + 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') + </programlisting> + + <para> + + 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: + + </para> + + <programlisting> + 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') + </programlisting> + + </section> + + <section> + <title>Enabled/Disabled Path Names: the &PackageVariable; Build Variable Function</title> + + <para> + + 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 <literal>yes</literal> or <literal>no</literal> keywords, + in addition to allow them + to supply an explicit path name. + &SCons; supports the &PackageVariable; + function to support this: + + </para> + + <programlisting> + vars = Variables('custom.py') + vars.Add(PackageVariable('PACKAGE', + 'Location package', + '/opt/location')) + env = Environment(variables = vars, + CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) + env.Program('foo.c') + </programlisting> + + <para> + + 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: + + </para> + + <screen> + % <userinput>scons -Q foo.o</userinput> + cc -o foo.o -c -DPACKAGE="/opt/location" foo.c + % <userinput>scons -Q PACKAGE=/usr/local/location foo.o</userinput> + cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c + % <userinput>scons -Q PACKAGE=yes foo.o</userinput> + cc -o foo.o -c -DPACKAGE="True" foo.c + % <userinput>scons -Q PACKAGE=no foo.o</userinput> + cc -o foo.o -c -DPACKAGE="False" foo.c + </screen> + + </section> </section> <section> - <title>Single Value From a List: the &EnumOption; Build Option</title> + <title>Adding Multiple Command-Line Build Variables at Once</title> <para> - 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: </para> <programlisting> - 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), + ) </programlisting> <para> - - The user can now explicity set the &COLOR; build option - to any of the specified allowed values: - </para> - <screen> - % <userinput>scons -Q COLOR=red foo.o</userinput> - cc -o foo.o -c -DCOLOR="red" foo.c - % <userinput>scons -Q COLOR=blue foo.o</userinput> - cc -o foo.o -c -DCOLOR="blue" foo.c - % <userinput>scons -Q COLOR=green foo.o</userinput> - cc -o foo.o -c -DCOLOR="green" foo.c - </screen> + </section> + + <section> + <title>Handling Unknown Command-Line Build Variables: the &UnknownVariables; Function</title> <para> - 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.) </para> - <screen> - % <userinput>scons -Q COLOR=magenta foo.o</userinput> - - scons: *** Invalid value for option COLOR: magenta - File "/home/my/project/SConstruct", line 5, in <module> - </screen> - <para> - 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 <literal>navy</literal> as a synonym for - <literal>blue</literal>. - 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 <emphasis>not</emphasis> 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: </para> <programlisting> - 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') </programlisting> <para> - As desired, the user can then use - <literal>navy</literal> on the command line, - and &SCons; will translate it into <literal>blue</literal> - 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 <emphasis>not</emphasis> + 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;: </para> <screen> - % <userinput>scons -Q COLOR=navy foo.o</userinput> - cc -o foo.o -c -DCOLOR="blue" foo.c + % <userinput>scons -Q NOT_KNOWN=foo</userinput> + Unknown variables: ['NOT_KNOWN'] </screen> <para> - 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. </para> - <screen> - % <userinput>scons -Q COLOR=Red foo.o</userinput> - - scons: *** Invalid value for option COLOR: Red - File "/home/my/project/SConstruct", line 5, in <module> - % <userinput>scons -Q COLOR=BLUE foo.o</userinput> - - scons: *** Invalid value for option COLOR: BLUE - File "/home/my/project/SConstruct", line 5, in <module> - % <userinput>scons -Q COLOR=nAvY foo.o</userinput> - - scons: *** Invalid value for option COLOR: nAvY - File "/home/my/project/SConstruct", line 5, in <module> - </screen> - <para> - The &EnumOption; function can take an additional - &ignorecase; keyword argument that, - when set to <literal>1</literal>, - 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 <literal>variables=</literal> + keyword argument of an &Environment; call. </para> - <programlisting> - 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') - </programlisting> - - <para> + </section> - Which yields the output: + </section> - </para> + <section id="sect-command-line-targets"> + <title>Command-Line Targets</title> - <screen> - % <userinput>scons -Q COLOR=Red foo.o</userinput> - cc -o foo.o -c -DCOLOR="Red" foo.c - % <userinput>scons -Q COLOR=BLUE foo.o</userinput> - cc -o foo.o -c -DCOLOR="BLUE" foo.c - % <userinput>scons -Q COLOR=nAvY foo.o</userinput> - cc -o foo.o -c -DCOLOR="blue" foo.c - % <userinput>scons -Q COLOR=green foo.o</userinput> - cc -o foo.o -c -DCOLOR="green" foo.c - </screen> + <section> + <title>Fetching Command-Line Targets: the &COMMAND_LINE_TARGETS; Variable</title> <para> - Notice that an &ignorecase; value of <literal>1</literal> - 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 <literal>2</literal>: + &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: </para> <programlisting> - 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') </programlisting> <para> - Now &SCons; will use values of - <literal>red</literal>, - <literal>green</literal> or - <literal>blue</literal> - 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: </para> <screen> - % <userinput>scons -Q COLOR=Red foo.o</userinput> - cc -o foo.o -c -DCOLOR="red" foo.c - % <userinput>scons -Q COLOR=nAvY foo.o</userinput> - cc -o foo.o -c -DCOLOR="blue" foo.c - % <userinput>scons -Q COLOR=GREEN foo.o</userinput> - cc -o foo.o -c -DCOLOR="green" foo.c + % <userinput>scons -Q</userinput> + cc -o foo.o -c foo.c + cc -o foo foo.o + % <userinput>scons -Q bar</userinput> + Don't forget to copy `bar' to the archive! + cc -o bar.o -c bar.c + cc -o bar bar.o </screen> - </section> - - <section> - <title>Multiple Values From a List: the &ListOption; Build Option</title> - <para> - 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. </para> - <programlisting> - 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') - </programlisting> + </section> + + <section> + <title>Controlling the Default Targets: the &Default; Function</title> <para> - 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: </para> - <screen> - % <userinput>scons -Q COLORS=red,blue foo.o</userinput> - cc -o foo.o -c -DCOLORS="red blue" foo.c - % <userinput>scons -Q COLORS=blue,green,red foo.o</userinput> - cc -o foo.o -c -DCOLORS="blue green red" foo.c - </screen> + <programlisting> + env = Environment() + hello = env.Program('hello.c') + env.Program('goodbye.c') + Default(hello) + </programlisting> <para> - 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: </para> <screen> - % <userinput>scons -Q COLORS=all foo.o</userinput> - cc -o foo.o -c -DCOLORS="red green blue" foo.c - % <userinput>scons -Q COLORS=none foo.o</userinput> - cc -o foo.o -c -DCOLORS="" foo.c + % <userinput>scons -Q</userinput> + cc -o hello.o -c hello.c + cc -o hello hello.o + % <userinput>scons -Q</userinput> + scons: `hello' is up to date. + % <userinput>scons -Q goodbye</userinput> + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o </screen> <para> - 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 + (<literal>.</literal>) on the command line + to tell &SCons; to build + everything in (or below) the current directory: </para> <screen> - % <userinput>scons -Q COLORS=magenta foo.o</userinput> - - scons: *** Error converting option: COLORS - Invalid value(s) for option: magenta - File "/home/my/project/SConstruct", line 5, in <module> + % <userinput>scons -Q .</userinput> + cc -o goodbye.o -c goodbye.c + cc -o goodbye goodbye.o + cc -o hello.o -c hello.c + cc -o hello hello.o </screen> - </section> - - <section> - <title>Path Names: the &PathOption; Build Option</title> - <para> - &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: </para> <programlisting> - 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) </programlisting> <para> - 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: </para> - <screen> - % <userinput>scons -Q foo.o</userinput> - cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c - % <userinput>scons -Q CONFIG=/usr/local/etc/other_config foo.o</userinput> - scons: `foo.o' is up to date. - </screen> + <programlisting> + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog1, prog3) + </programlisting> <para> - 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 + <application>prog1</application> + and + <application>prog3</application> + programs by default: </para> <screen> - % <userinput>scons -Q CONFIG=/does/not/exist foo.o</userinput> - - scons: *** Path for option CONFIG does not exist: /does/not/exist - File "/home/my/project/SConstruct", line 6, in <module> + % <userinput>scons -Q</userinput> + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + cc -o prog3.o -c prog3.c + cc -o prog3 prog3.o + % <userinput>scons -Q .</userinput> + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o </screen> <para> - &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;: </para> <programlisting> - 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') </programlisting> <para> - 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: </para> - <programlisting> - 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') - </programlisting> + <screen> + % <userinput>scons -Q</userinput> + 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 + % <userinput>scons -Q</userinput> + scons: `prog1' is up to date. + % <userinput>scons -Q .</userinput> + 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 + </screen> <para> - 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 <literal>None</literal> + variable: </para> <programlisting> - 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) </programlisting> <para> - 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: </para> - <programlisting> - 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') - </programlisting> + <screen> + % <userinput>scons -Q</userinput> + scons: *** No targets specified and no Default() targets found. Stop. + % <userinput>scons -Q .</userinput> + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + </screen> + + <section> + <title>Fetching the List of Default Targets: the &DEFAULT_TARGETS; Variable</title> + + <para> + + &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 <function>map</function> function + to run the list through <function>str</function>: + + </para> + + <programlisting> + prog1 = Program('prog1.c') + Default(prog1) + print "DEFAULT_TARGETS is", map(str, DEFAULT_TARGETS) + </programlisting> + + <para> + + (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 <literal>-Q</literal> flag when we run &SCons;:) + + </para> + + <screen> + % <userinput>scons</userinput> + 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. + </screen> + + <para> + + 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: + + </para> + + <programlisting> + 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) + </programlisting> + + <para> + + Which yields the output: + + </para> + + <screen> + % <userinput>scons</userinput> + 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. + </screen> + + <para> + + 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. + + </para> + + </section> </section> <section> - <title>Enabled/Disabled Path Names: the &PackageOption; Build Option</title> + <title>Fetching the List of Build Targets, Regardless of Origin: the &BUILD_TARGETS; Variable</title> <para> - 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 <literal>yes</literal> or <literal>no</literal> 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: </para> <programlisting> - 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 </programlisting> <para> - 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. </para> - <screen> - % <userinput>scons -Q foo.o</userinput> - cc -o foo.o -c -DPACKAGE="/opt/location" foo.c - % <userinput>scons -Q PACKAGE=/usr/local/location foo.o</userinput> - cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c - % <userinput>scons -Q PACKAGE=yes foo.o</userinput> - cc -o foo.o -c -DPACKAGE="True" foo.c - % <userinput>scons -Q PACKAGE=no foo.o</userinput> - cc -o foo.o -c -DPACKAGE="False" foo.c - </screen> + <para> - </section> + 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: - </section> + </para> - <section> - <title>Adding Multiple Command-Line Build Options at Once</title> + <programlisting> + prog1 = Program('prog1.c') + Program('prog2.c') + Default(prog1) + print "BUILD_TARGETS is", map(str, BUILD_TARGETS) + </programlisting> - <para> + <para> - 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: - </para> + </para> - <programlisting> - 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), - ) - </programlisting> + <screen> + % <userinput>scons -Q</userinput> + BUILD_TARGETS is ['prog1'] + cc -o prog1.o -c prog1.c + cc -o prog1 prog1.o + % <userinput>scons -Q prog2</userinput> + BUILD_TARGETS is ['prog2'] + cc -o prog2.o -c prog2.c + cc -o prog2 prog2.o + % <userinput>scons -Q -c .</userinput> + BUILD_TARGETS is ['.'] + Removed prog1.o + Removed prog1 + Removed prog2.o + Removed prog2 + </screen> - <para> - </para> + </section> </section> - - <!-- - - AddOption() function for things like - -prefix=, - -force - - --> diff --git a/doc/user/depends.in b/doc/user/depends.in index 7c15c52..e861cbe 100644 --- a/doc/user/depends.in +++ b/doc/user/depends.in @@ -391,6 +391,13 @@ </para> + <!-- + + We want to generate the output as follows, + but our "surrogate" system for generating the + output seems to get this wrong. + Just in-line the output for now. + <scons_output example="MD5-timestamp" os="posix"> <scons_output_command>scons -Q hello</scons_output_command> <scons_output_command>touch hello.c</scons_output_command> @@ -399,6 +406,22 @@ <scons_output_command>scons -Q hello</scons_output_command> </scons_output> + --> + + <screen> + % <userinput>scons -Q hello</userinput> + cc -o hello.o -c hello.c + cc -o hello hello.o + % <userinput>touch hello.c</userinput> + % <userinput>scons -Q hello</userinput> + scons: `hello' is up to date. + % <userinput>edit hello.c</userinput> + [CHANGE THE CONTENTS OF hello.c] + % <userinput>scons -Q hello</userinput> + cc -o hello.o -c hello.c + cc -o hello hello.o + </screen> + <para> However, the second call to &SCons; in the above output, @@ -489,21 +512,75 @@ <para> Note that in the function definition, - the <literal>dependency</literal> + the <varname>dependency</varname> (input file) is the first argument, - and then the <literal>target</literal>. + and then the ⌖. Both of these are passed to the functions as SCons &Node; objects, which we convert to strings using the Python <function>str()</function>. - The third argument, <literal>prev_ni</literal>, + + </para> + + <para> + + The third argument, <varname>prev_ni</varname>, is an object that holds the signature or timestamp information that was recorded about the dependency the last time the target was built. + A <varname>prev_ni</varname> object can hold + different information, + depending on the type of thing that the + <varname>dependency</varname> argument represents. + For normal files, + the <varname>prev_ni</varname> object + has the following attributes: </para> + <variablelist> + + <varlistentry> + <term>.csig</term> + + <listitem> + <para> + The <emphasis>content signature</emphasis>, + or MD5 checksum, of the contents of the + <varname>dependency</varname> + file the list time the ⌖ was built. + </para> + </listitem> + + </varlistentry> + + <varlistentry> + <term>.size</term> + + <listitem> + <para> + The size in bytes of the <varname>dependency</varname> + file the list time the target was built. + </para> + </listitem> + + </varlistentry> + + <varlistentry> + <term>.timestamp</term> + + <listitem> + <para> + The modification time of the <varname>dependency</varname> + file the list time the ⌖ was built. + </para> + </listitem> + + </varlistentry> + + </variablelist> + <para> Note that ignoring some of the arguments @@ -1157,6 +1234,36 @@ cc -o hello hello.o </screen> + <para> + + 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): + + </para> + + <programlisting> + hello = Program('hello.c') + goodbye = Program('goodbye.c') + Depends(hello, goodbye) + </programlisting> + + <para> + + in which case the dependency or dependencies + will be built before the target(s): + + </para> + + <screen> + % <userinput>scons -Q hello</userinput> + cc -c goodbye.c -o goodbye.o + cc -o goodbye goodbye.o + cc -c hello.c -o hello.o + cc -o hello hello.o + </screen> + </section> <section> @@ -1187,7 +1294,7 @@ </file> </scons_example> - <!-- XXX mention that you can use arrays for target and source? --> + <!-- XXX mention that you can use lists for target and source? --> <!-- <scons_output example="ignore"> @@ -1233,13 +1340,174 @@ </para> <programlisting> - hello = Program('hello.c') + hello = Program('hello.c', CPPPATH=['/usr/include']) Ignore(hello, '/usr/include/stdio.h') </programlisting> </section> <section> + <title>Order-Only Dependencies: the &Requires; Function</title> + + <para> + + Occasionally, + it may be useful to specify that a certain + file or directory must, if necessary, + be built or created before some other target is built, + but that changes to that file or directory + do <emphasis>not</emphasis> + require that the target itself be rebuilt. + Such a relationship is called an + <emphasis>order-only dependency</emphasis> + because it only affects the order in which + things must be built--the dependency before the target--but + it is not a strict dependency relationship + because the target should not + change in response to changes in the dependent file. + + </para> + + <para> + + For example, suppose that you want to create a file + every time you run a build + that identifies the time the build was performed, + the version number, etc., + and which is included in every program that you build. + The version file's contents will change every build. + If you specify a normal dependency relationship, + then every program that depends on + that file would be rebuilt every time you ran &SCons;. + For example, we could use some Python code in + a &SConstruct; file to create a new <filename>version.c</filename> file + with a string containing the current date every time + we run &SCons;, + and then link a program with the resulting object file + by listing <filename>version.c</filename> in the sources: + + </para> + + <scons_example name="no-Requires"> + <file name="SConstruct" printme="1"> + import time + + version_c_text = """ + char *date = "%s"; + """ % time.ctime(time.time()) + open('version.c', 'w').write(version_c_text) + + hello = Program(['hello.c', 'version.c']) + </file> + <file name="hello.c"> + extern char *date; + int main() { printf("Hello, %s! I was built: %s\n", date); } + </file> + </scons_example> + + <para> + + If we list <filename>version.c</filename> as an actual source file, + though, then <filename>version.o</filename> + will get rebuilt every time we run &SCons; + (because the &SConstruct; file itself changes + the contents of <filename>version.c</filename>) + and the <filename>hello</filename> executable + will get re-linked every time + (because the <filename>version.o</filename> file changes): + + </para> + + <!-- + + <scons_output example="no-Requires"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + gcc -o hello.o -c hello.c + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % <userinput>scons -Q</userinput> + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % <userinput>scons -Q</userinput> + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + </screen> + + <para> + + One solution is to use the &Requires; function + to specify that the <filename>version.o</filename> + must be rebuilt before it is used by the link step, + but that changes to <filename>version.o</filename> + should not actually cause the <filename>hello</filename> + executable to be re-linked: + + </para> + + <scons_example name="Requires"> + <file name="SConstruct" printme="1"> + 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) + </file> + <file name="hello.c"> + extern char *date; + int main() { printf("Hello, %s! I was built: %s\n", date); } + </file> + </scons_example> + + <para> + + Notice that because we can no longer list <filename>version.c</filename> + as one of the sources for the <filename>hello</filename> 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 <literal>version_obj</literal> + 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. + + </para> + + <para> + + With these changes, + we get the desired behavior of + re-building the <filename>version.o</filename> file, + and therefore re-linking the <filename>hello</filename> executable, + only when the <filename>hello.c</filename> has changed: + + </para> + + <scons_output example="Requires"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + </section> + + <section> <title>The &AlwaysBuild; Function</title> <para> diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 936e4b2..b19b4b2 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -396,6 +396,23 @@ </para> + <!-- + + We want to generate the output as follows, + but our "surrogate" system for generating the + output seems to get this wrong. + Just in-line the output for now. + + <scons_output example="MD5-timestamp" os="posix"> + <scons_output_command>scons -Q hello</scons_output_command> + <scons_output_command>touch hello.c</scons_output_command> + <scons_output_command>scons -Q hello</scons_output_command> + <scons_output_command output=" [CHANGE THE CONTENTS OF hello.c]">edit hello.c</scons_output_command> + <scons_output_command>scons -Q hello</scons_output_command> + </scons_output> + + --> + <screen> % <userinput>scons -Q hello</userinput> cc -o hello.o -c hello.c @@ -406,7 +423,8 @@ % <userinput>edit hello.c</userinput> [CHANGE THE CONTENTS OF hello.c] % <userinput>scons -Q hello</userinput> - scons: `hello' is up to date. + cc -o hello.o -c hello.c + cc -o hello hello.o </screen> <para> @@ -494,21 +512,75 @@ <para> Note that in the function definition, - the <literal>dependency</literal> + the <varname>dependency</varname> (input file) is the first argument, - and then the <literal>target</literal>. + and then the ⌖. Both of these are passed to the functions as SCons &Node; objects, which we convert to strings using the Python <function>str()</function>. - The third argument, <literal>prev_ni</literal>, + + </para> + + <para> + + The third argument, <varname>prev_ni</varname>, is an object that holds the signature or timestamp information that was recorded about the dependency the last time the target was built. + A <varname>prev_ni</varname> object can hold + different information, + depending on the type of thing that the + <varname>dependency</varname> argument represents. + For normal files, + the <varname>prev_ni</varname> object + has the following attributes: </para> + <variablelist> + + <varlistentry> + <term>.csig</term> + + <listitem> + <para> + The <emphasis>content signature</emphasis>, + or MD5 checksum, of the contents of the + <varname>dependency</varname> + file the list time the ⌖ was built. + </para> + </listitem> + + </varlistentry> + + <varlistentry> + <term>.size</term> + + <listitem> + <para> + The size in bytes of the <varname>dependency</varname> + file the list time the target was built. + </para> + </listitem> + + </varlistentry> + + <varlistentry> + <term>.timestamp</term> + + <listitem> + <para> + The modification time of the <varname>dependency</varname> + file the list time the ⌖ was built. + </para> + </listitem> + + </varlistentry> + + </variablelist> + <para> Note that ignoring some of the arguments @@ -1165,6 +1237,36 @@ cc -o hello hello.o </screen> + <para> + + 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): + + </para> + + <programlisting> + hello = Program('hello.c') + goodbye = Program('goodbye.c') + Depends(hello, goodbye) + </programlisting> + + <para> + + in which case the dependency or dependencies + will be built before the target(s): + + </para> + + <screen> + % <userinput>scons -Q hello</userinput> + cc -c goodbye.c -o goodbye.o + cc -o goodbye goodbye.o + cc -c hello.c -o hello.o + cc -o hello hello.o + </screen> + </section> <section> @@ -1186,7 +1288,7 @@ Ignore(hello, 'hello.h') </programlisting> - <!-- XXX mention that you can use arrays for target and source? --> + <!-- XXX mention that you can use lists for target and source? --> <!-- <scons_output example="ignore"> @@ -1232,13 +1334,170 @@ </para> <programlisting> - hello = Program('hello.c') + hello = Program('hello.c', CPPPATH=['/usr/include']) Ignore(hello, '/usr/include/stdio.h') </programlisting> </section> <section> + <title>Order-Only Dependencies: the &Requires; Function</title> + + <para> + + Occasionally, + it may be useful to specify that a certain + file or directory must, if necessary, + be built or created before some other target is built, + but that changes to that file or directory + do <emphasis>not</emphasis> + require that the target itself be rebuilt. + Such a relationship is called an + <emphasis>order-only dependency</emphasis> + because it only affects the order in which + things must be built--the dependency before the target--but + it is not a strict dependency relationship + because the target should not + change in response to changes in the dependent file. + + </para> + + <para> + + For example, suppose that you want to create a file + every time you run a build + that identifies the time the build was performed, + the version number, etc., + and which is included in every program that you build. + The version file's contents will change every build. + If you specify a normal dependency relationship, + then every program that depends on + that file would be rebuilt every time you ran &SCons;. + For example, we could use some Python code in + a &SConstruct; file to create a new <filename>version.c</filename> file + with a string containing the current date every time + we run &SCons;, + and then link a program with the resulting object file + by listing <filename>version.c</filename> in the sources: + + </para> + + <programlisting> + import time + + version_c_text = """ + char *date = "%s"; + """ % time.ctime(time.time()) + open('version.c', 'w').write(version_c_text) + + hello = Program(['hello.c', 'version.c']) + </programlisting> + + <para> + + If we list <filename>version.c</filename> as an actual source file, + though, then <filename>version.o</filename> + will get rebuilt every time we run &SCons; + (because the &SConstruct; file itself changes + the contents of <filename>version.c</filename>) + and the <filename>hello</filename> executable + will get re-linked every time + (because the <filename>version.o</filename> file changes): + + </para> + + <!-- + + <scons_output example="no-Requires"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + gcc -o hello.o -c hello.c + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % <userinput>scons -Q</userinput> + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + % <userinput>scons -Q</userinput> + gcc -o version.o -c version.c + gcc -o hello hello.o version.o + </screen> + + <para> + + One solution is to use the &Requires; function + to specify that the <filename>version.o</filename> + must be rebuilt before it is used by the link step, + but that changes to <filename>version.o</filename> + should not actually cause the <filename>hello</filename> + executable to be re-linked: + + </para> + + <programlisting> + 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) + </programlisting> + + <para> + + Notice that because we can no longer list <filename>version.c</filename> + as one of the sources for the <filename>hello</filename> 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 <literal>version_obj</literal> + 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. + + </para> + + <para> + + With these changes, + we get the desired behavior of + re-building the <filename>version.o</filename> file, + and therefore re-linking the <filename>hello</filename> executable, + only when the <filename>hello.c</filename> has changed: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o hello.o -c hello.c + cc -o version.o -c version.c + cc -o hello version.o hello.o + % <userinput>scons -Q</userinput> + scons: `.' is up to date. + % <userinput>edit hello.c</userinput> + [CHANGE THE CONTENTS OF hello.c] + % <userinput>scons -Q</userinput> + cc -o hello.o -c hello.c + cc -o hello version.o hello.o + % <userinput>scons -Q</userinput> + scons: `.' is up to date. + </screen> + + </section> + + <section> <title>The &AlwaysBuild; Function</title> <para> diff --git a/doc/user/environments.in b/doc/user/environments.in index 3fcec02..1181e55 100644 --- a/doc/user/environments.in +++ b/doc/user/environments.in @@ -1,25 +1,25 @@ <!-- - __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. + __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. --> @@ -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<DefaultRules> 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<Objects> 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<foo.o> and F<bar.o>. 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<lt>>. Consider the following command found in a F<Conscript> file in the F<test> 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<tgt> 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<test> 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<lt>:f> would expand to C<foo bar baz>, and C<%E<gt>:d> would expand to C<test>. @@ -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<tgt.in>: - @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: + <para> + + An <literal>environment</literal> + 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: + + </para> + + <variablelist> + + <varlistentry> + <term>External Environment</term> + + <listitem> + <para> + + The <literal>external environment</literal> + 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 <literal>os.environ</literal> dictionary. + See <xref linkend="sect-external-environments"></xref>, below. + + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>&ConsEnv;</term> + + <listitem> + <para> + + 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 <xref linkend="sect-construction-environments"></xref>, below. + + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Execution Environment</term> + + <listitem> + <para> + + An <literal>execution environment</literal> + 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 <literal>external environment</literal> + (see above). + See <xref linkend="sect-execution-environments"></xref>, below. + + </para> + </listitem> + </varlistentry> + + </variablelist> + + <para> + + 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. + + </para> + + <para> + + 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 <emphasis>not</emphasis> + 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. + + </para> + + <section id="sect-external-environments"> + <title>Using Values From the External Environment</title> + + <para> + + The <literal>external environment</literal> + variable settings that + the user has in force + when executing &SCons; + are available through the normal Python + <envar>os.environ</envar> + dictionary. + This means that you must add an + <literal>import os</literal> statuement + to any &SConscript; file + in which you want to use + values from the user's external environment. + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + import os + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + <para> + + More usefully, you can use the + <envar>os.environ</envar> + dictionary in your &SConscript; + files to initialize &consenvs; + with values from the user's external environment. + See the next section, + <xref linkend="sect-construction-environments"></xref>, + for information on how to do this. + + </para> + + </section> + + <section id="sect-construction-environments"> + <title>Construction Environments</title> + + <para> + + 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.) + + </para> + + <section> + <title>Creating a &ConsEnv;: the &Environment; Function</title> -=head2 The C<new> constructor + <para> + + A &consenv; is created by the &Environment; method: -The C<new> method is a Perl object constructor. That is, it is not invoked -via a reference to an existing construction environment B<reference>, but, -rather statically, using the name of the Perl B<package> where the -constructor is defined. The method is invoked like this: + </para> - $env = new cons(<overrides>); + <sconstruct> + env = Environment() + </sconstruct> + + <para> -The environment you get back is blessed into the package C<cons>, 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<ENV>), you will have to override -all of them. You can get around this difficulty by using the C<copy> 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. + + </para> + + <para> + + 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: + </para> -=head2 The C<clone> method + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment(CC = 'gcc', + CCFLAGS = '-O2') -The C<clone> method creates a clone of an existing construction environment, -and can be called as in the following example: + env.Program('foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> - $env2 = $env1->clone(<overrides>); + <para> -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 <literal>-O2</literal> + (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: + </para> -=head2 The C<copy> method + <scons_output example="ex1"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> -The C<copy> 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: + </section> - %env = $env1->copy(<overrides>); + <section> + <title>Fetching Values From a &ConsEnv;</title> + + <para> + + You can fetch individual construction variables + using the normal syntax + for accessing individual named items in a Python dictionary: -The value of C<ENV>, 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<PATH> variable in the default environment, you could do the following: + </para> - %cons = new cons()->copy(); - $cons{ENV}{PATH} = "<your path here>"; - $cons = new cons(%cons); + <scons_example name="ex6"> + <file name="SConstruct" printme="1"> + env = Environment() + print "CC is:", env['CC'] + </file> + </scons_example> -This will leave anything else that might be in the default execution -environment undisturbed. + <para> ---> + 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: - <para> - - 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.) - - </para> - - <para> - - A &consenv; is created by the &Environment; method: - - </para> - - <sconstruct> - env = Environment() - </sconstruct> - - <para> - - 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. - - </para> - - <para> - - 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: - - </para> - - <scons_example name="ex1"> - <file name="SConstruct" printme="1"> - env = Environment(CC = 'gcc', - CCFLAGS = '-O2') - - env.Program('foo.c') - </file> - <file name="foo.c"> - int main() { } - </file> - </scons_example> - - <para> - - 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 <literal>-O2</literal> - (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: - - </para> - - <scons_output example="ex1"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - <section> - <title>Multiple &ConsEnvs;</title> - - <para> - - 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 <literal>-O2</literal> flag - and another with the <literal>-g</literal> (debug) flag, - we would do this like so: - - </para> - - <scons_example name="ex2"> - <file name="SConstruct" printme="1"> - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') + </para> - opt.Program('foo', 'foo.c') + <scons_output example="ex6"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> - dbg.Program('bar', 'bar.c') - </file> - <file name="foo.c"> - int main() { } - </file> - <file name="bar.c"> - int main() { } - </file> - </scons_example> + <para> - <scons_output example="ex2"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + 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: - <para> + </para> - 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: + <scons_example name="ex6b"> + <file name="SConstruct" printme="1"> + env = Environment(FOO = 'foo', BAR = 'bar') + dict = env.Dictionary() + for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: + print "key = %s, value = %s" % (key, dict[key]) + </file> + </scons_Example> - </para> + <para> - <scons_example name="ex3"> - <file name="SConstruct" printme="1"> - 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') + </para> - dbg.Program('foo', 'foo.c') - </file> - <file name="foo.c"> - int main() { } - </file> - </scons_example> + <scons_output example="ex6b" os="posix"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> - <para> + <para> - Then &SCons; generates the following error: + And on Windows: - </para> + </para> - <scons_output example="ex3"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> - - <para> - - This is because the two &b-Program; calls have - each implicitly told &SCons; to generate an object file named - <filename>foo.o</filename>, - one with a &cv-link-CCFLAGS; value of - <literal>-O2</literal> - and one with a &cv-link-CCFLAGS; value of - <literal>-g</literal>. - &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 - <filename>foo.c</filename> - to a separately-named object file - using the &b-link-Object; builder, like so: - - </para> - - <scons_example name="ex4"> - <file name="SConstruct" printme="1"> - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') + <scons_output example="ex6b" os="win32"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> - o = opt.Object('foo-opt', 'foo.c') - opt.Program(o) + <para> - d = dbg.Object('foo-dbg', 'foo.c') - dbg.Program(d) - </file> - <file name="foo.c"> - int main() { } - </file> - </scons_example> + 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: - <para> + </para> - 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: + <sconstruct> + env = Environment() + dict = env.Dictionary() + keys = dict.keys() + keys.sort() + for key in keys: + print "construction variable = '%s', value = '%s'" % (key, dict[key]) + </sconstruct> - </para> + </section> - <scons_output example="ex4"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + <section> + <title>Expanding Values From a &ConsEnv;: the &subst; Method</title> - </section> + <para> - <section> - <title>Copying &ConsEnvs;</title> + Another way to get information from + a construction environment. + is to use the &subst; method + on a string containing <literal>$</literal> expansions + of construction variable names. + As a simple example, + the example from the previous + section that used + <literal>env['CC']</literal> + to fetch the value of &cv-link-CC; + could also be written as: - <para> + </para> - 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. + <sconstruct> + env = Environment() + print "CC is:", env.subst('$CC') + </sconstruct> - </para> + <para> - <para> + 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: + </para> - </para> + <sconstruct> + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env['CCCOM'] + </sconstruct> - <scons_example name="ex5"> - <file name="SConstruct" printme="1"> - env = Environment(CC = 'gcc') - opt = env.Clone(CCFLAGS = '-O2') - dbg = env.Clone(CCFLAGS = '-g') + <para> - 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) + </para> - d = dbg.Object('foo-dbg', 'foo.c') - dbg.Program(d) - </file> - <file name="foo.c"> - int main() { } - </file> - </scons_example> + <screen> + % <userinput>scons -Q</userinput> + CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES + scons: `.' is up to date. + </screen> - <para> + <para> - Then our output would look like: + Calling the &subst; method on <varname>$CCOM</varname>, + however: - </para> + </para> - <scons_output example="ex5"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + <sconstruct> + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env.subst('$CCCOM') + </sconstruct> - </section> + <para> - <section> - <title>Fetching Values From a &ConsEnv;</title> + Will recursively expand all of + the construction variables prefixed + with <literal>$</literal> (dollar signs), + showing us the final output: - <para> + </para> - You can fetch individual construction variables - using the normal syntax - for accessing individual named items in a Python dictionary: + <screen> + % <userinput>scons -Q</userinput> + CCCOM is: gcc -DFOO -c -o + scons: `.' is up to date. + </screen> - </para> + <para> - <scons_example name="ex6"> - <file name="SConstruct" printme="1"> - env = Environment() - print "CC is:", env['CC'] - </file> - </scons_example> + 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. - <para> + </para> - 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: + </section> - </para> + <section> + <title>Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function</title> - <scons_output example="ex6"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + <para> - <para> + 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: + </para> - </para> + <para> - <scons_example name="ex6b"> - <file name="SConstruct" printme="1"> - env = Environment(FOO = 'foo', BAR = 'bar') - dict = env.Dictionary() - for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: - print "key = %s, value = %s" % (key, dict[key]) - </file> - </scons_Example> + You can, however, control the settings + in the default contstruction environment + by using the &DefaultEnvironment; function + to initialize various settings: - <para> + </para> - This &SConstruct; file - will print the specified dictionary items for us on POSIX - systems as follows: + <sconstruct> - </para> + DefaultEnvironment(CC = '/usr/local/bin/gcc') - <scons_output example="ex6b" os="posix"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + </sconstruct> - <para> + <para> - And on Windows: + When configured as above, + all calls to the &Program; + or &Object; Builder + will build object files with the + <filename>/usr/local/bin/gcc</filename> + compiler. - </para> + </para> - <scons_output example="ex6b" os="win32"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + <para> - <para> + 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 <filename>/usr/local/bin/gcc</filename> + 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: + </para> - </para> + <sconstruct> - <sconstruct> - env = Environment() - dict = env.Dictionary() - keys = dict.keys() - keys.sort() - for key in keys: - print "construction variable = '%s', value = '%s'" % (key, dict[key]) - </sconstruct> + env = DefaultEnvironment() + env['CC'] = '/usr/local/bin/gcc' - </section> + </sconstruct> - <section> - <title>Expanding Values From a &ConsEnv;</title> + <para> - <para> + 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 - <literal>env['CC']</literal> - to fetch the value of &cv-link-CC; - could also be written as: + </para> - </para> + <sconstruct> - <sconstruct> - env = Environment() - print "CC is:", env.subst('$CC') - </sconstruct> + env = DefaultEnvironment(tools = ['gcc', 'gnulink'], + CC = '/usr/local/bin/gcc') + + </sconstruct> + + <para> + + 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 + <filename>/usr/local/bin/gcc</filename>. + + </para> + + </section> + + <section> + <title>Multiple &ConsEnvs;</title> - <para> + <para> - 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 <literal>-O2</literal> flag + and another with the <literal>-g</literal> (debug) flag, + we would do this like so: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('bar', 'bar.c') + </file> + <file name="foo.c"> + int main() { } + </file> + <file name="bar.c"> + int main() { } + </file> + </scons_example> + + <scons_output example="ex2"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + 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: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct" printme="1"> + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('foo', 'foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <para> + + Then &SCons; generates the following error: + + </para> + + <scons_output example="ex3"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + This is because the two &b-Program; calls have + each implicitly told &SCons; to generate an object file named + <filename>foo.o</filename>, + one with a &cv-link-CCFLAGS; value of + <literal>-O2</literal> + and one with a &cv-link-CCFLAGS; value of + <literal>-g</literal>. + &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 + <filename>foo.c</filename> + to a separately-named object file + using the &b-link-Object; builder, like so: + + </para> + + <scons_example name="ex4"> + <file name="SConstruct" printme="1"> + 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) + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <para> + + 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: + + </para> + + <scons_output example="ex4"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + </section> + + <section> + <title>Making Copies of &ConsEnvs;: the &Clone; Method</title> - </para> + <para> - <sconstruct> - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env['CCCOM'] - </sconstruct> + 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. + + </para> - <para> + <para> + + 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: + </para> - </para> + <scons_example name="ex5"> + <file name="SConstruct" printme="1"> + 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) + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <para> + + Then our output would look like: + + </para> + + <scons_output example="ex5"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + </section> + + <section> + <title>Replacing Values: the &Replace; Method</title> + + <para> + + You can replace existing construction variable values + using the &Replace; method: + + </para> + + <scons_example name="Replace1"> + <file name="SConstruct" printme="1"> + env = Environment(CCFLAGS = '-DDEFINE1') + env.Replace(CCFLAGS = '-DDEFINE2') + env.Program('foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <para> + + The replacing value + (<literal>-DDEFINE2</literal> in the above example) + completely replaces the value in the + construction environment: - <screen> - % <userinput>scons -Q</userinput> - CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES - scons: `.' is up to date. - </screen> + </para> - <para> + <scons_output example="Replace1"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> - Calling the &subst; method on <varname>$CCOM</varname>, - however: + <para> - </para> + You can safely call &Replace; + for construction variables that + don't exist in the construction environment: + + </para> - <sconstruct> - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env.subst('$CCCOM') - </sconstruct> + <scons_example name="Replace-nonexistent"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Replace(NEW_VARIABLE = 'xyzzy') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + </file> + </scons_example> - <para> + <para> + + In this case, + the construction variable simply + gets added to the construction environment: + + </para> + + <scons_output example="Replace-nonexistent"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> - Will recursively expand all of - the $-prefixed construction variables, - showing us the final output: + <para> - </para> + 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: - <screen> - % <userinput>scons -Q</userinput> - CCCOM is: gcc -DFOO -c -o - scons: `.' is up to date. - </screen> + </para> - <para> + <scons_example name="Replace2"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="foo.c"> + int main() { } + </file> + <file name="bar.c"> + int main() { } + </file> + </scons_example> - </para> + <para> - </section> + The timing of when the replacement + actually occurs relative + to when the targets get built + becomes apparent + if we run &scons; without the <literal>-Q</literal> + option: - <section> - <title>Modifying a &ConsEnv;</title> + </para> + + <scons_output example="Replace2"> + <scons_output_command>scons</scons_output_command> + </scons_output> + + <para> - <para> + Because the replacement occurs while + the &SConscript; files are being read, + the &cv-link-CCFLAGS; + variable has already been set to + <literal>-DDEFINE2</literal> + 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. + </para> - </para> + </section> - <section> - <title>Replacing Values in a &ConsEnv;</title> + <section> + <title>Setting Values Only If They're Not Already Defined: the &SetDefault; Method</title> - <para> + <para> - 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 <function>set_default</function> + method of Python dictionary objects: - </para> + </para> - <scons_example name="Replace1"> - <file name="SConstruct" printme="1"> - env = Environment(CCFLAGS = '-DDEFINE1') - env.Replace(CCFLAGS = '-DDEFINE2') - env.Program('foo.c') - </file> - <file name="foo.c"> - int main() { } - </file> - </scons_example> + <sconstruct> + env.SetDefault(SPECIAL_FLAG = '-extra-option') + </sconstruct> - <para> + <para> - The replacing value - (<literal>-DDEFINE2</literal> in the above example) - completely replaces the value in the - construction environment: + This is especially useful + when writing your own <literal>Tool</literal> modules + to apply variables to construction environments. + <!-- + See <xref linkend="chap-tool-modules"></xref> + for more information about writing + Tool modules. + --> - </para> + </para> - <scons_output example="Replace1"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + </section> - <para> + <section> + <title>Appending to the End of Values: the &Append; Method</title> - You can safely call &Replace; - for construction variables that - don't exist in the construction environment: + <para> - </para> + You can append a value to + an existing construction variable + using the &Append; method: - <scons_example name="Replace-nonexistent"> - <file name="SConstruct" printme="1"> - env = Environment() - env.Replace(NEW_VARIABLE = 'xyzzy') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - </file> - </scons_example> + </para> - <para> + <scons_example name="ex8"> + <file name="SConstruct" printme="1"> + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Append(CCFLAGS = ['-DLAST']) + env.Program('foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> - In this case, - the construction variable simply - gets added to the construction environment: + <para> - </para> + &SCons; then supplies both the <literal>-DMY_VALUE</literal> and + <literal>-DLAST</literal> flags when compiling the object file: - <scons_output example="Replace-nonexistent"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + </para> - <para> + <scons_output example="ex8"> + <scons_output_command>scons -Q</scons_output_command> + </scons_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: + <para> - </para> + If the construction variable doesn't already exist, + the &Append; method will create it: - <scons_example name="Replace2"> - <file name="SConstruct" printme="1"> - env = Environment(CCFLAGS = '-DDEFINE1') - print "CCFLAGS =", env['CCFLAGS'] - env.Program('foo.c') + </para> - env.Replace(CCFLAGS = '-DDEFINE2') - print "CCFLAGS =", env['CCFLAGS'] - env.Program('bar.c') - </file> - <file name="foo.c"> - int main() { } - </file> - <file name="bar.c"> - int main() { } - </file> - </scons_example> + <scons_example name="Append-nonexistent"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Append(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + </file> + </scons_example> - <para> + <para> - The timing of when the replacement - actually occurs relative - to when the targets get built - becomes apparent - if we run &scons; without the <literal>-Q</literal> - option: + Which yields: - </para> + </para> - <scons_output example="Replace2"> - <scons_output_command>scons</scons_output_command> - </scons_output> + <scons_output example="Append-nonexistent"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> - <para> + <para> - Because the replacement occurs while - the &SConscript; files are being read, - the &cv-link-CCFLAGS; - variable has already been set to - <literal>-DDEFINE2</literal> - 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. - </para> + </para> - </section> + </section> - <!-- + <section> + <title>Appending Unique Values: the &AppendUnique; Method</title> - <section> - <title>Setting Values Only If They're Not Already Defined</title> + <para> - <para> + 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: - XXX SetDefault() + </para> - </para> + <sconstruct> + env.AppendUnique(CCFLAGS=['-g']) + </sconstruct> - </section> + <para> - --> + In the above example, + the <literal>-g</literal> would be added + only if the &cv-CCFLAGS; variable + does not already contain a <literal>-g</literal> value. - <section> - <title>Appending to the End of Values in a &ConsEnv;</title> + </para> - <para> + </section> - You can append a value to - an existing construction variable - using the &Append; method: + <section> + <title>Appending to the Beginning of Values: the &Prepend; Method</title> - </para> + <para> - <scons_example name="ex8"> - <file name="SConstruct" printme="1"> - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Append(CCFLAGS = ' -DLAST') - env.Program('foo.c') - </file> - <file name="foo.c"> - int main() { } - </file> - </scons_example> + You can append a value to the beginning of + an existing construction variable + using the &Prepend; method: - <para> + </para> - &SCons; then supplies both the <literal>-DMY_VALUE</literal> and - <literal>-DLAST</literal> flags when compiling the object file: + <scons_example name="ex9"> + <file name="SConstruct" printme="1"> + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Prepend(CCFLAGS = ['-DFIRST']) + env.Program('foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> - </para> + <para> - <scons_output example="ex8"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + &SCons; then supplies both the <literal>-DFIRST</literal> and + <literal>-DMY_VALUE</literal> flags when compiling the object file: - <para> + </para> - If the construction variable doesn't already exist, - the &Append; method will create it: + <scons_output example="ex9"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> - </para> + <para> - <scons_example name="Append-nonexistent"> - <file name="SConstruct" printme="1"> - env = Environment() - env.Append(NEW_VARIABLE = 'added') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - </file> - </scons_example> + If the construction variable doesn't already exist, + the &Prepend; method will create it: - <para> + </para> - Which yields: + <scons_example name="Prepend-nonexistent"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Prepend(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + </file> + </scons_example> - </para> + <para> - <scons_output example="Append-nonexistent"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + Which yields: - <!-- + </para> - XXX AppendUnique() + <scons_output example="Prepend-nonexistent"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> - --> + <para> - </section> + 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. - <section> - <title>Appending to the Beginning of Values in a &ConsEnv;</title> + </para> - <para> + </section> - You can append a value to the beginning of - an existing construction variable - using the &Prepend; method: + <section> + <title>Prepending Unique Values: the &PrependUnique; Method</title> - </para> + <para> - <scons_example name="ex9"> - <file name="SConstruct" printme="1"> - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Prepend(CCFLAGS = '-DFIRST ') - env.Program('foo.c') - </file> - <file name="foo.c"> - int main() { } - </file> - </scons_example> + 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: - <para> + </para> - &SCons; then supplies both the <literal>-DFIRST</literal> and - <literal>-DMY_VALUE</literal> flags when compiling the object file: + <sconstruct> + env.PrependUnique(CCFLAGS=['-g']) + </sconstruct> - </para> + <para> - <scons_output example="ex9"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + In the above example, + the <literal>-g</literal> would be added + only if the &cv-CCFLAGS; variable + does not already contain a <literal>-g</literal> value. - <para> + </para> - If the construction variable doesn't already exist, - the &Prepend; method will create it: + </section> - </para> + </section> - <scons_example name="Prepend-nonexistent"> - <file name="SConstruct" printme="1"> - env = Environment() - env.Prepend(NEW_VARIABLE = 'added') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - </file> - </scons_example> + <section id="sect-execution-environments"> + <title>Controlling the Execution Environment for Issued Commands</title> + + <para> + + 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. - <para> + </para> - Which yields: + <para> - </para> + 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_output example="Prepend-nonexistent"> - <scons_output_command>scons -Q</scons_output_command> - </scons_output> + </para> - <!-- + <para> - XXX PrependUnique() + The default value of the &PATH; environment variable + on a POSIX system + is <literal>/usr/local/bin:/bin:/usr/bin</literal>. + 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. - --> + </para> - </section> + <para> - <!-- + 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: - <section> - <title>Adding to Values in the Execution Environment</title> + </para> - <para> + <sconstruct> + path = ['/usr/local/bin', '/bin', '/usr/bin'] + env = Environment(ENV = {'PATH' : path}) + </sconstruct> - XXX AppendENVPath() + <para> - XXX PrependENVPath() + 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: - </para> + </para> - </section> + <sconstruct> + env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] + </sconstruct> - --> + <para> - </section> + 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): + + </para> + + <sconstruct> + env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' + </sconstruct> + + <para> + + 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). + + </para> + + <!-- + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Command('foo', [], '__ROOT__/usr/bin/printenv.py') + </file> + <file name="__ROOT__/usr/bin/printenv.py" chmod="0755"> + #!/usr/bin/env python + import os + import sys + if len(sys.argv) > 1: + keys = sys.argv[1:] + else: + keys = os.environ.keys() + keys.sort() + for key in keys: + print " " + key + "=" + os.environ[key] + </file> + </scons_example> + + <para> + + </para> + + <scons_output example="ex1"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <section> + <title>Propagating &PATH; From the External Environment</title> + + <para> + + 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 <literal>os.environ</literal> + dictionary, + which is Python's way of letting you + get at the external environment: + + </para> + + <sconstruct> + import os + env = Environment(ENV = {'PATH' : os.environ['PATH']}) + </sconstruct> + + <para> + + 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: + + </para> + + <sconstruct> + import os + env = Environment(ENV = os.environ) + </sconstruct> + + <para> + + 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 <literal>/bin</literal> and + <literal>/usr/local/bin</literal> 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. + + </para> + + </section> + + <section> + <title>Adding to <varname>PATH</varname> Values in the Execution Environment</title> + + <para> + + 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 <envar>$PATH</envar> variable on Linux or POSIX systems, + or the <envar>%PATH%</envar> 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 <filename>/usr/local</filename> directories + to the <envar>$PATH</envar> and <envar>$LIB</envar> variables, + you might: + + </para> + + <sconstruct> + env = Environment(ENV = os.environ) + env.PrependENVPath('PATH', '/usr/local/bin') + env.AppendENVPath('LIB', '/usr/local/lib') + </sconstruct> + + <para> + + Note that the added values are strings, + and if you want to add multiple directories to + a variable like <envar>$PATH</envar>, + you must include the path separate character + (<literal>:</literal> on Linux or POSIX, + <literal>;</literal> on Windows) + in the string. + + </para> + + </section> + + </section> diff --git a/doc/user/environments.xml b/doc/user/environments.xml index 01ae2ed..0aac0d8 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -1,25 +1,25 @@ <!-- - __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. + __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. --> @@ -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<DefaultRules> 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<Objects> 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<foo.o> and F<bar.o>. 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<lt>>. Consider the following command found in a F<Conscript> file in the F<test> 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<tgt> 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<test> 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<lt>:f> would expand to C<foo bar baz>, and C<%E<gt>:d> would expand to C<test>. @@ -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<tgt.in>: - @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: + <para> + + An <literal>environment</literal> + 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: + + </para> + + <variablelist> + + <varlistentry> + <term>External Environment</term> + + <listitem> + <para> + + The <literal>external environment</literal> + 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 <literal>os.environ</literal> dictionary. + See <xref linkend="sect-external-environments"></xref>, below. + + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>&ConsEnv;</term> + + <listitem> + <para> + + 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 <xref linkend="sect-construction-environments"></xref>, below. + + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Execution Environment</term> + + <listitem> + <para> + + An <literal>execution environment</literal> + 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 <literal>external environment</literal> + (see above). + See <xref linkend="sect-execution-environments"></xref>, below. + + </para> + </listitem> + </varlistentry> + + </variablelist> + + <para> + + 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. + + </para> + + <para> + + 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 <emphasis>not</emphasis> + 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. + + </para> + + <section id="sect-external-environments"> + <title>Using Values From the External Environment</title> + + <para> + + The <literal>external environment</literal> + variable settings that + the user has in force + when executing &SCons; + are available through the normal Python + <envar>os.environ</envar> + dictionary. + This means that you must add an + <literal>import os</literal> statuement + to any &SConscript; file + in which you want to use + values from the user's external environment. + + </para> + <programlisting> + import os + </programlisting> -=head2 The C<new> constructor + <para> + + More usefully, you can use the + <envar>os.environ</envar> + dictionary in your &SConscript; + files to initialize &consenvs; + with values from the user's external environment. + See the next section, + <xref linkend="sect-construction-environments"></xref>, + for information on how to do this. + + </para> + + </section> + + <section id="sect-construction-environments"> + <title>Construction Environments</title> -The C<new> method is a Perl object constructor. That is, it is not invoked -via a reference to an existing construction environment B<reference>, but, -rather statically, using the name of the Perl B<package> where the -constructor is defined. The method is invoked like this: + <para> - $env = new cons(<overrides>); + 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<cons>, 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<ENV>), you will have to override -all of them. You can get around this difficulty by using the C<copy> method -on an existing construction environment. + </para> + <section> + <title>Creating a &ConsEnv;: the &Environment; Function</title> -=head2 The C<clone> method + <para> -The C<clone> 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(<overrides>); + </para> -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. + <programlisting> + env = Environment() + </programlisting> + <para> -=head2 The C<copy> 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<copy> 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: + </para> - %env = $env1->copy(<overrides>); + <para> -The value of C<ENV>, 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<PATH> 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} = "<your path here>"; - $cons = new cons(%cons); + </para> -This will leave anything else that might be in the default execution -environment undisturbed. + <programlisting> + import os ---> + env = Environment(CC = 'gcc', + CCFLAGS = '-O2') - <para> - - 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.) - - </para> - - <para> - - A &consenv; is created by the &Environment; method: - - </para> - - <programlisting> - env = Environment() - </programlisting> - - <para> - - 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. - - </para> - - <para> - - 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: - - </para> - - <programlisting> - env = Environment(CC = 'gcc', - CCFLAGS = '-O2') - - env.Program('foo.c') - </programlisting> - - <para> - - 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 <literal>-O2</literal> - (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: - - </para> - - <screen> - % <userinput>scons -Q</userinput> - gcc -o foo.o -c -O2 foo.c - gcc -o foo foo.o - </screen> - - <section> - <title>Multiple &ConsEnvs;</title> - - <para> - - 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 <literal>-O2</literal> flag - and another with the <literal>-g</literal> (debug) flag, - we would do this like so: - - </para> + env.Program('foo.c') + </programlisting> - <programlisting> - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') + <para> - 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 <literal>-O2</literal> + (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') - </programlisting> + </para> - <screen> - % <userinput>scons -Q</userinput> - 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 - </screen> + <screen> + % <userinput>scons -Q</userinput> + gcc -o foo.o -c -O2 foo.c + gcc -o foo foo.o + </screen> - <para> + </section> - 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: + <section> + <title>Fetching Values From a &ConsEnv;</title> - </para> + <para> - <programlisting> - 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: + + </para> + + <programlisting> + env = Environment() + print "CC is:", env['CC'] + </programlisting> - opt.Program('foo', 'foo.c') + <para> - dbg.Program('foo', 'foo.c') - </programlisting> + 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: - <para> + </para> - Then &SCons; generates the following error: + <screen> + % <userinput>scons -Q</userinput> + CC is: cc + scons: `.' is up to date. + </screen> - </para> + <para> - <screen> - % <userinput>scons -Q</userinput> - - scons: *** Two environments with different actions were specified for the same target: foo.o - File "/home/my/project/SConstruct", line 6, in <module> - </screen> + 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: - <para> + </para> + + <programlisting> + env = Environment(FOO = 'foo', BAR = 'bar') + dict = env.Dictionary() + for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: + print "key = %s, value = %s" % (key, dict[key]) + </programlisting> - This is because the two &b-Program; calls have - each implicitly told &SCons; to generate an object file named - <filename>foo.o</filename>, - one with a &cv-link-CCFLAGS; value of - <literal>-O2</literal> - and one with a &cv-link-CCFLAGS; value of - <literal>-g</literal>. - &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 - <filename>foo.c</filename> - to a separately-named object file - using the &b-link-Object; builder, like so: + <para> - </para> + This &SConstruct; file + will print the specified dictionary items for us on POSIX + systems as follows: - <programlisting> - opt = Environment(CCFLAGS = '-O2') - dbg = Environment(CCFLAGS = '-g') + </para> - o = opt.Object('foo-opt', 'foo.c') - opt.Program(o) + <screen> + % <userinput>scons -Q</userinput> + key = OBJSUFFIX, value = .o + key = LIBSUFFIX, value = .a + key = PROGSUFFIX, value = + scons: `.' is up to date. + </screen> - d = dbg.Object('foo-dbg', 'foo.c') - dbg.Program(d) - </programlisting> + <para> - <para> - - 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: - - </para> - - <screen> - % <userinput>scons -Q</userinput> - 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 - </screen> - - </section> - - <section> - <title>Copying &ConsEnvs;</title> - - <para> - - 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. - - </para> - - <para> - - 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: - - </para> + And on Windows: - <programlisting> - env = Environment(CC = 'gcc') - opt = env.Clone(CCFLAGS = '-O2') - dbg = env.Clone(CCFLAGS = '-g') + </para> - env.Program('foo', 'foo.c') + <screen> + C:\><userinput>scons -Q</userinput> + key = OBJSUFFIX, value = .obj + key = LIBSUFFIX, value = .lib + key = PROGSUFFIX, value = .exe + scons: `.' is up to date. + </screen> + + <para> - 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) - </programlisting> + </para> - <para> + <programlisting> + env = Environment() + dict = env.Dictionary() + keys = dict.keys() + keys.sort() + for key in keys: + print "construction variable = '%s', value = '%s'" % (key, dict[key]) + </programlisting> - Then our output would look like: + </section> - </para> + <section> + <title>Expanding Values From a &ConsEnv;: the &subst; Method</title> - <screen> - % <userinput>scons -Q</userinput> - 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 - </screen> + <para> - </section> + Another way to get information from + a construction environment. + is to use the &subst; method + on a string containing <literal>$</literal> expansions + of construction variable names. + As a simple example, + the example from the previous + section that used + <literal>env['CC']</literal> + to fetch the value of &cv-link-CC; + could also be written as: - <section> - <title>Fetching Values From a &ConsEnv;</title> + </para> - <para> + <programlisting> + env = Environment() + print "CC is:", env.subst('$CC') + </programlisting> - You can fetch individual construction variables - using the normal syntax - for accessing individual named items in a Python dictionary: + <para> - </para> + 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;: - <programlisting> - env = Environment() - print "CC is:", env['CC'] - </programlisting> + </para> - <para> + <programlisting> + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env['CCCOM'] + </programlisting> - 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: + <para> - </para> + Will print the unexpanded value of &cv-CCCOM;, + showing us the construction + variables that still need to be expanded: - <screen> - % <userinput>scons -Q</userinput> - CC is: cc - scons: `.' is up to date. - </screen> + </para> - <para> + <screen> + % <userinput>scons -Q</userinput> + CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES + scons: `.' is up to date. + </screen> - 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: + <para> - </para> + Calling the &subst; method on <varname>$CCOM</varname>, + however: - <programlisting> - env = Environment(FOO = 'foo', BAR = 'bar') - dict = env.Dictionary() - for key in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: - print "key = %s, value = %s" % (key, dict[key]) - </programlisting> + </para> - <para> + <programlisting> + env = Environment(CCFLAGS = '-DFOO') + print "CCCOM is:", env.subst('$CCCOM') + </programlisting> - This &SConstruct; file - will print the specified dictionary items for us on POSIX - systems as follows: + <para> - </para> + Will recursively expand all of + the construction variables prefixed + with <literal>$</literal> (dollar signs), + showing us the final output: - <screen> - % <userinput>scons -Q</userinput> - key = OBJSUFFIX, value = .o - key = LIBSUFFIX, value = .a - key = PROGSUFFIX, value = - scons: `.' is up to date. - </screen> + </para> - <para> + <screen> + % <userinput>scons -Q</userinput> + CCCOM is: gcc -DFOO -c -o + scons: `.' is up to date. + </screen> - And on Windows: + <para> + + 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. - </para> + </para> - <screen> - C:\><userinput>scons -Q</userinput> - key = OBJSUFFIX, value = .obj - key = LIBSUFFIX, value = .lib - key = PROGSUFFIX, value = .exe - scons: `.' is up to date. - </screen> + </section> - <para> + <section> + <title>Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function</title> - 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: + <para> - </para> + 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. - <programlisting> - env = Environment() - dict = env.Dictionary() - keys = dict.keys() - keys.sort() - for key in keys: - print "construction variable = '%s', value = '%s'" % (key, dict[key]) - </programlisting> + </para> - </section> + <para> - <section> - <title>Expanding Values From a &ConsEnv;</title> + You can, however, control the settings + in the default contstruction environment + by using the &DefaultEnvironment; function + to initialize various settings: - <para> + </para> - 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 - <literal>env['CC']</literal> - to fetch the value of &cv-link-CC; - could also be written as: + <programlisting> - </para> + DefaultEnvironment(CC = '/usr/local/bin/gcc') - <programlisting> - env = Environment() - print "CC is:", env.subst('$CC') - </programlisting> + </programlisting> - <para> + <para> - 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 + <filename>/usr/local/bin/gcc</filename> + compiler. - </para> + </para> - <programlisting> - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env['CCCOM'] - </programlisting> + <para> - <para> + 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 <filename>/usr/local/bin/gcc</filename> + 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: + </para> - </para> + <programlisting> - <screen> - % <userinput>scons -Q</userinput> - CCCOM is: $CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES - scons: `.' is up to date. - </screen> + env = DefaultEnvironment() + env['CC'] = '/usr/local/bin/gcc' - <para> + </programlisting> - Calling the &subst; method on <varname>$CCOM</varname>, - however: + <para> - </para> + 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: - <programlisting> - env = Environment(CCFLAGS = '-DFOO') - print "CCCOM is:", env.subst('$CCCOM') - </programlisting> + </para> - <para> + <programlisting> - Will recursively expand all of - the $-prefixed construction variables, - showing us the final output: + env = DefaultEnvironment(tools = ['gcc', 'gnulink'], + CC = '/usr/local/bin/gcc') + + </programlisting> + + <para> - </para> + 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 + <filename>/usr/local/bin/gcc</filename>. - <screen> - % <userinput>scons -Q</userinput> - CCCOM is: gcc -DFOO -c -o - scons: `.' is up to date. - </screen> + </para> - <para> + </section> - (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.) + <section> + <title>Multiple &ConsEnvs;</title> - </para> + <para> - </section> + 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 <literal>-O2</literal> flag + and another with the <literal>-g</literal> (debug) flag, + we would do this like so: - <section> - <title>Modifying a &ConsEnv;</title> + </para> + + <programlisting> + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('bar', 'bar.c') + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + 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 + </screen> + + <para> + + 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: + + </para> + + <programlisting> + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('foo', 'foo.c') + </programlisting> + + <para> + + Then &SCons; generates the following error: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + + scons: *** Two environments with different actions were specified for the same target: foo.o + File "/home/my/project/SConstruct", line 6, in <module> + </screen> + + <para> + + This is because the two &b-Program; calls have + each implicitly told &SCons; to generate an object file named + <filename>foo.o</filename>, + one with a &cv-link-CCFLAGS; value of + <literal>-O2</literal> + and one with a &cv-link-CCFLAGS; value of + <literal>-g</literal>. + &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 + <filename>foo.c</filename> + to a separately-named object file + using the &b-link-Object; builder, like so: + + </para> + + <programlisting> + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') - <para> + 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) + </programlisting> - </para> + <para> - <section> - <title>Replacing Values in a &ConsEnv;</title> + 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: - <para> + </para> - You can replace existing construction variable values - using the &Replace; method: + <screen> + % <userinput>scons -Q</userinput> + 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 + </screen> - </para> + </section> - <programlisting> - env = Environment(CCFLAGS = '-DDEFINE1') - env.Replace(CCFLAGS = '-DDEFINE2') - env.Program('foo.c') - </programlisting> + <section> + <title>Making Copies of &ConsEnvs;: the &Clone; Method</title> - <para> + <para> - The replacing value - (<literal>-DDEFINE2</literal> 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. - </para> + </para> - <screen> - % <userinput>scons -Q</userinput> - cc -o foo.o -c -DDEFINE2 foo.c - cc -o foo foo.o - </screen> + <para> - <para> + 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: + </para> + + <programlisting> + 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) + </programlisting> + + <para> + + Then our output would look like: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + 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 + </screen> - </para> + </section> - <programlisting> - env = Environment() - env.Replace(NEW_VARIABLE = 'xyzzy') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - </programlisting> + <section> + <title>Replacing Values: the &Replace; Method</title> + + <para> + + You can replace existing construction variable values + using the &Replace; method: + + </para> + + <programlisting> + env = Environment(CCFLAGS = '-DDEFINE1') + env.Replace(CCFLAGS = '-DDEFINE2') + env.Program('foo.c') + </programlisting> + + <para> + + The replacing value + (<literal>-DDEFINE2</literal> in the above example) + completely replaces the value in the + construction environment: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o foo.o -c -DDEFINE2 foo.c + cc -o foo foo.o + </screen> - <para> + <para> - 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: + + </para> + + <programlisting> + env = Environment() + env.Replace(NEW_VARIABLE = 'xyzzy') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + </programlisting> + + <para> - </para> + In this case, + the construction variable simply + gets added to the construction environment: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + NEW_VARIABLE = xyzzy + scons: `.' is up to date. + </screen> + + <para> - <screen> - % <userinput>scons -Q</userinput> - NEW_VARIABLE = xyzzy - scons: `.' is up to date. - </screen> + 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: - <para> + </para> - 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: + <programlisting> + env = Environment(CCFLAGS = '-DDEFINE1') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('foo.c') - </para> + env.Replace(CCFLAGS = '-DDEFINE2') + print "CCFLAGS =", env['CCFLAGS'] + env.Program('bar.c') + </programlisting> - <programlisting> - env = Environment(CCFLAGS = '-DDEFINE1') - print "CCFLAGS =", env['CCFLAGS'] - env.Program('foo.c') + <para> - env.Replace(CCFLAGS = '-DDEFINE2') - print "CCFLAGS =", env['CCFLAGS'] - env.Program('bar.c') - </programlisting> + The timing of when the replacement + actually occurs relative + to when the targets get built + becomes apparent + if we run &scons; without the <literal>-Q</literal> + option: - <para> + </para> - The timing of when the replacement - actually occurs relative - to when the targets get built - becomes apparent - if we run &scons; without the <literal>-Q</literal> - option: + <screen> + % <userinput>scons</userinput> + 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. + </screen> - </para> + <para> - <screen> - % <userinput>scons</userinput> - 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. - </screen> + Because the replacement occurs while + the &SConscript; files are being read, + the &cv-link-CCFLAGS; + variable has already been set to + <literal>-DDEFINE2</literal> + 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. - <para> + </para> - Because the replacement occurs while - the &SConscript; files are being read, - the &cv-link-CCFLAGS; - variable has already been set to - <literal>-DDEFINE2</literal> - 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. + </section> - </para> + <section> + <title>Setting Values Only If They're Not Already Defined: the &SetDefault; Method</title> - </section> + <para> - <!-- + 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 <function>set_default</function> + method of Python dictionary objects: - <section> - <title>Setting Values Only If They're Not Already Defined</title> + </para> - <para> + <programlisting> + env.SetDefault(SPECIAL_FLAG = '-extra-option') + </programlisting> - XXX SetDefault() + <para> - </para> + This is especially useful + when writing your own <literal>Tool</literal> modules + to apply variables to construction environments. + <!-- + See <xref linkend="chap-tool-modules"></xref> + for more information about writing + Tool modules. + --> - </section> + </para> - --> + </section> - <section> - <title>Appending to the End of Values in a &ConsEnv;</title> + <section> + <title>Appending to the End of Values: the &Append; Method</title> - <para> + <para> - 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: - </para> + </para> - <programlisting> - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Append(CCFLAGS = ' -DLAST') - env.Program('foo.c') - </programlisting> + <programlisting> + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Append(CCFLAGS = ['-DLAST']) + env.Program('foo.c') + </programlisting> - <para> + <para> - &SCons; then supplies both the <literal>-DMY_VALUE</literal> and - <literal>-DLAST</literal> flags when compiling the object file: + &SCons; then supplies both the <literal>-DMY_VALUE</literal> and + <literal>-DLAST</literal> flags when compiling the object file: - </para> + </para> - <screen> - % <userinput>scons -Q</userinput> - cc -o foo.o -c -DMY_VALUE -DLAST foo.c - cc -o foo foo.o - </screen> + <screen> + % <userinput>scons -Q</userinput> + cc -o foo.o -c -DMY_VALUE -DLAST foo.c + cc -o foo foo.o + </screen> - <para> + <para> - 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: - </para> + </para> - <programlisting> - env = Environment() - env.Append(NEW_VARIABLE = 'added') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - </programlisting> + <programlisting> + env = Environment() + env.Append(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + </programlisting> - <para> + <para> - Which yields: + Which yields: - </para> + </para> - <screen> - % <userinput>scons -Q</userinput> - NEW_VARIABLE = added - scons: `.' is up to date. - </screen> + <screen> + % <userinput>scons -Q</userinput> + NEW_VARIABLE = added + scons: `.' is up to date. + </screen> - <!-- + <para> - XXX AppendUnique() + 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. - --> + </para> - </section> + </section> - <section> - <title>Appending to the Beginning of Values in a &ConsEnv;</title> + <section> + <title>Appending Unique Values: the &AppendUnique; Method</title> - <para> + <para> - 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: - </para> + </para> - <programlisting> - env = Environment(CCFLAGS = '-DMY_VALUE') - env.Prepend(CCFLAGS = '-DFIRST ') - env.Program('foo.c') - </programlisting> + <programlisting> + env.AppendUnique(CCFLAGS=['-g']) + </programlisting> - <para> + <para> - &SCons; then supplies both the <literal>-DFIRST</literal> and - <literal>-DMY_VALUE</literal> flags when compiling the object file: + In the above example, + the <literal>-g</literal> would be added + only if the &cv-CCFLAGS; variable + does not already contain a <literal>-g</literal> value. - </para> + </para> - <screen> - % <userinput>scons -Q</userinput> - cc -o foo.o -c -DFIRST -DMY_VALUE foo.c - cc -o foo foo.o - </screen> + </section> - <para> + <section> + <title>Appending to the Beginning of Values: the &Prepend; Method</title> - If the construction variable doesn't already exist, - the &Prepend; method will create it: + <para> - </para> + You can append a value to the beginning of + an existing construction variable + using the &Prepend; method: - <programlisting> - env = Environment() - env.Prepend(NEW_VARIABLE = 'added') - print "NEW_VARIABLE =", env['NEW_VARIABLE'] - </programlisting> + </para> - <para> + <programlisting> + env = Environment(CCFLAGS = ['-DMY_VALUE']) + env.Prepend(CCFLAGS = ['-DFIRST']) + env.Program('foo.c') + </programlisting> - Which yields: + <para> - </para> + &SCons; then supplies both the <literal>-DFIRST</literal> and + <literal>-DMY_VALUE</literal> flags when compiling the object file: - <screen> - % <userinput>scons -Q</userinput> - NEW_VARIABLE = added - scons: `.' is up to date. - </screen> + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o foo.o -c -DFIRST -DMY_VALUE foo.c + cc -o foo foo.o + </screen> + + <para> + + If the construction variable doesn't already exist, + the &Prepend; method will create it: + + </para> + + <programlisting> + env = Environment() + env.Prepend(NEW_VARIABLE = 'added') + print "NEW_VARIABLE =", env['NEW_VARIABLE'] + </programlisting> + + <para> + + Which yields: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + NEW_VARIABLE = added + scons: `.' is up to date. + </screen> + + <para> + + 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. + + </para> + + </section> + + <section> + <title>Prepending Unique Values: the &PrependUnique; Method</title> + + <para> + + 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: + + </para> + + <programlisting> + env.PrependUnique(CCFLAGS=['-g']) + </programlisting> + + <para> - <!-- + In the above example, + the <literal>-g</literal> would be added + only if the &cv-CCFLAGS; variable + does not already contain a <literal>-g</literal> value. - XXX PrependUnique() + </para> - --> + </section> - </section> + </section> - <!-- + <section id="sect-execution-environments"> + <title>Controlling the Execution Environment for Issued Commands</title> - <section> - <title>Adding to Values in the Execution Environment</title> + <para> - <para> + 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. - XXX AppendENVPath() + </para> - XXX PrependENVPath() + <para> - </para> + 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. - </section> + </para> - --> + <para> - </section> + The default value of the &PATH; environment variable + on a POSIX system + is <literal>/usr/local/bin:/bin:/usr/bin</literal>. + 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. + + </para> + + <para> + + 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: + + </para> + + <programlisting> + path = ['/usr/local/bin', '/bin', '/usr/bin'] + env = Environment(ENV = {'PATH' : path}) + </programlisting> + + <para> + + 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: + + </para> + + <programlisting> + env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] + </programlisting> + + <para> + + 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): + + </para> + + <programlisting> + env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' + </programlisting> + + <para> + + 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). + + </para> + + <!-- + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Command('foo', [], '__ROOT__/usr/bin/printenv.py') + </file> + <file name="__ROOT__/usr/bin/printenv.py" chmod="0755"> + #!/usr/bin/env python + import os + import sys + if len(sys.argv) > 1: + keys = sys.argv[1:] + else: + keys = os.environ.keys() + keys.sort() + for key in keys: + print " " + key + "=" + os.environ[key] + </file> + </scons_example> + + <para> + + </para> + + <scons_output example="ex1"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <section> + <title>Propagating &PATH; From the External Environment</title> + + <para> + + 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 <literal>os.environ</literal> + dictionary, + which is Python's way of letting you + get at the external environment: + + </para> + + <programlisting> + import os + env = Environment(ENV = {'PATH' : os.environ['PATH']}) + </programlisting> + + <para> + + 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: + + </para> + + <programlisting> + import os + env = Environment(ENV = os.environ) + </programlisting> + + <para> + + 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 <literal>/bin</literal> and + <literal>/usr/local/bin</literal> 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. + + </para> + + </section> + + <section> + <title>Adding to <varname>PATH</varname> Values in the Execution Environment</title> + + <para> + + 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 <envar>$PATH</envar> variable on Linux or POSIX systems, + or the <envar>%PATH%</envar> 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 <filename>/usr/local</filename> directories + to the <envar>$PATH</envar> and <envar>$LIB</envar> variables, + you might: + + </para> + + <programlisting> + env = Environment(ENV = os.environ) + env.PrependENVPath('PATH', '/usr/local/bin') + env.AppendENVPath('LIB', '/usr/local/lib') + </programlisting> + + <para> + + Note that the added values are strings, + and if you want to add multiple directories to + a variable like <envar>$PATH</envar>, + you must include the path separate character + (<literal>:</literal> on Linux or POSIX, + <literal>;</literal> on Windows) + in the string. + + </para> + + </section> + + </section> diff --git a/doc/user/factories.in b/doc/user/factories.in index 222f02a..94af6a3 100644 --- a/doc/user/factories.in +++ b/doc/user/factories.in @@ -44,8 +44,17 @@ <para> 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. + <footnote> + <para> + 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. + </para> + </footnote> One way would be to use the &Copy; action factory in conjunction with the &Command; builder: @@ -229,13 +238,23 @@ <para> - (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. </para> + <para> + + 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 <literal>Delete("$SOURCE")</literal> + is not something you usually want to do! + + </para> + </section> <section> diff --git a/doc/user/factories.xml b/doc/user/factories.xml index 6d0de02..ae6e9d0 100644 --- a/doc/user/factories.xml +++ b/doc/user/factories.xml @@ -44,8 +44,17 @@ <para> 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. + <footnote> + <para> + 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. + </para> + </footnote> One way would be to use the &Copy; action factory in conjunction with the &Command; builder: @@ -207,13 +216,23 @@ <para> - (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. </para> + <para> + + 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 <literal>Delete("$SOURCE")</literal> + is not something you usually want to do! + + </para> + </section> <section> diff --git a/doc/user/help.in b/doc/user/help.in deleted file mode 100644 index 5ab7e83..0000000 --- a/doc/user/help.in +++ /dev/null @@ -1,136 +0,0 @@ -<!-- - - __COPYRIGHT__ - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY - KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE - WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---> - - <para> - - 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: - - </para> - - <scons_example name="ex1"> - <file name="SConstruct" printme="1"> - Help(""" - Type: 'scons program' to build the production program, - 'scons debug' to build the debug version. - """) - </file> - </scons_example> - - <para> - - (Note the above use of the Python triple-quote syntax, - which comes in very handy for - specifying multi-line strings like help text.) - - </para> - - <para> - - 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; <literal>-h</literal> option: - - </para> - - <scons_output example="ex1"> - <scons_output_command>scons -h</scons_output_command> - </scons_output> - - <para> - - 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. - - </para> - - <para> - - 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: - - </para> - - <scons_example name="ex2"> - <file name="SConstruct" printme="1"> - 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") - </file> - </scons_example> - - <para> - - Will display the complete help text on Windows: - - </para> - - <scons_output example="ex2" os="win32"> - <scons_output_command>scons -h</scons_output_command> - </scons_output> - - <para> - - But only show the relevant option on a Linux or UNIX system: - - </para> - - <scons_output example="ex2" os="posix"> - <scons_output_command>scons -h</scons_output_command> - </scons_output> - - <para> - - 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 <literal>-H</literal> option is used. - - </para> diff --git a/doc/user/help.xml b/doc/user/help.xml deleted file mode 100644 index ed2b5de..0000000 --- a/doc/user/help.xml +++ /dev/null @@ -1,153 +0,0 @@ -<!-- - - __COPYRIGHT__ - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY - KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE - WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---> - - <para> - - 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: - - </para> - - <programlisting> - Help(""" - Type: 'scons program' to build the production program, - 'scons debug' to build the debug version. - """) - </programlisting> - - <para> - - (Note the above use of the Python triple-quote syntax, - which comes in very handy for - specifying multi-line strings like help text.) - - </para> - - <para> - - 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; <literal>-h</literal> option: - - </para> - - <screen> - % <userinput>scons -h</userinput> - 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. - </screen> - - <para> - - 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. - - </para> - - <para> - - 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: - - </para> - - <programlisting> - 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") - </programlisting> - - <para> - - Will display the complete help text on Windows: - - </para> - - <screen> - C:\><userinput>scons -h</userinput> - 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. - </screen> - - <para> - - But only show the relevant option on a Linux or UNIX system: - - </para> - - <screen> - % <userinput>scons -h</userinput> - 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. - </screen> - - <para> - - 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 <literal>-H</literal> option is used. - - </para> diff --git a/doc/user/less-simple.in b/doc/user/less-simple.in index cf59319..23b585c 100644 --- a/doc/user/less-simple.in +++ b/doc/user/less-simple.in @@ -209,15 +209,19 @@ </section> <section> - <title>Making a list of files with Glob()</title> + <title>Making a list of files with &Glob;</title> <para> - 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 <emphasis>except</emphasis> - 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 <literal>*</literal>, <literal>?</literal> + and <literal>[abc]</literal> to match any of + <literal>a</literal>, <literal>b</literal> or <literal>c</literal>. + <literal>[!abc]</literal> is also supported, + to match any character <emphasis>except</emphasis> + <literal>a</literal>, <literal>b</literal> or <literal>c</literal>. + This makes many multi-source-file builds quite easy: </para> @@ -227,8 +231,8 @@ <para> - 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. </para> @@ -388,8 +392,8 @@ </para> <programlisting> - list = Split('main.c file1.c file2.c') - Program('program', list) + src_files = Split('main.c file1.c file2.c') + Program('program', src_files) </programlisting> <para> @@ -404,10 +408,10 @@ </para> <programlisting> - list = Split("""main.c - file1.c - file2.c""") - Program('program', list) + src_files = Split("""main.c + file1.c + file2.c""") + Program('program', src_files) </programlisting> <para> @@ -440,8 +444,8 @@ </para> <programlisting> - 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) </programlisting> <para> @@ -453,8 +457,8 @@ </para> <programlisting> - 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') </programlisting> <para> diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml index 5392518..35b6b60 100644 --- a/doc/user/less-simple.xml +++ b/doc/user/less-simple.xml @@ -198,15 +198,19 @@ </section> <section> - <title>Making a list of files with Glob()</title> + <title>Making a list of files with &Glob;</title> <para> - 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 <emphasis>except</emphasis> - 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 <literal>*</literal>, <literal>?</literal> + and <literal>[abc]</literal> to match any of + <literal>a</literal>, <literal>b</literal> or <literal>c</literal>. + <literal>[!abc]</literal> is also supported, + to match any character <emphasis>except</emphasis> + <literal>a</literal>, <literal>b</literal> or <literal>c</literal>. + This makes many multi-source-file builds quite easy: </para> @@ -216,8 +220,8 @@ <para> - 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. </para> @@ -377,8 +381,8 @@ </para> <programlisting> - list = Split('main.c file1.c file2.c') - Program('program', list) + src_files = Split('main.c file1.c file2.c') + Program('program', src_files) </programlisting> <para> @@ -393,10 +397,10 @@ </para> <programlisting> - list = Split("""main.c - file1.c - file2.c""") - Program('program', list) + src_files = Split("""main.c + file1.c + file2.c""") + Program('program', src_files) </programlisting> <para> @@ -429,8 +433,8 @@ </para> <programlisting> - 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) </programlisting> <para> @@ -442,8 +446,8 @@ </para> <programlisting> - 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') </programlisting> <para> diff --git a/doc/user/main.in b/doc/user/main.in index 0dbadfc..4c2c1b8 100644 --- a/doc/user/main.in +++ b/doc/user/main.in @@ -56,21 +56,23 @@ <!ENTITY command-line SYSTEM "command-line.xml"> <!ENTITY copyright SYSTEM "copyright.xml"> <!ENTITY depends SYSTEM "depends.xml"> - <!ENTITY ENV_file SYSTEM "ENV.xml"> <!ENTITY environments SYSTEM "environments.xml"> <!ENTITY errors SYSTEM "errors.xml"> <!ENTITY example SYSTEM "example.xml"> <!ENTITY factories SYSTEM "factories.xml"> <!ENTITY file-removal SYSTEM "file-removal.xml"> - <!ENTITY help SYSTEM "help.xml"> <!ENTITY hierarchy SYSTEM "hierarchy.xml"> <!ENTITY java SYSTEM "java.xml"> <!ENTITY install SYSTEM "install.xml"> <!ENTITY less-simple SYSTEM "less-simple.xml"> <!ENTITY libraries SYSTEM "libraries.xml"> <!ENTITY make SYSTEM "make.xml"> + <!ENTITY mergeflags SYSTEM "mergeflags.xml"> + <!ENTITY misc SYSTEM "misc.xml"> <!ENTITY nodes SYSTEM "nodes.xml"> + <!ENTITY output SYSTEM "output.xml"> <!ENTITY parseconfig SYSTEM "parseconfig.xml"> + <!ENTITY parseflags SYSTEM "parseflags.xml"> <!ENTITY preface SYSTEM "preface.xml"> <!ENTITY python SYSTEM "python.xml"> <!ENTITY repositories SYSTEM "repositories.xml"> @@ -83,7 +85,7 @@ <!ENTITY tasks SYSTEM "tasks.xml"> <!ENTITY tools SYSTEM "tools.xml"> <!ENTITY troubleshoot SYSTEM "troubleshoot.xml"> - <!ENTITY variables SYSTEM "variables.xml"> + <!ENTITY variables-xml SYSTEM "variables.xml"> <!ENTITY variants SYSTEM "variants.xml"> <!ENTITY builders-gen SYSTEM "builders.gen"> @@ -94,59 +96,26 @@ <!-- - XXX AllowSubstExceptions() - XXX EnsurePythonVersion() - XXX EnsureSConsVersion() - XXX Exit() XXX FindFile() XXX FindPathDirs() - XXX Flatten() XXX GetBuildPath() XXX GetLaunchDir() - XXX ParseConfig() - XXX MergeFlags() - XXX ParseFlags() - XXX ParseDepends() XXX Platform() XXX SConsignFile() - XXX SideEffect() XXX Tools() - XXX GetOption('clean') - XXX SetOption('clean') - XXX GetOption('duplicate') XXX SetOption('duplicate') XXX - - duplicate= - XXX GetOption('help') - XXX SetOption('help') - - XXX GetOption('num_jobs') - XXX SetOption('num_jobs') - - XXX Options.UnknownOption() - - XXX GetBuildFailures() - - XXX Requires() - XXX CheckTypeSize() - XXX Progress() - XXX - - diskcheck= XXX - - warn= - XXX ARGLIST - XXX ARGUMENTS - XXX BUILD_TARGETS - XXX COMMAND_LINE_TARGETS - XXX DEFAULT_TARGETS - --> <book> @@ -160,10 +129,10 @@ <edition>Revision &buildrevision; (&builddate;)</edition> - <pubdate>2004, 2005, 2006, 2007</pubdate> + <pubdate>2004, 2005, 2006, 2007, 2008</pubdate> <copyright> - <year>2004, 2005, 2006, 2007</year> + <year>2004, 2005, 2006, 2007, 2008</year> <holder>Steven Knight</holder> </copyright> @@ -211,22 +180,27 @@ </chapter> <chapter id="chap-environments"> - <title>Construction Environments</title> + <title>Environments</title> &environments; </chapter> - <!-- - + <!-- These next three sections should be combined into one chapter --> + <chapter id="chap-mergeflags"> + <title>Merging Options into the Environment: the &MergeFlags; Function</title> + &mergeflags; + </chapter> + <chapter id="chap-parseflags"> + <title>Separating Compile Arguments into their Variables: the &ParseFlags; Function</title> + &parseflags; + </chapter> <chapter id="chap-parseconfig"> <title>Finding Installed Library Information: the &ParseConfig; Function</title> &parseconfig; </chapter> - --> - - <chapter id="chap-ENV"> - <title>Controlling the External Environment Used to Execute Build Commands</title> - &ENV_file; + <chapter id="chap-output"> + <title>Controlling Build Output</title> + &output; </chapter> <chapter id="chap-command-line"> @@ -234,11 +208,6 @@ &command-line; </chapter> - <chapter id="chap-help"> - <title>Providing Build Help: the &Help; Function</title> - &help; - </chapter> - <chapter id="chap-install"> <title>Installing Files in Other Directories: the &Install; Builder</title> &install; @@ -250,7 +219,7 @@ </chapter> <chapter id="chap-file-removal"> - <title>Preventing Removal of Targets</title> + <title>Controlling Removal of Targets</title> &file-removal; </chapter> @@ -354,6 +323,11 @@ --> + <chapter id="chap-misc"> + <title>Miscellaneous Functionality</title> + &misc; + </chapter> + <chapter id="chap-troubleshooting"> <title>Troubleshooting</title> &troubleshoot; @@ -361,7 +335,7 @@ <appendix id="app-variables"> <title>Construction Variables</title> - &variables; + &variables-xml; </appendix> <appendix id="app-builders"> diff --git a/doc/user/main.xml b/doc/user/main.xml index 0dbadfc..181b2a8 100644 --- a/doc/user/main.xml +++ b/doc/user/main.xml @@ -56,21 +56,23 @@ <!ENTITY command-line SYSTEM "command-line.xml"> <!ENTITY copyright SYSTEM "copyright.xml"> <!ENTITY depends SYSTEM "depends.xml"> - <!ENTITY ENV_file SYSTEM "ENV.xml"> <!ENTITY environments SYSTEM "environments.xml"> <!ENTITY errors SYSTEM "errors.xml"> <!ENTITY example SYSTEM "example.xml"> <!ENTITY factories SYSTEM "factories.xml"> <!ENTITY file-removal SYSTEM "file-removal.xml"> - <!ENTITY help SYSTEM "help.xml"> <!ENTITY hierarchy SYSTEM "hierarchy.xml"> <!ENTITY java SYSTEM "java.xml"> <!ENTITY install SYSTEM "install.xml"> <!ENTITY less-simple SYSTEM "less-simple.xml"> <!ENTITY libraries SYSTEM "libraries.xml"> <!ENTITY make SYSTEM "make.xml"> + <!ENTITY mergeflags SYSTEM "mergeflags.xml"> + <!ENTITY misc SYSTEM "misc.xml"> <!ENTITY nodes SYSTEM "nodes.xml"> + <!ENTITY output SYSTEM "output.xml"> <!ENTITY parseconfig SYSTEM "parseconfig.xml"> + <!ENTITY parseflags SYSTEM "parseflags.xml"> <!ENTITY preface SYSTEM "preface.xml"> <!ENTITY python SYSTEM "python.xml"> <!ENTITY repositories SYSTEM "repositories.xml"> @@ -83,7 +85,7 @@ <!ENTITY tasks SYSTEM "tasks.xml"> <!ENTITY tools SYSTEM "tools.xml"> <!ENTITY troubleshoot SYSTEM "troubleshoot.xml"> - <!ENTITY variables SYSTEM "variables.xml"> + <!ENTITY variables-xml SYSTEM "variables.xml"> <!ENTITY variants SYSTEM "variants.xml"> <!ENTITY builders-gen SYSTEM "builders.gen"> @@ -94,59 +96,27 @@ <!-- - XXX AllowSubstExceptions() - XXX EnsurePythonVersion() - XXX EnsureSConsVersion() - XXX Exit() XXX FindFile() XXX FindPathDirs() - XXX Flatten() XXX GetBuildPath() XXX GetLaunchDir() - XXX ParseConfig() - XXX MergeFlags() - XXX ParseFlags() - XXX ParseDepends() XXX Platform() XXX SConsignFile() XXX SideEffect() XXX Tools() - XXX GetOption('clean') - XXX SetOption('clean') - XXX GetOption('duplicate') XXX SetOption('duplicate') XXX - - duplicate= - XXX GetOption('help') - XXX SetOption('help') - - XXX GetOption('num_jobs') - XXX SetOption('num_jobs') - - XXX Options.UnknownOption() - - XXX GetBuildFailures() - - XXX Requires() - XXX CheckTypeSize() - XXX Progress() - XXX - - diskcheck= XXX - - warn= - XXX ARGLIST - XXX ARGUMENTS - XXX BUILD_TARGETS - XXX COMMAND_LINE_TARGETS - XXX DEFAULT_TARGETS - --> <book> @@ -160,10 +130,10 @@ <edition>Revision &buildrevision; (&builddate;)</edition> - <pubdate>2004, 2005, 2006, 2007</pubdate> + <pubdate>2004, 2005, 2006, 2007, 2008</pubdate> <copyright> - <year>2004, 2005, 2006, 2007</year> + <year>2004, 2005, 2006, 2007, 2008</year> <holder>Steven Knight</holder> </copyright> @@ -211,22 +181,27 @@ </chapter> <chapter id="chap-environments"> - <title>Construction Environments</title> + <title>Environments</title> &environments; </chapter> - <!-- - + <!-- These next three sections should be combined into one chapter --> + <chapter id="chap-mergeflags"> + <title>Merging Options into the Environment: the &MergeFlags; Function</title> + &mergeflags; + </chapter> + <chapter id="chap-parseflags"> + <title>Separating Compile Arguments into their Variables: the &ParseFlags; Function</title> + &parseflags; + </chapter> <chapter id="chap-parseconfig"> <title>Finding Installed Library Information: the &ParseConfig; Function</title> &parseconfig; </chapter> - --> - - <chapter id="chap-ENV"> - <title>Controlling the External Environment Used to Execute Build Commands</title> - &ENV_file; + <chapter id="chap-output"> + <title>Controlling Build Output</title> + &output; </chapter> <chapter id="chap-command-line"> @@ -234,11 +209,6 @@ &command-line; </chapter> - <chapter id="chap-help"> - <title>Providing Build Help: the &Help; Function</title> - &help; - </chapter> - <chapter id="chap-install"> <title>Installing Files in Other Directories: the &Install; Builder</title> &install; @@ -250,7 +220,7 @@ </chapter> <chapter id="chap-file-removal"> - <title>Preventing Removal of Targets</title> + <title>Controlling Removal of Targets</title> &file-removal; </chapter> @@ -354,6 +324,11 @@ --> + <chapter id="chap-misc"> + <title>Miscellaneous Functionality</title> + &misc; + </chapter> + <chapter id="chap-troubleshooting"> <title>Troubleshooting</title> &troubleshoot; @@ -361,7 +336,7 @@ <appendix id="app-variables"> <title>Construction Variables</title> - &variables; + &variables-xml; </appendix> <appendix id="app-builders"> diff --git a/doc/user/mergeflags.in b/doc/user/mergeflags.in new file mode 100644 index 0000000..a148409 --- /dev/null +++ b/doc/user/mergeflags.in @@ -0,0 +1,137 @@ +<!-- + + __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. + +--> + + <para> + + &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. + + </para> + + <para> + + &MergeFlags; tries to be intelligent about merging options. + When merging options to any variable + whose name ends in <varname>PATH</varname>, + &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." + + </para> + + <scons_example name="MergeFlags1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + flags = { 'CCFLAGS' : '-whatever -O3' } + env.MergeFlags(flags) + print env['CCFLAGS'] + </file> + </scons_example> + + <scons_output example="MergeFlags1"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + Note that the default value for &cv-link-CCFLAGS; + <!-- + [TODO: for when we make CLVar public] + is a <varname>CLVar</varname>, + --> + is an internal &SCons; object + which automatically converts + the options we specified as a string into a list. + + </para> + + <scons_example name="MergeFlags2"> + <file name="SConstruct" printme="1"> + 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'] + </file> + </scons_example> + + <scons_output example="MergeFlags2"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + Note that the default value for &cv-link-CPPPATH; + <!-- + [TODO: for when we make CLVar public] + is a Python list, not a <varname>CLVar</varname>, + --> + is a normal Python list, + so we must specify its values as a list + in the dictionary we pass to the &MergeFlags; function. + + </para> + + <para> + + If &MergeFlags; is passed anything other than a dictionary, + it calls the &ParseFlags; method to convert it into a dictionary. + + </para> + + <scons_example name="MergeFlags3"> + <file name="SConstruct" printme="1"> + 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'] + </file> + </scons_example> + + <scons_output example="MergeFlags3"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + 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. + + </para> diff --git a/doc/user/mergeflags.xml b/doc/user/mergeflags.xml new file mode 100644 index 0000000..723ab74 --- /dev/null +++ b/doc/user/mergeflags.xml @@ -0,0 +1,138 @@ +<!-- + + __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. + +--> + + <para> + + &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. + + </para> + + <para> + + &MergeFlags; tries to be intelligent about merging options. + When merging options to any variable + whose name ends in <varname>PATH</varname>, + &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." + + </para> + + <programlisting> + env = Environment() + env.Append(CCFLAGS = '-option -O3 -O1') + flags = { 'CCFLAGS' : '-whatever -O3' } + env.MergeFlags(flags) + print env['CCFLAGS'] + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + ['-option', '-O1', '-whatever', '-O3'] + scons: `.' is up to date. + </screen> + + <para> + + Note that the default value for &cv-link-CCFLAGS; + <!-- + [TODO: for when we make CLVar public] + is a <varname>CLVar</varname>, + --> + is an internal &SCons; object + which automatically converts + the options we specified as a string into a list. + + </para> + + <programlisting> + 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'] + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include'] + scons: `.' is up to date. + </screen> + + <para> + + Note that the default value for &cv-link-CPPPATH; + <!-- + [TODO: for when we make CLVar public] + is a Python list, not a <varname>CLVar</varname>, + --> + is a normal Python list, + so we must specify its values as a list + in the dictionary we pass to the &MergeFlags; function. + + </para> + + <para> + + If &MergeFlags; is passed anything other than a dictionary, + it calls the &ParseFlags; method to convert it into a dictionary. + + </para> + + <programlisting> + 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'] + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + ['-option', '-O1', '-whatever', '-O3'] + ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include'] + scons: `.' is up to date. + </screen> + + <para> + + 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. + + </para> diff --git a/doc/user/misc.in b/doc/user/misc.in new file mode 100644 index 0000000..21bb0bb --- /dev/null +++ b/doc/user/misc.in @@ -0,0 +1,375 @@ +<!-- + + __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. + +--> + + <para> + + &SCons; supports a lot of additional functionality + that doesn't readily fit into the other chapters. + + </para> + + <section> + <title>Verifying the Python Version: the &EnsurePythonVersion; Function</title> + + <para> + + 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. + + </para> + + <para> + + &SCons; provides an &EnsurePythonVersion; function for this. + You simply pass it the major and minor versions + numbers of the version of Python you require: + + </para> + + <!-- + + TODO: Figure out how to generate the error message + regardless of executing Python version by faking out + the infrastructure in some way. + + <scons_example name="EnsurePythonVersion"> + <file name="SConstruct" printme="1"> + EnsurePythonVersion(2, 5) + </file> + </scons_example> + + --> + + <sconstruct> + EnsurePythonVersion(2, 5) + </sconstruct> + + <para> + + And then &SCons will exit with the following error + message when a user runs it with an unsupported + earlier version of Python: + + </para> + + <!-- + + TODO: Figure out how to generate the error message + regardless of executing Python version by faking out + the infrastructure in some way. + + <scons_output example="EnsurePythonVersion"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + Python 2.5 or greater required, but you have Python 2.3.6 + </screen> + + </section> + + <section> + <title>Verifying the SCons Version: the &EnsureSConsVersion; Function</title> + + <para> + + 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: + + </para> + + <!-- + + TODO: Figure out how to generate the error message + regardless of executing SCons version by faking out + the infrastructure in some way. + + <scons_example name="EnsureSConsVersion"> + <file name="SConstruct" printme="1"> + EnsureSConsVersion(1, 0) + </file> + </scons_example> + + --> + + <sconstruct> + EnsureSConsVersion(1, 0) + </sconstruct> + + <para> + + And then &SCons will exit with the following error + message when a user runs it with an unsupported + earlier version of &SCons;: + + </para> + + <!-- + + TODO: Figure out how to generate the error message + regardless of executing SCons version by faking out + the infrastructure in some way. + + <scons_output example="EnsureSConsVersion"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + SCons 1.0 or greater required, but you have SCons 0.98.5 + </screen> + + </section> + + <section> + <title>Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function</title> + + <para> + + &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: + + </para> + + <scons_example name="Exit"> + <file name="SConstruct" printme="1"> + if ARGUMENTS.get('FUTURE'): + print "The FUTURE option is not supported yet!" + Exit(2) + env = Environment() + env.Program('hello.c') + </file> + <file name="hello.c"> + hello.c + </file> + </scons_example> + + <scons_output example="Exit"> + <scons_output_command>scons -Q FUTURE=1</scons_output_command> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + 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 <literal>0</literal>, + which indicates successful execution. + + </para> + + <para> + + Note that the &Exit; function + is equivalent to calling the Python + <function>sys.exit</function> function + (which the it actually calls), + but because &Exit; is a &SCons; function, + you don't have to import the Python + <literal>sys</literal> module to use it. + + </para> + + </section> + + <section> + <title>Handling Nested Lists: the &Flatten; Function</title> + + <para> + + &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: + + </para> + + <scons_example name="Flatten1"> + <file name="SConstruct" printme="1"> + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + </file> + <file name="prog1.c"> + prog1.c + </file> + <file name="prog2.c"> + prog2.c + </file> + </scons_example> + + <para> + + Because the Builder calls in &SCons; + flatten their input lists, + this works just fine to build the program: + + </para> + + <scons_output example="Flatten1"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + But if you were debugging your build + and wanted to print the absolute path + of each object file in the + <varname>objects</varname> list, + you might try the following simple approach, + trying to print each Node's + <literal>abspath</literal> + attribute: + + </para> + + <scons_example name="Flatten2"> + <file name="SConstruct" printme="1"> + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in objects: + print object_file.abspath + </file> + <file name="prog1.c"> + prog1.c + </file> + <file name="prog2.c"> + prog2.c + </file> + </scons_example> + + <para> + + This does not work as expected + because each call to <function>str</function> + is operating an embedded list returned by + each &Object; call, + not on the underlying Nodes within those lists: + + </para> + + <scons_output example="Flatten2"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + The solution is to use the &Flatten; function + so that you can pass each Node to + the <function>str</function> separately: + + </para> + + <scons_example name="Flatten3"> + <file name="SConstruct" printme="1"> + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in Flatten(objects): + print object_file.abspath + </file> + <file name="prog1.c"> + prog1.c + </file> + <file name="prog2.c"> + prog2.c + </file> + </scons_example> + + <!-- + + TODO: can't use this now because it displays the temporary path name + + <scons_output example="Flatten3"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + /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 + </screen> + + </section> diff --git a/doc/user/misc.xml b/doc/user/misc.xml new file mode 100644 index 0000000..54b021a --- /dev/null +++ b/doc/user/misc.xml @@ -0,0 +1,355 @@ +<!-- + + __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. + +--> + + <para> + + &SCons; supports a lot of additional functionality + that doesn't readily fit into the other chapters. + + </para> + + <section> + <title>Verifying the Python Version: the &EnsurePythonVersion; Function</title> + + <para> + + 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. + + </para> + + <para> + + &SCons; provides an &EnsurePythonVersion; function for this. + You simply pass it the major and minor versions + numbers of the version of Python you require: + + </para> + + <!-- + + TODO: Figure out how to generate the error message + regardless of executing Python version by faking out + the infrastructure in some way. + + <scons_example name="EnsurePythonVersion"> + <file name="SConstruct" printme="1"> + EnsurePythonVersion(2, 5) + </file> + </scons_example> + + --> + + <programlisting> + EnsurePythonVersion(2, 5) + </programlisting> + + <para> + + And then &SCons; will exit with the following error + message when a user runs it with an unsupported + earlier version of Python: + + </para> + + <!-- + + TODO: Figure out how to generate the error message + regardless of executing Python version by faking out + the infrastructure in some way. + + <scons_output example="EnsurePythonVersion"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + Python 2.5 or greater required, but you have Python 2.3.6 + </screen> + + </section> + + <section> + <title>Verifying the SCons Version: the &EnsureSConsVersion; Function</title> + + <para> + + 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: + + </para> + + <!-- + + TODO: Figure out how to generate the error message + regardless of executing SCons version by faking out + the infrastructure in some way. + + <scons_example name="EnsureSConsVersion"> + <file name="SConstruct" printme="1"> + EnsureSConsVersion(1, 0) + </file> + </scons_example> + + --> + + <programlisting> + EnsureSConsVersion(1, 0) + </programlisting> + + <para> + + And then &SCons; will exit with the following error + message when a user runs it with an unsupported + earlier version of &SCons;: + + </para> + + <!-- + + TODO: Figure out how to generate the error message + regardless of executing SCons version by faking out + the infrastructure in some way. + + <scons_output example="EnsureSConsVersion"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + SCons 1.0 or greater required, but you have SCons 0.98.5 + </screen> + + </section> + + <section> + <title>Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function</title> + + <para> + + &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: + + </para> + + <programlisting> + if ARGUMENTS.get('FUTURE'): + print "The FUTURE option is not supported yet!" + Exit(2) + env = Environment() + env.Program('hello.c') + </programlisting> + + <screen> + % <userinput>scons -Q FUTURE=1</userinput> + The FUTURE option is not supported yet! + % <userinput>scons -Q</userinput> + cc -o hello.o -c hello.c + cc -o hello hello.o + </screen> + + <para> + + 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 <literal>0</literal>, + which indicates successful execution. + + </para> + + <para> + + Note that the &Exit; function + is equivalent to calling the Python + <function>sys.exit</function> function + (which the it actually calls), + but because &Exit; is a &SCons; function, + you don't have to import the Python + <literal>sys</literal> module to use it. + + </para> + + </section> + + <section> + <title>Handling Nested Lists: the &Flatten; Function</title> + + <para> + + &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: + + </para> + + <programlisting> + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + </programlisting> + + <para> + + Because the Builder calls in &SCons; + flatten their input lists, + this works just fine to build the program: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o prog1.o -c prog1.c + cc -o prog2.o -c -DFOO prog2.c + cc -o prog1 prog1.o prog2.o + </screen> + + <para> + + But if you were debugging your build + and wanted to print the absolute path + of each object file in the + <varname>objects</varname> list, + you might try the following simple approach, + trying to print each Node's + <literal>abspath</literal> + attribute: + + </para> + + <programlisting> + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in objects: + print object_file.abspath + </programlisting> + + <para> + + This does not work as expected + because each call to <function>str</function> + is operating an embedded list returned by + each &Object; call, + not on the underlying Nodes within those lists: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + AttributeError: NodeList instance has no attribute 'abspath': + File "/home/my/project/SConstruct", line 8: + print object_file.abspath + </screen> + + <para> + + The solution is to use the &Flatten; function + so that you can pass each Node to + the <function>str</function> separately: + + </para> + + <programlisting> + objects = [ + Object('prog1.c'), + Object('prog2.c', CCFLAGS='-DFOO'), + ] + Program(objects) + + for object_file in Flatten(objects): + print object_file.abspath + </programlisting> + + <!-- + + TODO: can't use this now because it displays the temporary path name + + <scons_output example="Flatten3"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + /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 + </screen> + + </section> diff --git a/doc/user/output.in b/doc/user/output.in new file mode 100644 index 0000000..02d7484 --- /dev/null +++ b/doc/user/output.in @@ -0,0 +1,681 @@ +<!-- + + __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. + +--> + + <para> + + 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. + + </para> + + <section> + <title>Providing Build Help: the &Help; Function</title> + + <para> + + 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: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + Help(""" + Type: 'scons program' to build the production program, + 'scons debug' to build the debug version. + """) + </file> + </scons_example> + + <para> + + (Note the above use of the Python triple-quote syntax, + which comes in very handy for + specifying multi-line strings like help text.) + + </para> + + <para> + + 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; <literal>-h</literal> option: + + </para> + + <scons_output example="ex1"> + <scons_output_command>scons -h</scons_output_command> + </scons_output> + + <para> + + 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. + + </para> + + <para> + + 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: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + 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") + </file> + </scons_example> + + <para> + + Will display the complete help text on Windows: + + </para> + + <scons_output example="ex2" os="win32"> + <scons_output_command>scons -h</scons_output_command> + </scons_output> + + <para> + + But only show the relevant option on a Linux or UNIX system: + + </para> + + <scons_output example="ex2" os="posix"> + <scons_output_command>scons -h</scons_output_command> + </scons_output> + + <para> + + 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 <literal>-H</literal> option is used. + + </para> + + </section> + + <section> + <title>Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables</title> + + <para> + + 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 <envar>$*COM</envar> variables + that specify the command lines + used to build various types of target files + have a corresponding <envar>$*COMSTR</envar> variable + that can be set to an alternative + string that will be displayed + when the target is built. + + </para> + + <para> + + For example, suppose you want to + have &SCons; display a + <literal>"Compiling"</literal> + message whenever it's compiling an object file, + and a + <literal>"Linking"</literal> + when it's linking an executable. + You could write a &SConstruct; file + that looks like: + + </para> + + <scons_example name="COMSTR"> + <file name="SConstruct" printme="1"> + env = Environment(CCCOMSTR = "Compiling $TARGET", + LINKCOMSTR = "Linking $TARGET") + env.Program('foo.c') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + <para> + + Which would then yield the output: + + </para> + + <!-- + + <scons_output example="COMSTR" os="posix"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + Compiling foo.o + Linking foo + </screen> + + <para> + + &SCons; performs complete variable substitution + on <envar>$*COMSTR</envar> 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. + + </para> + + <para> + + 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. + + </para> + + <para> + + 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 + <varname>VERBOSE</varname> + command-line variable to your &SConstruct; file. + A simple configuration for this might look like: + + </para> + + <scons_example name="COMSTR-VERBOSE"> + <file name="SConstruct" printme="1"> + env = Environment() + if ARGUMENTS.get('VERBOSE') != "1': + env['CCCOMSTR'] = "Compiling $TARGET" + env['LINKCOMSTR'] = "Linking $TARGET" + env.Program('foo.c') + </file> + <file name="foo.c"> + foo.c + </file> + </scons_example> + + <para> + + + By only setting the appropriate + <envar>$*COMSTR</envar> variables + if the user specifies + <literal>VERBOSE=1</literal> + on the command line, + the user has control + over how &SCons; + displays these particular command lines: + + </para> + + <!-- + + <scons_output example="COMSTR-VERBOSE" os="posix"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q -c</scons_output_command> + <scons_output_command>scons -Q VERBOSE=1</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + Compiling foo.o + Linking foo + % <userinput>scons -Q -c</userinput> + Removed foo.o + Removed foo + % <userinput>scons -Q VERBOSE=1</userinput> + cc -o foo.o -c foo.c + cc -o foo foo.o + </screen> + + </section> + + <section> + <title>Providing Build Progress Output: the &Progress; Function</title> + + <para> + + 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. + + </para> + + <para> + + 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. + + </para> + + <scons_example name="Progress-TARGET"> + <file name="SConstruct" printme="1"> + Progress('Evaluating $TARGET\n') + Program('f1.c') + Program('f2.c') + </file> + <file name="f1.c"> + f1.c + </file> + <file name="f2.c"> + f2.c + </file> + </scons_example> + + <para> + + 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 + <literal>print</literal> statement), + and we must specify the + <literal>\n</literal> + that we want printed at the end of the configured string. + This configuration, then, + will have &SCons; + print that it is <literal>Evaluating</literal> + each file that it encounters + in turn as it traverses the dependency graph: + + </para> + + <scons_output example="Progress-TARGET" os="posix"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + 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 + (<literal>\r</literal>) + so that each file name gets re-printed on the same line. + Such a configuration would look like: + + </para> + + <sconstruct> + Progress('$TARGET\r', + file=open('/dev/tty', 'w'), + overwrite=True) + Program('f1.c') + Program('f2.c') + </sconstruct> + + <para> + + Note that we also specified the + <literal>overwrite=True</literal> 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 + <literal>overwrite=True</literal> 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 + <filename>/dev/tty</filename> file + for direct access (on POSIX) to + the user's screen. + On Windows, the equivalent would be to open + the <filename>con:</filename> file name. + + </para> + + <para> + + Also, it's important to know that although you can use + <literal>$TARGET</literal> to substitute the name of + the node in the string, + the &Progress; function does <emphasis>not</emphasis> + perform general variable substitution + (because there's not necessarily a construction + environment involved in evaluating a node + like a source file, for example). + + </para> + + <para> + + 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: + + </para> + + <sconstruct> + Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) + Program('f1.c') + Program('f2.c') + </sconstruct> + + <para> + + Note that here we have also used the + <literal>interval=</literal> + keyword argument to have &SCons; + only print a new "spinner" string + once every five evaluated nodes. + Using an <literal>interval=</literal> count, + even with strings that use <literal>$TARGET</literal> 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. + + </para> + + <para> + + 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: + + </para> + + <scons_example name="Progress-callable"> + <file name="SConstruct" printme="1"> + screen = open('/dev/tty', 'w') + count = 0 + def progress_function(node) + count += 1 + screen.write('Node %4d: %s\r' % (count, node)) + + Progress(progress_function) + </file> + </scons_example> + + <para> + + Of course, if you choose, + you could completely ignore the + <varname>node</varname> argument to the function, + and just print a count, + or anything else you wish. + + </para> + + <para> + + (Note that there's an obvious follow-on question here: + how would you find the total number of nodes + that <emphasis>will be</emphasis> + 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.) + + </para> + + </section> + + <section> + <title>Printing Detailed Build Status: the &GetBuildFailures; Function</title> + + <para> + + 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. + + </para> + + <para> + + SCons provides a &GetBuildFailures; method that + you can use in a python <function>atexit</function> 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 <literal>-j</literal>. Here's a + simple example: + + </para> + + <scons_example name="gbf1"> + <file name="SConstruct" printme="1"> + 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) + </file> + </scons_example> + + <para> + + The <function>atexit.register</function> call + registers <function>print_build_failures</function> + as an <function>atexit</function> 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 + <literal>.node</literal>, + <literal>.errstr</literal>, + <literal>.filename</literal>, and + <literal>.command</literal>. + The <literal>filename</literal> is not necessarily + the same file as the <literal>node</literal>; the + <literal>node</literal> is the target that was + being built when the error occurred, while the + <literal>filename</literal>is 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. + + </para> + + <para> + + Here is a more complete example showing how to + turn each element of &GetBuildFailures; into a string: + + </para> + + <scons_example name="gbf2"> + <file name="SConstruct" printme="1"> + # 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) + </file> + </scons_example> + + <para> + + When this runs, you'll see the appropriate output: + + </para> + + <scons_output example="gbf2"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q fail=1</scons_output_command> + </scons_output> + + </section> diff --git a/doc/user/output.xml b/doc/user/output.xml new file mode 100644 index 0000000..d42457a --- /dev/null +++ b/doc/user/output.xml @@ -0,0 +1,691 @@ +<!-- + + __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. + +--> + + <para> + + 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. + + </para> + + <section> + <title>Providing Build Help: the &Help; Function</title> + + <para> + + 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: + + </para> + + <programlisting> + Help(""" + Type: 'scons program' to build the production program, + 'scons debug' to build the debug version. + """) + </programlisting> + + <para> + + (Note the above use of the Python triple-quote syntax, + which comes in very handy for + specifying multi-line strings like help text.) + + </para> + + <para> + + 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; <literal>-h</literal> option: + + </para> + + <screen> + % <userinput>scons -h</userinput> + 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. + </screen> + + <para> + + 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. + + </para> + + <para> + + 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: + + </para> + + <programlisting> + 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") + </programlisting> + + <para> + + Will display the complete help text on Windows: + + </para> + + <screen> + C:\><userinput>scons -h</userinput> + 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. + </screen> + + <para> + + But only show the relevant option on a Linux or UNIX system: + + </para> + + <screen> + % <userinput>scons -h</userinput> + 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. + </screen> + + <para> + + 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 <literal>-H</literal> option is used. + + </para> + + </section> + + <section> + <title>Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables</title> + + <para> + + 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 <envar>$*COM</envar> variables + that specify the command lines + used to build various types of target files + have a corresponding <envar>$*COMSTR</envar> variable + that can be set to an alternative + string that will be displayed + when the target is built. + + </para> + + <para> + + For example, suppose you want to + have &SCons; display a + <literal>"Compiling"</literal> + message whenever it's compiling an object file, + and a + <literal>"Linking"</literal> + when it's linking an executable. + You could write a &SConstruct; file + that looks like: + + </para> + + <programlisting> + env = Environment(CCCOMSTR = "Compiling $TARGET", + LINKCOMSTR = "Linking $TARGET") + env.Program('foo.c') + </programlisting> + + <para> + + Which would then yield the output: + + </para> + + <!-- + + <scons_output example="COMSTR" os="posix"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + Compiling foo.o + Linking foo + </screen> + + <para> + + &SCons; performs complete variable substitution + on <envar>$*COMSTR</envar> 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. + + </para> + + <para> + + 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. + + </para> + + <para> + + 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 + <varname>VERBOSE</varname> + command-line variable to your &SConstruct; file. + A simple configuration for this might look like: + + </para> + + <programlisting> + env = Environment() + if ARGUMENTS.get('VERBOSE') != "1': + env['CCCOMSTR'] = "Compiling $TARGET" + env['LINKCOMSTR'] = "Linking $TARGET" + env.Program('foo.c') + </programlisting> + + <para> + + + By only setting the appropriate + <envar>$*COMSTR</envar> variables + if the user specifies + <literal>VERBOSE=1</literal> + on the command line, + the user has control + over how &SCons; + displays these particular command lines: + + </para> + + <!-- + + <scons_output example="COMSTR-VERBOSE" os="posix"> + <scons_output_command>scons -Q</scons_output_command> + <scons_output_command>scons -Q -c</scons_output_command> + <scons_output_command>scons -Q VERBOSE=1</scons_output_command> + </scons_output> + + --> + + <screen> + % <userinput>scons -Q</userinput> + Compiling foo.o + Linking foo + % <userinput>scons -Q -c</userinput> + Removed foo.o + Removed foo + % <userinput>scons -Q VERBOSE=1</userinput> + cc -o foo.o -c foo.c + cc -o foo foo.o + </screen> + + </section> + + <section> + <title>Providing Build Progress Output: the &Progress; Function</title> + + <para> + + 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. + + </para> + + <para> + + 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. + + </para> + + <programlisting> + Progress('Evaluating $TARGET\n') + Program('f1.c') + Program('f2.c') + </programlisting> + + <para> + + 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 + <literal>print</literal> statement), + and we must specify the + <literal>\n</literal> + that we want printed at the end of the configured string. + This configuration, then, + will have &SCons; + print that it is <literal>Evaluating</literal> + each file that it encounters + in turn as it traverses the dependency graph: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + 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 . + </screen> + + <para> + + 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 + (<literal>\r</literal>) + so that each file name gets re-printed on the same line. + Such a configuration would look like: + + </para> + + <programlisting> + Progress('$TARGET\r', + file=open('/dev/tty', 'w'), + overwrite=True) + Program('f1.c') + Program('f2.c') + </programlisting> + + <para> + + Note that we also specified the + <literal>overwrite=True</literal> 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 + <literal>overwrite=True</literal> 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 + <filename>/dev/tty</filename> file + for direct access (on POSIX) to + the user's screen. + On Windows, the equivalent would be to open + the <filename>con:</filename> file name. + + </para> + + <para> + + Also, it's important to know that although you can use + <literal>$TARGET</literal> to substitute the name of + the node in the string, + the &Progress; function does <emphasis>not</emphasis> + perform general variable substitution + (because there's not necessarily a construction + environment involved in evaluating a node + like a source file, for example). + + </para> + + <para> + + 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: + + </para> + + <programlisting> + Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5) + Program('f1.c') + Program('f2.c') + </programlisting> + + <para> + + Note that here we have also used the + <literal>interval=</literal> + keyword argument to have &SCons; + only print a new "spinner" string + once every five evaluated nodes. + Using an <literal>interval=</literal> count, + even with strings that use <literal>$TARGET</literal> 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. + + </para> + + <para> + + 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: + + </para> + + <programlisting> + screen = open('/dev/tty', 'w') + count = 0 + def progress_function(node) + count += 1 + screen.write('Node %4d: %s\r' % (count, node)) + + Progress(progress_function) + </programlisting> + + <para> + + Of course, if you choose, + you could completely ignore the + <varname>node</varname> argument to the function, + and just print a count, + or anything else you wish. + + </para> + + <para> + + (Note that there's an obvious follow-on question here: + how would you find the total number of nodes + that <emphasis>will be</emphasis> + 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.) + + </para> + + </section> + + <section> + <title>Printing Detailed Build Status: the &GetBuildFailures; Function</title> + + <para> + + 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. + + </para> + + <para> + + SCons provides a &GetBuildFailures; method that + you can use in a python <function>atexit</function> 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 <literal>-j</literal>. Here's a + simple example: + + </para> + + <programlisting> + 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) + </programlisting> + + <para> + + The <function>atexit.register</function> call + registers <function>print_build_failures</function> + as an <function>atexit</function> 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 + <literal>.node</literal>, + <literal>.errstr</literal>, + <literal>.filename</literal>, and + <literal>.command</literal>. + The <literal>filename</literal> is not necessarily + the same file as the <literal>node</literal>; the + <literal>node</literal> is the target that was + being built when the error occurred, while the + <literal>filename</literal>is 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. + + </para> + + <para> + + Here is a more complete example showing how to + turn each element of &GetBuildFailures; into a string: + + </para> + + <programlisting> + # 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) + </programlisting> + + <para> + + When this runs, you'll see the appropriate output: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + scons: `.' is up to date. + Build succeeded. + % <userinput>scons -Q fail=1</userinput> + scons: *** Source `source' not found, needed by target `target'. Stop. + FAILED!!!! + Failed building Source `source' not found, needed by target `target'. + </screen> + + </section> diff --git a/doc/user/parseconfig.in b/doc/user/parseconfig.in index 45b58cc..e5cc564 100644 --- a/doc/user/parseconfig.in +++ b/doc/user/parseconfig.in @@ -25,48 +25,56 @@ <para> - 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 <filename>config</filename> - 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 <filename>config</filename> + 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 <filename>lib</filename> + would be found by calling a utility named <filename>lib-config</filename>. + + </para> + + <para> + + A more recent convention is that these options + are available from the generic <filename>pkg-config</filename> 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. </para> <para> &SCons; construction environments have a &ParseConfig; method - that executes a utility and configures - the appropriate construction variables + that executes a <filename>*config</filename> utility + (either <filename>pkg-config</filename> 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. </para> - </para> - <scons_example name="ParseConfig1"> <file name="SConstruct" printme="1"> env = Environment() - env.ParseConfig("pkg-config x11") - </file> - <file name="f1.c"> - int f1() { } - </file> - <file name="f2.c"> - int f2() { } - </file> - <file name="f3.c"> - int f3() { } + env['CPPPATH'] = ['/lib/compat'] + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] </file> </scons_example> <para> - &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. </para> @@ -76,6 +84,31 @@ <para> - XXX + In the example above, &SCons; has added the include directory to + <varname>CPPPATH</varname>. + (Depending upon what other flags are emitted by the + <filename>pkg-config</filename> command, + other variables may have been extended as well.) </para> + + <para> + + Note that the options are merged with existing options using + the &MergeFlags; method, + so that each option only occurs once in the construction variable: + + </para> + + <scons_example name="ParseConfig2"> + <file name="SConstruct" printme="1"> + env = Environment() + env.ParseConfig("pkg-config x11 --cflags --libs") + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] + </file> + </scons_example> + + <scons_output example="ParseConfig2"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> diff --git a/doc/user/parseconfig.xml b/doc/user/parseconfig.xml index 25ea12c..46bd4dc 100644 --- a/doc/user/parseconfig.xml +++ b/doc/user/parseconfig.xml @@ -25,47 +25,90 @@ <para> - 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 <filename>config</filename> - 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 <filename>config</filename> + 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 <filename>lib</filename> + would be found by calling a utility named <filename>lib-config</filename>. + + </para> + + <para> + + A more recent convention is that these options + are available from the generic <filename>pkg-config</filename> 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. </para> <para> &SCons; construction environments have a &ParseConfig; method - that executes a utility and configures - the appropriate construction variables + that executes a <filename>*config</filename> utility + (either <filename>pkg-config</filename> 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. </para> - </para> - <programlisting> env = Environment() - env.ParseConfig("pkg-config x11") + env['CPPPATH'] = ['/lib/compat'] + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] </programlisting> <para> - &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. </para> <screen> % <userinput>scons -Q</userinput> + ['/lib/compat', '/usr/X11/include'] scons: `.' is up to date. </screen> <para> - XXX + In the example above, &SCons; has added the include directory to + <varname>CPPPATH</varname>. + (Depending upon what other flags are emitted by the + <filename>pkg-config</filename> command, + other variables may have been extended as well.) </para> + + <para> + + Note that the options are merged with existing options using + the &MergeFlags; method, + so that each option only occurs once in the construction variable: + + </para> + + <programlisting> + env = Environment() + env.ParseConfig("pkg-config x11 --cflags --libs") + env.ParseConfig("pkg-config x11 --cflags --libs") + print env['CPPPATH'] + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + ['/usr/X11/include'] + scons: `.' is up to date. + </screen> diff --git a/doc/user/parseflags.in b/doc/user/parseflags.in new file mode 100644 index 0000000..733ee1d --- /dev/null +++ b/doc/user/parseflags.in @@ -0,0 +1,184 @@ +<!-- + + __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. + +--> + + <para> + + &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. + + </para> + + <para> + + &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. + + </para> + + <para> + + &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.) + + </para> + + <scons_example name="ParseFlags1"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="f1.c"> + int main() { return 0; } + </file> + </scons_example> + + <scons_output example="ParseFlags1" os="posix"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + Note that if the options are limited to generic types + like those above, + they will be correctly translated for other platform types: + + </para> + + <scons_output example="ParseFlags1" os="win32"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + 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: + + </para> + + <scons_example name="ParseFlags2"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="f1.c"> + int main() { return 0; } + </file> + </scons_example> + + <scons_output example="ParseFlags2"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + &ParseFlags; will also accept a (recursive) list of strings as input; + the list is flattened before the strings are processed: + + </para> + + <scons_example name="ParseFlags3"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="f1.c"> + int main() { return 0; } + </file> + </scons_example> + + <scons_output example="ParseFlags3"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + 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: + + </para> + + <scons_example name="ParseFlags4"> + <file name="SConstruct" printme="1"> + 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') + </file> + <file name="f1.c"> + int main() { return 0; } + </file> + </scons_example> + + <scons_output example="ParseFlags4"> + <scons_output_command>scons -Q</scons_output_command> + </scons_output> + + <para> + + &ParseFlags; is regularly updated for new options; + consult the man page for details about those currently recognized. + + </para> diff --git a/doc/user/parseflags.xml b/doc/user/parseflags.xml new file mode 100644 index 0000000..e52bd78 --- /dev/null +++ b/doc/user/parseflags.xml @@ -0,0 +1,187 @@ +<!-- + + __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. + +--> + + <para> + + &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. + + </para> + + <para> + + &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. + + </para> + + <para> + + &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.) + + </para> + + <programlisting> + 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') + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + 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 + </screen> + + <para> + + Note that if the options are limited to generic types + like those above, + they will be correctly translated for other platform types: + + </para> + + <screen> + C:\><userinput>scons -Q</userinput> + 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 + </screen> + + <para> + + 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: + + </para> + + <programlisting> + 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') + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + CCFLAGS -whatever + cc -o f1.o -c -whatever f1.c + cc -o f1 f1.o + </screen> + + <para> + + &ParseFlags; will also accept a (recursive) list of strings as input; + the list is flattened before the strings are processed: + + </para> + + <programlisting> + 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') + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + 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 + </screen> + + <para> + + 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: + + </para> + + <programlisting> + 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') + </programlisting> + + <screen> + % <userinput>scons -Q</userinput> + 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 + </screen> + + <para> + + &ParseFlags; is regularly updated for new options; + consult the man page for details about those currently recognized. + + </para> diff --git a/doc/user/separate.in b/doc/user/separate.in index dc77af3..f23111d 100644 --- a/doc/user/separate.in +++ b/doc/user/separate.in @@ -467,11 +467,11 @@ program using the F<build/foo.c> path name. </section> <section> - <title>Using Glob() with &VariantDir;</title> + <title>Using &Glob; with &VariantDir;</title> <para> - 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 <filename>src/SConscript</filename> @@ -503,8 +503,9 @@ program using the F<build/foo.c> path name. <para> 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 <filename>f1.c</filename> + and <filename>f2.c</filename> in src, + we would see the following output: </para> @@ -516,8 +517,8 @@ program using the F<build/foo.c> path name. <para> - The Glob function returns Nodes in the build/ tree, as - you'd expect. + The &Glob; function returns Nodes in the + <filename>build/</filename> tree, as you'd expect. </para> diff --git a/doc/user/separate.xml b/doc/user/separate.xml index c5e90e0..01f15ee 100644 --- a/doc/user/separate.xml +++ b/doc/user/separate.xml @@ -458,11 +458,11 @@ program using the F<build/foo.c> path name. </section> <section> - <title>Using Glob() with &VariantDir;</title> + <title>Using &Glob; with &VariantDir;</title> <para> - 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 <filename>src/SConscript</filename> @@ -478,8 +478,9 @@ program using the F<build/foo.c> path name. <para> 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 <filename>f1.c</filename> + and <filename>f2.c</filename> in src, + we would see the following output: </para> @@ -496,8 +497,8 @@ program using the F<build/foo.c> path name. <para> - The Glob function returns Nodes in the build/ tree, as - you'd expect. + The &Glob; function returns Nodes in the + <filename>build/</filename> tree, as you'd expect. </para> diff --git a/doc/user/sideeffect.in b/doc/user/sideeffect.in new file mode 100644 index 0000000..cbeefae --- /dev/null +++ b/doc/user/sideeffect.in @@ -0,0 +1,216 @@ +<!-- + + __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. + +--> + + <!-- + + <para> + + If &SCons; is unaware that a build step produces an extra file, + the &SideEffect; method can be used to identify it, + so that the file can be used as a dependency in subsequent build steps. + However, the primary use for the &SideEffect; method + is to prevent two build steps from simultaneously modifying the same file. + + </para> + + TODO: currently doesn't work due to issue #2154: + http://scons.tigris.org/issues/show_bug.cgi?id=2154 + + <para> + + If more than one build step creates or manipulates the same file, + it can cause unpleasant results if both build steps are run at the same time. + The shared file is declared as a side-effect of building the primary targets + and &SCons; will prevent the two build steps from running in parallel. + + </para> + + <para> + + In this example, the <filename>SConscript</filename> uses + &SideEffect; to inform &SCons; about the additional output file. + + </para> + + <scons_example name="SideEffectSimple"> + <file name="SConstruct" printme="1"> + env = Environment() + f2 = env.Command('file2', 'log', Copy('$TARGET', '$SOURCE')) + f1 = env.Command('file1', [], + 'echo >$TARGET data1; echo >log updated file1')) + env.SideEffect('log', env.Command('file1', [], + 'echo >$TARGET data1; echo >log updated file1')) + </file> + </scons_example> + + <para> + + Even when run in parallel mode, &SCons; will run the two steps in order: + + </para> + + <scons_output example="SideEffectSimple"> + <scons_output_command>scons -Q --jobs=2</scons_output_command> + </scons_output> + + --> + + <para> + + 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 <application>build</application> + (in the local directory) + with command-line arguments that write + log information to a common + <filename>logfile.txt</filename> file: + + </para> + + <screen> + 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') + <screen> + + <para> + + 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). + + </para> + + <para> + + We can make sure that &SCons; does not + run these <application>build</application> + commands at the same time + by using the &SideEffect; function + to specify that updating + the <filename>logfile.txt</filename> file + is a side effect of building the specified + <filename>file1</filename> + and + <filename>file2</filename> + target files: + + </para> + + <scons_example name="SideEffectShared"> + <file name="SConstruct" printme="1"> + 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) + </file> + <file name="file1.in">file1.in</file> + <file name="file2.in">file2.in</file> + <file name="build" chmod="0755"> + cat + </file> + </scons_example> + + <para> + + </para> + + <para> + + This makes sure the the two + <application>./build</application> steps are run sequentially, + even withthe <filename>--jobs=2</filename> in the command line: + + </para> + + <scons_output example="SideEffectShared"> + <scons_output_command>scons -Q --jobs=2</scons_output_command> + </scons_output> + + <para> + + 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: + + </para> + + <scons_example name="SideEffectParallel"> + <file name="SConstruct" printme="1"> + 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) + </file> + </scons_example> + + <scons_output example="SideEffectParallel"> + <scons_output_command>scons -Q --jobs=2</scons_output_command> + </scons_output> + + <para> + + 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 <filename>foo.ilk</filename> + alongside compiling <filename>foo.obj</filename> file. + Specifying <filename>foo.ilk</filename> as a + side-effect of <filename>foo.obj</filename> + is <emphasis>not</emphasis> 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. + + </para> diff --git a/doc/user/sideeffect.xml b/doc/user/sideeffect.xml new file mode 100644 index 0000000..2cb4254 --- /dev/null +++ b/doc/user/sideeffect.xml @@ -0,0 +1,211 @@ +<!-- + + __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. + +--> + + <!-- + + <para> + + If &SCons; is unaware that a build step produces an extra file, + the &SideEffect; method can be used to identify it, + so that the file can be used as a dependency in subsequent build steps. + However, the primary use for the &SideEffect; method + is to prevent two build steps from simultaneously modifying the same file. + + </para> + + TODO: currently doesn't work due to issue #2154: + http://scons.tigris.org/issues/show_bug.cgi?id=2154 + + <para> + + If more than one build step creates or manipulates the same file, + it can cause unpleasant results if both build steps are run at the same time. + The shared file is declared as a side-effect of building the primary targets + and &SCons; will prevent the two build steps from running in parallel. + + </para> + + <para> + + In this example, the <filename>SConscript</filename> uses + &SideEffect; to inform &SCons; about the additional output file. + + </para> + + <scons_example name="SideEffectSimple"> + <file name="SConstruct" printme="1"> + env = Environment() + f2 = env.Command('file2', 'log', Copy('$TARGET', '$SOURCE')) + f1 = env.Command('file1', [], + 'echo >$TARGET data1; echo >log updated file1')) + env.SideEffect('log', env.Command('file1', [], + 'echo >$TARGET data1; echo >log updated file1')) + </file> + </scons_example> + + <para> + + Even when run in parallel mode, &SCons; will run the two steps in order: + + </para> + + <scons_output example="SideEffectSimple"> + <scons_output_command>scons -Q --jobs=2</scons_output_command> + </scons_output> + + --> + + <para> + + 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 <application>build</application> + (in the local directory) + with command-line arguments that write + log information to a common + <filename>logfile.txt</filename> file: + + </para> + + <screen> + 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') + <screen> + + <para> + + 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). + + </para> + + <para> + + We can make sure that &SCons; does not + run these <application>build</application> + commands at the same time + by using the &SideEffect; function + to specify that updating + the <filename>logfile.txt</filename> file + is a side effect of building the specified + <filename>file1</filename> + and + <filename>file2</filename> + target files: + + </para> + + <programlisting> + 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) + </programlisting> + + <para> + + </para> + + <para> + + This makes sure the the two + <application>./build</application> steps are run sequentially, + even withthe <filename>--jobs=2</filename> in the command line: + + </para> + + <screen> + % <userinput>scons -Q --jobs=2</userinput> + ./build --log logfile.txt file1.in file1.out + ./build --log logfile.txt file2.in file2.out + </screen> + + <para> + + 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: + + </para> + + <programlisting> + 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) + </programlisting> + + <screen> + % <userinput>scons -Q --jobs=2</userinput> + echo > file1.out data1 + echo > file2.out data2 + </screen> + + <para> + + 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 <filename>foo.ilk</filename> + alongside compiling <filename>foo.obj</filename> file. + Specifying <filename>foo.ilk</filename> as a + side-effect of <filename>foo.obj</filename> + is <emphasis>not</emphasis> 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. + + </para> diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml index 510587a..2d525b9 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': [], |