Construction Environments
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 accomodates 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:
By default, SCons intializes 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 -O2 -c -o foo.o foo.c
gcc -o foo foo.o
|
Multiple Construction Environments
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 -g -c -o bar.o bar.c
cc -o bar bar.o
cc -O2 -c -o foo.o 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 "SConstruct", line 6, in ?
|
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 call, 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 -g -c -o foo-dbg.o foo.c
cc -o foo-dbg foo-dbg.o
cc -O2 -c -o foo-opt.o foo.c
cc -o foo-opt foo-opt.o
|