%scons; %builders-mod; %functions-mod; %tools-mod; %variables-mod; ]> Separating Source and Build Trees: Variant Directories It's often useful to keep any built files completely separate from the source files. Consider if you have a project to build software for a variety of different controller hardware. The boards are able to share a lot of code, so it makes sense to keep them in the same source tree, but certain build options in the source code and header files differ. If you build "Controller A" first, then "Controller B", on the "Controller B" build everything would have to be rebuilt, because &SCons; recognizes that the build instructions are different from those used in the "Controller A" build for each target - the build instructions are part of &SCons;'s out-of-date calculation. Now when you go back and build for "Controller A", things have to be rebuilt from scratch again for the same reason. However, if you can separate the locations of the output files, this problem can be avoided. You can even set up to do both builds in one invocation of &SCons;. You can enable this separation by establishing one or more variant directory trees that are used to perform the build in, and thus provide a unique home for object files, libraries, and executable programs, etc. for a specific flavor, or variant, of build. &SCons; tracks targets by their path, so when the variant directory is included, objects belonging to "Controller A" can have different build instructions than those belonging to "Controller B" without triggering ping-ponging rebuilds. &SCons; provides two ways to do this, one through the &f-link-SConscript; function that we've already seen, and the second through a more flexible &f-link-VariantDir; function. Historical note: the &VariantDir; function used to be called &BuildDir;, a name which was removed because the &SCons; functionality differs from a familiar model of a "build directory" implemented by other build systems like GNU Autotools. You might still find references to the old name on the Internet in postings about &SCons;, but it no longer works.
Specifying a Variant Directory Tree as Part of an &SConscript; Call The most straightforward way to establish a variant directory tree relies the fact that the usual way to set up a build hierarchy is to have an SConscript file in the source subdirectory. If you pass a &variant_dir; argument to the &f-link-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 src ls build No files were built in &src;, they went to &build;. The build output might show a bit of a surprise: the object file build/hello.o and the executable file build/hello were built 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, and that file is now seen in &build;. 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 (it also duplicated &SConscript;). The next section explains why &SCons; does this.
Why &SCons; Duplicates Source Files in a Variant Directory Tree The important thing to understand is that when you set up a variant directory, &SCons; performs the build in that directory. It turns out it's easiest to ensure where build products end up by just building in place. Since the build is happening in a place different from where the sources are, the most straightforward way to guarantee a correct build is for &SCons; to copy them there. The most direct reason to duplicate source files in variant directories is simply that some tools (mostly older versions) 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=False when you call the &SConscript; function: SConscript('src/SConscript', variant_dir='build', duplicate=False) 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=False argument that you can specify for an &SConscript; call: VariantDir('build', 'src', duplicate=False) 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 more natural to use it with a subsidiary &SConscript; file, because then you don't have to adjust your individual build instructions to use the variant directory path. 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 &f-link-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.