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 which you have already seen. What you haven't seen, though, is that 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')
 

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
    gcc -c -O2 foo.c -o foo.o
    gcc -o foo foo.o
 

Multiple Construction Environments

So far, all of our examples have created a single construction environment 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 construction environments, each with a separate variable name. We can then use these separate construction environments 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')
   

      % scons
      cc -c -O2 bar.c -o bar.o
      cc -o bar bar.o
      cc -c -g foo.c -o foo.o
      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
      scons: *** Two different environments 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. 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 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
      cc -c -g foo.c -o foo-dbg.o
      cc -o foo-dbg foo-dbg.o
      cc -c -O2 foo.c -o foo-opt.o
      cc -o foo-opt foo-opt.o