It's often useful to keep any built files completely
separate from the source files.
In &SCons;, this is usually done by creating one or more separate
variant directory trees
that are used to hold the built objects files, libraries,
and executable programs, etc.
for a specific flavor, or variant, of build.
&SCons; provides two ways to do this,
one through the &SConscript; function that we've already seen,
and the second through a more flexible &VariantDir; function.
One historical note: the &VariantDir; function
used to be called &BuildDir;.
That name is still supported
but has been deprecated
because the &SCons; functionality
differs from the model of a "build directory"
implemented by other build systems like the GNU Autotools.
Specifying a Variant Directory Tree as Part of an &SConscript; Call
The most straightforward way to establish a variant directory tree
uses the fact that the usual way to
set up a build hierarchy is to have an
&SConscript; file in the source subdirectory.
If you then pass a &variant_dir; argument to the
&SConscript; function call:
SConscript('src/SConscript', variant_dir='build')
env = Environment()
env.Program('hello.c')
int main() { printf("Hello, world!\n"); }
&SCons; will then build all of the files in
the &build; subdirectory:
ls src
scons -Q
ls build
But wait a minute--what's going on here?
&SCons; created the object file
build/hello.o
in the &build; subdirectory,
as expected.
But even though our &hello_c; file lives in the &src; subdirectory,
&SCons; has actually compiled a
build/hello.c file
to create the object file.
What's happened is that &SCons; has duplicated
the &hello_c; file from the &src; subdirectory
to the &build; subdirectory,
and built the program from there.
The next section explains why &SCons; does this.
Why &SCons; Duplicates Source Files in a Variant Directory Tree
&SCons; duplicates source files in variant directory trees
because it's the most straightforward way to guarantee a correct build
regardless of include-file directory paths,
relative references between files,
or tool support for putting files in different locations,
and the &SCons; philosophy is to, by default,
guarantee a correct build in all cases.
The most direct reason to duplicate source files
in variant directories
is simply that some tools (mostly older vesions)
are written to only build their output files
in the same directory as the source files.
In this case, the choices are either
to build the output file in the source directory
and move it to the variant directory,
or to duplicate the source files in the variant directory.
Additionally,
relative references between files
can cause problems if we don't
just duplicate the hierarchy of source files
in the variant directory.
You can see this at work in
use of the C preprocessor #include
mechanism with double quotes, not angle brackets:
#include "file.h"
The de facto standard behavior
for most C compilers in this case
is to first look in the same directory
as the source file that contains the #include line,
then to look in the directories in the preprocessor search path.
Add to this that the &SCons; implementation of
support for code repositories
(described below)
means not all of the files
will be found in the same directory hierarchy,
and the simplest way to make sure
that the right include file is found
is to duplicate the source files into the variant directory,
which provides a correct build
regardless of the original location(s) of the source files.
Although source-file duplication guarantees a correct build
even in these end-cases,
it can usually be safely disabled.
The next section describes
how you can disable the duplication of source files
in the variant directory.
Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree
In most cases and with most tool sets,
&SCons; can place its target files in a build subdirectory
without
duplicating the source files
and everything will work just fine.
You can disable the default &SCons; behavior
by specifying duplicate=0
when you call the &SConscript; function:
SConscript('src/SConscript', variant_dir='build', duplicate=0)
When this flag is specified,
&SCons; uses the variant directory
like most people expect--that is,
the output files are placed in the variant directory
while the source files stay in the source directory:
% ls src
SConscript
hello.c
% scons -Q
cc -c src/hello.c -o build/hello.o
cc -o build/hello build/hello.o
% ls build
hello
hello.o
The &VariantDir; Function
Use the &VariantDir; function to establish that target
files should be built in a separate directory
from the source files:
VariantDir('build', 'src')
env = Environment()
env.Program('build/hello.c')
int main() { printf("Hello, world!\n"); }
Note that when you're not using
an &SConscript; file in the &src; subdirectory,
you must actually specify that
the program must be built from
the build/hello.c
file that &SCons; will duplicate in the
&build; subdirectory.
When using the &VariantDir; function directly,
&SCons; still duplicates the source files
in the variant directory by default:
ls src
scons -Q
ls build
You can specify the same duplicate=0 argument
that you can specify for an &SConscript; call:
VariantDir('build', 'src', duplicate=0)
env = Environment()
env.Program('build/hello.c')
int main() { printf("Hello, world!\n"); }
In which case &SCons;
will disable duplication of the source files:
ls src
scons -Q
ls build
Using &VariantDir; With an &SConscript; File
Even when using the &VariantDir; function,
it's much more natural to use it with
a subsidiary &SConscript; file.
For example, if the
src/SConscript
looks like this:
VariantDir('build', 'src')
SConscript('build/SConscript')
env = Environment()
env.Program('hello.c')
int main() { printf("Hello, world!\n"); }
Then our &SConstruct; file could look like:
Yielding the following output:
ls src
scons -Q
ls build
Notice that this is completely equivalent
to the use of &SConscript; that we
learned about in the previous section.
Using Glob() with &VariantDir;
The Glob() file name pattern matching function
works just as usual when using &VariantDir;.
For example, if the
src/SConscript
looks like this:
VariantDir('build', 'src')
SConscript('build/SConscript')
env = Environment()
env.Program('hello', Glob('*.c'))
#include "f2.h"
int main() { printf(f2()); }
const char * f2() { return("Hello, world!\n"); }
const char * f2();
Then with the same &SConstruct; file as in the previous section,
and source files f1.c and f2.c in src, we would see the following
output:
ls src
scons -Q
ls build
The Glob function returns Nodes in the build/ tree, as
you'd expect.