summaryrefslogtreecommitdiff
path: root/doc/user/builders-writing.in
diff options
context:
space:
mode:
Diffstat (limited to 'doc/user/builders-writing.in')
-rw-r--r--doc/user/builders-writing.in1087
1 files changed, 1087 insertions, 0 deletions
diff --git a/doc/user/builders-writing.in b/doc/user/builders-writing.in
new file mode 100644
index 0000000..4f54d99
--- /dev/null
+++ b/doc/user/builders-writing.in
@@ -0,0 +1,1087 @@
+<!--
+
+ 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 Adding new methods
+
+For slightly more demanding changes, you may wish to add new methods to the
+C<cons> package. Here's an example of a very simple extension,
+C<InstallScript>, which installs a tcl script in a requested location, but
+edits the script first to reflect a platform-dependent path that needs to be
+installed in the script:
+
+ # cons::InstallScript - Create a platform dependent version of a shell
+ # script by replacing string ``#!your-path-here'' with platform specific
+ # path $BIN_DIR.
+
+ sub cons::InstallScript {
+ my ($env, $dst, $src) = @_;
+ Command $env $dst, $src, qq(
+ sed s+your-path-here+$BIN_DIR+ %< > %>
+ chmod oug+x %>
+ );
+ }
+
+Notice that this method is defined directly in the C<cons> package (by
+prefixing the name with C<cons::>). A change made in this manner will be
+globally visible to all environments, and could be called as in the
+following example:
+
+ InstallScript $env "$BIN/foo", "foo.tcl";
+
+For a small improvement in generality, the C<BINDIR> variable could be
+passed in as an argument or taken from the construction environment-,-as
+C<%BINDIR>.
+
+
+=head2 Overriding methods
+
+Instead of adding the method to the C<cons> name space, you could define a
+new package which inherits existing methods from the C<cons> package and
+overrides or adds others. This can be done using Perl's inheritance
+mechanisms.
+
+The following example defines a new package C<cons::switch> which
+overrides the standard C<Library> method. The overridden method builds
+linked library modules, rather than library archives. A new
+constructor is provided. Environments created with this constructor
+will have the new library method; others won't.
+
+ package cons::switch;
+ BEGIN {@ISA = 'cons'}
+
+ sub new {
+ shift;
+ bless new cons(@_);
+ }
+
+ sub Library {
+ my($env) = shift;
+ my($lib) = shift;
+ my(@objs) = Objects $env @_;
+ Command $env $lib, @objs, q(
+ %LD -r %LDFLAGS %< -o %>
+ );
+ }
+
+This functionality could be invoked as in the following example:
+
+ $env = new cons::switch(@overrides);
+ ...
+ Library $env 'lib.o', 'foo.c', 'bar.c';
+
+-->
+
+ <para>
+
+ Although &SCons; provides many useful methods
+ for building common software products:
+ programs, libraries, documents.
+ you frequently want to be
+ able to build some other type of file
+ not supported directly by &SCons;.
+ Fortunately, &SCons; makes it very easy
+ to define your own &Builder; objects
+ for any custom file types you want to build.
+ (In fact, the &SCons; interfaces for creating
+ &Builder; objects are flexible enough and easy enough to use
+ that all of the the &SCons; built-in &Builder; objects
+ are created the mechanisms described in this section.)
+
+ </para>
+
+ <section>
+ <title>Writing Builders That Execute External Commands</title>
+
+ <para>
+
+ The simplest &Builder; to create is
+ one that executes an external command.
+ For example, if we want to build
+ an output file by running the contents
+ of the input file through a command named
+ <literal>foobuild</literal>,
+ creating that &Builder; might look like:
+
+ </para>
+
+ <programlisting>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ </programlisting>
+
+ <para>
+
+ All the above line does is create a free-standing
+ &Builder; object.
+ The next section will show us how to actually use it.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Attaching a Builder to a &ConsEnv;</title>
+
+ <para>
+
+ A &Builder; object isn't useful
+ until it's attached to a &consenv;
+ so that we can call it to arrange
+ for files to be built.
+ This is done through the &cv-link-BUILDERS;
+ &consvar; in an environment.
+ The &cv-BUILDERS; variable is a Python dictionary
+ that maps the names by which you want to call
+ various &Builder; objects to the objects themselves.
+ For example, if we want to call the
+ &Builder; we just defined by the name
+ <function>Foo</function>,
+ our &SConstruct; file might look like:
+
+ </para>
+
+ <scons_example name="ex1">
+ <file name="SConstruct">
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ env.Foo('file.foo', 'file.input')
+ </file>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ </sconstruct>
+
+ <para>
+
+ With the &Builder; attached to our &consenv;
+ with the name <function>Foo</function>,
+ we can now actually call it like so:
+
+ </para>
+
+ <programlisting>
+ env.Foo('file.foo', 'file.input')
+ </programlisting>
+
+ <para>
+
+ Then when we run &SCons; it looks like:
+
+ </para>
+
+ <scons_output example="ex1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note, however, that the default &cv-BUILDERS;
+ variable in a &consenv;
+ comes with a default set of &Builder; objects
+ already defined:
+ &b-link-Program;, &b-link-Library;, etc.
+ And when we explicitly set the &cv-BUILDERS; variable
+ when we create the &consenv;,
+ the default &Builder;s are no longer part of
+ the environment:
+
+ </para>
+
+ <!--
+ The ToolSurrogate stuff that's used to capture output initializes
+ SCons.Defaults.ConstructionEnvironment with its own list of TOOLS.
+ In this next example, we want to show the user that when they
+ set the BUILDERS explicitly, the call to env.Program() generates
+ an AttributeError. This won't happen with all of the default
+ ToolSurrogates in the default construction environment. To make the
+ AttributeError show up, we have to overwite the default construction
+ environment's TOOLS variable so Program() builder doesn't show up.
+
+ We do this by executing a slightly different SConstruct file than the
+ one we print in the guide, with two extra statements at the front
+ that overwrite the TOOLS variable as described. Note that we have
+ to jam those statements on to the first line to keep the line number
+ in the generated error consistent with what the user will see in the
+ User's Guide.
+ -->
+ <scons_example name="ex2">
+ <file name="SConstruct">
+ import SCons.Defaults; SCons.Defaults.ConstructionEnvironment['TOOLS'] = {}; bld = Builder(action = 'foobuild &lt; $SOURCE &gt; $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </file>
+ <file name="SConstruct.printme" printme="1">
+ bld = Builder(action = 'foobuild &lt; $SOURCE &gt; $TARGET')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </file>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ </scons_example>
+
+ <scons_output example="ex2">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ To be able to use both our own defined &Builder; objects
+ and the default &Builder; objects in the same &consenv;,
+ you can either add to the &cv-BUILDERS; variable
+ using the &Append; function:
+
+ </para>
+
+ <scons_example name="ex3">
+ <file name="SConstruct">
+ env = Environment()
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env.Append(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </file>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ env = Environment()
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env.Append(BUILDERS = {'Foo' : bld})
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </sconstruct>
+
+ <para>
+
+ Or you can explicitly set the appropriately-named
+ key in the &cv-BUILDERS; dictionary:
+
+ </para>
+
+ <sconstruct>
+ env = Environment()
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+ env['BUILDERS']['Foo'] = bld
+ env.Foo('file.foo', 'file.input')
+ env.Program('hello.c')
+ </sconstruct>
+
+ <para>
+
+ Either way, the same &consenv;
+ can then use both the newly-defined
+ <function>Foo</function> &Builder;
+ and the default &b-link-Program; &Builder;:
+
+ </para>
+
+ <scons_output example="ex3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Letting &SCons; Handle The File Suffixes</title>
+
+ <para>
+
+ By supplying additional information
+ when you create a &Builder;,
+ you can let &SCons; add appropriate file
+ suffixes to the target and/or the source file.
+ For example, rather than having to specify
+ explicitly that you want the <literal>Foo</literal>
+ &Builder; to build the <literal>file.foo</literal>
+ target file from the <literal>file.input</literal> source file,
+ you can give the <literal>.foo</literal>
+ and <literal>.input</literal> suffixes to the &Builder;,
+ making for more compact and readable calls to
+ the <literal>Foo</literal> &Builder;:
+
+ </para>
+
+ <scons_example name="ex4">
+ <file name="SConstruct">
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET',
+ suffix = '.foo',
+ src_suffix = '.input')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ env.Foo('file1')
+ env.Foo('file2')
+ </file>
+ <file name="file1.input">
+ file1.input
+ </file>
+ <file name="file2.input">
+ file2.input
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ bld = Builder(action = 'foobuild < $SOURCE > $TARGET',
+ suffix = '.foo',
+ src_suffix = '.input')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file1')
+ env.Foo('file2')
+ </sconstruct>
+
+ <scons_output example="ex4">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ You can also supply a <literal>prefix</literal> keyword argument
+ if it's appropriate to have &SCons; append a prefix
+ to the beginning of target file names.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Builders That Execute Python Functions</title>
+
+ <para>
+
+ In &SCons;, you don't have to call an external command
+ to build a file.
+ You can, instead, define a Python function
+ that a &Builder; object can invoke
+ to build your target file (or files).
+ Such a &buildfunc; definition looks like:
+
+ </para>
+
+ <programlisting>
+ def build_function(target, source, env):
+ # Code to build "target" from "source"
+ return None
+ </programlisting>
+
+ <para>
+
+ The arguments of a &buildfunc; are:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>target</term>
+
+ <listitem>
+ <para>
+
+ A list of Node objects representing
+ the target or targets to be
+ built by this builder function.
+ The file names of these target(s)
+ may be extracted using the Python &str; function.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>source</term>
+
+ <listitem>
+ <para>
+
+ A list of Node objects representing
+ the sources to be
+ used by this builder function to build the targets.
+ The file names of these source(s)
+ may be extracted using the Python &str; function.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>env</term>
+
+ <listitem>
+ <para>
+
+ The &consenv; used for building the target(s).
+ The builder function may use any of the
+ environment's construction variables
+ in any way to affect how it builds the targets.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ The builder function must
+ return a <literal>0</literal> or <literal>None</literal> value
+ if the target(s) are built successfully.
+ The builder function
+ may raise an exception
+ or return any non-zero value
+ to indicate that the build is unsuccessful,
+
+ </para>
+
+ <para>
+
+ Once you've defined the Python function
+ that will build your target file,
+ defining a &Builder; object for it is as
+ simple as specifying the name of the function,
+ instead of an external command,
+ as the &Builder;'s
+ <literal>action</literal>
+ argument:
+
+ </para>
+
+ <scons_example name="ex5">
+ <file name="SConstruct" printme="1">
+ def build_function(target, source, env):
+ # Code to build "target" from "source"
+ return None
+ bld = Builder(action = build_function,
+ suffix = '.foo',
+ src_suffix = '.input')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file')
+ </file>
+ <file name="file.input">
+ file.input
+ </file>
+ </scons_example>
+
+ <para>
+
+ And notice that the output changes slightly,
+ reflecting the fact that a Python function,
+ not an external command,
+ is now called to build the target file:
+
+ </para>
+
+ <scons_output example="ex5">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <section>
+ <title>Builders That Create Actions Using a &Generator;</title>
+
+ <para>
+
+ &SCons; Builder objects can create an action "on the fly"
+ by using a function called a &generator;.
+ This provides a great deal of flexibility to
+ construct just the right list of commands
+ to build your target.
+ A &generator; looks like:
+
+ </para>
+
+ <programlisting>
+ def generate_actions(source, target, env, for_signature):
+ return 'foobuild < %s > %s' % (target[0], source[0])
+ </programlisting>
+
+ <para>
+
+ The arguments of a &generator; are:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>source</term>
+
+ <listitem>
+ <para>
+
+ A list of Node objects representing
+ the sources to be built
+ by the command or other action
+ generated by this function.
+ The file names of these source(s)
+ may be extracted using the Python &str; function.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>target</term>
+
+ <listitem>
+ <para>
+
+ A list of Node objects representing
+ the target or targets to be built
+ by the command or other action
+ generated by this function.
+ The file names of these target(s)
+ may be extracted using the Python &str; function.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>env</term>
+
+ <listitem>
+ <para>
+
+ The &consenv; used for building the target(s).
+ The generator may use any of the
+ environment's construction variables
+ in any way to determine what command
+ or other action to return.
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>for_signature</term>
+
+ <listitem>
+ <para>
+
+ A flag that specifies whether the
+ generator is being called to contribute to a build signature,
+ as opposed to actually executing the command.
+
+ <!-- XXX NEED MORE HERE, describe generators use in signatures -->
+
+ </para>
+ </listitem>
+
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ The &generator; must return a
+ command string or other action that will be used to
+ build the specified target(s) from the specified source(s).
+
+ </para>
+
+ <para>
+
+ Once you've defined a &generator;,
+ you create a &Builder; to use it
+ by specifying the generator keyword argument
+ instead of <literal>action</literal>.
+
+ </para>
+
+ <scons_example name="ex6">
+ <file name="SConstruct">
+ def generate_actions(source, target, env, for_signature):
+ return 'foobuild < %s > %s' % (source[0], target[0])
+ bld = Builder(generator = generate_actions,
+ suffix = '.foo',
+ src_suffix = '.input')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ env.Foo('file')
+ </file>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ def generate_actions(source, target, env, for_signature):
+ return 'foobuild < %s > %s' % (source[0], target[0])
+ bld = Builder(generator = generate_actions,
+ suffix = '.foo',
+ src_suffix = '.input')
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file')
+ </sconstruct>
+
+ <scons_output example="ex6">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ Note that it's illegal to specify both an
+ <literal>action</literal>
+ and a
+ <literal>generator</literal>
+ for a &Builder;.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Builders That Modify the Target or Source Lists Using an &Emitter;</title>
+
+ <para>
+
+ &SCons; supports the ability for a Builder to modify the
+ lists of target(s) from the specified source(s).
+ You do this by defining an &emitter; function
+ that takes as its arguments
+ the list of the targets passed to the builder,
+ the list of the sources passed to the builder,
+ and the construction environment.
+ The emitter function should return the modified
+ lists of targets that should be built
+ and sources from which the targets will be built.
+
+ </para>
+
+ <para>
+
+ For example, suppose you want to define a Builder
+ that always calls a <filename>foobuild</filename> program,
+ and you want to automatically add
+ a new target file named
+ <filename>new_target</filename>
+ and a new source file named
+ <filename>new_source</filename>
+ whenever it's called.
+ The &SConstruct; file might look like this:
+
+ </para>
+
+ <scons_example name="ex7">
+ <file name="SConstruct">
+ def modify_targets(target, source, env):
+ target.append('new_target')
+ source.append('new_source')
+ return target, source
+ bld = Builder(action = 'foobuild $TARGETS - $SOURCES',
+ suffix = '.foo',
+ src_suffix = '.input',
+ emitter = modify_targets)
+ env = Environment(BUILDERS = {'Foo' : bld})
+ import os
+ env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
+ env.Foo('file')
+ </file>
+ <file name="file.input">
+ file.input
+ </file>
+ <file name="new_source">
+ new_source
+ </file>
+ <file name="foobuild" chmod="0755">
+ cat
+ </file>
+ </scons_example>
+
+ <sconstruct>
+ def modify_targets(target, source, env):
+ target.append('new_target')
+ source.append('new_source')
+ return target, source
+ bld = Builder(action = 'foobuild $TARGETS - $SOURCES',
+ suffix = '.foo',
+ src_suffix = '.input',
+ emitter = modify_targets)
+ env = Environment(BUILDERS = {'Foo' : bld})
+ env.Foo('file')
+ </sconstruct>
+
+ <para>
+
+ And would yield the following output:
+
+ </para>
+
+ <scons_output example="ex7">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ One very flexible thing that you can do is
+ use a construction variable to specify
+ different emitter functions for different
+ construction variable.
+ To do this, specify a string
+ containing a construction variable
+ expansion as the emitter when you call
+ the &Builder; function,
+ and set that construction variable to
+ the desired emitter function
+ in different construction environments:
+
+ </para>
+
+ <scons_example name="MY_EMITTER">
+
+ <file name="SConstruct" printme="1">
+ bld = Builder(action = 'my_command $SOURCES > $TARGET',
+ suffix = '.foo',
+ src_suffix = '.input',
+ emitter = '$MY_EMITTER')
+ def modify1(target, source, env):
+ return target, source + ['modify1.in']
+ def modify2(target, source, env):
+ return target, source + ['modify2.in']
+ env1 = Environment(BUILDERS = {'Foo' : bld},
+ MY_EMITTER = modify1)
+ env2 = Environment(BUILDERS = {'Foo' : bld},
+ MY_EMITTER = modify2)
+ env1.Foo('file1')
+ env2.Foo('file2')
+ import os
+ env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd()
+ env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd()
+ </file>
+ <file name="file1.input">
+ file1.input
+ </file>
+ <file name="file2.input">
+ file2.input
+ </file>
+ <file name="modify1.in">
+ modify1.input
+ </file>
+ <file name="modify2.in">
+ modify2.input
+ </file>
+ <file name="my_command" chmod="0755">
+ cat
+ </file>
+ </file>
+
+ </scons_example>
+
+ <sconstruct>
+ bld = Builder(action = 'my_command $SOURCES > $TARGET',
+ suffix = '.foo',
+ src_suffix = '.input',
+ emitter = '$MY_EMITTER')
+ def modify1(target, source, env):
+ return target, source + ['modify1.in']
+ def modify2(target, source, env):
+ return target, source + ['modify2.in']
+ env1 = Environment(BUILDERS = {'Foo' : bld},
+ MY_EMITTER = modify1)
+ env2 = Environment(BUILDERS = {'Foo' : bld},
+ MY_EMITTER = modify2)
+ env1.Foo('file1')
+ env2.Foo('file2')
+ </file>
+ </sconstruct>
+
+ <para>
+
+ In this example, the <filename>modify1.in</filename>
+ and <filename>modify2.in</filename> files
+ get added to the source lists
+ of the different commands:
+
+ </para>
+
+ <scons_output example="MY_EMITTER">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ <!--
+
+ <section>
+ <title>target_factor=, source_factory=</title>
+
+ </section>
+
+ <section>
+ <title>target_scanner=, source_scanner=</title>
+
+ </section>
+
+ <section>
+ <title>multi=</title>
+
+ </section>
+
+ <section>
+ <title>single_source=</title>
+
+ </section>
+
+ <section>
+ <title>src_builder=</title>
+
+ </section>
+
+ <section>
+ <title>ensure_suffix=</title>
+
+ </section>
+
+ -->
+
+ <section>
+ <title>Where To Put Your Custom Builders and Tools</title>
+
+ <para>
+
+ The <filename>site_scons</filename> directory gives you a place to
+ put Python modules you can import into your SConscripts
+ (site_scons), add-on tools that can integrate into &SCons;
+ (site_scons/site_tools), and a site_scons/site_init.py file that
+ gets read before any &SConstruct; or &SConscript;, allowing you to
+ change &SCons;'s default behavior.
+
+ </para>
+
+ <para>
+
+ If you get a tool from somewhere (the &SCons; wiki or a third party,
+ for instance) and you'd like to use it in your project, the
+ <filename>site_scons</filename> dir is the simplest place to put it.
+ Tools come in two flavors; either a Python function that operates on
+ an &Environment; or a Python file containing two functions, exists()
+ and generate().
+
+ </para>
+
+ <para>
+
+ A single-function Tool can just be included in your
+ <filename>site_scons/site_init.py</filename> file where it will be
+ parsed and made available for use. For instance, you could have a
+ <filename>site_scons/site_init.py</filename> file like this:
+
+ </para>
+
+ <scons_example name="site1">
+ <file name="site_scons/site_init.py" printme=1>
+ def TOOL_ADD_HEADER(env):
+ """A Tool to add a header from $HEADER to the source file"""
+ add_header = Builder(action=['echo "$HEADER" > $TARGET',
+ 'cat $SOURCE >> $TARGET'])
+ env.Append(BUILDERS = {'AddHeader' : add_header})
+ env['HEADER'] = '' # set default value
+ </file>
+ <file name="SConstruct">
+ env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+ env.AddHeader('tgt', 'src')
+ </file>
+ <file name="src">
+ hi there
+ </file>
+ </scons_example>
+
+ <para>
+
+ and a &SConstruct; like this:
+
+ </para>
+
+ <sconstruct>
+ # Use TOOL_ADD_HEADER from site_scons/site_init.py
+ env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
+ env.AddHeader('tgt', 'src')
+ </sconstruct>
+
+ <para>
+
+ The <function>TOOL_ADD_HEADER</function> tool method will be
+ called to add the <function>AddHeader</function> tool to the
+ environment.
+
+ </para>
+
+ <!--
+ <scons_output example="site1" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+ -->
+
+ <para>
+ Similarly, a more full-fledged tool with
+ <function>exists()</function> and <function>generate()</function>
+ methods can be installed in
+ <filename>site_scons/site_tools/toolname.py</filename>. Since
+ <filename>site_scons/site_tools</filename> is automatically added
+ to the head of the tool search path, any tool found there will be
+ available to all environments. Furthermore, a tool found there
+ will override a built-in tool of the same name, so if you need to
+ change the behavior of a built-in tool, site_scons gives you the
+ hook you need.
+ </para>
+
+ <para>
+ Many people have a library of utility Python functions they'd like
+ to include in &SConscript;s; just put that module in
+ <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
+ choice. For instance you can do something like this in
+ <filename>site_scons/my_utils.py</filename> to add build_id and MakeWorkDir functions:
+ </para>
+
+ <scons_example name="site2">
+ <file name="site_scons/my_utils.py" printme=1>
+ from SCons.Script import * # for Execute and Mkdir
+ def build_id():
+ """Return a build ID (stub version)"""
+ return "100"
+ def MakeWorkDir(workdir):
+ """Create the specified dir immediately"""
+ Execute(Mkdir(workdir))
+ </file>
+ <file name="SConscript">
+ import my_utils
+ MakeWorkDir('/tmp/work')
+ print "build_id=" + my_utils.build_id()
+ </file>
+ </scons_example>
+
+ <para>
+
+ And then in your &SConscript; or any sub-&SConscript; anywhere in
+ your build, you can import <filename>my_utils</filename> and use it:
+
+ </para>
+
+ <sconstruct>
+ import my_utils
+ print "build_id=" + my_utils.build_id()
+ my_utils.MakeWorkDir('/tmp/work')
+ </sconstruct>
+
+ <para>
+ Note that although you can put this library in
+ <filename>site_scons/site_init.py</filename>,
+ it is no better there than <filename>site_scons/my_utils.py</filename>
+ since you still have to import that module into your &SConscript;.
+ Also note that in order to refer to objects in the SCons namespace
+ such as &Environment; or &Mkdir; or &Execute; in any file other
+ than a &SConstruct; or &SConscript; you always need to do
+ </para>
+ <sconstruct>
+ from SCons.Script import *
+ </sconstruct>
+
+ <para>
+ This is true in modules in <filename>site_scons</filename> such as
+ <filename>site_scons/site_init.py</filename> as well.
+ </para>
+
+ <para>
+
+ If you have a machine-wide site dir you'd like to use instead of
+ <filename>./site_scons</filename>, use the
+ <literal>--site-dir</literal> option to point to your dir.
+ <filename>site_init.py</filename> and
+ <filename>site_tools</filename> will be located under that dir.
+ To avoid using a <filename>site_scons</filename> dir at all, even
+ if it exists, use the <literal>--no-site-dir</literal> option.
+
+ </para>
+
+ </section>
+
+
+ <!--
+
+ <section>
+ <title>Builders That Use Other Builders</title>
+
+ <para>
+
+ XXX para
+
+ </para>
+
+ <scons_example name="ex8">
+ <file name="SConstruct" printme="1">
+ env = Environment()
+ #env.SourceCode('.', env.BitKeeper('my_command'))
+ env.Program('hello.c')
+ </file>
+ <file name="hello.c">
+ hello.c
+ </file>
+ </scons_example>
+
+ <scons_output example="ex8">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ </section>
+
+ -->