14.5. Sharing Environments (and Other Variables) Between SConscript Files

In the previous example, each of the subsidiary SConscript files created its own construction environment by calling Environment separately. This obviously works fine, but if each program must be built with the same construction variables, it's cumbersome and error-prone to initialize separate construction environments in the same way over and over in each subsidiary SConscript file.

SCons supports the ability to export variables from an SConscript file so they can be imported by other SConscript files, thus allowing you to share common initialized values throughout your build hierarchy.

14.5.1. Exporting Variables

There are two ways to export a variable from an SConscript file. The first way is to call the Export function. Export is pretty flexible - in the simplest form, you pass it a string that represents the name of the variable, and Export stores that with its value:

env = Environment()

You may export more than one variable name at a time:

env = Environment()
debug = ARGUMENTS['debug']
Export('env', 'debug')

Because a Python identifier cannot contain spaces, Export assumes a string containing spaces is is a shortcut for multiple variable names to export and splits it up for you:

env = Environment()
debug = ARGUMENTS['debug']
Export('env debug')

You can also pass Export a dictionary of values. This form allows the opportunity to export a variable from the current scope under a different name - in this example, the value of foo is exported under the name "bar":

env = Environment()
foo = "FOO"
args = {"env": env, "bar": foo}

Export will also accept arguments in keyword style. This form adds the ability to create exported variables that have not actually been set locally in the SConscript file. When used this way, the key is the intended variable name, not a string representation as with the other forms:

Export(MODE="DEBUG", TARGET="arm")

The styles can be mixed, though Python function calling syntax requires all non-keyword arguments to precede any keyword arguments in the call.

The Export function adds the variables to a global location from which other SConscript files can import. Calls to Export are cumulative. When you call Export you are actually updating a Python dictionary, so it is fine to export a variable you have already exported, but when doing so, the previous value is lost.

The other way to export is you can specify a list of variables as a second argument to the SConscript function call:

SConscript('src/SConscript', 'env')

Or (preferably, for readability) using the exports keyword argument:

SConscript('src/SConscript', exports='env')

These calls export the specified variables to only the listed SConscript file(s). You may specify more than one SConscript file in a list:

SConscript(['src1/SConscript', 'src2/SConscript'], exports='env')

This is functionally equivalent to calling the SConscript function multiple times with the same exports argument, one per SConscript file.

14.5.2. Importing Variables

Once a variable has been exported from a calling SConscript file, it may be used in other SConscript files by calling the Import function:

env.Program('prog', ['prog.c'])

The Import call makes the previously defined env variable available to the SConscript file. Assuming env is a construction environment, after import it can be used to build programs, libraries, etc. The use case of passing around a construction environment is extremely common in larger scons builds.

Like the Export function, the Import function can be called with multiple variable names:

Import('env', 'debug')
env = env.Clone(DEBUG=debug)
env.Program('prog', ['prog.c'])

In this example, we pull in the common construction environment env, and use the value of the debug variable to make a modified copy by passing that to a Clone call.

The Import function will (like Export) split a string containing white-space into separate variable names:

Import('env debug')
env = env.Clone(DEBUG=debug)
env.Program('prog', ['prog.c'])

Import prefers a local definition to a global one, so that if there is a global export of foo, and the calling SConscript has exported foo to this SConscript, the import will find the foo exported to this SConscript.

Lastly, as a special case, you may import all of the variables that have been exported by supplying an asterisk to the Import function:

env = env.Clone(DEBUG=debug)
env.Program('prog', ['prog.c'])

If you're dealing with a lot of SConscript files, this can be a lot simpler than keeping arbitrary lists of imported variables up to date in each file.

14.5.3. Returning Values From an SConscript File

Sometimes, you would like to be able to use information from a subsidiary SConscript file in some way. For example, suppose that you want to create one library from object files built by several subsidiary SConscript files. You can do this by using the Return function to return values from the subsidiary SConscript files to the calling file. Like Import and Export, Return takes a string representation of the variable name, not the variable name itself.

If, for example, we have two subdirectories foo and bar that should each contribute an object file to a library, what we'd like to be able to do is collect the object files from the subsidiary SConscript calls like this:

env = Environment()
objs = []
for subdir in ['foo', 'bar']:
    o = SConscript('%s/SConscript' % subdir)
env.Library('prog', objs)

We can do this by using the Return function in the foo/SConscript file like this:

obj = env.Object('foo.c')

(The corresponding bar/SConscript file should be pretty obvious.) Then when we run SCons, the object files from the subsidiary subdirectories are all correctly archived in the desired library:

% scons -Q
cc -o bar/bar.o -c bar/bar.c
cc -o foo/foo.o -c foo/foo.c
ar rc libprog.a foo/foo.o bar/bar.o
ranlib libprog.a