Canned Build Options

SCons User Guide 0.98.0
Prev | Chapter 9. Controlling a Build From the Command Line | Next

# 9.9. Canned Build Options

SCons provides a number of functions that provide ready-made behaviors for various types of command-line build options.

## 9.9.1. True/False Values: the `BoolOption` Build Option

It's often handy to be able to specify an option that controls a simple Boolean variable with a `true` or `false` value. It would be even more handy to accomodate users who have different preferences for how to represent `true` or `false` values. The `BoolOption` function makes it easy to accomodate a variety of common values that represent `true` or `false`.

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>

## 9.9.2. Single Value From a List: the `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

## 9.9.3. Multiple Values From a List: the `ListOption` Build Option

Another way in which you might want to allow users to control build option is to specify a list of one or more legal values. SCons supports this through the `ListOption` function. If, for example, we want a user to be able to set a `COLORS` option to one or more of the legal list of values:

               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>

## 9.9.4. Path Names: the `PathOption` Build Option

SCons supports a `PathOption` function to make it easy to create a build option to control an expected path name. If, for example, you need to define a variable in the preprocessor that controls the location of a configuration file:

               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')

## 9.9.5. Enabled/Disabled Path Names: the `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

Prev | Home | Next
Reading Build Options From a File | Up | Adding Multiple Command-Line Build Options at Once