diff options
Diffstat (limited to 'doc/user/hierarchy.xml')
-rw-r--r-- | doc/user/hierarchy.xml | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/doc/user/hierarchy.xml b/doc/user/hierarchy.xml new file mode 100644 index 0000000..3495c5f --- /dev/null +++ b/doc/user/hierarchy.xml @@ -0,0 +1,746 @@ +<!-- + + Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + + +=head2 The Build command + +By default, Cons does not change its working directory to the directory +containing a subsidiary F<Conscript> file it is including. This behavior +can be enabled for a build by specifying, in the top-level F<Construct> +file: + + Conscript_chdir 1; + +When enabled, Cons will change to the subsidiary F<Conscript> file's +containing directory while reading in that file, and then change back +to the top-level directory once the file has been processed. + +It is expected that this behavior will become the default in some future +version of Cons. To prepare for this transition, builds that expect +Cons to remain at the top of the build while it reads in a subsidiary +F<Conscript> file should explicitly disable this feature as follows: + + Conscript_chdir 0; + +=head2 Relative, top-relative, and absolute file names + +(There is another file prefix, ``!'', that is interpreted specially by +Cons. See discussion of the C<Link> command, below, for details.) + + +=head2 Using modules in build scripts + +You may pull modules into each F<Conscript> file using the normal Perl +C<use> or C<require> statements: + + use English; + require My::Module; + +Each C<use> or C<require> only affects the one F<Conscript> file in which +it appears. To use a module in multiple F<Conscript> files, you must +put a C<use> or C<require> statement in each one that needs the module. + + +=head2 Scope of variables + +The top-level F<Construct> file and all F<Conscript> files begin life in +a common, separate Perl package. B<Cons> controls the symbol table for +the package so that, the symbol table for each script is empty, except +for the F<Construct> file, which gets some of the command line arguments. +All of the variables that are set or used, therefore, are set by the +script itself, not by some external script. + +Variables can be explicitly B<imported> by a script from its parent +script. To import a variable, it must have been B<exported> by the parent +and initialized (otherwise an error will occur). + + +=head2 The Export command + +The C<Export> command is used as in the following example: + + $env = new cons(); + $INCLUDE = "#export/include"; + $LIB = "#export/lib"; + Export qw( env INCLUDE LIB ); + Build qw( util/Conscript ); + +The values of the simple variables mentioned in the C<Export> list will be +squirreled away by any subsequent C<Build> commands. The C<Export> command +will only export Perl B<scalar> variables, that is, variables whose name +begins with C<$>. Other variables, objects, etc. can be exported by +reference, but all scripts will refer to the same object, and this object +should be considered to be read-only by the subsidiary scripts and by the +original exporting script. It's acceptable, however, to assign a new value +to the exported scalar variable, that won't change the underlying variable +referenced. This sequence, for example, is OK: + + $env = new cons(); + Export qw( env INCLUDE LIB ); + Build qw( util/Conscript ); + $env = new cons(CFLAGS => '-O'); + Build qw( other/Conscript ); + +It doesn't matter whether the variable is set before or after the C<Export> +command. The important thing is the value of the variable at the time the +C<Build> command is executed. This is what gets squirreled away. Any +subsequent C<Export> commands, by the way, invalidate the first: you must +mention all the variables you wish to export on each C<Export> command. + + +=head2 The Import command + +Variables exported by the C<Export> command can be imported into subsidiary +scripts by the C<Import> command. The subsidiary script always imports +variables directly from the superior script. Consider this example: + + Import qw( env INCLUDE ); + +This is only legal if the parent script exported both C<$env> and +C<$INCLUDE>. It also must have given each of these variables values. It is +OK for the subsidiary script to only import a subset of the exported +variables (in this example, C<$LIB>, which was exported by the previous +example, is not imported). + +All the imported variables are automatically re-exported, so the sequence: + + Import qw ( env INCLUDE ); + Build qw ( beneath-me/Conscript ); + +will supply both C<$env> and C<$INCLUDE> to the subsidiary file. If only +C<$env> is to be exported, then the following will suffice: + + Import qw ( env INCLUDE ); + Export qw ( env ); + Build qw ( beneath-me/Conscript ); + +Needless to say, the variables may be modified locally before invoking +C<Build> on the subsidiary script. + +=head2 Build script evaluation order + +The only constraint on the ordering of build scripts is that superior +scripts are evaluated before their inferior scripts. The top-level +F<Construct> file, for instance, is evaluated first, followed by any +inferior scripts. This is all you really need to know about the evaluation +order, since order is generally irrelevant. Consider the following C<Build> +command: + + Build qw( + drivers/display/Conscript + drivers/mouse/Conscript + parser/Conscript + utilities/Conscript + ); + +We've chosen to put the script names in alphabetical order, simply because +that's the most convenient for maintenance purposes. Changing the order will +make no difference to the build. + +--> + + <para> + + The source code for large software projects + rarely stays in a single directory, + but is nearly always divided into a + hierarchy of directories. + Organizing a large software build using &SCons; + involves creating a hierarchy of build scripts + using the &SConscript; function. + + </para> + + <section> + <title>&SConscript; Files</title> + + <para> + + As we've already seen, + the build script at the top of the tree is called &SConstruct;. + The top-level &SConstruct; file can + use the &SConscript; function to + include other subsidiary scripts in the build. + These subsidiary scripts can, in turn, + use the &SConscript; function + to include still other scripts in the build. + By convention, these subsidiary scripts are usually + named &SConscript;. + For example, a top-level &SConstruct; file might + arrange for four subsidiary scripts to be included + in the build as follows: + + </para> + + <programlisting> + SConscript(['drivers/display/SConscript', + 'drivers/mouse/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + </programlisting> + + <para> + + In this case, the &SConstruct; file + lists all of the &SConscript; files in the build explicitly. + (Note, however, that not every directory in the tree + necessarily has an &SConscript; file.) + Alternatively, the <literal>drivers</literal> + subdirectory might contain an intermediate + &SConscript; file, + in which case the &SConscript; call in + the top-level &SConstruct; file + would look like: + + </para> + + <programlisting> + SConscript(['drivers/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + </programlisting> + + <para> + + And the subsidiary &SConscript; file in the + <literal>drivers</literal> subdirectory + would look like: + + </para> + + <programlisting> + SConscript(['display/SConscript', + 'mouse/SConscript']) + </programlisting> + + <para> + + Whether you list all of the &SConscript; files in the + top-level &SConstruct; file, + or place a subsidiary &SConscript; file in + intervening directories, + or use some mix of the two schemes, + is up to you and the needs of your software. + + </para> + + </section> + + <section> + <title>Path Names Are Relative to the &SConscript; Directory</title> + + <para> + + Subsidiary &SConscript; files make it easy to create a build + hierarchy because all of the file and directory names + in a subsidiary &SConscript; files are interpreted + relative to the directory in which the &SConscript; file lives. + Typically, this allows the &SConscript; file containing the + instructions to build a target file + to live in the same directory as the source files + from which the target will be built, + making it easy to update how the software is built + whenever files are added or deleted + (or other changes are made). + + </para> + + <para> + + For example, suppose we want to build two programs + &prog1; and &prog2; in two separate directories + with the same names as the programs. + One typical way to do this would be + with a top-level &SConstruct; file like this: + + </para> + + <programlisting> + SConscript(['prog1/SConscript', + 'prog2/SConscript']) + </programlisting> + + <para> + + And subsidiary &SConscript; files that look like this: + + </para> + + + <programlisting> + env = Environment() + env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c']) + </programlisting> + + <para> + + And this: + + </para> + + + <programlisting> + env = Environment() + env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c']) + </programlisting> + + <para> + + Then, when we run &SCons; in the top-level directory, + our build looks like: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o prog1/foo1.o -c prog1/foo1.c + cc -o prog1/foo2.o -c prog1/foo2.c + cc -o prog1/main.o -c prog1/main.c + cc -o prog1/prog1 prog1/main.o prog1/foo1.o prog1/foo2.o + cc -o prog2/bar1.o -c prog2/bar1.c + cc -o prog2/bar2.o -c prog2/bar2.c + cc -o prog2/main.o -c prog2/main.c + cc -o prog2/prog2 prog2/main.o prog2/bar1.o prog2/bar2.o + </screen> + + <para> + + Notice the following: + + First, you can have files with the same names + in multiple directories, like main.c in the above example. + + Second, unlike standard recursive use of &Make;, + &SCons; stays in the top-level directory + (where the &SConstruct; file lives) + and issues commands that use the path names + from the top-level directory to the + target and source files within the hierarchy. + + </para> + + </section> + + <section> + <title>Top-Level Path Names in Subsidiary &SConscript; Files</title> + + <para> + + If you need to use a file from another directory, + it's sometimes more convenient to specify + the path to a file in another directory + from the top-level &SConstruct; directory, + even when you're using that file in + a subsidiary &SConscript; file in a subdirectory. + You can tell &SCons; to interpret a path name + as relative to the top-level &SConstruct; directory, + not the local directory of the &SConscript; file, + by appending a &hash; (hash mark) + to the beginning of the path name: + + </para> + + <programlisting> + env = Environment() + env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c']) + </programlisting> + + <para> + + In this example, + the <literal>lib</literal> directory is + directly underneath the top-level &SConstruct; directory. + If the above &SConscript; file is in a subdirectory + named <literal>src/prog</literal>, + the output would look like: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o lib/foo1.o -c lib/foo1.c + cc -o src/prog/foo2.o -c src/prog/foo2.c + cc -o src/prog/main.o -c src/prog/main.c + cc -o src/prog/prog src/prog/main.o lib/foo1.o src/prog/foo2.o + </screen> + + <para> + + (Notice that the <literal>lib/foo1.o</literal> object file + is built in the same directory as its source file. + See <xref linkend="chap-separate"></xref>, below, + for information about + how to build the object file in a different subdirectory.) + + </para> + + </section> + + <section> + <title>Absolute Path Names</title> + + <para> + + Of course, you can always specify + an absolute path name for a file--for example: + + </para> + + <programlisting> + env = Environment() + env.Program('prog', ['main.c', '/usr/joe/lib/foo1.c', 'foo2.c']) + </programlisting> + + <para> + + Which, when executed, would yield: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o src/prog/foo2.o -c src/prog/foo2.c + cc -o src/prog/main.o -c src/prog/main.c + cc -o /usr/joe/lib/foo1.o -c /usr/joe/lib/foo1.c + cc -o src/prog/prog src/prog/main.o /usr/joe/lib/foo1.o src/prog/foo2.o + </screen> + + <para> + + (As was the case with top-relative path names, + notice that the <literal>/usr/joe/lib/foo1.o</literal> object file + is built in the same directory as its source file. + See <xref linkend="chap-separate"></xref>, below, + for information about + how to build the object file in a different subdirectory.) + + </para> + + </section> + + <section> + <title>Sharing Environments (and Other Variables) Between &SConscript; Files</title> + + <para> + + In the previous example, + each of the subsidiary &SConscript; files + created its own construction environment + by calling &Environment; separately. + This obviously works fine, + but if each program must be built + with the same construction variables, + it's cumbersome and error-prone to initialize + separate construction environments + in the same way over and over in each subsidiary + &SConscript; file. + + </para> + + <para> + + &SCons; supports the ability to <emphasis>export</emphasis> variables + from a parent &SConscript; file + to its subsidiary &SConscript; files, + which allows you to share common initialized + values throughout your build hierarchy. + + </para> + + <section> + <title>Exporting Variables</title> + + <para> + + There are two ways to export a variable, + such as a construction environment, + from an &SConscript; file, + so that it may be used by other &SConscript; files. + First, you can call the &Export; + function with a list of variables, + or a string of white-space separated variable names. + Each call to &Export; adds one + or more variables to a global list + of variables that are available for import + by other &SConscript; files. + + </para> + + <programlisting> + env = Environment() + Export('env') + </programlisting> + + <para> + + You may export more than one variable name at a time: + + </para> + + <programlisting> + env = Environment() + debug = ARGUMENTS['debug'] + Export('env', 'debug') + </programlisting> + + <para> + + Because white space is not legal in Python variable names, + the &Export; function will even automatically split + a string into separate names for you: + + </para> + + <programlisting> + Export('env debug') + </programlisting> + + <para> + + Second, you can specify a list of + variables to export as a second argument + to the &SConscript; function call: + + </para> + + <programlisting> + SConscript('src/SConscript', 'env') + </programlisting> + + <para> + + Or as the &exports; keyword argument: + + </para> + + <programlisting> + SConscript('src/SConscript', exports='env') + </programlisting> + + <para> + + These calls export the specified variables + to only the listed &SConscript; files. + You may, however, specify more than one + &SConscript; file in a list: + + </para> + + <programlisting> + SConscript(['src1/SConscript', + 'src2/SConscript'], exports='env') + </programlisting> + + <para> + + This is functionally equivalent to + calling the &SConscript; function + multiple times with the same &exports; argument, + one per &SConscript; file. + + </para> + + </section> + + <section> + <title>Importing Variables</title> + + <para> + + Once a variable has been exported from a calling + &SConscript; file, + it may be used in other &SConscript; files + by calling the &Import; function: + + </para> + + <programlisting> + Import('env') + env.Program('prog', ['prog.c']) + </programlisting> + + <para> + + The &Import; call makes the <literal>env</literal> construction + environment available to the &SConscript; file, + after which the variable can be used to build + programs, libraries, etc. + + </para> + + <para> + + Like the &Export; function, + the &Import; function can be used + with multiple variable names: + + </para> + + <programlisting> + Import('env', 'debug') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + </programlisting> + + <para> + + And the &Import; function will similarly + split a string along white-space + into separate variable names: + + </para> + + <programlisting> + Import('env debug') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + </programlisting> + + <para> + + Lastly, as a special case, + you may import all of the variables that + have been exported by supplying an asterisk + to the &Import; function: + + </para> + + <programlisting> + Import('*') + env = env.Clone(DEBUG = debug) + env.Program('prog', ['prog.c']) + </programlisting> + + <para> + + If you're dealing with a lot of &SConscript; files, + this can be a lot simpler than keeping + arbitrary lists of imported variables in each file. + + </para> + + </section> + + <section> + <title>Returning Values From an &SConscript; File</title> + + <para> + + Sometimes, you would like to be able to + use information from a subsidiary + &SConscript; file in some way. + For example, + suppose that you want to create one + library from source files + scattered throughout a number + of subsidiary &SConscript; files. + You can do this by using the &Return; + function to return values + from the subsidiary &SConscript; files + to the calling file. + + </para> + + <para> + + If, for example, we have two subdirectories + &foo; and &bar; + that should each contribute a source + file to a Library, + what we'd like to be able to do is + collect the object files + from the subsidiary &SConscript; calls + like this: + + </para> + + <programlisting> + env = Environment() + Export('env') + objs = [] + for subdir in ['foo', 'bar']: + o = SConscript('%s/SConscript' % subdir) + objs.append(o) + env.Library('prog', objs) + </programlisting> + + <para> + + We can do this by using the &Return; + function in the + <literal>foo/SConscript</literal> file like this: + + </para> + + + <programlisting> + Import('env') + obj = env.Object('foo.c') + Return('obj') + </programlisting> + + <para> + + (The corresponding + <literal>bar/SConscript</literal> + file should be pretty obvious.) + Then when we run &SCons;, + the object files from the subsidiary subdirectories + are all correctly archived in the desired library: + + </para> + + <screen> + % <userinput>scons -Q</userinput> + cc -o bar/bar.o -c bar/bar.c + cc -o foo/foo.o -c foo/foo.c + ar rc libprog.a foo/foo.o bar/bar.o + ranlib libprog.a + </screen> + + <!-- + XXX Return(stop=False) + --> + + </section> + + </section> + + <!-- + + <section> + <title>Executing From a Subdirectory: the -D, -u and -U Options</title> + + <para> + + XXX -D, -u and -U + + </para> + + </section> + + --> |