It is rare that all of the software in a large,
complicated system needs to be built the same way.
For example, different source files may need different options
enabled on the command line,
or different executable programs need to be linked
with different libraries.
SCons accommodates these different build
requirements by allowing you to create and
configure multiple construction environments
that control how the software is built.
Technically, a construction environment is an object
that has a number of associated
construction variables, each with a name and a value.
(A construction environment also has an attached
set of Builder
methods,
about which we'll learn more later.)
A construction environment is created by the Environment
method:
env = Environment()
By default, SCons initializes every new construction environment with a set of construction variables based on the tools that it finds on your system, plus the default set of builder methods necessary for using those tools. The construction variables are initialized with values describing the C compiler, the Fortran compiler, the linker, etc., as well as the command lines to invoke them.
When you initialize a construction environment you can set the values of the environment's construction variables to control how a program is built. For example:
env = Environment(CC = 'gcc', CCFLAGS = '-O2') env.Program('foo.c')
The construction environment in this example
is still initialized with the same default
construction variable values,
except that the user has explicitly specified use of the
GNU C compiler gcc,
and further specifies that the -O2
(optimization level two)
flag should be used when compiling the object file.
In other words, the explicit initializations of
$CC
and $CCFLAGS
override the default values in the newly-created
construction environment.
So a run from this example would look like:
% scons -Q gcc -o foo.o -c -O2 foo.c gcc -o foo foo.o
The real advantage of construction environments is that you can create as many different construction environments as you need, each tailored to a different way to build some piece of software or other file. If, for example, we need to build one program with the -O2 flag and another with the -g (debug) flag, we would do this like so:
opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') opt.Program('foo', 'foo.c') dbg.Program('bar', 'bar.c')
% scons -Q cc -o bar.o -c -g bar.c cc -o bar bar.o cc -o foo.o -c -O2 foo.c cc -o foo foo.o
We can even use multiple construction environments to build
multiple versions of a single program.
If you do this by simply trying to use the
Program
builder with both environments, though,
like this:
opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') opt.Program('foo', 'foo.c') dbg.Program('foo', 'foo.c')
Then SCons generates the following error:
% scons -Q scons: *** Two environments with different actions were specified for the same target: foo.o File "/home/my/project/SConstruct", line 6, in <module>
This is because the two Program
calls have
each implicitly told SCons to generate an object file named
foo.o,
one with a $CCFLAGS
value of
-O2
and one with a $CCFLAGS
value of
-g.
SCons can't just decide that one of them
should take precedence over the other,
so it generates the error.
To avoid this problem,
we must explicitly specify
that each environment compile
foo.c
to a separately-named object file
using the Object
builder, like so:
opt = Environment(CCFLAGS = '-O2') dbg = Environment(CCFLAGS = '-g') o = opt.Object('foo-opt', 'foo.c') opt.Program(o) d = dbg.Object('foo-dbg', 'foo.c') dbg.Program(d)
Notice that each call to the Object
builder
returns a value,
an internal SCons object that
represents the object file that will be built.
We then use that object
as input to the Program
builder.
This avoids having to specify explicitly
the object file name in multiple places,
and makes for a compact, readable
SConstruct file.
Our SCons output then looks like:
% scons -Q cc -o foo-dbg.o -c -g foo.c cc -o foo-dbg foo-dbg.o cc -o foo-opt.o -c -O2 foo.c cc -o foo-opt foo-opt.o