Add new Options flavors. (Hartmut Goebel)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 4 Nov 2003 18:03:06 +0000 (18:03 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 4 Nov 2003 18:03:06 +0000 (18:03 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@837 fdb21ef1-2011-0410-befe-b5e4ea1792b1

30 files changed:
bin/docrun [new file with mode: 0644]
bin/files
doc/man/scons.1
doc/scons.mod
doc/user/MANIFEST
doc/user/command-line.in [new file with mode: 0644]
doc/user/command-line.sgml [new file with mode: 0644]
doc/user/default.in [deleted file]
doc/user/default.sgml [deleted file]
doc/user/main.in
doc/user/main.sgml
doc/user/run.in
doc/user/run.sgml
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Options/BoolOption.py [new file with mode: 0644]
src/engine/SCons/Options/BoolOptionTests.py [new file with mode: 0644]
src/engine/SCons/Options/EnumOption.py [new file with mode: 0644]
src/engine/SCons/Options/EnumOptionTests.py [new file with mode: 0644]
src/engine/SCons/Options/ListOption.py [new file with mode: 0644]
src/engine/SCons/Options/ListOptionTests.py [new file with mode: 0644]
src/engine/SCons/Options/OptionsTests.py [moved from src/engine/SCons/OptionsTests.py with 93% similarity]
src/engine/SCons/Options/PackageOption.py [new file with mode: 0644]
src/engine/SCons/Options/PackageOptionTests.py [new file with mode: 0644]
src/engine/SCons/Options/PathOption.py [new file with mode: 0644]
src/engine/SCons/Options/PathOptionTests.py [new file with mode: 0644]
src/engine/SCons/Options/__init__.py [moved from src/engine/SCons/Options.py with 84% similarity]
src/engine/SCons/Script/SConscript.py
src/setup.py
test/OptionsTypes.py [new file with mode: 0644]

diff --git a/bin/docrun b/bin/docrun
new file mode 100644 (file)
index 0000000..57ad202
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if test $# -eq 0; then
+    for f in doc/user/*.in; do
+        sgml=doc/user/`basename $f .in`.sgml
+        echo $f:
+        python bin/sconsoutput.py $f
+    done
+else
+    for a in $*; do
+        f=doc/user/$a.in
+        sgml=doc/user/$a.sgml
+        echo $f:
+        python bin/sconsoutput.py $f
+    done
+fi
index 574e17c013b30f4f0b197bae1eeee9443fac41a7..cc8bf10a9dde5e7cabcbb051625e8cace22f7db0 100644 (file)
--- a/bin/files
+++ b/bin/files
 ./SCons/Node/FS.py
 ./SCons/Node/Python.py
 ./SCons/Node/__init__.py
-./SCons/Options.py
+./SCons/Options/__init__.py
+./SCons/Options/BoolOption.py
+./SCons/Options/EnumOption.py
+./SCons/Options/ListOption.py
+./SCons/Options/PackageOption.py
+./SCons/Options/PathOption.py
 ./SCons/Platform/__init__.py
 ./SCons/Platform/aix.py
 ./SCons/Platform/cygwin.py
index a37853985d66195b65584899a4e1309ba5328626..ff2864ce91b1882111fd5336aea42ec8a66031f0 100644 (file)
@@ -5499,6 +5499,27 @@ should take a single argument: value. Example:
 opts.Add('CC', 'The C compiler')
 .EE
 
+.TP
+.RI AddOptions( list )
+A wrapper script that adds
+multiple customizable construction variables
+to an Options object.
+.I list
+is a list of tuple or list objects
+that contain the arguments
+for an individual call to the
+.B Add
+method.
+
+.ES
+opt.AddOptions(
+       ('debug', '', 0),
+       ('CC', 'The C compiler'),
+       ('VALIDATE', 'An option for testing validation',
+        'notset', validator, None),
+    )
+.EE
+
 .TP
 .RI Update( env ", [" args ])
 This updates a construction environment
@@ -5562,6 +5583,192 @@ variables. Example:
 CC = 'my_cc'
 .EE
 
+To make it more convenient to work with customizable Options,
+.B scons
+provides a number of functions
+that make it easy to set up
+various types of Options:
+
+.TP
+.RI BoolOption( key ", " help ", " default )
+Return a tuple of arguments
+to set up a Boolean option.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+The option will interpret the values
+.BR y ,
+.BR yes ,
+.BR t ,
+.BR true ,
+.BR 1 ,
+.B on
+and
+.B all
+as true,
+and the values
+.BR n ,
+.BR no ,
+.BR f ,
+.BR false ,
+.BR 0 ,
+.B off
+and
+.B none
+as false.
+
+.TP
+.RI EnumOption( key ", " help ", " default ", " allowed_values ", [" map ", " ignorecase ])
+Return a tuple of arguments
+to set up an option
+whose value may be one
+of a specified list of legal enumerated values.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+The option will only support those
+values in the
+.I allowed_values
+list.
+The optional
+.I map
+argument is a dictionary
+that can be used to convert
+input values into specific legal values
+in the
+.I allowed_values
+list.
+If the value of
+.I ignore_case
+is
+.B 0
+(the default),
+then the values are case-sensitive.
+If the value of
+.I ignore_case
+is
+.BR 1 ,
+then values will be matched
+case-insensitive.
+If the value of
+.I ignore_case
+is
+.BR 1 ,
+then values will be matched
+case-insensitive,
+and all input values will be
+converted to lower case.
+
+.TP
+.RI ListOption( key ", " help ", " default ", " names )
+Return a tuple of arguments
+to set up an option
+whose value may be one or more
+of a specified list of legal enumerated values.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+The option will only support the values
+.BR all ,
+.BR none ,
+or the values in the
+.I names
+list.
+More than one value may be specified,
+with all values separated by commas.
+
+.TP
+.RI PackageOption( key ", " help ", " default )
+Return a tuple of arguments
+to set up an option
+whose value is a path name
+of a package that may be
+enabled, disabled or 
+given an explicit path name.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+The option will support the values
+.BR yes ,
+.BR true ,
+.BR on ,
+.BR enable
+or
+.BR search ,
+in which case the specified
+.I default
+will be used,
+or the option may be set to an
+arbitrary string
+(typically the path name to a package
+that is being enabled).
+The option will also support the values
+.BR no ,
+.BR flase ,
+.BR off
+or
+.BR disable
+to disable use of the specified option.
+
+.TP
+.RI PathOption( key ", " help ", " default )
+Return a tuple of arguments
+to set up an option
+whose value is expected to be a path name.
+The option will use
+the specified name
+.IR key ,
+have a default value of
+.IR default ,
+and display the specified
+.I help
+text.
+
+.RE
+These functions make it
+convenient to create a number
+of options with consistent behavior
+in a single call to the
+.B AddOptions
+method:
+
+.ES
+opts.AddOptions(
+    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),
+)
+.EE
+
 .SH EXTENDING SCONS
 .SS Builder Objects
 .B scons
index 958444d20fe3ff38b7afe139afd6546643d6f85d..d9b955f4d5b9d4436705fe37d8abd9bf071d341e 100644 (file)
 
 -->
 
+<!ENTITY Add "<function>Add</function>">
+<!ENTITY AddOptions "<function>AddOptions</function>">
 <!ENTITY Alias "<function>Alias</function>">
 <!ENTITY Aliases "<function>Aliases</function>">
 <!ENTITY Append "<function>Append</function>">
+<!ENTITY BoolOption "<function>BoolOption</function>">
 <!ENTITY Build "<function>Build</function>">
 <!ENTITY CacheDir "<function>CacheDir</function>">
 <!ENTITY Clean "<function>Clean</function>">
 <!ENTITY Default "<function>Default</function>">
 <!ENTITY DefaultRules "<function>DefaultRules</function>">
 <!ENTITY Depends "<function>Depends</function>">
+<!ENTITY EnumOption "<function>EnumOption</function>">
 <!ENTITY Environment "<function>Environment</function>">
 <!ENTITY Export "<function>Export</function>">
+<!ENTITY GenerateHelpText "<function>GenerateHelpText</function>">
 <!ENTITY Help "<function>Help</function>">
 <!ENTITY Ignore "<function>Ignore</function>">
 <!ENTITY Import "<function>Import</function>">
 <!ENTITY Install "<function>Install</function>">
 <!ENTITY InstallAs "<function>InstallAs</function>">
 <!ENTITY Link "<function>Link</function>">
+<!ENTITY ListOption "<function>ListOption</function>">
 <!ENTITY Local "<function>Local</function>">
 <!ENTITY Module "<function>Module</function>">
 <!ENTITY Objects "<function>Objects</function>">
+<!ENTITY Options "<function>Options</function>">
+<!ENTITY PackageOption "<function>PackageOption</function>">
+<!ENTITY PathOption "<function>PathOption</function>">
 <!ENTITY Precious "<function>Precious</function>">
 <!ENTITY Prepend "<function>Prepend</function>">
 <!ENTITY Replace "<function>Replace</function>">
 
 
 
+<!--
+
+  Global variables.
+
+-->
+
+<!ENTITY ARGUMENTS "<varname>ARGUMENTS</varname>">
+
+
+
 <!--
 
   Construction variables.
 
 <!ENTITY BUILDERMAP "<varname>BUILDERMAP</varname>">
 <!ENTITY BUILDERS "<varname>BUILDERS</varname>">
+<!ENTITY CC "<varname>CC</varname>">
+<!ENTITY CCFLAGS "<varname>CCFLAGS</varname>">
+<!ENTITY COLOR "<varname>COLOR</varname>">
+<!ENTITY COLORS "<varname>COLORS</varname>">
+<!ENTITY CONFIG "<varname>CONFIG</varname>">
+<!ENTITY CPPDEFINES "<varname>CPPDEFINES</varname>">
 <!ENTITY LIBDIRPREFIX "<varname>LIBDIRPREFIX</varname>">
 <!ENTITY LIBDIRSUFFIX "<varname>LIBDIRSUFFIX</varname>">
 <!ENTITY LIBLINKPREFIX "<varname>LIBLINKPREFIX</varname>">
 <!ENTITY LIBLINKSUFFIX "<varname>LIBLINKSUFFIX</varname>">
+<!ENTITY LIBPATH "<varname>LIBPATH</varname>">
+<!ENTITY LIBS "<varname>LIBS</varname>">
 <!ENTITY LINK "<varname>LINK</varname>">
 <!ENTITY LINKCOM "<varname>LINKCOM</varname>">
 <!ENTITY LINKFLAGS "<varname>LINKFLAGS</varname>">
+<!ENTITY RELEASE "<varname>RELEASE</varname>">
+<!ENTITY RELEASE_BUILD "<varname>RELEASE_BUILD</varname>">
 <!ENTITY SCANNERMAP "<varname>SCANNERMAP</varname>">
 <!ENTITY SCANNERS "<varname>SCANNERS</varname>">
 <!ENTITY TARFLAGS "<varname>TARFLAGS</varname>">
 
 -->
 
-<!ENTITY CC "<varname>CC</varname>">
-<!ENTITY CCFLAGS "<varname>CCFLAGS</varname>">
-<!ENTITY LIBPATH "<varname>LIBPATH</varname>">
-<!ENTITY LIBS "<varname>LIBS</varname>">
 <!ENTITY PYTHONPATH "<varname>PYTHONPATH</varname>">
 <!ENTITY SCONSFLAGS "<varname>SCONSFLAGS</varname>">
 
 
 -->
 
+<!ENTITY allowed_values "<varname>allowed_values</varname>">
 <!ENTITY build_dir "<varname>build_dir</varname>">
+<!ENTITY map "<varname>map</varname>">
+<!ENTITY ignorecase "<varname>ignorecase</varname>">
+<!ENTITY options "<varname>options</varname>">
 <!ENTITY exports "<varname>exports</varname>">
 <!ENTITY source "<varname>source</varname>">
 <!ENTITY target "<varname>target</varname>">
 
 
 
+<!--
+
+  Values of function and method arguments.
+
+-->
+
+<!ENTITY all "<literal>all</literal>">
+<!ENTITY none "<literal>none</literal>">
+
+
+
 <!--
 
   Builder and Scanner objects.
 <!ENTITY signature "<literal>signature</literal>">
 <!ENTITY buildsignature "<literal>build signature</literal>">
 
+<!ENTITY true "<literal>true</literal>">
+<!ENTITY false "<literal>false</literal>">
+
 <!--
 
   File and program names used in examples.
 <!ENTITY bar "<application>bar</application>">
 <!ENTITY common1_c "<application>common1.c</application>">
 <!ENTITY common2_c "<application>common2.c</application>">
+<!ENTITY custom_py "<filename>custom.py</filename>">
 <!ENTITY goodbye "<application>goodbye</application>">
 <!ENTITY file_dll "<application>file.dll</application>">
 <!ENTITY file_lib "<application>file.lib</application>">
index 134a24bb50a84b742f34e5b5c94f8906f14236c8..337c8e01a0a5067c55cb11157cc0546fd79456ae 100644 (file)
@@ -5,10 +5,10 @@ builders-built-in.sgml
 builders-commands.sgml
 builders-writing.sgml
 caching.sgml
+command-line.sgml
 cons.pl
 cons.sgml
 copyright.sgml
-default.sgml
 depends.sgml
 environments.sgml
 errors.sgml
diff --git a/doc/user/command-line.in b/doc/user/command-line.in
new file mode 100644 (file)
index 0000000..09ee8ce
--- /dev/null
@@ -0,0 +1,1182 @@
+<!--
+
+  Copyright (c) 2001, 2002, 2003 Steven Knight
+
+  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; 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.
+
+  </para>
+
+  <section>
+  <title>Not Having to Specify Command-Line Options Each Time:  the &SCONSFLAGS; Environment Variable</title>
+
+    <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 always specify a value of <literal>-j 2</literal>
+    to run the builds in parallel.
+
+    </para>
+
+    <scons_example name="SCONSFLAGS">
+      <file name="SConstruct">
+      def b(target, source, env):
+          pass
+      def s(target, source, env):
+          return "    ..."
+      a = Action(b, strfunction = s)
+      env = Environment(BUILDERS = {'A' : a})
+      env.A('foo.out', 'foo.in')
+      </file>
+      <file name="foo.in">
+      foo.in
+      </file>
+    </scons_example>
+
+    <scons_output example="SCONSFLAGS">
+      <command>scons</command>
+      <command>export SCONSFLAGS="-Q"</command>
+      <command>scons</command>
+    </scons_output>
+
+    <para>
+
+    </para>
+
+  </section>
+
+  <section>
+  <title>Controlling the Default Targets</title>
+
+    <para>
+
+    One of the most basic things you can control
+    is which targets &SCons; will build by default.
+    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="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>
+
+    This &SConstruct; file knows how to build two programs,
+    &hello; and &goodbye;,
+    but only builds the
+    &hello; program by default:
+
+    </para>
+
+    <scons_output example="Default1">
+       <command>scons -Q</command>
+       <command>scons -Q</command>
+       <command>scons -Q goodbye</command>
+    </scons_output>
+
+    <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>
+
+    <scons_output example="Default1">
+       <command>scons -Q .</command>
+    </scons_output>
+
+    <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:
+
+    </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>
+
+    <para>
+
+    Or you can specify more than one target
+    in a single call to the &Default; function:
+
+    </para>
+
+    <programlisting>
+       env = Environment()
+       prog1 = env.Program('prog1.c')
+       prog2 = env.Program('prog2.c')
+       prog3 = env.Program('prog3.c')
+       Default(prog1, prog3)
+    </programlisting>
+
+    <para>
+
+    Either of these last two examples
+    will build only the
+    <application>prog1</application>
+    and
+    <application>prog3</application>
+    programs by default:
+
+    </para>
+
+    <scons_output example="Default2">
+       <command>scons -Q</command>
+       <command>scons -Q .</command>
+    </scons_output>
+
+    <para>
+
+    You can list a directory as
+    an argument to &Default;:
+
+    </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>
+
+    <para>
+
+    In which case only the target(s) in that
+    directory will be built by default:
+
+    </para>
+
+    <scons_output example="Default3">
+       <command>scons -Q</command>
+       <command>scons -Q</command>
+       <command>scons -Q .</command>
+    </scons_output>
+
+    <para>
+
+    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="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>
+
+    Which would produce build output like:
+
+    </para>
+
+    <scons_output example="Default4">
+       <command>scons -Q</command>
+       <command>scons -Q .</command>
+    </scons_output>
+
+  </section>
+
+  <!--
+
+  <section>
+  <title>Getting at Command-Line Targets</title>
+
+    <para>
+
+    XXX
+
+    </para>
+
+  </section>
+
+  -->
+
+  <section>
+  <title>Command-Line <varname>variable</varname>=<varname>value</varname> Build Options</title>
+
+    <para>
+
+    You may want to control various aspects
+    of your build by allowing the user
+    to specify <varname>variable</varname>=<varname>value</varname>
+    values on the command line.
+    For example, suppose you
+    want users to be able to
+    build a debug version of a program
+    by running &SCons; as follows:
+
+    </para>
+
+    <literallayout>
+      % scons -Q debug=1
+    </literallayout>
+
+    <para>
+
+    &SCons; provides an &ARGUMENTS; dictionary
+    that stores all of the
+    <varname>variable</varname>=<varname>value</varname>
+    assignments from the command line.
+    This allows you to modify
+    aspects of your build in response
+    to specifications on the command line.
+    (Note that unless you want to require
+    that users <emphasis>always</emphasis>
+    specify an option,
+    you probably want to use
+    the Python
+    <literal>ARGUMENTS.get()</literal> function,
+    which allows you to specify a default value
+    to be used if there is no specification
+    on the command line.)
+
+    </para>
+
+    <para>
+
+    The following code sets the &CCFLAGS; construction
+    variable in response to the <varname>debug</varname>
+    flag being set in the &ARGUMENTS; dictionary:
+
+    </para>
+
+    <scons_example name="ARGUMENTS">
+       <file name="SConstruct" printme="1">
+       env = Environment()
+       debug = ARGUMENTS.get('debug', 0)
+       if int(debug):
+           env.Append(CCFLAGS = '-g')
+       env.Program('prog.c')
+       </file>
+       <file name="prog.c">
+       prog.c
+       </file>
+    </scons_example>
+
+    <para>
+
+    This results in the <varname>-g</varname>
+    compiler option being used when
+    <literal>debug=1</literal>
+    is used on the command line:
+
+    </para>
+
+    <scons_output example="ARGUMENTS">
+       <command>scons -Q debug=0</command>
+       <command>scons -Q debug=0</command>
+       <command>scons -Q debug=1</command>
+       <command>scons -Q debug=1</command>
+    </scons_output>
+
+    <para>
+
+    Notice that &SCons; keeps track of
+    the last values used to build the object files,
+    and as a result correctly rebuilds
+    the object and executable files
+    only when the value of the <literal>debug</literal>
+    argument has changed.
+
+    </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.
+
+    </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
+    &CPPDEFINES; construction variable:
+
+    </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>
+
+    <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:
+
+    </para>
+
+    <scons_output example="Options1">
+      <command>scons -Q RELEASE=1</command>
+    </scons_output>
+
+  </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:
+
+    </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>
+
+    <para>
+
+    &SCons; will now display some useful text
+    when the <literal>-h</literal> option is used:
+
+    </para>
+
+    <scons_output example="Options_Help">
+      <command>scons -Q -h</command>
+    </scons_output>
+
+    <para>
+
+    Notice that the help output shows the default value,
+    and the current actual value of the build option.
+
+    </para>
+
+  </section>
+
+  <section>
+  <title>Reading Build Options From a File</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 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>
+
+    <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>
+
+    <para>
+
+    This then allows us to control the &RELEASE;
+    variable by setting it in the &custom_py; file:
+
+    </para>
+
+    <scons_example_file example="Options_custom_py_1" name="custom.py"></scons_example_file>
+
+    <para>
+
+    Note that this file is actually executed
+    like a Python script.
+    Now when we run &SCons;:
+
+    </para>
+
+    <scons_output example="Options_custom_py_1">
+      <command>scons -Q</command>
+    </scons_output>
+
+    <para>
+
+    And if we change the contents of &custom_py; to:
+
+    </para>
+
+    <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>
+
+    The object files are rebuilt appropriately
+    with the new option:
+
+    </para>
+
+    <scons_output example="Options_custom_py_2">
+      <command>scons -Q</command>
+    </scons_output>
+
+  </section>
+
+  <section>
+  <title>Canned Build Options</title>
+
+    <para>
+
+    &SCons; provides a number of functions
+    that provide ready-made behaviors
+    for various types of command-line build options.
+
+    </para>
+
+    <section>
+    <title>True/False Values:  the &BoolOption; Build Option</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;.
+
+      </para>
+
+      <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:
+
+      </para>
+
+      <scons_example name="BoolOption">
+        <file name="SConstruct" printme="1">
+           opts = Options('custom.py')
+           opts.Add(BoolOption('RELEASE', 0, 'Set to build for release'))
+           env = Environment(options = opts,
+                             CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'})
+           env.Program('foo.c')
+        </file>
+        <file name="foo.c">
+        foo.c
+        </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>:
+
+      </para>
+
+      <scons_output example="BoolOption">
+        <command>scons -Q RELEASE=yes foo.o</command>
+      </scons_output>
+
+      <scons_output example="BoolOption">
+        <command>scons -Q RELEASE=t foo.o</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>
+
+      <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="BoolOption">
+        <command>scons -Q RELEASE=no foo.o</command>
+      </scons_output>
+
+      <scons_output example="BoolOption">
+        <command>scons -Q RELEASE=f foo.o</command>
+      </scons_output>
+
+      <para>
+
+      Other values that equate to &true; 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="BoolOption">
+        <command>scons -Q RELEASE=bad_value foo.o</command>
+      </scons_output>
+
+    </section>
+
+    <section>
+    <title>Single Value From a List:  the &EnumOption; Build Option</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:
+
+      </para>
+
+      <scons_example name="EnumOption">
+        <file name="SConstruct" printme="1">
+           opts = Options('custom.py')
+           opts.Add(EnumOption('COLOR', 'red', 'Set background color',
+                               allowed_values=('red', 'green', 'blue')))
+           env = Environment(options = opts,
+                             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 option
+      to any of the specified allowed values:
+
+      </para>
+
+      <scons_output example="EnumOption">
+        <command>scons -Q COLOR=red foo.o</command>
+        <command>scons -Q COLOR=blue foo.o</command>
+        <command>scons -Q COLOR=green foo.o</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="EnumOption">
+        <command>scons -Q COLOR=magenta foo.o</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 &map; dictionary
+      that will map its key values
+      to the desired legal value:
+
+      </para>
+
+      <scons_example name="EnumOption_map">
+        <file name="SConstruct" printme="1">
+           opts = Options('custom.py')
+           opts.Add(EnumOption('COLOR', 'red', 'Set background color',
+                               allowed_values=('red', 'green', 'blue'),
+                               map={'navy':'blue'}))
+           env = Environment(options = opts,
+                             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;
+      option to build a target:
+
+      </para>
+
+      <scons_output example="EnumOption_map">
+        <command>scons -Q COLOR=navy foo.o</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:
+
+      </para>
+
+      <scons_output example="EnumOption">
+        <command>scons -Q COLOR=Red foo.o</command>
+        <command>scons -Q COLOR=BLUE foo.o</command>
+        <command>scons -Q COLOR=nAvY foo.o</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:
+
+      </para>
+
+      <scons_example name="EnumOption_ic1">
+        <file name="SConstruct" printme="1">
+           opts = Options('custom.py')
+           opts.Add(EnumOption('COLOR', 'red', 'Set background color',
+                               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>
+
+      Which yields the output:
+
+      </para>
+
+      <scons_output example="EnumOption_ic1">
+        <command>scons -Q COLOR=Red foo.o</command>
+        <command>scons -Q COLOR=BLUE foo.o</command>
+        <command>scons -Q COLOR=nAvY foo.o</command>
+        <command>scons -Q COLOR=green foo.o</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="EnumOption_ic2">
+        <file name="SConstruct" printme="1">
+           opts = Options('custom.py')
+           opts.Add(EnumOption('COLOR', 'red', 'Set background color',
+                               allowed_values=('red', 'green', 'blue'),
+                               map={'navy':'blue'},
+                               ignorecase=2))
+           env = Environment(options = opts,
+                             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="EnumOption_ic2">
+        <command>scons -Q COLOR=Red foo.o</command>
+        <command>scons -Q COLOR=nAvY foo.o</command>
+        <command>scons -Q COLOR=GREEN foo.o</command>
+      </scons_output>
+
+    </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:
+
+      </para>
+
+      <scons_example name="ListOption">
+        <file name="SConstruct" printme="1">
+           opts = Options('custom.py')
+           opts.Add(ListOption('COLORS', 0, 'List of colors',
+                               ['red', 'green', 'blue']))
+           env = Environment(options = opts,
+                             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="ListOption">
+        <command>scons -Q COLORS=red,blue foo.o</command>
+        <command>scons -Q COLORS=blue,green,red foo.o</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:
+
+      </para>
+
+      <scons_output example="ListOption">
+        <command>scons -Q COLORS=all foo.o</command>
+        <command>scons -Q COLORS=none foo.o</command>
+      </scons_output>
+
+      <para>
+
+      And, of course, an illegal value
+      still generates an error message:
+
+      </para>
+
+      <scons_output example="ListOption">
+        <command>scons -Q COLORS=magenta foo.o</command>
+      </scons_output>
+
+    </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 control the location of a
+      configuration file:
+
+      </para>
+
+      <scons_example name="PathOption">
+        <file name="SConstruct" printme="1">
+           opts = Options('custom.py')
+           opts.Add(PathOption('CONFIG', '__ROOT__/etc/my_config', 'Path to configuration file'))
+           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>
+
+      <para>
+
+      This then allows the user to
+      override the &CONFIG; build option
+      on the command line as necessary:
+
+      </para>
+
+      <scons_output example="PathOption">
+        <command>scons -Q foo.o</command>
+        <command>scons -Q CONFIG=__ROOT__/usr/local/etc/other_config foo.o</command>
+      </scons_output>
+
+    </section>
+
+    <section>
+    <title>Enabled/Disabled Path Names: the &PackageOption; Build Option</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:
+
+      </para>
+
+      <scons_example name="PackageOption">
+        <file name="SConstruct" printme="1">
+           opts = Options('custom.py')
+           opts.Add(PackageOption('PACKAGE', '__ROOT__/opt/location', 'Location package'))
+           env = Environment(options = opts,
+                             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 &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:
+
+      </para>
+
+      <scons_output example="PackageOption">
+        <command>scons -Q foo.o</command>
+        <command>scons -Q PACKAGE=__ROOT__/usr/local/location foo.o</command>
+        <command>scons -Q PACKAGE=yes foo.o</command>
+        <command>scons -Q PACKAGE=no foo.o</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>
diff --git a/doc/user/command-line.sgml b/doc/user/command-line.sgml
new file mode 100644 (file)
index 0000000..3377b69
--- /dev/null
@@ -0,0 +1,1138 @@
+<!--
+
+  Copyright (c) 2001, 2002, 2003 Steven Knight
+
+  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; 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.
+
+  </para>
+
+  <section>
+  <title>Not Having to Specify Command-Line Options Each Time:  the &SCONSFLAGS; Environment Variable</title>
+
+    <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 always specify a value of <literal>-j 2</literal>
+    to run the builds in parallel.
+
+    </para>
+
+    
+
+    <literallayout>
+      % <userinput>scons</userinput>
+      scons: Reading SConscript files ...
+          ...
+      scons: done reading SConscript files.
+      scons: Building targets ...
+      scons: `.' is up to date.
+      scons: done building targets.
+      % <userinput>export SCONSFLAGS="-Q"</userinput>
+      % <userinput>scons</userinput>
+      scons: Reading SConscript files ...
+          ...
+      scons: done reading SConscript files.
+      scons: Building targets ...
+      scons: `.' is up to date.
+      scons: done building targets.
+    </literallayout>
+
+    <para>
+
+    </para>
+
+  </section>
+
+  <section>
+  <title>Controlling the Default Targets</title>
+
+    <para>
+
+    One of the most basic things you can control
+    is which targets &SCons; will build by default.
+    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>
+
+    <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:
+
+    </para>
+
+    <literallayout>
+       % <userinput>scons -Q</userinput>
+       cc -c -o hello.o hello.c
+       cc -o hello hello.o
+       % <userinput>scons -Q</userinput>
+       scons: `hello' is up to date.
+       % <userinput>scons -Q goodbye</userinput>
+       cc -c -o goodbye.o goodbye.c
+       cc -o goodbye goodbye.o
+    </literallayout>
+
+    <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>
+
+    <literallayout>
+       % <userinput>scons -Q .</userinput>
+       cc -c -o goodbye.o goodbye.c
+       cc -o goodbye goodbye.o
+       cc -c -o hello.o hello.c
+       cc -o hello hello.o
+    </literallayout>
+
+    <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:
+
+    </para>
+
+    <programlisting>
+       env = Environment()
+       prog1 = env.Program('prog1.c')
+       Default(prog1)
+       prog2 = env.Program('prog2.c')
+       prog3 = env.Program('prog3.c')
+       Default(prog3)
+    </programlisting>
+
+    <para>
+
+    Or you can specify more than one target
+    in a single call to the &Default; function:
+
+    </para>
+
+    <programlisting>
+       env = Environment()
+       prog1 = env.Program('prog1.c')
+       prog2 = env.Program('prog2.c')
+       prog3 = env.Program('prog3.c')
+       Default(prog1, prog3)
+    </programlisting>
+
+    <para>
+
+    Either of these last two examples
+    will build only the
+    <application>prog1</application>
+    and
+    <application>prog3</application>
+    programs by default:
+
+    </para>
+
+    <literallayout>
+       % <userinput>scons -Q</userinput>
+       cc -c -o prog1.o prog1.c
+       cc -o prog1 prog1.o
+       cc -c -o prog3.o prog3.c
+       cc -o prog3 prog3.o
+       % <userinput>scons -Q .</userinput>
+       cc -c -o prog2.o prog2.c
+       cc -o prog2 prog2.o
+    </literallayout>
+
+    <para>
+
+    You can list a directory as
+    an argument to &Default;:
+
+    </para>
+
+    <programlisting>
+       env = Environment()
+       env.Program(['prog1/main.c', 'prog1/foo.c'])
+       env.Program(['prog2/main.c', 'prog2/bar.c'])
+       Default('prog1')
+    </programlisting>
+
+    <para>
+
+    In which case only the target(s) in that
+    directory will be built by default:
+
+    </para>
+
+    <literallayout>
+       % <userinput>scons -Q</userinput>
+       cc -c -o prog1/foo.o prog1/foo.c
+       cc -c -o prog1/main.o 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 -c -o prog2/bar.o prog2/bar.c
+       cc -c -o prog2/main.o prog2/main.c
+       cc -o prog2/main prog2/main.o prog2/bar.o
+    </literallayout>
+
+    <para>
+
+    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>
+       env = Environment()
+       prog1 = env.Program('prog1.c')
+       prog2 = env.Program('prog2.c')
+       Default(None)
+    </programlisting>
+
+    <para>
+
+    Which would produce build output like:
+
+    </para>
+
+    <literallayout>
+       % <userinput>scons -Q</userinput>
+       scons: *** No targets specified and no Default() targets found.  Stop.
+       % <userinput>scons -Q .</userinput>
+       cc -c -o prog1.o prog1.c
+       cc -o prog1 prog1.o
+       cc -c -o prog2.o prog2.c
+       cc -o prog2 prog2.o
+    </literallayout>
+
+  </section>
+
+  <!--
+
+  <section>
+  <title>Getting at Command-Line Targets</title>
+
+    <para>
+
+    XXX
+
+    </para>
+
+  </section>
+
+  -->
+
+  <section>
+  <title>Command-Line <varname>variable</varname>=<varname>value</varname> Build Options</title>
+
+    <para>
+
+    You may want to control various aspects
+    of your build by allowing the user
+    to specify <varname>variable</varname>=<varname>value</varname>
+    values on the command line.
+    For example, suppose you
+    want users to be able to
+    build a debug version of a program
+    by running &SCons; as follows:
+
+    </para>
+
+    <literallayout>
+      % scons -Q debug=1
+    </literallayout>
+
+    <para>
+
+    &SCons; provides an &ARGUMENTS; dictionary
+    that stores all of the
+    <varname>variable</varname>=<varname>value</varname>
+    assignments from the command line.
+    This allows you to modify
+    aspects of your build in response
+    to specifications on the command line.
+    (Note that unless you want to require
+    that users <emphasis>always</emphasis>
+    specify an option,
+    you probably want to use
+    the Python
+    <literal>ARGUMENTS.get()</literal> function,
+    which allows you to specify a default value
+    to be used if there is no specification
+    on the command line.)
+
+    </para>
+
+    <para>
+
+    The following code sets the &CCFLAGS; construction
+    variable in response to the <varname>debug</varname>
+    flag being set in the &ARGUMENTS; dictionary:
+
+    </para>
+
+    <programlisting>
+       env = Environment()
+       debug = ARGUMENTS.get('debug', 0)
+       if int(debug):
+           env.Append(CCFLAGS = '-g')
+       env.Program('prog.c')
+    </programlisting>
+
+    <para>
+
+    This results in the <varname>-g</varname>
+    compiler option being used when
+    <literal>debug=1</literal>
+    is used on the command line:
+
+    </para>
+
+    <literallayout>
+       % <userinput>scons -Q debug=0</userinput>
+       cc -c -o prog.o prog.c
+       cc -o prog prog.o
+       % <userinput>scons -Q debug=0</userinput>
+       scons: `.' is up to date.
+       % <userinput>scons -Q debug=1</userinput>
+       cc -g -c -o prog.o prog.c
+       cc -o prog prog.o
+       % <userinput>scons -Q debug=1</userinput>
+       scons: `.' is up to date.
+    </literallayout>
+
+    <para>
+
+    Notice that &SCons; keeps track of
+    the last values used to build the object files,
+    and as a result correctly rebuilds
+    the object and executable files
+    only when the value of the <literal>debug</literal>
+    argument has changed.
+
+    </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.
+
+    </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
+    &CPPDEFINES; construction variable:
+
+    </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'])
+    </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:
+
+    </para>
+
+    <literallayout>
+      % <userinput>scons -Q RELEASE=1</userinput>
+      cc -DRELEASE_BUILD=1 -c -o bar.o bar.c
+      cc -DRELEASE_BUILD=1 -c -o foo.o foo.c
+      cc -o foo foo.o bar.o
+    </literallayout>
+
+  </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:
+
+    </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>
+
+    <literallayout>
+      % <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.
+    </literallayout>
+
+    <para>
+
+    Notice that the help output shows the default value,
+    and the current actual value of the build option.
+
+    </para>
+
+  </section>
+
+  <section>
+  <title>Reading Build Options From a File</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 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>
+
+    <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>
+
+    This then allows us to control the &RELEASE;
+    variable by setting it in the &custom_py; file:
+
+    </para>
+
+    <programlisting>
+      RELEASE = 1
+      </programlisting>
+
+    <para>
+
+    Note that this file is actually executed
+    like a Python script.
+    Now when we run &SCons;:
+
+    </para>
+
+    <literallayout>
+      % <userinput>scons -Q</userinput>
+      cc -DRELEASE_BUILD=1 -c -o bar.o bar.c
+      cc -DRELEASE_BUILD=1 -c -o foo.o foo.c
+      cc -o foo foo.o bar.o
+    </literallayout>
+
+    <para>
+
+    And if we change the contents of &custom_py; to:
+
+    </para>
+
+    <programlisting>
+      RELEASE = 0
+    </programlisting>
+
+    <para>
+
+    The object files are rebuilt appropriately
+    with the new option:
+
+    </para>
+
+    <literallayout>
+      % <userinput>scons -Q</userinput>
+      cc -DRELEASE_BUILD=0 -c -o bar.o bar.c
+      cc -DRELEASE_BUILD=0 -c -o foo.o foo.c
+      cc -o foo foo.o bar.o
+    </literallayout>
+
+  </section>
+
+  <section>
+  <title>Canned Build Options</title>
+
+    <para>
+
+    &SCons; provides a number of functions
+    that provide ready-made behaviors
+    for various types of command-line build options.
+
+    </para>
+
+    <section>
+    <title>True/False Values:  the &BoolOption; Build Option</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;.
+
+      </para>
+
+      <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:
+
+      </para>
+
+      <programlisting>
+           opts = Options('custom.py')
+           opts.Add(BoolOption('RELEASE', 0, 'Set to build for release'))
+           env = Environment(options = opts,
+                             CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'})
+           env.Program('foo.c')
+      </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>:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q RELEASE=yes foo.o</userinput>
+        cc -DRELEASE_BUILD=1 -c -o foo.o foo.c
+      </literallayout>
+
+      <literallayout>
+        % <userinput>scons -Q RELEASE=t foo.o</userinput>
+        cc -DRELEASE_BUILD=1 -c -o foo.o foo.c
+      </literallayout>
+
+      <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>
+
+      <literallayout>
+        % <userinput>scons -Q RELEASE=no foo.o</userinput>
+        cc -DRELEASE_BUILD=0 -c -o foo.o foo.c
+      </literallayout>
+
+      <literallayout>
+        % <userinput>scons -Q RELEASE=f foo.o</userinput>
+        cc -DRELEASE_BUILD=0 -c -o foo.o foo.c
+      </literallayout>
+
+      <para>
+
+      Other values that equate to &true; 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>
+
+      <literallayout>
+        % <userinput>scons -Q RELEASE=bad_value foo.o</userinput>
+        
+        scons: *** Error converting option: RELEASE
+        Invalid value for boolean option: bad_value
+        File "SConstruct", line 4, in ?
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>Single Value From a List:  the &EnumOption; Build Option</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:
+
+      </para>
+
+      <programlisting>
+           opts = Options('custom.py')
+           opts.Add(EnumOption('COLOR', 'red', 'Set background color',
+                               allowed_values=('red', 'green', 'blue')))
+           env = Environment(options = opts,
+                             CPPDEFINES={'COLOR' : '"${COLOR}"'})
+           env.Program('foo.c')
+      </programlisting>
+
+      <para>
+
+      The user can now explicity set the &COLOR; build option
+      to any of the specified allowed values:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q COLOR=red foo.o</userinput>
+        cc -DCOLOR="red" -c -o foo.o foo.c
+        % <userinput>scons -Q COLOR=blue foo.o</userinput>
+        cc -DCOLOR="blue" -c -o foo.o foo.c
+        % <userinput>scons -Q COLOR=green foo.o</userinput>
+        cc -DCOLOR="green" -c -o foo.o foo.c
+      </literallayout>
+
+      <para>
+
+      But, almost more importantly,
+      an attempt to set &COLOR;
+      to a value that's not in the list
+      generates an error message:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q COLOR=magenta foo.o</userinput>
+        
+        scons: *** Invalid value for option COLOR: magenta
+        File "SConstruct", line 5, in ?
+      </literallayout>
+
+      <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 &map; dictionary
+      that will map its key values
+      to the desired legal value:
+
+      </para>
+
+      <programlisting>
+           opts = Options('custom.py')
+           opts.Add(EnumOption('COLOR', 'red', 'Set background color',
+                               allowed_values=('red', 'green', 'blue'),
+                               map={'navy':'blue'}))
+           env = Environment(options = opts,
+                             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;
+      option to build a target:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q COLOR=navy foo.o</userinput>
+        cc -DCOLOR="blue" -c -o foo.o foo.c
+      </literallayout>
+
+      <para>
+
+      By default, when using the &EnumOption; function,
+      arguments that differ
+      from the legal values
+      only in case
+      are treated as illegal values:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q COLOR=Red foo.o</userinput>
+        
+        scons: *** Invalid value for option COLOR: Red
+        File "SConstruct", line 5, in ?
+        % <userinput>scons -Q COLOR=BLUE foo.o</userinput>
+        
+        scons: *** Invalid value for option COLOR: BLUE
+        File "SConstruct", line 5, in ?
+        % <userinput>scons -Q COLOR=nAvY foo.o</userinput>
+        
+        scons: *** Invalid value for option COLOR: nAvY
+        File "SConstruct", line 5, in ?
+      </literallayout>
+
+      <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:
+
+      </para>
+
+      <programlisting>
+           opts = Options('custom.py')
+           opts.Add(EnumOption('COLOR', 'red', 'Set background color',
+                               allowed_values=('red', 'green', 'blue'),
+                               map={'navy':'blue'},
+                               ignorecase=1))
+           env = Environment(options = opts,
+                             CPPDEFINES={'COLOR' : '"${COLOR}"'})
+           env.Program('foo.c')
+      </programlisting>
+
+      <para>
+
+      Which yields the output:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q COLOR=Red foo.o</userinput>
+        cc -DCOLOR="Red" -c -o foo.o foo.c
+        % <userinput>scons -Q COLOR=BLUE foo.o</userinput>
+        cc -DCOLOR="BLUE" -c -o foo.o foo.c
+        % <userinput>scons -Q COLOR=nAvY foo.o</userinput>
+        cc -DCOLOR="blue" -c -o foo.o foo.c
+        % <userinput>scons -Q COLOR=green foo.o</userinput>
+        cc -DCOLOR="green" -c -o foo.o foo.c
+      </literallayout>
+
+      <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>
+           opts = Options('custom.py')
+           opts.Add(EnumOption('COLOR', 'red', 'Set background color',
+                               allowed_values=('red', 'green', 'blue'),
+                               map={'navy':'blue'},
+                               ignorecase=2))
+           env = Environment(options = opts,
+                             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>
+
+      <literallayout>
+        % <userinput>scons -Q COLOR=Red foo.o</userinput>
+        cc -DCOLOR="red" -c -o foo.o foo.c
+        % <userinput>scons -Q COLOR=nAvY foo.o</userinput>
+        cc -DCOLOR="blue" -c -o foo.o foo.c
+        % <userinput>scons -Q COLOR=GREEN foo.o</userinput>
+        cc -DCOLOR="green" -c -o foo.o foo.c
+      </literallayout>
+
+    </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:
+
+      </para>
+
+      <programlisting>
+           opts = Options('custom.py')
+           opts.Add(ListOption('COLORS', 0, 'List of colors',
+                               ['red', 'green', 'blue']))
+           env = Environment(options = opts,
+                             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>
+
+      <literallayout>
+        % <userinput>scons -Q COLORS=red,blue foo.o</userinput>
+        TypeError: sequence item 0: expected string, int found:
+        % <userinput>scons -Q COLORS=blue,green,red foo.o</userinput>
+        TypeError: sequence item 0: expected string, int found:
+      </literallayout>
+
+      <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:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q COLORS=all foo.o</userinput>
+        TypeError: sequence item 0: expected string, int found:
+        % <userinput>scons -Q COLORS=none foo.o</userinput>
+        TypeError: sequence item 0: expected string, int found:
+      </literallayout>
+
+      <para>
+
+      And, of course, an illegal value
+      still generates an error message:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q COLORS=magenta foo.o</userinput>
+        TypeError: sequence item 0: expected string, int found:
+      </literallayout>
+
+    </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 control the location of a
+      configuration file:
+
+      </para>
+
+      <programlisting>
+           opts = Options('custom.py')
+           opts.Add(PathOption('CONFIG', '/etc/my_config', 'Path to configuration file'))
+           env = Environment(options = opts,
+                             CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'})
+           env.Program('foo.c')
+      </programlisting>
+
+      <para>
+
+      This then allows the user to
+      override the &CONFIG; build option
+      on the command line as necessary:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q foo.o</userinput>
+        
+        scons: *** Path does not exist for option CONFIG: Path to configuration file
+        File "SConstruct", line 4, in ?
+        % <userinput>scons -Q CONFIG=/usr/local/etc/other_config foo.o</userinput>
+        cc -DCONFIG_FILE="/usr/local/etc/other_config" -c -o foo.o foo.c
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>Enabled/Disabled Path Names: the &PackageOption; Build Option</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:
+
+      </para>
+
+      <programlisting>
+           opts = Options('custom.py')
+           opts.Add(PackageOption('PACKAGE', '/opt/location', 'Location package'))
+           env = Environment(options = opts,
+                             CPPDEFINES={'PACKAGE' : '"$PACKAGE"'})
+           env.Program('foo.c')
+      </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:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons -Q foo.o</userinput>
+        
+        scons: *** Path does not exist for option PACKAGE: Location package
+        File "SConstruct", line 4, in ?
+        % <userinput>scons -Q PACKAGE=/usr/local/location foo.o</userinput>
+        cc -DPACKAGE="/usr/local/location" -c -o foo.o foo.c
+        % <userinput>scons -Q PACKAGE=yes foo.o</userinput>
+        cc -DPACKAGE="1" -c -o foo.o foo.c
+        % <userinput>scons -Q PACKAGE=no foo.o</userinput>
+        cc -DPACKAGE="0" -c -o foo.o foo.c
+      </literallayout>
+
+    </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>
+
+    <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>
+
+    <para>
+    </para>
+
+  </section>
diff --git a/doc/user/default.in b/doc/user/default.in
deleted file mode 100644 (file)
index 2410da6..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-<!--
-
-  Copyright (c) 2001, 2002, 2003 Steven Knight
-
-  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.
-
--->
-
-<!--
-
-=head1 Default targets
-
-Until now, we've demonstrated invoking Cons with an explicit target
-to build:
-
-  % cons hello
-
-Normally, Cons does not build anything unless a target is specified,
-but specifying '.' (the current directory) will build everything:
-
-  % cons               # does not build anything
-
-  % cons .             # builds everything under the top-level directory
-
-Adding the C<Default> method to any F<Construct> or F<Conscript> file will add
-the specified targets to a list of default targets.  Cons will build
-these defaults if there are no targets specified on the command line.
-So adding the following line to the top-level F<Construct> file will mimic
-Make's typical behavior of building everything by default:
-
-  Default '.';
-
-The following would add the F<hello> and F<goodbye> commands (in the
-same directory as the F<Construct> or F<Conscript> file) to the default list:
-
-  Default qw(
-       hello
-       goodbye
-  );
-
-The C<Default> method may be used more than once to add targets to the
-default list.
-
--->
-
-   <para>
-
-   As mentioned previously,
-   &SCons; will 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 should be built by default.
-   You do this with the &Default; function:
-
-   </para>
-
-   <scons_example name="ex1">
-      <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>
-
-   This &SConstruct; file knows how to build two programs,
-   &hello; and &goodbye;,
-   but only builds the
-   &hello; program by default:
-
-   </para>
-
-   <scons_output example="ex1">
-      <command>scons -Q</command>
-      <command>scons -Q</command>
-      <command>scons -Q goodbye</command>
-   </scons_output>
-
-   <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>
-
-   <scons_output example="ex1">
-      <command>scons -Q .</command>
-   </scons_output>
-
-   <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:
-
-   </para>
-
-   <scons_example name="ex2">
-      <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>
-
-   Or you can specify more than one target
-   in a single call to the &Default; function:
-
-   </para>
-
-   <programlisting>
-      env = Environment()
-      prog1 = env.Program('prog1.c')
-      prog2 = env.Program('prog2.c')
-      prog3 = env.Program('prog3.c')
-      Default(prog1, prog3)
-   </programlisting>
-
-   <para>
-
-   Either of these last two examples
-   will build only the
-   <application>prog1</application>
-   and
-   <application>prog3</application>
-   programs by default:
-
-   </para>
-
-   <scons_output example="ex2">
-      <command>scons -Q</command>
-      <command>scons -Q .</command>
-   </scons_output>
-
-   <para>
-
-   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="ex3">
-      <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>
-
-   Which would produce build output like:
-
-   </para>
-
-   <scons_output example="ex3">
-      <command>scons -Q</command>
-      <command>scons -Q .</command>
-   </scons_output>
diff --git a/doc/user/default.sgml b/doc/user/default.sgml
deleted file mode 100644 (file)
index fa21bae..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-<!--
-
-  Copyright (c) 2001, 2002, 2003 Steven Knight
-
-  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.
-
--->
-
-<!--
-
-=head1 Default targets
-
-Until now, we've demonstrated invoking Cons with an explicit target
-to build:
-
-  % cons hello
-
-Normally, Cons does not build anything unless a target is specified,
-but specifying '.' (the current directory) will build everything:
-
-  % cons               # does not build anything
-
-  % cons .             # builds everything under the top-level directory
-
-Adding the C<Default> method to any F<Construct> or F<Conscript> file will add
-the specified targets to a list of default targets.  Cons will build
-these defaults if there are no targets specified on the command line.
-So adding the following line to the top-level F<Construct> file will mimic
-Make's typical behavior of building everything by default:
-
-  Default '.';
-
-The following would add the F<hello> and F<goodbye> commands (in the
-same directory as the F<Construct> or F<Conscript> file) to the default list:
-
-  Default qw(
-       hello
-       goodbye
-  );
-
-The C<Default> method may be used more than once to add targets to the
-default list.
-
--->
-
-   <para>
-
-   As mentioned previously,
-   &SCons; will 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 should be built by default.
-   You do this with the &Default; function:
-
-   </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:
-
-   </para>
-
-   <literallayout>
-      % <userinput>scons -Q</userinput>
-      cc -c -o hello.o hello.c
-      cc -o hello hello.o
-      % <userinput>scons -Q</userinput>
-      scons: `hello' is up to date.
-      % <userinput>scons -Q goodbye</userinput>
-      cc -c -o goodbye.o goodbye.c
-      cc -o goodbye goodbye.o
-   </literallayout>
-
-   <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>
-
-   <literallayout>
-      % <userinput>scons -Q .</userinput>
-      cc -c -o goodbye.o goodbye.c
-      cc -o goodbye goodbye.o
-      cc -c -o hello.o hello.c
-      cc -o hello hello.o
-   </literallayout>
-
-   <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:
-
-   </para>
-
-   <programlisting>
-      env = Environment()
-      prog1 = env.Program('prog1.c')
-      Default(prog1)
-      prog2 = env.Program('prog2.c')
-      prog3 = env.Program('prog3.c')
-      Default(prog3)
-   </programlisting>
-
-   <para>
-
-   Or you can specify more than one target
-   in a single call to the &Default; function:
-
-   </para>
-
-   <programlisting>
-      env = Environment()
-      prog1 = env.Program('prog1.c')
-      prog2 = env.Program('prog2.c')
-      prog3 = env.Program('prog3.c')
-      Default(prog1, prog3)
-   </programlisting>
-
-   <para>
-
-   Either of these last two examples
-   will build only the
-   <application>prog1</application>
-   and
-   <application>prog3</application>
-   programs by default:
-
-   </para>
-
-   <literallayout>
-      % <userinput>scons -Q</userinput>
-      cc -c -o prog1.o prog1.c
-      cc -o prog1 prog1.o
-      cc -c -o prog3.o prog3.c
-      cc -o prog3 prog3.o
-      % <userinput>scons -Q .</userinput>
-      cc -c -o prog2.o prog2.c
-      cc -o prog2 prog2.o
-   </literallayout>
-
-   <para>
-
-   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>
-      env = Environment()
-      prog1 = env.Program('prog1.c')
-      prog2 = env.Program('prog2.c')
-      Default(None)
-   </programlisting>
-
-   <para>
-
-   Which would produce build output like:
-
-   </para>
-
-   <literallayout>
-      % <userinput>scons -Q</userinput>
-      scons: *** No targets specified and no Default() targets found.  Stop.
-      % <userinput>scons -Q .</userinput>
-      cc -c -o prog1.o prog1.c
-      cc -o prog1 prog1.o
-      cc -c -o prog2.o prog2.c
-      cc -o prog2 prog2.o
-   </literallayout>
index e0ae71d433bfb59540871f69f72c0c9d27e94517..4aa4d9c9200f3163e29879283563fe88d5ecaf28 100644 (file)
@@ -39,9 +39,9 @@
     <!ENTITY builders-commands SYSTEM "builders-commands.sgml">
     <!ENTITY builders-writing SYSTEM "builders-writing.sgml">
     <!ENTITY caching SYSTEM "caching.sgml">
+    <!ENTITY command-line SYSTEM "command-line.sgml">
     <!ENTITY cons SYSTEM "cons.sgml">
     <!ENTITY copyright SYSTEM "copyright.sgml">
-    <!ENTITY default SYSTEM "default.sgml">
     <!ENTITY depends SYSTEM "depends.sgml">
     <!ENTITY environments SYSTEM "environments.sgml">
     <!ENTITY errors SYSTEM "errors.sgml">
     &environments;
   </chapter>
 
-  <chapter id="chap-default">
-    <title>Default Targets</title>
-    &default;
+  <chapter id="chap-command-line">
+    <title>Controlling a Build From the Command Line</title>
+    &command-line;
   </chapter>
 
   <chapter id="chap-help">
index e0ae71d433bfb59540871f69f72c0c9d27e94517..4aa4d9c9200f3163e29879283563fe88d5ecaf28 100644 (file)
@@ -39,9 +39,9 @@
     <!ENTITY builders-commands SYSTEM "builders-commands.sgml">
     <!ENTITY builders-writing SYSTEM "builders-writing.sgml">
     <!ENTITY caching SYSTEM "caching.sgml">
+    <!ENTITY command-line SYSTEM "command-line.sgml">
     <!ENTITY cons SYSTEM "cons.sgml">
     <!ENTITY copyright SYSTEM "copyright.sgml">
-    <!ENTITY default SYSTEM "default.sgml">
     <!ENTITY depends SYSTEM "depends.sgml">
     <!ENTITY environments SYSTEM "environments.sgml">
     <!ENTITY errors SYSTEM "errors.sgml">
     &environments;
   </chapter>
 
-  <chapter id="chap-default">
-    <title>Default Targets</title>
-    &default;
+  <chapter id="chap-command-line">
+    <title>Controlling a Build From the Command Line</title>
+    &command-line;
   </chapter>
 
   <chapter id="chap-help">
index d8e19f87c762dd38076123ca6d5f3ca17373b961..0d3e7f051dac1e1240527a7f8f584907f5f01385 100644 (file)
@@ -373,44 +373,3 @@ do an unconstrained build before committing the integration).
     </para>
 
   </section>
-
-  <section>
-  <title>The &SCONSFLAGS; Environment Variable</title>
-
-    <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 always specify a value of <literal>-j 2</literal>
-    to run the builds in parallel.
-
-    </para>
-
-    <scons_example name="SCONSFLAGS">
-      <file name="SConstruct">
-      def b(target, source, env):
-          pass
-      def s(target, source, env):
-          return "    ..."
-      a = Action(b, strfunction = s)
-      env = Environment(BUILDERS = {'A' : a})
-      env.A('foo.out', 'foo.in')
-      </file>
-      <file name="foo.in">
-      foo.in
-      </file>
-    </scons_example>
-
-    <scons_output example="SCONSFLAGS">
-      <command>scons</command>
-      <command>export SCONSFLAGS="-Q"</command>
-      <command>scons</command>
-    </scons_output>
-
-    <para>
-
-    </para>
-
-  </section>
index 9cdb07cb66b81ba4d519be43ceec6919a298369f..0d3e7f051dac1e1240527a7f8f584907f5f01385 100644 (file)
@@ -373,43 +373,3 @@ do an unconstrained build before committing the integration).
     </para>
 
   </section>
-
-  <section>
-  <title>The &SCONSFLAGS; Environment Variable</title>
-
-    <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 always specify a value of <literal>-j 2</literal>
-    to run the builds in parallel.
-
-    </para>
-
-    
-
-    <literallayout>
-      % <userinput>scons</userinput>
-      scons: Reading SConscript files ...
-          ...
-      scons: done reading SConscript files.
-      scons: Building targets ...
-      scons: `.' is up to date.
-      scons: done building targets.
-      % <userinput>export SCONSFLAGS="-Q"</userinput>
-      % <userinput>scons</userinput>
-      scons: Reading SConscript files ...
-          ...
-      scons: done reading SConscript files.
-      scons: Building targets ...
-      scons: `.' is up to date.
-      scons: done building targets.
-    </literallayout>
-
-    <para>
-
-    </para>
-
-  </section>
index c81e8706d2958c48a9640e94c1137ebaf85a0e6c..29053390769d1a97579097a82fb7385810d07c64 100644 (file)
 
 RELEASE X.XX - XXX, XX XXX XXXX XX:XX:XX -XXXX
 
+  From Hartmut Goebel:
+
+  - Add several new types of canned functions to help create options:
+    BoolOption(), EnumOption(), ListOption(), PackageOption(),
+    PathOption().
+
   From Steven Knight:
 
   - Fix use of CPPDEFINES with C++ source files.
index 24e3d76005b5f2594e3ad08e2754b9043ebe94f5..c5fe7a373fe0ec15c5b5de0839b7ab8f8f745a2d 100644 (file)
@@ -16,7 +16,12 @@ SCons/Optik/__init__.py
 SCons/Optik/errors.py
 SCons/Optik/option.py
 SCons/Optik/option_parser.py
-SCons/Options.py
+SCons/Options/__init__.py
+SCons/Options/BoolOption.py
+SCons/Options/EnumOption.py
+SCons/Options/ListOption.py
+SCons/Options/PackageOption.py
+SCons/Options/PathOption.py
 SCons/Platform/__init__.py
 SCons/Platform/aix.py
 SCons/Platform/cygwin.py
diff --git a/src/engine/SCons/Options/BoolOption.py b/src/engine/SCons/Options/BoolOption.py
new file mode 100644 (file)
index 0000000..f38bf02
--- /dev/null
@@ -0,0 +1,88 @@
+"""engine.SCons.Options.BoolOption
+
+This file defines the option type for SCons implementing true/false values.
+
+Usage example:
+
+  opts = Options()
+  opts.Add(BoolOption('embedded', 'build for an embedded system', 0))
+  ...
+  if env['embedded'] == 1:
+    ...
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+__all__ = ('BoolOption', 'True', 'False')
+
+import string
+
+import SCons.Errors
+
+__true_strings  = ('y', 'yes', 'true', 't', '1', 'on' , 'all' )
+__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none')
+
+# we need this since SCons should work version indepentant
+True, False = 1, 0
+
+
+def _text2bool(val):
+    """
+    Converts strings to True/False depending on the 'truth' expressed by
+    the string. If the string can't be converted, the original value
+    will be returned.
+
+    See '__true_strings' and '__false_strings' for values considered
+    'true' or 'false respectivly.
+
+    This is usable as 'converter' for SCons' Options.
+    """
+    lval = string.lower(val)
+    if lval in __true_strings: return True
+    if lval in __false_strings: return False
+    raise ValueError("Invalid value for boolean option: %s" % val)
+
+
+def _validator(key, val, env):
+    """
+    Validates the given value to be either '0' or '1'.
+    
+    This is usable as 'validator' for SCons' Options.
+    """
+    if not env[key] in (True, False):
+        raise SCons.Errors.UserError(
+            'Invalid value for boolean option %s: %s' % (key, env[key]))
+
+
+def BoolOption(key, help, default):
+    """
+    The input parameters describe a boolen option, thus they are
+    returned with the correct converter and validator appended. The
+    'help' text will by appended by '(yes|no) to show the valid
+    valued. The result is usable for input to opts.Add().
+    """
+    return (key, '%s (yes|no)' % help, default,
+            _validator, _text2bool)
diff --git a/src/engine/SCons/Options/BoolOptionTests.py b/src/engine/SCons/Options/BoolOptionTests.py
new file mode 100644 (file)
index 0000000..845f251
--- /dev/null
@@ -0,0 +1,121 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Options
+
+class BoolOptionTestCase(unittest.TestCase):
+    def test_BoolOption(self):
+        """Test BoolOption creation"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.BoolOption('test', 'test option help', 0))
+
+        o = opts.options[0]
+        assert o.key == 'test', o.key
+        assert o.help == 'test option help (yes|no)', o.help
+        assert o.default == 0, o.default
+        assert not o.validator is None, o.validator
+        assert not o.converter is None, o.converter
+
+    def test_converter(self):
+        """Test the BoolOption converter"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.BoolOption('test', 'test option help', 0))
+
+        o = opts.options[0]
+
+        true_values = [
+                'y',    'Y',
+                'yes',  'YES',
+                't',    'T',
+                'true', 'TRUE',
+                'on',   'ON',
+                'all',  'ALL',
+                '1',
+        ]
+        false_values = [
+                'n',    'N',
+                'no',   'NO',
+                'f',    'F',
+                'false', 'FALSE',
+                'off',  'OFF',
+                'none', 'NONE',
+                '0',
+        ]
+
+        for t in true_values:
+            x = o.converter(t)
+            assert x, "converter returned false for '%s'" % t
+
+        for f in false_values:
+            x = o.converter(f)
+            assert not x, "converter returned true for '%s'" % f
+
+        caught = None
+        try:
+            o.converter('x')
+        except ValueError:
+            caught = 1
+        assert caught, "did not catch expected ValueError"
+
+    def test_validator(self):
+        """Test the BoolOption validator"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.BoolOption('test', 'test option help', 0))
+
+        o = opts.options[0]
+
+        env = {
+            'T' : SCons.Options.True,
+            'F' : SCons.Options.False,
+            'N' : 'xyzzy',
+        }
+
+        o.validator('T', 0, env)
+
+        o.validator('F', 0, env)
+
+        caught = None
+        try:
+            o.validator('N', 0, env)
+        except SCons.Errors.UserError:
+            caught = 1
+        assert caught, "did not catch expected UserError for N"
+
+        caught = None
+        try:
+            o.validator('NOSUCHKEY', 0, env)
+        except KeyError:
+            caught = 1
+        assert caught, "did not catch expected KeyError for NOSUCHKEY"
+
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(BoolOptionTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)
diff --git a/src/engine/SCons/Options/EnumOption.py b/src/engine/SCons/Options/EnumOption.py
new file mode 100644 (file)
index 0000000..d4e2ac1
--- /dev/null
@@ -0,0 +1,101 @@
+"""engine.SCons.Options.EnumOption
+
+This file defines the option type for SCons allowing only specified
+input-values.
+
+Usage example:
+
+  opts = Options()
+  opts.Add(EnumOption('debug', 'debug output and symbols', 'no',
+                      allowed_values=('yes', 'no', 'full'),
+                      map={}, ignorecase=2))
+  ...
+  if env['debug'] == 'full':
+    ...
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+__all__ = ('EnumOption',)
+
+import string
+
+import SCons.Errors
+
+def _validator(key, val, env, vals):
+    if not val in vals:
+        raise SCons.Errors.UserError(
+            'Invalid value for option %s: %s' % (key, val))
+
+
+def EnumOption(key, help, default, allowed_values, map={}, ignorecase=0):
+    """
+    The input parameters describe a option with only certain values
+    allowed. They are returned with an appropriate converter and
+    validator appended. The result is usable for input to
+    Options.Add().
+
+    'key' and 'default' are the values to be passed on to Options.Add().
+
+    'help' will be appended by the allowed values automatically
+
+    'allowed_values' is a list of strings, which are allowed as values
+    for this option.
+
+    The 'map'-dictionary may be used for converting the input value
+    into canonical values (eg. for aliases).
+
+    'ignorecase' defines the behaviour of the validator:
+
+    If ignorecase == 0, the validator/converter are case-sensitive.
+    If ignorecase == 1, the validator/converter are case-insensitive.
+    If ignorecase == 2, the validator/converter is case-insensitive and
+                        the converted value will always be lower-case.
+
+    The 'validator' tests whether the value is in the list of allowed
+    values. The 'converter' converts input values according to the
+    given 'map'-dictionary (unmapped input values are returned
+    unchanged). 
+    """
+    help = '%s (%s)' % (help, string.join(allowed_values, '|'))
+    # define validator
+    if ignorecase >= 1:
+        validator = lambda key, val, env, vals=allowed_values: \
+                    _validator(key, string.lower(val), env, vals)
+    else:
+        validator = lambda key, val, env, vals=allowed_values: \
+                    _validator(key, val, env, vals)
+    # define converter
+    if ignorecase == 2:
+        converter = lambda val, map=map: \
+                    string.lower(map.get(string.lower(val), val))
+    elif ignorecase == 1:
+        converter = lambda val, map=map: \
+                    map.get(string.lower(val), val)
+    else:
+        converter = lambda val, map=map: \
+                    map.get(val, val)
+    return (key, help, default, validator, converter)
diff --git a/src/engine/SCons/Options/EnumOptionTests.py b/src/engine/SCons/Options/EnumOptionTests.py
new file mode 100644 (file)
index 0000000..20ae6c3
--- /dev/null
@@ -0,0 +1,198 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Options
+
+class EnumOptionTestCase(unittest.TestCase):
+    def test_EnumOption(self):
+        """Test EnumOption creation"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.EnumOption('test', 'test option help', 0,
+                                          ['one', 'two', 'three'],
+                                          {}))
+
+        o = opts.options[0]
+        assert o.key == 'test', o.key
+        assert o.help == 'test option help (one|two|three)', o.help
+        assert o.default == 0, o.default
+        assert not o.validator is None, o.validator
+        assert not o.converter is None, o.converter
+
+    def test_converter(self):
+        """Test the EnumOption converter"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.EnumOption('test', 'test option help', 0,
+                                          ['one', 'two', 'three']))
+
+        o = opts.options[0]
+
+        for a in ['one', 'two', 'three', 'no_match']:
+            x = o.converter(a)
+            assert x == a, x
+
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.EnumOption('test', 'test option help', 0,
+                                          ['one', 'two', 'three'],
+                                          {'1' : 'one',
+                                           '2' : 'two',
+                                           '3' : 'three'}))
+
+        o = opts.options[0]
+
+        x = o.converter('one')
+        assert x == 'one', x
+        x = o.converter('1')
+        assert x == 'one', x
+
+        x = o.converter('two')
+        assert x == 'two', x
+        x = o.converter('2')
+        assert x == 'two', x
+
+        x = o.converter('three')
+        assert x == 'three', x
+        x = o.converter('3')
+        assert x == 'three', x
+
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.EnumOption('test0', 'test option help', 0,
+                                          ['one', 'two', 'three'],
+                                          {'a' : 'one',
+                                           'b' : 'two',
+                                           'c' : 'three'},
+                                          ignorecase=0))
+        opts.Add(SCons.Options.EnumOption('test1', 'test option help', 0,
+                                          ['one', 'two', 'three'],
+                                          {'a' : 'one',
+                                           'b' : 'two',
+                                           'c' : 'three'},
+                                          ignorecase=1))
+        opts.Add(SCons.Options.EnumOption('test2', 'test option help', 0,
+                                          ['one', 'two', 'three'],
+                                          {'a' : 'one',
+                                           'b' : 'two',
+                                           'c' : 'three'},
+                                          ignorecase=2))
+
+        o0 = opts.options[0]
+        o1 = opts.options[1]
+        o2 = opts.options[2]
+
+        table = {
+            'one'   : ['one',   'one',   'one'],
+            'One'   : ['One',   'One',   'one'],
+            'ONE'   : ['ONE',   'ONE',   'one'],
+            'two'   : ['two',   'two',   'two'],
+            'twO'   : ['twO',   'twO',   'two'],
+            'TWO'   : ['TWO',   'TWO',   'two'],
+            'three' : ['three', 'three', 'three'],
+            'thRee' : ['thRee', 'thRee', 'three'],
+            'THREE' : ['THREE', 'THREE', 'three'],
+            'a'     : ['one',   'one',   'one'],
+            'A'     : ['A',     'one',   'one'],
+            'b'     : ['two',   'two',   'two'],
+            'B'     : ['B',     'two',   'two'],
+            'c'     : ['three', 'three', 'three'],
+            'C'     : ['C',     'three', 'three'],
+        }
+
+        for k, l in table.items():
+            x = o0.converter(k)
+            assert x == l[0], "o0 got %s, expected %s" % (x, l[0])
+            x = o1.converter(k)
+            assert x == l[1], "o1 got %s, expected %s" % (x, l[1])
+            x = o2.converter(k)
+            assert x == l[2], "o2 got %s, expected %s" % (x, l[2])
+
+    def test_validator(self):
+        """Test the EnumOption validator"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.EnumOption('test0', 'test option help', 0,
+                                          ['one', 'two', 'three'],
+                                          {'a' : 'one',
+                                           'b' : 'two',
+                                           'c' : 'three'},
+                                          ignorecase=0))
+        opts.Add(SCons.Options.EnumOption('test1', 'test option help', 0,
+                                          ['one', 'two', 'three'],
+                                          {'a' : 'one',
+                                           'b' : 'two',
+                                           'c' : 'three'},
+                                          ignorecase=1))
+        opts.Add(SCons.Options.EnumOption('test2', 'test option help', 0,
+                                          ['one', 'two', 'three'],
+                                          {'a' : 'one',
+                                           'b' : 'two',
+                                           'c' : 'three'},
+                                          ignorecase=2))
+
+        o0 = opts.options[0]
+        o1 = opts.options[1]
+        o2 = opts.options[2]
+
+        def valid(o, v):
+            o.validator('X', v, {})
+
+        def invalid(o, v):
+            caught = None
+            try:
+                o.validator('X', v, {})
+            except SCons.Errors.UserError:
+                caught = 1
+            assert caught, "did not catch expected UserError for o = %s, v = %s" % (o.key, v)
+
+        table = {
+            'one'   : [  valid,   valid,   valid],
+            'One'   : [invalid,   valid,   valid],
+            'ONE'   : [invalid,   valid,   valid],
+            'two'   : [  valid,   valid,   valid],
+            'twO'   : [invalid,   valid,   valid],
+            'TWO'   : [invalid,   valid,   valid],
+            'three' : [  valid,   valid,   valid],
+            'thRee' : [invalid,   valid,   valid],
+            'THREE' : [invalid,   valid,   valid],
+            'a'     : [invalid, invalid, invalid],
+            'A'     : [invalid, invalid, invalid],
+            'b'     : [invalid, invalid, invalid],
+            'B'     : [invalid, invalid, invalid],
+            'c'     : [invalid, invalid, invalid],
+            'C'     : [invalid, invalid, invalid],
+            'no_v'  : [invalid, invalid, invalid],
+        }
+
+        for v, l in table.items():
+            l[0](o0, v)
+            l[1](o1, v)
+            l[2](o2, v)
+
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(EnumOptionTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)
diff --git a/src/engine/SCons/Options/ListOption.py b/src/engine/SCons/Options/ListOption.py
new file mode 100644 (file)
index 0000000..20b7e99
--- /dev/null
@@ -0,0 +1,131 @@
+"""engine.SCons.Options.ListOption
+
+This file defines the option type for SCons implementing 'lists'.
+
+A 'list' option may either be 'all', 'none' or a list of names
+separated by comma. After the option has been processed, the option
+value holds either the named list elements, all list elemens or no
+list elements at all.
+
+Usage example:
+
+  list_of_libs = Split('x11 gl qt ical')
+
+  opts = Options()
+  opts.Add(ListOption('shared',
+                      'libraries to build as shared libraries',
+                      'all',
+                      elems = list_of_libs))
+  ...
+  for lib in list_of_libs:
+     if lib in env['shared']:
+         env.SharedObject(...)
+     else:
+         env.Object(...)
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+# Know Bug: This should behave like a Set-Type, but does not really,
+# since elements can occur twice.
+
+__all__ = ('ListOption',)
+
+import string
+import UserList
+
+
+class _ListOption(UserList.UserList):
+    def __init__(self, allowedElems, initlist):
+        UserList.UserList.__init__(self, filter(None, initlist))
+        self.allowedElems = allowedElems[:]
+        self.allowedElems.sort()
+        
+    def __cmp__(self, other):
+        raise NotImplementedError
+    def __eq__(self, other):
+        raise NotImplementedError
+    def __ge__(self, other):
+        raise NotImplementedError
+    def __gt__(self, other):
+        raise NotImplementedError
+    def __le__(self, other):
+        raise NotImplementedError
+    def __lt__(self, other):
+        raise NotImplementedError
+    def __str__(self):
+        if len(self) == 0:
+            return 'none'
+        self.data.sort()
+        if self.data == self.allowedElems:
+            return 'all'
+        else:
+            return string.join(self, ',')
+    #def __repr__(self):
+    #  todo: implement this
+    
+def _converter(val, allowedElems):
+    """
+    """
+    if val == 'none':
+        val = []
+    elif val == 'all':
+        val = allowedElems
+    else:
+        val = filter(None, string.split(val, ','))
+        notAllowed = []
+        for v in val:
+            if not v in allowedElems:
+                notAllowed.append(v)
+        if notAllowed:
+            raise ValueError("Invalid value(s) for option: %s" %
+                             string.join(notAllowed, ','))
+    return _ListOption(allowedElems, val)
+
+
+## def _validator(key, val, env):
+##     """
+##     """
+##     # todo: write validater for pgk list
+##     return 1
+
+
+def ListOption(key, help, default, names):
+    """
+    The input parameters describe a 'package list' option, thus they
+    are returned with the correct converter and validater appended. The
+    result is usable for input to opts.Add() .
+
+    A 'package list' option may either be 'all', 'none' or a list of
+    package names (separated by space).
+    """
+    names_str = 'allowed names: %s' % string.join(names, ' ')
+    help = string.join(
+        (help, '(all|none|comma-separated list of names)', names_str),
+        '\n    ')
+    return (key, help, default,
+            None, #_validator,
+            lambda val, elems=names: _converter(val, elems))
diff --git a/src/engine/SCons/Options/ListOptionTests.py b/src/engine/SCons/Options/ListOptionTests.py
new file mode 100644 (file)
index 0000000..2175720
--- /dev/null
@@ -0,0 +1,101 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Options
+
+class ListOptionTestCase(unittest.TestCase):
+    def test_ListOption(self):
+        """Test ListOption creation"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.ListOption('test', 'test option help', 'all',
+                                          ['one', 'two', 'three']))
+
+        o = opts.options[0]
+        assert o.key == 'test', o.key
+        assert o.help == 'test option help\n    (all|none|comma-separated list of names)\n    allowed names: one two three', repr(o.help)
+        assert o.default == 'all', o.default
+        assert o.validator is None, o.validator
+        assert not o.converter is None, o.converter
+
+    def test_converter(self):
+        """Test the ListOption converter"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.ListOption('test', 'test option help', 'all',
+                                          ['one', 'two', 'three']))
+
+        o = opts.options[0]
+
+        x = o.converter('all')
+        assert str(x) == 'all', x
+
+        x = o.converter('none')
+        assert str(x) == 'none', x
+
+        x = o.converter('one')
+        assert str(x) == 'one', x
+
+        x = o.converter('two')
+        assert str(x) == 'two', x
+
+        x = o.converter('three')
+        assert str(x) == 'three', x
+
+        x = o.converter('one,two')
+        assert str(x) == 'one,two', x
+        x = o.converter('two,one')
+        assert str(x) == 'one,two', x
+
+        x = o.converter('one,three')
+        assert str(x) == 'one,three', x
+        x = o.converter('three,one')
+        assert str(x) == 'one,three', x
+
+        x = o.converter('two,three')
+        assert str(x) == 'three,two', x
+        x = o.converter('three,two')
+        assert str(x) == 'three,two', x
+
+        x = o.converter('one,two,three')
+        assert str(x) == 'all', x
+
+        x = o.converter('three,two,one')
+        assert str(x) == 'all', x
+
+        caught = None
+        try:
+            x = o.converter('no_match')
+        except ValueError:
+            caught = 1
+        assert caught, "did not catch expected ValueError"
+
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(ListOptionTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)
similarity index 93%
rename from src/engine/SCons/OptionsTests.py
rename to src/engine/SCons/Options/OptionsTests.py
index 87fbc9af7b2067e88e559e5e1e99f197c19b2c50..ab931e29a42384362cdf51746bef6363e6dae68d 100644 (file)
@@ -60,6 +60,7 @@ def checkSave(file, expected):
     assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict)
 
 class OptionsTestCase(unittest.TestCase):
+
     def test_Add(self):
         """Test adding to an Options object"""
         opts = SCons.Options.Options()
@@ -118,6 +119,30 @@ class OptionsTestCase(unittest.TestCase):
         o = opts.options[2]
         o.validator(o.key, o.converter(o.default), {})
 
+    def test_AddOptions(self):
+        """Test adding a list of options to an Options object"""
+        opts = SCons.Options.Options()
+
+        opts.AddOptions(('VAR2',),
+                        ('ANSWER2',
+                         'THE answer to THE question',
+                         "42",
+                         check,
+                         lambda x: int(x) + 12))
+
+        o = opts.options[0]
+        assert o.key == 'VAR2', o.key
+        assert o.help == '', o.help
+        assert o.default == None, o.default
+        assert o.validator == None, o.validator
+        assert o.converter == None, o.converter
+
+        o = opts.options[1]
+        assert o.key == 'ANSWER2', o.key
+        assert o.help == 'THE answer to THE question', o.help
+        assert o.default == "42", o.default
+        o.validator(o.key, o.converter(o.default), {})
+
     def test_Update(self):
         """Test updating an Environment"""
 
diff --git a/src/engine/SCons/Options/PackageOption.py b/src/engine/SCons/Options/PackageOption.py
new file mode 100644 (file)
index 0000000..5940974
--- /dev/null
@@ -0,0 +1,106 @@
+"""engine.SCons.Options
+
+This file defines the option type for SCons implementing 'package
+activation'.
+
+To be used whenever a 'package' may be enabled/disabled and the
+package path may be specified.
+
+Usage example:
+
+  Examples:
+      x11=no   (disables X11 support)
+      x11=yes  (will search for the package installation dir)
+      x11=/usr/local/X11 (will check this path for existance)
+
+  To replace autoconf's --with-xxx=yyy 
+
+  opts = Options()
+  opts.Add(PackageOption('x11',
+                         'use X11 installed here (yes = search some places',
+                         'yes'))
+  ...
+  if env['x11'] == True:
+      dir = ... search X11 in some standard places ...
+      env['x11'] = dir 
+  if env['x11']:
+      ... build with x11 ...
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+__all__ = ('PackageOption', 'True', 'False')
+
+import string
+
+from BoolOption import True, False
+import SCons.Errors
+
+__enable_strings  = ('yes', 'true',  'on', 'enable', 'search')
+__disable_strings = ('no',  'false', 'off', 'disable')
+
+def _converter(val):
+    """
+    """
+    lval = string.lower(val)
+    if lval in __enable_strings: return True
+    if lval in __disable_strings: return False
+    #raise ValueError("Invalid value for boolean option: %s" % val)
+    return val
+
+
+def _validator(key, val, env, searchfunc):
+    # NB: searchfunc is currenty undocumented and unsupported
+    """
+    """
+    # todo: write validator, check for path
+    import os
+    if env[key] == False:
+        pass
+    elif env[key] == True:
+        if searchfunc:
+            env[key] = searchfunc(key, val)
+    elif not os.path.exists(val):
+        raise SCons.Errors.UserError(
+            'Path does not exist for option %s: %s' % (key, val))
+
+
+def PackageOption(key, help, default, searchfunc=None):
+    # NB: searchfunc is currenty undocumented and unsupported
+    """
+    The input parameters describe a 'package list' option, thus they
+    are returned with the correct converter and validator appended. The
+    result is usable for input to opts.Add() .
+
+    A 'package list' option may either be 'all', 'none' or a list of
+    package names (seperated by space).
+    """
+    help = string.join(
+        (help, '( yes | no | /path/to/%s )' % key),
+        '\n    ')
+    return (key, help, default,
+            lambda k, v, e, f=searchfunc: _validator(k,v,e,f),
+            _converter)
diff --git a/src/engine/SCons/Options/PackageOptionTests.py b/src/engine/SCons/Options/PackageOptionTests.py
new file mode 100644 (file)
index 0000000..d9fbe7b
--- /dev/null
@@ -0,0 +1,109 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Options
+
+import TestCmd
+
+class PackageOptionTestCase(unittest.TestCase):
+    def test_PackageOption(self):
+        """Test PackageOption creation"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path'))
+
+        o = opts.options[0]
+        assert o.key == 'test', o.key
+        assert o.help == 'test option help\n    ( yes | no | /path/to/test )', repr(o.help)
+        assert o.default == '/default/path', o.default
+        assert not o.validator is None, o.validator
+        assert not o.converter is None, o.converter
+
+    def test_converter(self):
+        """Test the PackageOption converter"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path'))
+
+        o = opts.options[0]
+
+        true_values = [
+                'yes',    'YES',
+                'true',   'TRUE',
+                'on',     'ON',
+                'enable', 'ENABLE',
+                'search', 'SEARCH',
+        ]
+        false_values = [
+                'no',      'NO',
+                'false',   'FALSE',
+                'off',     'OFF',
+                'disable', 'DISABLE',
+        ]
+
+        for t in true_values:
+            x = o.converter(t)
+            assert x, "converter returned false for '%s'" % t
+
+        for f in false_values:
+            x = o.converter(f)
+            assert not x, "converter returned true for '%s'" % f
+
+        x = o.converter('/explicit/path')
+        assert x == '/explicit/path', x
+
+    def test_validator(self):
+        """Test the PackageOption validator"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.PackageOption('test', 'test option help', '/default/path'))
+
+        test = TestCmd.TestCmd(workdir='')
+        test.write('exists', 'exists\n')
+
+        o = opts.options[0]
+
+        env = {'F':0, 'T':1, 'X':'x'}
+
+        exists = test.workpath('exists')
+        does_not_exist = test.workpath('does_not_exist')
+
+        o.validator('F', '/path', env)
+        o.validator('T', '/path', env)
+        o.validator('X', exists, env)
+
+        caught = None
+        try:
+            o.validator('X', does_not_exist, env)
+        except SCons.Errors.UserError:
+            caught = 1
+        assert caught, "did not catch expected UserError"
+
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(PackageOptionTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)
diff --git a/src/engine/SCons/Options/PathOption.py b/src/engine/SCons/Options/PathOption.py
new file mode 100644 (file)
index 0000000..cea4750
--- /dev/null
@@ -0,0 +1,85 @@
+"""engine.SCons.Options.PathOption
+
+This file defines an option type for SCons implementing 'package
+activation'.
+
+To be used whenever a 'package' may be enabled/disabled and the
+package path may be specified.
+
+Usage example:
+
+  Examples:
+      x11=no   (disables X11 support)
+      x11=yes  (will search for the package installation dir)
+      x11=/usr/local/X11 (will check this path for existance)
+
+  To replace autoconf's --with-xxx=yyy 
+
+  opts = Options()
+
+  opts = Options()
+  opts.Add(PathOption('qtdir',
+                      'where the root of Qt is installed',
+                      qtdir))
+  opts.Add(PathOption('qt_includes',
+                      'where the Qt includes are installed',
+                      '$qtdir/includes'))
+  opts.Add(PathOption('qt_libraries',
+                      'where the Qt library is installed',
+                      '$qtdir/lib'))
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+__all__ = ('PathOption',)
+
+import os
+
+import SCons.Errors
+
+def _validator(key, val, env):
+    """
+    """
+    # todo: write validator, check for path
+    if not os.path.exists(val):
+        raise SCons.Errors.UserError(
+            'Path does not exist for option %s: %s' % (key, val))
+
+
+def PathOption(key, help, default):
+    # NB: searchfunc is currenty undocumented and unsupported
+    """
+    The input parameters describe a 'path list' option, thus they
+    are returned with the correct converter and validator appended. The
+    result is usable for input to opts.Add() .
+
+    A 'package list' option may either be 'all', 'none' or a list of
+    package names (seperated by space).
+    """
+    return (key, '%s ( /path/to/%s )' % (help, key), default,
+            _validator, None)
+
diff --git a/src/engine/SCons/Options/PathOptionTests.py b/src/engine/SCons/Options/PathOptionTests.py
new file mode 100644 (file)
index 0000000..358f8e7
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import unittest
+
+import SCons.Errors
+import SCons.Options
+
+import TestCmd
+
+class PathOptionTestCase(unittest.TestCase):
+    def test_PathOption(self):
+        """Test PathOption creation"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.PathOption('test', 'test option help', '/default/path'))
+
+        o = opts.options[0]
+        assert o.key == 'test', o.key
+        assert o.help == 'test option help ( /path/to/test )', repr(o.help)
+        assert o.default == '/default/path', o.default
+        assert not o.validator is None, o.validator
+        assert o.converter is None, o.converter
+
+    def test_validator(self):
+        """Test the PathOption validator"""
+        opts = SCons.Options.Options()
+        opts.Add(SCons.Options.PathOption('test', 'test option help', '/default/path'))
+
+        test = TestCmd.TestCmd(workdir='')
+        test.write('exists', 'exists\n')
+
+        o = opts.options[0]
+
+        o.validator('X', test.workpath('exists'), {})
+
+        caught = None
+        try:
+            o.validator('X', test.workpath('does_not_exist'), {})
+        except SCons.Errors.UserError:
+            caught = 1
+        assert caught, "did not catch expected UserError"
+
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(PathOptionTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)
similarity index 84%
rename from src/engine/SCons/Options.py
rename to src/engine/SCons/Options/__init__.py
index f4d2bcc2946bceada7494626554013c2ddda79d6..6236102a1dae09192b05610e976773b05ff10c15 100644 (file)
@@ -1,7 +1,7 @@
 """engine.SCons.Options
 
-This file defines the Options class that is used to add user-friendly customizable
-variables to a scons build.
+This file defines the Options class that is used to add user-friendly
+customizable variables to an SCons build.
 """
 
 #
@@ -35,6 +35,12 @@ import SCons.Errors
 import SCons.Util
 import SCons.Warnings
 
+from BoolOption import BoolOption, True, False  # okay
+from EnumOption import EnumOption  # okay
+from ListOption import ListOption  # naja
+from PackageOption import PackageOption # naja
+from PathOption import PathOption # okay
+
 
 class Options:
     """
@@ -56,12 +62,25 @@ class Options:
         elif files:
            self.files = files
 
+    def _do_add(self, key, help="", default=None, validator=None, converter=None):
+        class Option:
+            pass
+
+        option = Option()
+        option.key = key
+        option.help = help
+        option.default = default
+        option.validator = validator
+        option.converter = converter
+
+        self.options.append(option)
+
 
     def Add(self, key, help="", default=None, validator=None, converter=None, **kw):
         """
         Add an option.
 
-        key - the name of the variable
+        key - the name of the variable, or a list or tuple of arguments
         help - optional help text for the options
         default - optional default value
         validator - optional function that is called to validate the option's value
@@ -70,8 +89,13 @@ class Options:
                     putting it in the environment.
         """
 
-        if not SCons.Util.is_valid_construction_var(key):
-            raise SCons.Errors.UserError, "Illegal Options.Add() key `%s'" % key
+        if SCons.Util.is_List(key) or type(key) == type(()):
+            apply(self._do_add, key)
+            return
+
+        if not SCons.Util.is_String(key) or \
+           not SCons.Util.is_valid_construction_var(key):
+            raise SCons.Errors.UserError, "Illegal Options.Add() key `%s'" % str(key)
 
         if kw.has_key('validater'):
             SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
@@ -80,17 +104,27 @@ class Options:
             if validator is None:
                 validator = kw['validater']
 
-        class Option:
-            pass
+        self._do_add(key, help, default, validator, converter)
 
-        option = Option()
-        option.key = key
-        option.help = help
-        option.default = default
-        option.validator = validator
-        option.converter = converter
 
-        self.options.append(option)
+    def AddOptions(self, *optlist):
+        """
+        Add a list of options.
+
+        Each list element is a tuple/list of arguments to be passed on
+        to the underlying method for adding options.
+        
+        Example:
+          opt.AddOptions(
+            ('debug', '', 0),
+            ('CC', 'The C compiler'),
+            ('VALIDATE', 'An option for testing validation', 'notset',
+             validator, None),
+            )
+        """
+        for o in optlist:
+            apply(self._do_add, o)
+
 
     def Update(self, env, args=None):
         """
index 924ddf37a03392f900a76a998b40554784fec29a..fff18694a0768c91ac36e30efd885e564664b9c6 100644 (file)
@@ -627,17 +627,22 @@ def BuildDefaultGlobals():
     globals = {}
     globals['Action']            = SCons.Action.Action
     globals['ARGUMENTS']         = arguments
+    globals['BoolOption']        = SCons.Options.BoolOption
     globals['Builder']           = SCons.Builder.Builder
     globals['Configure']         = SCons.SConf.SConf
+    globals['EnumOption']        = SCons.Options.EnumOption
     globals['Environment']       = SCons.Environment.Environment
+    globals['ListOption']        = SCons.Options.ListOption
     globals['Options']           = Options
+    globals['PackageOption']     = SCons.Options.PackageOption
+    globals['PathOption']        = SCons.Options.PathOption
     globals['Platform']          = SCons.Platform.Platform
     globals['Return']            = Return
     globals['Scanner']           = SCons.Scanner.Base
     globals['Tool']              = SCons.Tool.Tool
     globals['WhereIs']           = SCons.Util.WhereIs
 
-    # Functions we're in the process of converting to Environment methods.
+    # Functions we might still convert to Environment methods.
     globals['CScan']             = SCons.Defaults.CScan
     globals['DefaultEnvironment'] = SCons.Defaults.DefaultEnvironment
 
index be38a844c2848b555c446a26485c10a21f689a3c..be3171d208c2002a61b97210fb6527800881840f 100644 (file)
@@ -198,6 +198,7 @@ arguments = {
     'packages'         : ["SCons",
                           "SCons.Node",
                           "SCons.Optik",
+                          "SCons.Options",
                           "SCons.Platform",
                           "SCons.Scanner",
                           "SCons.Script",
diff --git a/test/OptionsTypes.py b/test/OptionsTypes.py
new file mode 100644 (file)
index 0000000..43cbfbe
--- /dev/null
@@ -0,0 +1,400 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import TestSCons, SCons.Errors
+import string, os
+
+test = TestSCons.TestSCons()
+
+def check(expect):
+    result = string.split(test.stdout(), '\n')
+    assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect)
+
+#### test BoolOption #####
+
+test.write('SConstruct', """
+from SCons.Options import BoolOption
+
+opts = Options(args=ARGUMENTS)
+opts.AddOptions(
+    BoolOption('warnings', 'compilation with -Wall and similiar', 1),
+    BoolOption('profile', 'create profiling informations', 0),
+    )
+
+env = Environment(options=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['warnings']
+print env['profile']
+
+Default(env.Alias('dummy', None))
+""")
+
+test.run()
+check(['1', '0'])
+
+test.run(arguments='warnings=0 profile=no profile=true')
+check(['0', '1'])
+
+test.run(arguments='warnings=irgendwas',
+         stderr = """
+scons: *** Error converting option: warnings
+Invalid value for boolean option: irgendwas
+File "SConstruct", line 10, in ?
+""", status=2)
+
+
+#### test EnumOption ####
+
+test.write('SConstruct', """
+from SCons.Options import EnumOption
+
+list_of_libs = Split('x11 gl qt ical')
+
+opts = Options(args=ARGUMENTS)
+opts.AddOptions(
+    EnumOption('debug', 'debug output and symbols', 'no',
+               allowed_values=('yes', 'no', 'full'),
+               map={}, ignorecase=0),  # case sensitive
+    EnumOption('guilib', 'gui lib to use', 'gtk',
+               allowed_values=('motif', 'gtk', 'kde'),
+               map={}, ignorecase=1), # case insensitive
+    EnumOption('some', 'some option', 'xaver',
+               allowed_values=('xaver', 'eins'),
+               map={}, ignorecase=2), # make lowercase
+    )
+
+env = Environment(options=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['debug']
+print env['guilib']
+print env['some']
+
+Default(env.Alias('dummy', None))
+""")
+
+
+test.run(); check(['no', 'gtk', 'xaver'])
+test.run(arguments='debug=yes guilib=Motif some=xAVER')
+check(['yes', 'Motif', 'xaver'])
+test.run(arguments='debug=full guilib=KdE some=EiNs')
+check(['full', 'KdE', 'eins'])
+
+test.run(arguments='debug=FULL',
+         stderr = """
+scons: *** Invalid value for option debug: FULL
+File "SConstruct", line 19, in ?
+""", status=2)
+
+test.run(arguments='guilib=IrGeNdwas',
+         stderr = """
+scons: *** Invalid value for option guilib: irgendwas
+File "SConstruct", line 19, in ?
+""", status=2)
+
+test.run(arguments='some=IrGeNdwas',
+         stderr = """
+scons: *** Invalid value for option some: irgendwas
+File "SConstruct", line 19, in ?
+""", status=2)
+         
+
+
+#### test ListOption ####
+
+test.write('SConstruct', """
+from SCons.Options import ListOption
+
+list_of_libs = Split('x11 gl qt ical')
+
+opts = Options(args=ARGUMENTS)
+opts.AddOptions(
+    ListOption('shared',
+               'libraries to build as shared libraries',
+               'all',
+               names = list_of_libs),
+    )
+
+env = Environment(options=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['shared']
+if 'ical' in env['shared']: print '1'
+else: print '0'
+for x in  env['shared']:
+    print x,
+print
+print env.subst('$shared')
+Default(env.Alias('dummy', None))
+""")
+
+test.run()
+check(['all', '1', 'gl ical qt x11', 'gl ical qt x11'])
+test.run(arguments='shared=none')
+check(['none', '0', '', ''])
+test.run(arguments='shared=')
+check(['none', '0', '', ''])
+test.run(arguments='shared=x11,ical')
+check(['ical,x11', '1', 'ical x11', 'ical x11'])
+test.run(arguments='shared=x11,,ical,,')
+check(['ical,x11', '1', 'ical x11', 'ical x11'])
+
+
+test.run(arguments='shared=foo',
+         stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo
+File "SConstruct", line 14, in ?
+""", status=2)
+
+# be paranoid in testing some more combinations
+
+test.run(arguments='shared=foo,ical',
+         stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo
+File "SConstruct", line 14, in ?
+""", status=2)
+
+test.run(arguments='shared=ical,foo',
+         stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo
+File "SConstruct", line 14, in ?
+""", status=2)
+
+test.run(arguments='shared=ical,foo,x11',
+         stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo
+File "SConstruct", line 14, in ?
+""", status=2)
+
+test.run(arguments='shared=foo,x11,,,bar',
+         stderr = """
+scons: *** Error converting option: shared
+Invalid value(s) for option: foo,bar
+File "SConstruct", line 14, in ?
+""", status=2)
+
+
+#### test PackageOption ####
+
+test.write('SConstruct', """
+from SCons.Options import PackageOption
+
+opts = Options(args=ARGUMENTS)
+opts.AddOptions(
+    PackageOption('x11',
+                  'use X11 installed here (yes = search some places',
+                  'yes'),
+    )
+
+env = Environment(options=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['x11']
+Default(env.Alias('dummy', None))
+""")
+
+test.run()
+check(['1'])
+test.run(arguments='x11=no'); check(['0'])
+test.run(arguments='"x11=%s"' % test.workpath()); check([test.workpath()])
+
+test.run(arguments='x11=0',
+         stderr = """
+scons: *** Path does not exist for option x11: 0
+File "SConstruct", line 11, in ?
+""", status=2)
+
+test.run(arguments='x11=/non/existing/path/',
+         stderr = """
+scons: *** Path does not exist for option x11: /non/existing/path/
+File "SConstruct", line 11, in ?
+""", status=2)
+         
+
+
+#### test PathOption ####
+
+test.subdir('lib', 'qt', ['qt', 'lib'], 'nolib' )
+workpath = test.workpath()
+libpath = os.path.join(workpath, 'lib')
+
+test.write('SConstruct', """
+from SCons.Options import PathOption
+
+qtdir = '%s'
+
+opts = Options(args=ARGUMENTS)
+opts.AddOptions(
+    PathOption('qtdir', 'where the root of Qt is installed', qtdir),
+    PathOption('qt_libraries', 'where the Qt library is installed', '%s'),
+    )
+
+env = Environment(options=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['qtdir']
+print env['qt_libraries']
+print env.subst('$qt_libraries')
+
+Default(env.Alias('dummy', None))
+""" % (workpath, os.path.join('$qtdir', 'lib') ))
+
+qtpath = workpath
+libpath = os.path.join(qtpath, 'lib')
+test.run()
+check([qtpath, os.path.join('$qtdir', 'lib'), libpath])
+
+qtpath = os.path.join(workpath, 'qt')
+libpath = os.path.join(qtpath, 'lib')
+test.run(arguments='"qtdir=%s"' % qtpath)
+check([qtpath, os.path.join('$qtdir', 'lib'), libpath])
+
+qtpath = workpath
+libpath = os.path.join(qtpath, 'nolib')
+test.run(arguments='"qt_libraries=%s"' % libpath)
+check([qtpath, libpath, libpath])
+
+qtpath = os.path.join(workpath, 'qt')
+libpath = os.path.join(workpath, 'nolib')
+test.run(arguments='"qtdir=%s" "qt_libraries=%s"' % (qtpath, libpath))
+check([qtpath, libpath, libpath])
+
+qtpath = os.path.join(workpath, 'non', 'existing', 'path')
+test.run(arguments='"qtdir=%s"' % qtpath,
+         stderr = """
+scons: *** Path does not exist for option qtdir: %s
+File "SConstruct", line 12, in ?
+""" % qtpath, status=2)
+
+test.run(arguments='"qt_libraries=%s"' % qtpath,
+         stderr = """
+scons: *** Path does not exist for option qt_libraries: %s
+File "SConstruct", line 12, in ?
+""" % qtpath, status=2)
+
+
+### test help messages ####
+
+workpath = test.workpath()
+qtpath  = os.path.join(workpath, 'qt')
+libpath = os.path.join(qtpath, 'lib')
+libdirvar = os.path.join('$qtdir', 'lib')
+         
+test.write('SConstruct', """
+from SCons.Options import BoolOption, EnumOption, ListOption, \
+   PackageOption, PathOption
+
+list_of_libs = Split('x11 gl qt ical')
+qtdir = '%(qtdir)s'
+
+opts = Options(args=ARGUMENTS)
+opts.AddOptions(
+    BoolOption('warnings', 'compilation with -Wall and similiar', 1),
+    BoolOption('profile', 'create profiling informations', 0),
+    EnumOption('debug', 'debug output and symbols', 'no',
+               allowed_values=('yes', 'no', 'full'),
+               map={}, ignorecase=0),  # case sensitive
+    EnumOption('guilib', 'gui lib to use', 'gtk',
+               allowed_values=('motif', 'gtk', 'kde'),
+               map={}, ignorecase=1), # case insensitive
+    EnumOption('some', 'some option', 'xaver',
+               allowed_values=('xaver', 'eins'),
+               map={}, ignorecase=2), # make lowercase
+    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),
+    PathOption('qt_libraries',
+               'where the Qt library is installed',
+               '%(libdirvar)s'),
+    )
+
+env = Environment(options=opts)
+Help(opts.GenerateHelpText(env))
+
+print env['warnings']
+print env['profile']
+
+Default(env.Alias('dummy', None))
+""" % {'qtdir': qtpath, 'libdirvar': libdirvar, 'libdir': libpath})
+
+
+test.run(arguments='-h',
+         stdout = """scons: Reading SConscript files ...
+scons: done reading SConscript files.
+
+warnings: compilation with -Wall and similiar (yes|no)
+    default: 1
+    actual: 1
+
+profile: create profiling informations (yes|no)
+    default: 0
+    actual: 0
+
+debug: debug output and symbols (yes|no|full)
+    default: no
+    actual: no
+
+guilib: gui lib to use (motif|gtk|kde)
+    default: gtk
+    actual: gtk
+
+some: some option (xaver|eins)
+    default: xaver
+    actual: xaver
+
+shared: libraries to build as shared libraries
+    (all|none|comma-separated list of names)
+    allowed names: x11 gl qt ical
+    default: all
+    actual: x11 gl qt ical
+
+x11: use X11 installed here (yes = search some places)
+    ( yes | no | /path/to/x11 )
+    default: yes
+    actual: 1
+
+qtdir: where the root of Qt is installed ( /path/to/qtdir )
+    default: %(qtdir)s
+    actual: %(qtdir)s
+
+qt_libraries: where the Qt library is installed ( /path/to/qt_libraries )
+    default: $qtdir/lib
+    actual: %(libdir)s
+
+Use scons -H for help about command-line options.
+""" % {'qtdir': qtpath, 'libdirvar': libdirvar, 'libdir': libpath})
+
+
+test.pass_test()