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. 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 -Q
Multiple &ConsEnvs; The real advantage of construction environments become apparent when you realize 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') int main() { } int main() { } scons -Q 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 -Q 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) int main() { } 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
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 which sets &CCFLAGS; for debugging: env = Environment(CC = 'gcc') opt = env.Copy(CCFLAGS = '-O2') dbg = env.Copy(CCFLAGS = '-g') env.Program('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 -Q
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 -Q 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 in ['OBJSUFFIX', 'LIBSUFFIX', 'PROGSUFFIX']: print "key = %s, value = %s" % (key, dict[key]) This &SConstruct; file will print the specified dictionary items for us on POSIX systems as follows: scons -Q And on Win32: scons -Q
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 -Q
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 -Q
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 -Q