summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bin/docrun16
-rw-r--r--bin/files7
-rw-r--r--doc/man/scons.1207
-rw-r--r--doc/scons.mod52
-rw-r--r--doc/user/MANIFEST2
-rw-r--r--doc/user/command-line.in1182
-rw-r--r--doc/user/command-line.sgml1138
-rw-r--r--doc/user/default.in216
-rw-r--r--doc/user/default.sgml209
-rw-r--r--doc/user/main.in8
-rw-r--r--doc/user/main.sgml8
-rw-r--r--doc/user/run.in41
-rw-r--r--doc/user/run.sgml40
-rw-r--r--src/CHANGES.txt6
-rw-r--r--src/engine/MANIFEST.in7
-rw-r--r--src/engine/SCons/Options/BoolOption.py88
-rw-r--r--src/engine/SCons/Options/BoolOptionTests.py121
-rw-r--r--src/engine/SCons/Options/EnumOption.py101
-rw-r--r--src/engine/SCons/Options/EnumOptionTests.py198
-rw-r--r--src/engine/SCons/Options/ListOption.py131
-rw-r--r--src/engine/SCons/Options/ListOptionTests.py101
-rw-r--r--src/engine/SCons/Options/OptionsTests.py (renamed from src/engine/SCons/OptionsTests.py)25
-rw-r--r--src/engine/SCons/Options/PackageOption.py106
-rw-r--r--src/engine/SCons/Options/PackageOptionTests.py109
-rw-r--r--src/engine/SCons/Options/PathOption.py85
-rw-r--r--src/engine/SCons/Options/PathOptionTests.py70
-rw-r--r--src/engine/SCons/Options/__init__.py (renamed from src/engine/SCons/Options.py)62
-rw-r--r--src/engine/SCons/Script/SConscript.py7
-rw-r--r--src/setup.py1
-rw-r--r--test/OptionsTypes.py400
30 files changed, 4208 insertions, 536 deletions
diff --git a/bin/docrun b/bin/docrun
new file mode 100644
index 0000000..57ad202
--- /dev/null
+++ b/bin/docrun
@@ -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
diff --git a/bin/files b/bin/files
index 574e17c..cc8bf10 100644
--- a/bin/files
+++ b/bin/files
@@ -10,7 +10,12 @@
./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
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index a378539..ff2864c 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -5500,6 +5500,27 @@ 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
.I env
@@ -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
diff --git a/doc/scons.mod b/doc/scons.mod
index 958444d..d9b955f 100644
--- a/doc/scons.mod
+++ b/doc/scons.mod
@@ -112,9 +112,12 @@
-->
+<!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>">
@@ -124,17 +127,23 @@
<!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>">
@@ -160,19 +169,39 @@
<!--
+ 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>">
@@ -186,10 +215,6 @@
-->
-<!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>">
@@ -201,7 +226,11 @@
-->
+<!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>">
@@ -210,6 +239,17 @@
<!--
+ Values of function and method arguments.
+
+-->
+
+<!ENTITY all "<literal>all</literal>">
+<!ENTITY none "<literal>none</literal>">
+
+
+
+<!--
+
Builder and Scanner objects.
-->
@@ -276,6 +316,9 @@
<!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.
@@ -285,6 +328,7 @@
<!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>">
diff --git a/doc/user/MANIFEST b/doc/user/MANIFEST
index 134a24b..337c8e0 100644
--- a/doc/user/MANIFEST
+++ b/doc/user/MANIFEST
@@ -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
index 0000000..09ee8ce
--- /dev/null
+++ b/doc/user/command-line.in
@@ -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
index 0000000..3377b69
--- /dev/null
+++ b/doc/user/command-line.sgml
@@ -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
index 2410da6..0000000
--- a/doc/user/default.in
+++ /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
index fa21bae..0000000
--- a/doc/user/default.sgml
+++ /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>
diff --git a/doc/user/main.in b/doc/user/main.in
index e0ae71d..4aa4d9c 100644
--- a/doc/user/main.in
+++ b/doc/user/main.in
@@ -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">
@@ -115,9 +115,9 @@
&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">
diff --git a/doc/user/main.sgml b/doc/user/main.sgml
index e0ae71d..4aa4d9c 100644
--- a/doc/user/main.sgml
+++ b/doc/user/main.sgml
@@ -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">
@@ -115,9 +115,9 @@
&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">
diff --git a/doc/user/run.in b/doc/user/run.in
index d8e19f8..0d3e7f0 100644
--- a/doc/user/run.in
+++ b/doc/user/run.in
@@ -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>
diff --git a/doc/user/run.sgml b/doc/user/run.sgml
index 9cdb07c..0d3e7f0 100644
--- a/doc/user/run.sgml
+++ b/doc/user/run.sgml
@@ -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>
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index c81e870..2905339 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -10,6 +10,12 @@
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.
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index 24e3d76..c5fe7a3 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -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
index 0000000..f38bf02
--- /dev/null
+++ b/src/engine/SCons/Options/BoolOption.py
@@ -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
index 0000000..845f251
--- /dev/null
+++ b/src/engine/SCons/Options/BoolOptionTests.py
@@ -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
index 0000000..d4e2ac1
--- /dev/null
+++ b/src/engine/SCons/Options/EnumOption.py
@@ -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
index 0000000..20ae6c3
--- /dev/null
+++ b/src/engine/SCons/Options/EnumOptionTests.py
@@ -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
index 0000000..20b7e99
--- /dev/null
+++ b/src/engine/SCons/Options/ListOption.py
@@ -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
index 0000000..2175720
--- /dev/null
+++ b/src/engine/SCons/Options/ListOptionTests.py
@@ -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)
diff --git a/src/engine/SCons/OptionsTests.py b/src/engine/SCons/Options/OptionsTests.py
index 87fbc9a..ab931e2 100644
--- a/src/engine/SCons/OptionsTests.py
+++ b/src/engine/SCons/Options/OptionsTests.py
@@ -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
index 0000000..5940974
--- /dev/null
+++ b/src/engine/SCons/Options/PackageOption.py
@@ -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
index 0000000..d9fbe7b
--- /dev/null
+++ b/src/engine/SCons/Options/PackageOptionTests.py
@@ -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
index 0000000..cea4750
--- /dev/null
+++ b/src/engine/SCons/Options/PathOption.py
@@ -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
index 0000000..358f8e7
--- /dev/null
+++ b/src/engine/SCons/Options/PathOptionTests.py
@@ -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)
diff --git a/src/engine/SCons/Options.py b/src/engine/SCons/Options/__init__.py
index f4d2bcc..6236102 100644
--- a/src/engine/SCons/Options.py
+++ b/src/engine/SCons/Options/__init__.py
@@ -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):
"""
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
index 924ddf3..fff1869 100644
--- a/src/engine/SCons/Script/SConscript.py
+++ b/src/engine/SCons/Script/SConscript.py
@@ -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
diff --git a/src/setup.py b/src/setup.py
index be38a84..be3171d 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -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
index 0000000..43cbfbe
--- /dev/null
+++ b/test/OptionsTypes.py
@@ -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()