SCons Pages:

  1. SCons: An Introduction
  2. Making Builds with SCons
  3. SCons for Developers


On this page:

Also see:

SCons for Developers

Be sure to read SCons: An Introduction, to familiarize yourself
with the tagging convention, New Release Manager, SCons
Installer, External Libraries Directory Structure, etc.


  • At the time of this writing, the latest version of SCons
    installed at SLAC is version 2.1.0

SLAC Linux SCons Installation

Note: SCons is centrally maintained at SLAC. Several versions are installed in:


The command


will run the current production version.

With a typical set-up (e.g., running group.cshrc), you need not be concerned with the precise location since the scons command will be in your path.

Developer and Source Releases

Developer Releases provide all source and associated files required for rebuilding and debugging the code as necessary as well as all installed files created when the release is built.

Developer Releases may be obtained via one of the Installers (GUI or command-line) or from RMViewer. Pick the release and platform you're interested in and check the Download menu in the upper left.

Source releases when unpacked are identical to a cvs checkout of the corresponding tag. They also may be obtained from RMViewer or from one of the Installers.

Building Against a Release

It sometimes is convenient to build a small set of packages under development against binaries from an existing release — for example, one of the production builds in nfs space at SLAC, or your own local copy of a production build. In order to get this effect with SCons, we've added a custom option --supersede that takes as value the path to a root directory containing all of your development packages. A typical invocation on Linux, assuming you are in the diretory containing SConstruct, would look like this:

scons --with-GLAST-EXT=$GLAST_EXT --supersede=/scratch/mySupersede
      --compile-debug myPkg

Build products from such a command will be written to subdirectories (lib/variant-name, exe/variant-name, include, etc.) of the supersede directory. During compilation the supersede area will be searched first, then the base include area. Similarly, during links the supersede lib area will be searched first. It's a good idea — and in many cases absolutely necessary — to build private copies of any packages that depends on one of the packages you're developing. Otherwise, the code picked up in a link may not be what you expect.

NOTE: If you are building against an existing release which you do not own, such as an RM build in the SLAC file system, it might not be convenient or desirable to cd to that directory. In that case, use the SCons option -C theDir (long form --directory=theDir). SCons will then first cd to the directory you specify before doing anything else (but only while it's active; when SCons completes your working directory will be unchanged).

See also remarks concerning the use of a supersede path in the GoGui documentation.

SAS Use of SCons

Support for SCons involves several types of locally-written files. Two of these are specific to individual packages: SConscript and (where "xx" represents package name; e.g., All packages must have an SConscript file. The package maintainer should be familiar with it and capable of modifying it if, for example, build products for the package change. is only needed for packages which build libraries and only very rarely needs to be modified. These files are discussed in more detail in the following sections.

Remaining support files — e.g. SConstruct, allExternals.scons, containerSettings/externals.scons — are global to any individual package and are only occasionally of interest to package developers . Developers can freely modify their local copies but should not commit changes to CVS without contacting the package owner: SCons maintenance crew for SConstruct or anything else in the SConsFiles package; GlastRelease (ScienceTools) head honcho for files in GlastRelease-scons/containerSettings (respectively ScienceTools-scons/containerSettings).

Minimal SConscript File

The following example shows a minimal SConscript file for the imaginary package myPkg which will build nothing, but which will import some of the tools which may be necessary to build typical package targets. This file should be stored in the top level directory of the package.

Note SConscript files are Python code, following the usual Python syntax. In particular, # indicates that the rest of the text on the line is a comment. Such comments will be rendered in red in all examples to follow.

# -*- python -*-
# $Id: SConscript,v 1.2 2008/11/12 00:51:54 jrb Exp $
# Authors: Joanne Bogart: <>
# Version: myPkg-05-05-06
import platform     # may be needed if platform-dependent code follows
# Packages which build anything at all will need the Import lines
# and at least one of the lines following to clone an environment
progEnv = baseEnv.Clone()  
# Needed if the package builds any programs
libEnv = baseEnv.Clone()   # ..if the package builds a static or shared library
swigEnv = baseEnv.Clone()  # ..if the package builds a swig wrapper library

The first two lines are strongly recommended. The first identifies the file as Python; the second embeds current CVS versioning information in the file.

WARNING! The next two lines (author and versioning lines) are REQUIRED. You will not be able to tag your package without them. The text following Version: must be a legal tag string for the package (package name followed by 3 sets of 2 decimal digits identifying major, minor and patch number with adjacent fields separated by hyphens). There may also be an optional fourth field consisting of two letters followed by two digits, e.g. gr03. This form should be used for tags along branches other than MAIN.

The two Import() calls import two SCons objects that have been defined at the top level; baseEnv is the basic build environment created by the top level, and includes all necessary options such as debug, optimized, third party library locations, etc.

WARNING! The base environment must NEVER be modified. Any changes to the base environment are applied to all packages, independent of the order in which they were processed. Instead, clone one or more additional environments from it, as in this example. See more on construction environments below.

listFiles is a function defined at the top level. This function may be used any time you wish to pass a list of files to be included for creating a shared library, static library, program, etc. It is imported in this example because most packages end up using it. As will be demonstrated in examples to follow, listFiles can do wildcard expansion.

Simple Static Library

After creating the SConscript file, targets to be compiled by SCons are created in a static library. The additional code needed to build this library (and assuming the package has no other targets) is shown below:

# Include next line only if myLib depends on other libraries
libEnv.Tool('', depsOnly=1)

myLib = libEnv.StaticLibrary('myLib', listFiles(['src/*.cxx']))
# progEnv below can be replaced by any other environment, e.g. libEnv,
# if there is no progEnv, but progEnv is typical.

progEnv.Tool('registerTargets', package = 'myPackage',
       staticLibraryCxts = [[myLib, libEnv]], includes = ['myPackage/myLib.h'])

The first line creates a static library object containing all the information necessary to build the library at a later point. Notice the libEnv variable that is used, creating one of the copies made of the base environment. This copy should NEVER be used for creating an application because applications need different link options (must link to the library) from those required for the library itself.

WARNING! If this rule is violated, it will cause errors in other packages that will be hard to track back down to this source.

The StaticLibrary function call takes two arguments. The first specifies the name that should be given to the library. This name should not include any prefixes/suffixes that are platform specific. SCons will take care of adding those automatically. On Unix-based systems, this example would create a library named libmyLib.a.

The second argument, is a list of files to be compiled into the library. In this case, it is specified that the files to be included are in the src directory, relative to the top directory of the package, and are named *.cxx (i.e., anything ending with .cxx). Should only a single file be needed for the compilation of that library, the listFiles invocation may be replaced with a single-element list, e.g. ['src/myLib.cxx']

The final line registers objects at the top level to be compiled when appropriate. (This is not a standard SCons tool; rather, it is a custom extension to SCons.) Note that the progEnv copy of the environment is typically used to register objects, even if the objects are libraries, and it is strongly recommended that you abide by this convention.

Arguments used by the Tool() call are as follows:

  • The first argument is the name of the tool to be called. In this context, this argument always has value 'registerTargets'.
  • The second argument is the name of the package.
  • The third argument is a list of two-item lists (each one a library objects and the environment with which it was created) which contains entries for each static library to be registered for this package. There are several other keyword arguments which must be used to register other kinds of library objects (shared, swig, etc.)

The name of the library object used (e.g., myLib in the above example) is the same as the one used to store the library object returned by libEnv.StaticLibrary() call on the previous line. The second item in the list is libEnv because that is the environment used in the StaticLibrary call.

  • The next argument is a list of include files to be registered.

These are ONLY the public include files necessary to use the libraries that are going to be registered. They can be specified with a literal list as in the example above, or by means of listFiles.

Note: Other arguments can also be specified to registerTargets; a complete list is provided in a later section.


Simple Shared Library

The code for a package with a simple shared library target (i.e., not a swig library, not a Gaudi component library, and so forth) would look something like this:

libEnv.Tool('addLinkDeps', package='myPackage', toBuild='shared')
mySharedLib = libEnv.SharedLibrary('mySharedLib',
                                   ['src/file1.cxx', 'src/file2.cxx'])
progEnv.Tool('registerTargets', package='myPackage',
             libraryCxts=[[mySharedLib, libEnv]],
             include=['myPackage/myLib.h', 'myPackage/file1.h'])

The first line, an invocation of the locally written tool addLinkDeps is required because dependencies for shared libraries are treated differently for different OSes. addLinkDeps will do the right thing for each OS. There is no counterpart in the static library example because for static libraries all OSes require the same behavior (which is to not add any dependencies on other libraries).

Note that the call libEnv.SharedLibrary( ) is used instead of the libEnv.StaticLibrary( ) call; however, the argument structure is identical.

In this example sources are listed explicitly rather than using listFiles

As before, this object must be registered to be included in any SCons builds. Since it's a different type of object, a different keyword (libraryCxts) is used, but the syntax is otherwise identical to the static library case.


Simple Application

Creating a simple application requires that we add a section similar to the following:

myApp = progEnv.Program('myProgram', ['src/myProgram.cxx'])

The progEnv copy of the base environment is used to create a program objection.

WARNING! For this task, never use the copy of the environment for creating libraries. If this rule is violated, it will cause errors in other packages that will be hard to track back to this source.

The arguments provided to the progEnv.Program() call are similar to those for libraries:

  • The first argument is the name of the executable, making sure to exclude any platform specific prefixes or suffixes.

Note: On Windows SCons will create a program called myProgram.exe.

  • The second argument is a list of source code files to compile the program. All three methods described in the previous two sections are valid here as well:
    • listFiles()
    • ['single file']
    • ['list', 'of', 'files'])

And, just as in the previous sections, the program object must be registered with the top level before it can be used. Assuming the static and shared libraries from before still exist and we want to add this program to the registration call, we would modify the registration line as follows:

progEnv.Tool('registerTargets', package = 'myPackage',
             libraryCxts = [[myLib, libEnv]],
             includes = ['myPackage/myLib.h', 'myPackage/file1.h'],
             binaryCxts = [[myApp, progEnv]])

The binaryCxts keyword takes a list of two-element lists [program-object, associated-environment], similar to libraryCxts.

Note: When creating test applications, register them with the testAppCxts keyword argument instead of binaryCxts.


Elements of a Typical SConscript

Now that we've seen several examples, it's time to step back and note common features of SConscript files.

Construction Environments

Each target (library or executable) is built in the context of a construction environment. The original environment, baseEnv, which has been created with appropriate global settings, is repeatedly cloned as needed within package SConscript files with lines like

libEnv = baseEnv.Clone()

A different copy of the base environment is needed whenever the target requires different settings (e.g. compiler options, linker options, collection of libraries included in the link) than other targets.

For example, in a package which builds a library and executables, the executables need to link to the library but of course the library doesn't link to itself. Therefore the executables cannot use the library copy (by convention called libEnv); They need their own copy, progEnv. If a particular executable requires different compiler settings or must link to an additional library not used by the others, it, too, will need a separate copy of baseEnv. Swig wrapper libraries need to link to the "regular" package library and require other special settings not appropriate for other targets, so they also need their own environment.

SConscript Template

SConscripts are composed of the following:

  1. Initial boilerplate as in the minimal SConscript example, including required author and versioning lines, Import lines, and at least one clone of the base environment.

  2. Definitions of one or more targets (e.g., libraries, executables). Typically this involves two phases:

    configuring the environment used to build a target or targets.

    If the same environment is used to build several targets (e.g., multiple applications) the environment configuration is only done once. Examples of configuration operations are:

    • calls to the addLinkDeps tool
    • calls to for some package xx
    • calls to the addLibrary tool to add a dependency on an external library
    • adding compile options

    Some of these are explained in more detail below.

    invoking a Builder

    Calls to Builders tend to have a similar form:

    theTarget = someEnv.builderName('fileToBuild', sources,..)

    where someEnv has been configured appropriately in the first phase. Note that 'fileToBuild' is usually not the complete file name of the object built and can even represent more than one file. For example, in the case of a shared library, if 'name' is specified, the resulting file will be called '' on Linux. On Windows, 'name.lib' and 'name.dll' will be built (and also name.dll.manifest for newer versions of Studio).

  3. Invocation of the tool registerTargets. This serves several functions:
    • creates install targets for items specified in its arguments. For example, include files are normally installed in a subdirectory of the top-level include directory named after the package. Libraries are installed under lib/variant-name (e.g., under lib/redhat4-i686-32bit-gcc34-Debug)
    • creates alias names and associates targets with them so that one can, e.g., conveniently build all targets defined in a particular package, or install all include files with a single SCons command.
    • creates targets for solution and project files (only for Windows with Visual Studio 2008).

    Note that each variable holding the result of a call to a builder (theTarget in the above generic example) should appear somewhere among the arguments to registerTargets in a two-element list where the other element is the environment used to build it. For example, if the package builds a shared library like this..

    myLib = libEnv.SharedLibrary('xyz', listFiles(['src/*.cxx'])) of the keyword arguments for registerTargets should be

    libraryCxts=[[myLib, libEnv]]

    See also the section below on Arguments to RegisterTargets.


OS Specific Conditions

To perform functions on certain platforms only, use regular python conditionals around the functions. For example, to define the TRAP_FPE macro only on Linux platforms, append:

if platform.system() == 'Linux':
       progEnv.Append(CPPDEFINES = 'TRAP_FPE')

The platform.system() call returns the name of the OS. In this case, we wish to know if we are running on a Linux platform and, if that is the case, we wish to add a -DTRAP_FPE to the gcc command line.

Note: The progEnv.Append() call is explained later. (See Compiler Options.)


Libraries that Depend on Other Libraries

SCons performs dependency computations at the source code level. It does not compute dependencies of various binary packages such as the dependency of library A on library B when compiling library A into application A. A package maintainer writing application A does not wish to know all dependencies of all libraries; he only needs to know the DIRECT dependencies of the application.

Similarly, the package maintainer of library A should not have to know all the dependencies of library B when creating library A; he only needs to know that library A depends on library B. SCons, by default, does not have this ability.

The package maintainer for A only has to know that application A depends on library A, but the owner also has to know that library A depends on library B, ... and so on, until all dependencies have been met.

Fortunately, SCons provides a "tool" that simplifies this problem so that only direct dependencies need be specified. When the package owner creates library A in the SConscript files, the package owner also creates an additional file to record the DIRECT dependencies of library A. This file must have a specific name called <package>, and it has to be located in the top level of the package, together with the SConscript file.

In our example, the owner of 'myPackage' has two libraries: myLib and mySharedLib. Assume that myLib DIRECTLY depends on some other library of some other package (called someOtherPackage) and myLib also depends on the external library xerces. The contents of would be as follows:

def generate(env, **kw):
     if not kw.get('depsOnly', 0):
         env.Tool('addLibrary', library = ['libA'])
     env.Tool('addLibrary', library = env['xercesLibs'])

def exists(env):
    return 1

Both of these python functions need to exist at all times. The second of these functions (exists()) is for features currently not used by us, so it should always be specified as shown. The first function is what creates the recursive computation of library dependencies. The first line in the if statement adds libA to the dependencies. This line is put inside an if statement that determines if it should be added or not, because the dependencies of libA need to be specified when libA is created.

However, when libA is being created, we can't specify that it should include libA. This would create a recursive dependency. As a result, when we want to build libA and we want to catch all the dependencies (not always the case; see following section), we call this function; but we also pass an additional argument setting depsOnly = 1, so that the recursive dependency isn't created.

The remaining lines — those of the form env.Tool(..) — add the dependencies of libA to the argument env so that any target linked with env will have these libraries (and all the libraries they depend upon) added to the link command.

Note: These must be only DIRECT dependencies to keep computation fast. Unnecessary listings will slow SCons down considerably.

How and When to Add Library Dependencies to an Environment

Any package myPkg building a library needs a file as described above to list its direct dependencies, but the circumstances under which a particular target needs those dependencies are not immediately obvious.

  • Link commands for program targets must always include the libraries they depend upon, hence should be invoked on their environments for each package xx building a library that the program target DIRECTLY depends upon.
  • Static library targets aren't created with a link command. Never invoke any xxLib tools for environments building static libraries.
  • Shared library targets are the most complicated case. Depending on operating system and kind of shared library target, libraries the target library depends upon may or may not be necessary in its link command. And under some circumstances, adding dependencies which are not necessary can create problems. It is not safe to always add dependencies.

Because of this complication, another tool, addLinkDeps has been written to encapsulate the information of whether or not should be invoked (and to invoke it if appropriate) when the shared library belonging to myPkg is built. addLinkDeps takes the following keyword arguments:

package name of tool to be invoked. E.g., package='aPkg' will cause to be invoked if appropriate for OS and target type.
Full toolname to be invoked, e.g. 'aPkgLib'. At least one of package, toolname must be specified. If both are supplied, toolname takes precedence.
Kind of target to be built. Recognized values are 'shared', 'component', 'swig', 'rootLib', 'static' and 'program'; defaults to 'program'. For toBuild='static' the tool returns immediately without doing anything. For toBuild='program' the specified tool will always be invoked. For the others, the tool may or may not be invoked.
has the same meaning as for, but is only respected if value of toBuild is 'swig' because in all other cases, the value which should be passed to the xxxLib tool is predetermined.

In case toBuild is 'swig', depsOnly should have value 0 if and only if the swig wrapper is built by the same package which builds the library it's wrapping. Default value is 0.

For a package which builds a "simple" shared library and a swig library wrapping it, lines similar to the following should be included in the package SConscript:

libEnv.Tool('addLinkDeps', package='myPackage', toBuild='shared')
myLib = libEnv.SharedLibrary('myPkgLib', listFiles('src/*.cxx'))

swigEnv.Tool('addLinkDeps', package='myPackage', toBuild='swig')
mySwigWrapper = swigEnv.SwigLibrary('_py_myPkgLib', 'src/py_myPkg.i')

# More lines defining apps, test programs, etc. Then register:
progEnv.Tool('registerTargets', package = 'myPackage',
             libraryCxts = [[myLib, libEnv]],
             swigCxts = [[mySwigWrapper, swigEnv]],
             includes = ... )

Details will vary depending on naming convention you choose to use for the libraries and the .i file

Currently there's no way to specify which library created by a single package we wish to use. We recommend that developers organize their packages such that each package builds at most one library, static or shared, that another target might need to link to. (Swig libraries don't count since they never appear in link commands.)


Application Dependence on Libraries

With the dependency tree generated by the previous section for libraries, package owners wishing to create dependencies on libraries for their applications need only list the DIRECT dependencies of their applications.

In our example, the owner of myPackage currently has one application myApp. Assuming this application depends on some library from myPackage as well as the external library ROOT DIRECTLY, he would add these lines to his SConscript file:

# Or, equivalent to the above,
# progEnv.Tool('addLinkDeps', package='myPackage', toBuild='program')

progEnv.Tool('addLibrary', library = env['ROOTLibs'])

Technically, the ordering of these two calls does not matter. However, it is strongly recommended that these calls be made prior to the call for generating the application. This will make it easier for a human to understand the code at a later point.

The first line will call the generate function from myPackageLib created as shown in the previous section. (The alternate version, invoking addLinkDeps, does precisely the same thing.) That function will add the library from that package to the dependencies of myApp, along with any other libraries that myPackage's library depends on.

The second call adds ROOT to the libraries that myApp depends on DIRECTLY. Should myApp not need ROOT directly but through some other package's library, it should not be included here. It is the responsibility of that package to add the ROOT dependency.


Arguments to RegisterTargets

Registration functionality is an extension of SCons created by us. The registration is done by a call to progEnv.Tool('registerTargets, 'mypackage', ...). There are a minimum of two arguments.

The first argument has to always be 'registerTarget', and the second argument always has to be the name of the package performing the function call. Additional arguments, all optional, can be any of the following:

List of two-element lists. Each interior list consists of static library and environment used to build it

Value is analogous to that for staticLibraryCxts except here the first element is a simple shared library (where "simple" means not a swig wrapper library, not a gaudi component, not a root library)

Value is analogous to that for staticLibraryCxts but for Gaudi component library targets.

Value is analogous to that for staticLibraryCxts but for swig wrapper library targets.

Value is analogous to that for staticLibraryCxts but for shared ROOT libraries.

Value is analogous to that for staticLibraryCxts but for static ROOT libraries.

Value is analogous to that for staticLibraryCxts but for test application targets.

Value is analogous to that for staticLibraryCxts but for Gaudi component library targets.

List of file paths for includes which will need to be installed for this package. Normally all are installed under $INST_DIR/include/<package>

installs includes under $INST_DIR/include/topInclude or just $INST_DIR/include if special value "*NONE*" is given. Defaults to value of package keyword argument.

List of file paths for data files to be installed under $INST_DIR/data/<package>

List of file paths for xml files to be installed

List of file paths for job options files to be installed under $INST_DIR/jo/<package> (applies only to GlastRelease)

List of file paths to be installed under $INST_DIR/pfiles (unlike the preceding, there is no <package> component to path)

List of file paths to be installed under $INST_DIR/python. There is no <package> component but module structure is respected.

List of python programs to be installed and wrapped with (unix) shell script. Designed specially for use by ASP.

Note: The complicated argument structure for registering libraries and executables, and the distinctions among all the different kinds of libraries are required in order to build project files on Windows.

Compiler Options

SCons provides some tools for specifying common compiler options in a compiler- and platform-independent manner; they should be used whenever possible and appropriate. Less common options may have to be specified in a compiler dependent way; therefore, prior to setting such an option, one should perform a check for which OS is being executed. (See OS Specific Conditions.)

Caution! In almost all cases these compiler options are lists that need to be appended to. If they are simply assigned new values, they will overwrite older options already defined. And..., in order to prevent repeating the same compile options several times, one should only append if the option does not exist. These requirements are fulfilled by using a single function call: AppendUnique().

For example, to add a unique preprocessor definition to the compiler when compiling an application you would do:

progEnv.AppendUnique(CPPFLAGS = ['TRAP_FPE')

This would add a -DTRAPF_FPE to the compiler options if one didn't already exist.

For a complete description of all SCons-provided compiler options, see


Common Platform-independent Compiler Options

The following compiler options appear frequently enough that SCons takes care of converting them to the format required by the target compiler, and it is recommended that you use them when possible:

Specification of C preprocessor definitions. SCons will take care of expressing the definition with syntax appropriate to the compiler and platform.
The list of directories that the C preprocessor will search for include directories. If you need to set this, code might not be organized properly. CPPPATH will already have include directories for all externals and for public headers belonging to other packages.
The list of directories that will be searched for libraries. You should never need to set this.
  • LIBS
A list of one or more libraries that will be linked with any executable programs created by this environment. You should never need to set this.

Less Common Platform-dependent Compiler Options

The following compiler options need to be specified differently, depending on which compiler is being used, and should therefore be wrapped around if statements:

General options passed to the static library archiver. You should never need to set this.
General options that are passed to the C and C++ compilers.
General options that are passed to the C compiler (C only; not C++).
General options that are passed to the C++ compiler. By default, this includes the value of CCFLAGS, so that setting CCFLAGS affects both C and C++ compilation.

External Libraries

Note: SCons uses a different directory structure than that used by CMT; in particular for SCons externals there is no separate top-level directory to distinguish between Debug and Opt libraries as there is for CMT. Therefore the CMT external library structure cannot be used with SCons (and vice versa). (For the curious: for SCons externals, when both Debug and Opt versions exist, the version designation for Debug has the suffix -dbg; e.g. for Gaudi on some platforms there is a v21r7-gl1 version and also v21r7-gl1-dbg.)

All the directories containing external libraries are automatically added to the library path of the compiler. Each package only needs to add the library names it will need.

For example, to add CLHEP to list of libraries linked to, you would use:

progEnv.Tool('addLibrary', library = progEnv['clhepLibs'])

Note: An example of external libraries similar to those listed below is used in Libraries that Depend on Other Libraries. The same names for accessing the external libraries should be used in that section as the ones defined below.

Each external library and the libraries required for linking against it are stored in a variable like the one shown above for clhepLibs. Currently these are the external libraries available to be linked against. The progEnv['clhepLibs'] should be replaced with the name of the libraries you wish to add.

Note: Adding a library is only rarely necessary. Don't do it unless you know why you need it.

ROOTrootLibs. Also rootGuiLibs or minuitLibs if needed.
fswdfiLibs or fswDecipherLibs or fswMOOTLibs

Also see: External Libraries: Directory Structure.

Owned by: Joanne Bogart and Heather Kelly

Last updated by: Joanne Bogart 05/25/2012