Introduction to SCons

SCons is a tool to build compile source code into (closer-to) executable format. It has broadly the same objectives to the commonly used Make program. This page highlights some features and peculiarities which that I have come across in designing a build system for CASA.

Two stages of operation

In most builds of moderate complexity, the building process can be divided into two stages:

  1. Creating a directed acyclic graph of dependencies in the build
  2. Executing commands (“actions” in SCons language) to build any components which the dependency graph indicates may have changed

This is important because in over-complex designs, determining the dependencies before the actual building stage can be difficult and this both impacts the performance of the build process overall and makes writing the prescription for building significantly harder.

An example is CCMTools (see for example Building CCMTools as required by CASA) which generates lots of different files in a single invocation, and the names of the files which are generated are not easily predictable. In this case dependency tracking can be implemented using a two-stage build process, where first pass is used to generate the dependency information.

Hierarchical builds

Communication of targets/sources

Within each SConscript file, normal Python variables can be used to keep track of targets created by each builder. This is very useful to create a clear and logical relationship of dependence in a large project. For example:

mylib=env.SharedLibrary("mylib", ["source1", "source2"])

the identity of the target created by the builder (in this a shared library) is stored in mylib variable and can be used to indicate dependencies.

The target identities stored in python variables can also be passed to subsidiary SConscript files by putting them into the environment and exporting them.

Useful command-line options

Review of some of the command options to SCons which may be useful

Why is a component being rebuilt?

Use the --debug=explain option.

Error, but themessage doesn’t have much information

Use the --debug=stacktrace option.

Parallel build

Use the “-j 8” (replace with appropriate number) option. Very significant speed-ups are possible.

Shared-library builder

Multiple sources with the same name

Curiously, the shared-library SCons builder will accept multiple identical sources and pass them all to the linker, which will normally lead to “multiple definition” errors. When this is a possibility, the list of sources should be filtered so each only appears once in the list.

Adjusting the name of the library

Libraries generated on Linux normally have a “lib” prefix and “.so” extension. The prefix can be adjusted by setting the SHLIBPREFIX variable when calling the builder.

Multi-action builders

Action order considered significant

The order in which a builder executes its actions is always considered significant, and any change in the order will lead to a an execution of the builder. This sometimes leads to undesirable results, for example a rebuild due to change in the order in which files are copied to a staging location even though that has no significance on the outcome builder.

I came across this problem when using the Scanner and generator for CCMTools binding builder, because the actions were created in the order the sources are passed to the generator, and as some of these are created from a Glob call their order is not deterministic. The solution is simply to alphabetically sort the sources before invoking the builder, e.g.:

idlfilel=_extFiles(source, "idl")
xmlfilel=su.uniquifyFiles(_extFiles(source, "xml"))
# Sort the sources so that there aren't false action changes
# triggering rebuild due to simple re-ordering of copy/delete
idlfilel.sort()
xmlfilel.sort()

This prevents unnecessary rebuilds