SCons provides a number of functions that provide ready-made behaviors for various types of command-line build options.
BoolOption
Build Option
It's often handy to be able to specify an
option that controls a simple Boolean variable
with a true or false value.
It would be even more handy to accomodate
users who have different preferences for how to represent
true or false values.
The BoolOption
function
makes it easy to accomodate a variety of
common values that represent
true or false.
The BoolOption
function takes three arguments:
the name of the build option,
the default value of the build option,
and the help string for the option.
It then returns appropriate information for
passing to the Add
method of an Options
object, like so:
opts = Options('custom.py') opts.Add(BoolOption('RELEASE', 'Set to build for release', 0)) env = Environment(options = opts, CPPDEFINES={'RELEASE_BUILD' : '${RELEASE}'}) env.Program('foo.c')
With this build option,
the RELEASE
variable can now be enabled by
setting it to the value yes
or t:
% scons -Q RELEASE=yes foo.o cc -o foo.o -c -DRELEASE_BUILD=True foo.c
% scons -Q RELEASE=t foo.o cc -o foo.o -c -DRELEASE_BUILD=True foo.c
Other values that equate to true include y, 1, on and all.
Conversely, RELEASE
may now be given a false
value by setting it to
no
or
f:
% scons -Q RELEASE=no foo.o cc -o foo.o -c -DRELEASE_BUILD=False foo.c
% scons -Q RELEASE=f foo.o cc -o foo.o -c -DRELEASE_BUILD=False foo.c
Other values that equate to false include n, 0, off and none.
Lastly, if a user tries to specify any other value, SCons supplies an appropriate error message:
% scons -Q RELEASE=bad_value foo.o scons: *** Error converting option: RELEASE Invalid value for boolean option: bad_value File "/home/my/project/SConstruct", line 4, in <module>
EnumOption
Build Option
Suppose that we want a user to be able to
set a COLOR
option
that selects a background color to be
displayed by an application,
but that we want to restrict the
choices to a specific set of allowed colors.
This can be set up quite easily
using the EnumOption
,
which takes a list of allowed_values
in addition to the variable name,
default value,
and help text arguments:
opts = Options('custom.py') opts.Add(EnumOption('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'))) env = Environment(options = opts, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c')
The user can now explicity set the COLOR
build option
to any of the specified allowed values:
% scons -Q COLOR=red foo.o cc -o foo.o -c -DCOLOR="red" foo.c % scons -Q COLOR=blue foo.o cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=green foo.o cc -o foo.o -c -DCOLOR="green" foo.c
But, almost more importantly,
an attempt to set COLOR
to a value that's not in the list
generates an error message:
% scons -Q COLOR=magenta foo.o scons: *** Invalid value for option COLOR: magenta File "/home/my/project/SConstruct", line 5, in <module>
The EnumOption
function also supports a way
to map alternate names to allowed values.
Suppose, for example,
that we want to allow the user
to use the word navy as a synonym for
blue.
We do this by adding a map
dictionary
that will map its key values
to the desired legal value:
opts = Options('custom.py') opts.Add(EnumOption('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'})) env = Environment(options = opts, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c')
As desired, the user can then use
navy on the command line,
and SCons will translate it into blue
when it comes time to use the COLOR
option to build a target:
% scons -Q COLOR=navy foo.o cc -o foo.o -c -DCOLOR="blue" foo.c
By default, when using the EnumOption
function,
arguments that differ
from the legal values
only in case
are treated as illegal values:
% scons -Q COLOR=Red foo.o scons: *** Invalid value for option COLOR: Red File "/home/my/project/SConstruct", line 5, in <module> % scons -Q COLOR=BLUE foo.o scons: *** Invalid value for option COLOR: BLUE File "/home/my/project/SConstruct", line 5, in <module> % scons -Q COLOR=nAvY foo.o scons: *** Invalid value for option COLOR: nAvY File "/home/my/project/SConstruct", line 5, in <module>
The EnumOption
function can take an additional
ignorecase
keyword argument that,
when set to 1,
tells SCons to allow case differences
when the values are specified:
opts = Options('custom.py') opts.Add(EnumOption('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'}, ignorecase=1)) env = Environment(options = opts, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c')
Which yields the output:
% scons -Q COLOR=Red foo.o cc -o foo.o -c -DCOLOR="Red" foo.c % scons -Q COLOR=BLUE foo.o cc -o foo.o -c -DCOLOR="BLUE" foo.c % scons -Q COLOR=nAvY foo.o cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=green foo.o cc -o foo.o -c -DCOLOR="green" foo.c
Notice that an ignorecase
value of 1
preserves the case-spelling that the user supplied.
If you want SCons to translate the names
into lower-case,
regardless of the case used by the user,
specify an ignorecase
value of 2:
opts = Options('custom.py') opts.Add(EnumOption('COLOR', 'Set background color', 'red', allowed_values=('red', 'green', 'blue'), map={'navy':'blue'}, ignorecase=2)) env = Environment(options = opts, CPPDEFINES={'COLOR' : '"${COLOR}"'}) env.Program('foo.c')
Now SCons will use values of red, green or blue regardless of how the user spells those values on the command line:
% scons -Q COLOR=Red foo.o cc -o foo.o -c -DCOLOR="red" foo.c % scons -Q COLOR=nAvY foo.o cc -o foo.o -c -DCOLOR="blue" foo.c % scons -Q COLOR=GREEN foo.o cc -o foo.o -c -DCOLOR="green" foo.c
ListOption
Build Option
Another way in which you might want to allow users
to control build option is to
specify a list of one or more legal values.
SCons supports this through the ListOption
function.
If, for example, we want a user to be able to set a
COLORS
option to one or more of the legal list of values:
opts = Options('custom.py') opts.Add(ListOption('COLORS', 'List of colors', 0, ['red', 'green', 'blue'])) env = Environment(options = opts, CPPDEFINES={'COLORS' : '"${COLORS}"'}) env.Program('foo.c')
A user can now specify a comma-separated list of legal values, which will get translated into a space-separated list for passing to the any build commands:
% scons -Q COLORS=red,blue foo.o cc -o foo.o -c -DCOLORS="red blue" foo.c % scons -Q COLORS=blue,green,red foo.o cc -o foo.o -c -DCOLORS="blue green red" foo.c
In addition, the ListOption
function
allows the user to specify explicit keywords of
all or none
to select all of the legal values,
or none of them, respectively:
% scons -Q COLORS=all foo.o cc -o foo.o -c -DCOLORS="red green blue" foo.c % scons -Q COLORS=none foo.o cc -o foo.o -c -DCOLORS="" foo.c
And, of course, an illegal value still generates an error message:
% scons -Q COLORS=magenta foo.o scons: *** Error converting option: COLORS Invalid value(s) for option: magenta File "/home/my/project/SConstruct", line 5, in <module>
PathOption
Build Option
SCons supports a PathOption
function
to make it easy to create a build option
to control an expected path name.
If, for example, you need to
define a variable in the preprocessor
that controls the location of a
configuration file:
opts = Options('custom.py') opts.Add(PathOption('CONFIG', 'Path to configuration file', '/etc/my_config')) env = Environment(options = opts, CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) env.Program('foo.c')
This then allows the user to
override the CONFIG
build option
on the command line as necessary:
% scons -Q foo.o cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c % scons -Q CONFIG=/usr/local/etc/other_config foo.o scons: `foo.o' is up to date.
By default, PathOption
checks to make sure
that the specified path exists and generates an error if it
doesn't:
% scons -Q CONFIG=/does/not/exist foo.o scons: *** Path for option CONFIG does not exist: /does/not/exist File "/home/my/project/SConstruct", line 6, in <module>
PathOption
provides a number of methods
that you can use to change this behavior.
If you want to ensure that any specified paths are,
in fact, files and not directories,
use the PathOption.PathIsFile
method:
opts = Options('custom.py') opts.Add(PathOption('CONFIG', 'Path to configuration file', '/etc/my_config', PathOption.PathIsFile)) env = Environment(options = opts, CPPDEFINES={'CONFIG_FILE' : '"$CONFIG"'}) env.Program('foo.c')
Conversely, to ensure that any specified paths are
directories and not files,
use the PathOption.PathIsDir
method:
opts = Options('custom.py') opts.Add(PathOption('DBDIR', 'Path to database directory', '/var/my_dbdir', PathOption.PathIsDir)) env = Environment(options = opts, CPPDEFINES={'DBDIR' : '"$DBDIR"'}) env.Program('foo.c')
If you want to make sure that any specified paths
are directories,
and you would like the directory created
if it doesn't already exist,
use the PathOption.PathIsDirCreate
method:
opts = Options('custom.py') opts.Add(PathOption('DBDIR', 'Path to database directory', '/var/my_dbdir', PathOption.PathIsDirCreate)) env = Environment(options = opts, CPPDEFINES={'DBDIR' : '"$DBDIR"'}) env.Program('foo.c')
Lastly, if you don't care whether the path exists,
is a file, or a directory,
use the PathOption.PathAccept
method
to accept any path that the user supplies:
opts = Options('custom.py') opts.Add(PathOption('OUTPUT', 'Path to output file or directory', None, PathOption.PathAccept)) env = Environment(options = opts, CPPDEFINES={'OUTPUT' : '"$OUTPUT"'}) env.Program('foo.c')
PackageOption
Build Option
Sometimes you want to give users
even more control over a path name variable,
allowing them to explicitly enable or
disable the path name
by using yes or no keywords,
in addition to allow them
to supply an explicit path name.
SCons supports the PackageOption
function to support this:
opts = Options('custom.py') opts.Add(PackageOption('PACKAGE', 'Location package', '/opt/location')) env = Environment(options = opts, CPPDEFINES={'PACKAGE' : '"$PACKAGE"'}) env.Program('foo.c')
When the SConscript file uses the PackageOption
funciton,
user can now still use the default
or supply an overriding path name,
but can now explicitly set the
specified variable to a value
that indicates the package should be enabled
(in which case the default should be used)
or disabled:
% scons -Q foo.o cc -o foo.o -c -DPACKAGE="/opt/location" foo.c % scons -Q PACKAGE=/usr/local/location foo.o cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c % scons -Q PACKAGE=yes foo.o cc -o foo.o -c -DPACKAGE="True" foo.c % scons -Q PACKAGE=no foo.o cc -o foo.o -c -DPACKAGE="False" foo.c