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 &consenvs;
that control how the software is built.
Technically, a &consenv; is an object
that has a number of associated
&consvars;, each with a name and a value.
(A &consenv; also has an attached
set of &Builder; methods,
about which we'll learn more later.)
A &consenv; is created by the &Environment;
method which you have already seen.
What you haven't seen, though,
is that when you initialize a &consenv;,
you can set the values of the
environment's &consvars;
to control how a program is built.
For example:
env = Environment(CC = 'gcc',
CCFLAGS = '-O2')
env.Program('foo.c')
int main() { }
This example, rather than using the default,
explicitly specifies 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.
So a run from this example would look like:
scons
Multiple &ConsEnvs;
So far,
all of our examples have
created a single &consenv; named
env.
env, however,
is simply a Python variable name,
and you can use any other variable name that you like.
For example:
my_env = Environment(CC = 'gcc',
CCFLAGS = '-O2')
my_env.Program('foo.c')
This opens up the possibility of
using multiple &consenvs;,
each with a separate variable name.
We can then use these separate &consenvs;
to build different programs in different ways:
opt = Environment(CCFLAGS = '-O2')
dbg = Environment(CCFLAGS = '-g')
opt.Program('foo', 'foo.c')
dbg.Program('bar', 'bar.c')
int main() { }
int main() { }
scons
We can even use multiple &consenvs; 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')
int main() { }
Then &SCons; generates the following error:
scons
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.
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)
int main() { }
Notice that each call to the &Object; builder
returns a value,
an internal &SCons; object that
represents the 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
Copying &ConsEnvs;
Sometimes you want more than one &consenv;
to share the same values for one or more variables.
Rather than always having to repeat all of the common
variables when you create each &consenv;,
you can use the &Copy; method
to create a copy of a &consenv;.
Like the &Environment; call that creates a &consenv;,
the &Copy; method takes &consvar; assignments,
which will override the values in the copied &consenv;.
For example, suppose we want to use &gcc;
to create three versions of a program,
one optimized, one debug, and one with neither.
We could do this by creating a "base" &consenv;
that sets &CC; to &gcc;,
and then creating two copies,
one which sets &CCFLAGS; for optimization
and the other with sets &CCFLAGS; for debugging:
env = Environment(CC = 'gcc')
opt = env.Copy(CCFLAGS = '-O2')
dbg = env.Copy(CCFLAGS = '-g')
e = opt.Object('foo', 'foo.c')
o = opt.Object('foo-opt', 'foo.c')
opt.Program(o)
d = dbg.Object('foo-dbg', 'foo.c')
dbg.Program(d)
int main() { }
Then our output would look like:
scons
Fetching Values From a &ConsEnv;
You can fetch individual construction variables
using the normal syntax
for accessing individual named items in a Python dictionary:
env = Environment()
print "CC is:", env['CC']
This example &SConstruct; file doesn't build anything,
but because it's actually a Python script,
it will print the value of &CC; for us:
scons
CC is: cc
A &consenv;, however,
is actually a Python object with
associated methods, etc.
If you want to have direct access to only the
dictionary of construction variables,
you can fetch this using the &Dictionary; method:
env = Environment(FOO = 'foo', BAR = 'bar')
dict = env.Dictionary()
for key, value in dict.items():
print "key = %s, value = %s % (key, value)
This &SConstruct; file
will print the dictionary items for us as follows:
% scons
key = FOO, value = foo
key = BAR, value = bar
Modifying a &ConsEnv;
&SCons; provides various methods that
support modifying existing values in a &consenv;.
Replacing Values in a &ConsEnv;
You can replace existing construction variable values
using the &Replace; method:
env = Environment(CCFLAGS = '-DDEFINE1')
env.Program('foo.c')
env.Replace(CCFLAGS = '-DDEFINE2')
env.Program('bar.c')
int main() { }
int main() { }
The replaced value completely overwrites
scons
Appending to the End of Values in a &ConsEnv;
You can append a value to
an existing construction variable
using the &Append; method:
env = Environment(CCFLAGS = '-DMY_VALUE')
env.Append(CCFLAGS = ' -DLAST')
env.Program('foo.c')
int main() { }
scons
Appending to the Beginning of Values in a &ConsEnv;
You can append a value to the beginning
an existing construction variable
using the &Prepend; method:
env = Environment(CCFLAGS = '-DMY_VALUE')
env.Prepend(CCFLAGS = '-DFIRST ')
env.Program('foo.c')
int main() { }
scons