Commits

Anonymous committed d6e9ef5

Branch for User's Guide changes.

Comments (0)

Files changed (37)

 #
 # Documentation.
 #
-BuildDir('build/doc', 'doc')
-
 Export('env', 'whereis')
 
-SConscript('build/doc/SConscript')
+SConscript('doc/SConscript')
 
 #
 # If we're running in the actual Aegis project, pack up a complete

bin/sconsexamples.py

+#!/usr/bin/env python2
+#
+# scons_examples.py -   an SGML preprocessor for capturing SCons output
+#                       and inserting into examples in our DocBook
+#                       documentation
+#
+
+# This script looks for some SGML tags that describe SCons example
+# configurations and commands to execute in those configurations, and
+# uses TestCmd.py to execute the commands and insert the output into
+# the output SGML.  This way, we can run a script and update all of
+# our example output without having to do a lot of laborious by-hand
+# checking.
+#
+# An "SCons example" looks like this, and essentially describes a set of
+# input files (program source files as well as SConscript files):
+#
+#       <scons_example name="ex1">
+#         <file name="SConstruct" printme="1">
+#           env = Environment()
+#           env.Program('foo')
+#         </file>
+#         <file name="foo.c">
+#           int main() { printf("foo.c\n"); }
+#         </file>
+#       </scons_example>
+#
+# The <file> contents within the <scons_example> tag will get written
+# into a temporary directory whenever example output needs to be
+# generated.  By default, the <file> contents are not inserted into text
+# directly, unless you set the "printme" attribute on one or more files,
+# in which case they will get inserted within a <programlisting> tag.
+# This makes it easy to define the example at the appropriate
+# point in the text where you intend to show the SConstruct file.
+#
+# Note that you should usually give the <scons_example> a "name"
+# attribute so that you can refer to the example configuration later to
+# run SCons and generate output.
+#
+# If you just want to show a file's contents without worry about running
+# SCons, there's a shorter <sconstruct> tag:
+#
+#       <sconstruct>
+#         env = Environment()
+#         env.Program('foo')
+#       </sconstruct>
+#
+# This is essentially equivalent to <scons_example><file printme="1">,
+# but it's more straightforward.
+#
+# SCons output is generated from the following sort of tag:
+#
+#       <scons_output example="ex1" os="posix">
+#         <command>scons -Q foo</command>
+#         <command>scons -Q foo</command>
+#       </scons_output>
+#
+# You tell it which example to use with the "example" attribute, and
+# then give it a list of <command> tags to execute.  You can also supply
+# an "os" tag, which specifies the type of operating system this example
+# is intended to show; if you omit this, default value is "posix".
+#
+# The generated SGML will show the command line (with the appropriate
+# command-line prompt for the operating system), execute the command in
+# a temporary directory with the example files, capture the standard
+# output from SCons, and insert it into the text as appropriate.
+# Error output gets passed through to your error output so you
+# can see if there are any problems executing the command.
+#
+
+import os
+import os.path
+import re
+import sgmllib
+import string
+import sys
+
+sys.path.append(os.path.join(os.getcwd(), 'etc'))
+sys.path.append(os.path.join(os.getcwd(), 'build', 'etc'))
+
+scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py')
+if not os.path.exists(scons_py):
+    scons_py = os.path.join('src', 'script', 'scons.py')
+
+scons_lib_dir = os.path.join(os.getcwd(), 'bootstrap', 'src', 'engine')
+if not os.path.exists(scons_lib_dir):
+    scons_lib_dir = os.path.join(os.getcwd(), 'src', 'engine')
+
+import TestCmd
+
+# The regular expression that identifies entity references in the
+# standard sgmllib omits the underscore from the legal characters.
+# Override it with our own regular expression that adds underscore.
+sgmllib.entityref = re.compile('&([a-zA-Z][-_.a-zA-Z0-9]*)[^-_a-zA-Z0-9]')
+
+class DataCollector:
+    """Generic class for collecting data between a start tag and end
+    tag.  We subclass for various types of tags we care about."""
+    def __init__(self):
+        self.data = ""
+    def afunc(self, data):
+        self.data = self.data + data
+
+class Example(DataCollector):
+    """An SCons example.  This is essentially a list of files that
+    will get written to a temporary directory to collect output
+    from one or more SCons runs."""
+    def __init__(self):
+        DataCollector.__init__(self)
+        self.files = []
+        self.dirs = []
+
+class File(DataCollector):
+    """A file, that will get written out to a temporary directory
+    for one or more SCons runs."""
+    def __init__(self, name):
+        DataCollector.__init__(self)
+        self.name = name
+
+class Directory(DataCollector):
+    """A directory, that will get created in a temporary directory
+    for one or more SCons runs."""
+    def __init__(self, name):
+        DataCollector.__init__(self)
+        self.name = name
+
+class Output(DataCollector):
+    """Where the command output goes.  This is essentially
+    a list of commands that will get executed."""
+    def __init__(self):
+        DataCollector.__init__(self)
+        self.commandlist = []
+
+class Command(DataCollector):
+    """A tag for where the command output goes.  This is essentially
+    a list of commands that will get executed."""
+    pass
+
+Prompt = {
+    'posix' : '% ',
+    'win32' : 'C:\\>'
+}
+
+# Magick SCons hackery.
+#
+# So that our examples can still use the default SConstruct file, we
+# actually feed the following into SCons via stdin and then have it
+# SConscript() the SConstruct file.  This stdin wrapper creates a set
+# of ToolSurrogates for the tools for the appropriate platform.  These
+# Surrogates print output like the real tools and behave like them
+# without actually having to be on the right platform or have the right
+# tool installed.
+#
+# The upshot:  We transparently change the world out from under the
+# top-level SConstruct file in an example just so we can get the
+# command output.
+
+Stdin = """\
+import SCons.Defaults
+
+platform = '%s'
+
+class Curry:
+    def __init__(self, fun, *args, **kwargs):
+        self.fun = fun
+        self.pending = args[:]
+        self.kwargs = kwargs.copy()
+
+    def __call__(self, *args, **kwargs):
+        if kwargs and self.kwargs:
+            kw = self.kwargs.copy()
+            kw.update(kwargs)
+        else:
+            kw = kwargs or self.kwargs
+
+        return apply(self.fun, self.pending + args, kw)
+
+def Str(target, source, env, cmd=""):
+    return env.subst(cmd, target=target, source=source)
+
+class ToolSurrogate:
+    def __init__(self, tool, variable, func):
+        self.tool = tool
+        self.variable = variable
+        self.func = func
+    def __call__(self, env):
+        t = Tool(self.tool)
+        t.generate(env)
+        orig = env[self.variable]
+        env[self.variable] = Action(self.func, strfunction=Curry(Str, cmd=orig))
+
+def Null(target, source, env):
+    pass
+
+def Cat(target, source, env):
+    target = str(target[0])
+    f = open(target, "wb")
+    for src in map(str, source):
+        f.write(open(src, "rb").read())
+    f.close()
+
+ToolList = {
+    'posix' :   [('cc', 'CCCOM', Cat),
+                 ('link', 'LINKCOM', Cat),
+                 ('tar', 'TARCOM', Null),
+                 ('zip', 'ZIPCOM', Null)],
+    'win32' :   [('msvc', 'CCCOM', Cat),
+                 ('mslink', 'LINKCOM', Cat)]
+}
+
+tools = map(lambda t: apply(ToolSurrogate, t), ToolList[platform])
+
+SCons.Defaults.ConstructionEnvironment.update({
+    'PLATFORM' : platform,
+    'TOOLS'    : tools,
+})
+
+SConscript('SConstruct')
+"""
+
+class MySGML(sgmllib.SGMLParser):
+    """A subclass of the standard Python 2.2 sgmllib SGML parser.
+
+    Note that this doesn't work with the 1.5.2 sgmllib module, because
+    that didn't have the ability to work with ENTITY declarations.
+    """
+    def __init__(self):
+        sgmllib.SGMLParser.__init__(self)
+        self.examples = {}
+        self.afunclist = []
+
+    def handle_data(self, data):
+        try:
+            f = self.afunclist[-1]
+        except IndexError:
+            sys.stdout.write(data)
+        else:
+            f(data)
+
+    def handle_comment(self, data):
+        sys.stdout.write('<!--' + data + '-->')
+
+    def handle_decl(self, data):
+        sys.stdout.write('<!' + data + '>')
+
+    def unknown_starttag(self, tag, attrs):
+        try:
+            f = self.example.afunc
+        except AttributeError:
+            f = sys.stdout.write
+        if not attrs:
+            f('<' + tag + '>')
+        else:
+            f('<' + tag)
+            for name, value in attrs:
+                f(' ' + name + '=' + '"' + value + '"')
+            f('>')
+
+    def unknown_endtag(self, tag):
+        sys.stdout.write('</' + tag + '>')
+
+    def unknown_entityref(self, ref):
+        sys.stdout.write('&' + ref + ';')
+
+    def unknown_charref(self, ref):
+        sys.stdout.write('&#' + ref + ';')
+
+    def start_scons_example(self, attrs):
+        t = filter(lambda t: t[0] == 'name', attrs)
+        if t:
+            name = t[0][1]
+            try:
+               e = self.examples[name]
+            except KeyError:
+               e = self.examples[name] = Example()
+        else:
+            e = Example()
+        for name, value in attrs:
+            setattr(e, name, value)
+        self.e = e
+        self.afunclist.append(e.afunc)
+
+    def end_scons_example(self):
+        e = self.e
+        files = filter(lambda f: f.printme, e.files)
+        if files:
+            sys.stdout.write('<programlisting>')
+            for f in files:
+                if f.printme:
+                    i = len(f.data) - 1
+                    while f.data[i] == ' ':
+                        i = i - 1
+                    output = string.replace(f.data[:i+1], '__ROOT__', '')
+                    sys.stdout.write(output)
+            if e.data and e.data[0] == '\n':
+                e.data = e.data[1:]
+            sys.stdout.write(e.data + '</programlisting>')
+        delattr(self, 'e')
+        self.afunclist = self.afunclist[:-1]
+
+    def start_file(self, attrs):
+        try:
+            e = self.e
+        except AttributeError:
+            self.error("<file> tag outside of <scons_example>")
+        t = filter(lambda t: t[0] == 'name', attrs)
+        if not t:
+            self.error("no <file> name attribute found")
+        try:
+            e.prefix
+        except AttributeError:
+            e.prefix = e.data
+            e.data = ""
+        f = File(t[0][1])
+        f.printme = None
+        for name, value in attrs:
+            setattr(f, name, value)
+        e.files.append(f)
+        self.afunclist.append(f.afunc)
+
+    def end_file(self):
+        self.e.data = ""
+        self.afunclist = self.afunclist[:-1]
+
+    def start_directory(self, attrs):
+        try:
+            e = self.e
+        except AttributeError:
+            self.error("<directory> tag outside of <scons_example>")
+        t = filter(lambda t: t[0] == 'name', attrs)
+        if not t:
+            self.error("no <directory> name attribute found")
+        try:
+            e.prefix
+        except AttributeError:
+            e.prefix = e.data
+            e.data = ""
+        d = Directory(t[0][1])
+        for name, value in attrs:
+            setattr(d, name, value)
+        e.dirs.append(d)
+        self.afunclist.append(d.afunc)
+
+    def end_directory(self):
+        self.e.data = ""
+        self.afunclist = self.afunclist[:-1]
+
+    def start_scons_example_file(self, attrs):
+        t = filter(lambda t: t[0] == 'example', attrs)
+        if not t:
+            self.error("no <scons_example_file> example attribute found")
+        exname = t[0][1]
+        try:
+            e = self.examples[exname]
+        except KeyError:
+            self.error("unknown example name '%s'" % exname)
+        fattrs = filter(lambda t: t[0] == 'name', attrs)
+        if not fattrs:
+            self.error("no <scons_example_file> name attribute found")
+        fname = fattrs[0][1]
+        f = filter(lambda f, fname=fname: f.name == fname, e.files)
+        if not f:
+            self.error("example '%s' does not have a file named '%s'" % (exname, fname))
+        self.f = f[0]
+
+    def end_scons_example_file(self):
+        f = self.f
+        sys.stdout.write('<programlisting>')
+        i = len(f.data) - 1
+        while f.data[i] == ' ':
+            i = i - 1
+        sys.stdout.write(f.data[:i+1] + '</programlisting>')
+        delattr(self, 'f')
+
+    def start_scons_output(self, attrs):
+        t = filter(lambda t: t[0] == 'example', attrs)
+        if not t:
+            self.error("no <scons_output> example attribute found")
+        exname = t[0][1]
+        try:
+            e = self.examples[exname]
+        except KeyError:
+            self.error("unknown example name '%s'" % exname)
+        # Default values for an example.
+        o = Output()
+        o.os = 'posix'
+        o.e = e
+        # Locally-set.
+        for name, value in attrs:
+            setattr(o, name, value)
+        self.o = o
+        self.afunclist.append(o.afunc)
+
+    def end_scons_output(self):
+        o = self.o
+        e = o.e
+        t = TestCmd.TestCmd(workdir='', combine=1)
+        t.subdir('ROOT', 'WORK')
+        for d in e.dirs:
+            dir = t.workpath('WORK', d.name)
+            if not os.path.exists(dir):
+                os.makedirs(dir)
+        for f in e.files:
+            i = 0
+            while f.data[i] == '\n':
+                i = i + 1
+            lines = string.split(f.data[i:], '\n')
+            i = 0
+            while lines[0][i] == ' ':
+                i = i + 1
+            lines = map(lambda l, i=i: l[i:], lines)
+            path = string.replace(f.name, '__ROOT__', t.workpath('ROOT'))
+            dir, name = os.path.split(f.name)
+            if dir:
+                dir = t.workpath('WORK', dir)
+                if not os.path.exists(dir):
+                    os.makedirs(dir)
+            content = string.join(lines, '\n')
+            content = string.replace(content,
+                                     '__ROOT__',
+                                     t.workpath('ROOT'))
+            t.write(t.workpath('WORK', f.name), content)
+        i = len(o.prefix)
+        while o.prefix[i-1] != '\n':
+            i = i - 1
+        sys.stdout.write('<literallayout>' + o.prefix[:i])
+        p = o.prefix[i:]
+        for c in o.commandlist:
+            sys.stdout.write(p + Prompt[o.os])
+            d = string.replace(c.data, '__ROOT__', '')
+            sys.stdout.write('<userinput>' + d + '</userinput>\n')
+            e = string.replace(c.data, '__ROOT__', t.workpath('ROOT'))
+            args = string.split(e)[1:]
+            os.environ['SCONS_LIB_DIR'] = scons_lib_dir
+            t.run(interpreter = sys.executable,
+                  program = scons_py,
+                  arguments = '-f - ' + string.join(args),
+                  chdir = t.workpath('WORK'),
+                  stdin = Stdin % o.os)
+            out = string.replace(t.stdout(), t.workpath('ROOT'), '')
+            if out:
+                lines = string.split(out, '\n')
+                if lines:
+                    while lines[-1] == '':
+                        lines = lines[:-1]
+                    for l in lines:
+                        sys.stdout.write(p + l + '\n')
+            #err = t.stderr()
+            #if err:
+            #    sys.stderr.write(err)
+        if o.data[0] == '\n':
+            o.data = o.data[1:]
+        sys.stdout.write(o.data + '</literallayout>')
+        delattr(self, 'o')
+        self.afunclist = self.afunclist[:-1]
+
+    def start_command(self, attrs):
+        try:
+            o = self.o
+        except AttributeError:
+            self.error("<command> tag outside of <scons_output>")
+        try:
+            o.prefix
+        except AttributeError:
+            o.prefix = o.data
+            o.data = ""
+        c = Command()
+        o.commandlist.append(c)
+        self.afunclist.append(c.afunc)
+
+    def end_command(self):
+        self.o.data = ""
+        self.afunclist = self.afunclist[:-1]
+
+    def start_sconstruct(self, attrs):
+        sys.stdout.write('<programlisting>')
+
+    def end_sconstruct(self):
+        sys.stdout.write('</programlisting>')
+
+try:
+    file = sys.argv[1]
+except IndexError:
+    file = '-'
+
+if file == '-':
+    f = sys.stdin
+else:
+    try:
+        f = open(file, 'r')
+    except IOError, msg:
+        print file, ":", msg
+        sys.exit(1)
+
+data = f.read()
+if f is not sys.stdin:
+    f.close()
+
+x = MySGML()
+for c in data:
+    x.feed(c)
+x.close()
 
 Import('env', 'whereis')
 
+build = os.path.join('#build', 'doc')
+
 #
 #
 #
 tidy = whereis('tidy')
 
 tar_deps = []
-tar_list = ""
+tar_list = []
 
 entity_re = re.compile(r'<!entity\s+(?:%\s+)?(?:\S+)\s+SYSTEM\s+"([^"]*)">', re.I)
 format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format="([^"]*)")?')
     # rebuild all the docs every time just because the date changes.
     #
     date, ver, rev = env.Dictionary('DATE', 'VERSION', 'REVISION')
-    verfile = str(File("version.sgml"))
+    #version_sgml = File(os.path.join(build, "version.sgml"))
+    version_sgml = File("version.sgml")
+    verfile = str(version_sgml)
     try:
         os.unlink(verfile)
     except OSError:
         main = os.path.join(doc, 'main.sgml')
         out = 'main.out'
 
-        htmldir = os.path.join('HTML', 'scons-%s' % doc)
+        # Hard-coding the scons-src path is a bit of a hack.  This can
+        # be reworked when a better solution presents itself.
+        scons_src_main = os.path.join('#build', 'scons-src', 'doc', main)
+        env.Ignore(scons_src_main, version_sgml)
+
+        htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc)
         htmlindex = os.path.join(htmldir, docs[doc]['htmlindex'])
-        html = os.path.join('HTML', 'scons-%s.html' % doc)
-        ps = os.path.join('PS', 'scons-%s.ps' % doc)
-        pdf = os.path.join('PDF', 'scons-%s.pdf' % doc)
-        text = os.path.join('TEXT', 'scons-%s.txt' % doc)
+        html = os.path.join(build, 'HTML', 'scons-%s.html' % doc)
+        ps = os.path.join(build, 'PS', 'scons-%s.ps' % doc)
+        pdf = os.path.join(build, 'PDF', 'scons-%s.pdf' % doc)
+        text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc)
 
         if docs[doc].get('html') and jade:
             cmds = [
             env.Command(html, main, cmds)
             Local(html)
 
-            env.Ignore([html, htmlindex], "version.sgml")
+            env.Ignore([html, htmlindex], version_sgml)
 
             tar_deps.extend([html, htmlindex])
-            tar_list = string.join([tar_list, html, htmldir], " ")
+            tar_list.extend([html, htmldir])
 
             if fig2dev:
                 for g in docs[doc].get('graphics', []):
             ])
             Local(ps)
 
-            env.Ignore(ps, "version.sgml")
+            env.Ignore(ps, version_sgml)
 
             tar_deps.append(ps)
-            tar_list = tar_list + " " + ps
+            tar_list.append(ps)
 
             if fig2dev:
                 for g in docs[doc].get('graphics', []):
                     fig = os.path.join(doc, '%s.fig' % g)
-                    eps = os.path.join('PS', '%s.eps' % g)
+                    eps = os.path.join(build, 'PS', '%s.eps' % g)
                     env.Command(eps, fig, "%s -L eps $SOURCES $TARGET" % fig2dev)
                     env.Depends(ps, eps)
                     Local(eps)
             ])
             Local(pdf)
 
-            env.Ignore(pdf, "version.sgml")
+            env.Ignore(pdf, version_sgml)
 
             tar_deps.append(pdf)
-            tar_list = tar_list + " " + pdf
+            tar_list.append(pdf)
 
         if docs[doc].get('text') and jade and lynx:
             env.Command(text, html, "lynx -dump ${SOURCE.abspath} > $TARGET")
             Local(text)
 
-            env.Ignore(text, "version.sgml")
+            env.Ignore(text, version_sgml)
 
             tar_deps.append(text)
-            tar_list = tar_list + " " + text
+            tar_list.append(text)
 
 #
 # Man page(s), in good ol' troff format.
     man_1 = os.path.join('man', '%s.1' % man)
 
     if groff:
-        ps = os.path.join('PS', '%s-man.ps' % man)
-        text = os.path.join('TEXT', '%s-man.txt' % man)
+        ps = os.path.join(build, 'PS', '%s-man.ps' % man)
+        text = os.path.join(build, 'TEXT', '%s-man.txt' % man)
 
         env.Command(ps, man_1, "groff -man -Tps $SOURCES > $TARGET")
         Local(ps)
         Local(text)
 
         tar_deps.extend([ps, text])
-        tar_list = string.join([tar_list, ps, text], " ")
+        tar_list.extend([ps, text])
 
     if man2html:
-        html = os.path.join('HTML' , '%s-man.html' % man)
+        html = os.path.join(build, 'HTML' , '%s-man.html' % man)
 
         cmds = [ "man2html $SOURCES > $TARGET" ]
         if tidy:
         Local(html)
 
         tar_deps.append(html)
-        tar_list = tar_list + " " + html
+        tar_list.append(html)
 
 #
 # Now actually create the tar file of the documentation,
 # for easy distribution to the web site.
 #
 if tar_deps:
+    tar_list = map(lambda x: x[11:], tar_list)
     env.Command(doc_tar_gz, tar_deps,
                 "tar cf${TAR_HFLAG} - -C build/doc %s | gzip > $TARGET" % tar_list)
     Local(doc_tar_gz)

doc/user/actions.in

+<!--
+
+  Copyright (c) 2001, 2002, 2003 Steven Knight
+
+  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.
+
+-->
+
+<!--
+
+=head1 Build actions
+
+Cons supports several types of B<build actions> that can be performed
+to construct one or more target files.  Usually, a build action is
+a construction command, that is, a command-line string that invokes
+an external command.  Cons can also execute Perl code embedded in a
+command-line string, and even supports an experimental ability to build
+a target file by executing a Perl code reference directly.
+
+A build action is usually specified as the value of a construction
+variable:
+
+  $env = new cons(
+	CCCOM         => '%CC %CFLAGS %_IFLAGS -c %< -o %>',
+	LINKCOM       => '[perl] &link_executable("%>", "%<")',
+	ARCOM         => sub { my($env, $target, @sources) = @_;
+				 # code to create an archive
+				}
+  );
+
+A build action may be associated directly with one or more target files
+via the C<Command> method; see below.
+
+=head2 Construction commands
+
+A construction command goes through expansion of construction variables
+and C<%-> pseudo-variables, as described above, to create the actual
+command line that Cons will execute to generate the target file or
+files.
+
+After substitution occurs, strings of white space are converted into
+single blanks, and leading and trailing white space is eliminated. It
+is therefore currently not possible to introduce variable length white
+space in strings passed into a command.
+
+If a multi-line command string is provided, the commands are executed
+sequentially. If any of the commands fails, then none of the rest are
+executed, and the target is not marked as updated, i.e. a new signature is
+not stored for the target.
+
+Normally, if all the commands succeed, and return a zero status (or whatever
+platform-specific indication of success is required), then a new signature
+is stored for the target. If a command erroneously reports success even
+after a failure, then Cons will assume that the target file created by that
+command is accurate and up-to-date.
+
+The first word of each command string, after expansion, is assumed to be an
+executable command looked up on the C<PATH> environment variable (which is,
+in turn, specified by the C<ENV> construction variable). If this command is
+found on the path, then the target will depend upon it: the command will
+therefore be automatically built, as necessary. It's possible to write
+multi-part commands to some shells, separated by semi-colons. Only the first
+command word will be depended upon, however, so if you write your command
+strings this way, you must either explicitly set up a dependency (with the
+C<Depends> method), or be sure that the command you are using is a system
+command which is expected to be available. If it isn't available, you will,
+of course, get an error.
+
+Cons normally prints a command before executing it.  This behavior is
+suppressed if the first character of the command is C<@>.  Note that
+you may need to separate the C<@> from the command name or escape it to
+prevent C<@cmd> from looking like an array to Perl quote operators that
+perform interpolation:
+
+  # The first command line is incorrect,
+  # because "@cp" looks like an array
+  # to the Perl qq// function.
+  # Use the second form instead.
+  Command $env 'foo', 'foo.in', qq(
+	@cp %< tempfile
+	@ cp tempfile %>
+  );
+
+If there are shell meta characters anywhere in the expanded command line,
+such as C<E<lt>>, C<E<gt>>, quotes, or semi-colon, then the command
+will actually be executed by invoking a shell. This means that a command
+such as:
+
+  cd foo
+
+alone will typically fail, since there is no command C<cd> on the path. But
+the command string:
+
+  cd $<:d; tar cf $>:f $<:f
+
+when expanded will still contain the shell meta character semi-colon, and a
+shell will be invoked to interpret the command. Since C<cd> is interpreted
+by this sub-shell, the command will execute as expected.
+
+=head2 Perl expressions
+
+If any command (even one within a multi-line command) begins with
+C<[perl]>, the remainder of that command line will be evaluated by the
+running Perl instead of being forked by the shell.  If an error occurs
+in parsing the Perl code, or if the Perl expression returns 0 or undef,
+the command will be considered to have failed.  For example, here is a
+simple command which creates a file C<foo> directly from Perl:
+
+  $env = new cons();
+  Command $env 'foo',
+    qq([perl] open(FOO,'>foo');print FOO "hi\\n"; close(FOO); 1);
+
+Note that when the command is executed, you are in the same package as
+when the F<Construct> or F<Conscript> file was read, so you can call
+Perl functions you've defined in the same F<Construct> or F<Conscript>
+file in which the C<Command> appears:
+
+  $env = new cons();
+  sub create_file {
+	my $file = shift;
+	open(FILE, ">$file");
+	print FILE "hi\n";
+	close(FILE);
+	return 1;
+  }
+  Command $env 'foo', "[perl] &create_file('%>')";
+
+The Perl string will be used to generate the signature for the derived
+file, so if you change the string, the file will be rebuilt.  The contents
+of any subroutines you call, however, are not part of the signature,
+so if you modify a called subroutine such as C<create_file> above,
+the target will I<not> be rebuilt.  Caveat user.
+
+=head2 Perl code references [EXPERIMENTAL]
+
+Cons supports the ability to create a derived file by directly executing
+a Perl code reference.  This feature is considered EXPERIMENTAL and
+subject to change in the future.
+
+A code reference may either be a named subroutine referenced by the
+usual C<\&> syntax:
+
+  sub build_output {
+	my($env, $target, @sources) = @_;
+	print "build_output building $target\n";
+	open(OUT, ">$target");
+	foreach $src (@sources) {
+	    if (! open(IN, "<$src")) {
+		print STDERR "cannot open '$src': $!\n";
+		return undef;
+	    }
+	    print OUT, <IN>;
+	}
+	close(OUT);
+	return 1;
+  }
+  Command $env 'output', \&build_output;
+
+or the code reference may be an anonymous subroutine:
+
+  Command $env 'output', sub {
+	my($env, $target, @sources) = @_;
+	print "building $target\n";
+	open(FILE, ">$target");
+	print FILE "hello\n";
+	close(FILE);
+	return 1;
+  };
+
+To build the target file, the referenced subroutine is passed, in order:
+the construction environment used to generate the target; the path
+name of the target itself; and the path names of all the source files
+necessary to build the target file.
+
+The code reference is expected to generate the target file, of course,
+but may manipulate the source and target files in any way it chooses.
+The code reference must return a false value (C<undef> or C<0>) if
+the build of the file failed.  Any true value indicates a successful
+build of the target.
+
+Building target files using code references is considered EXPERIMENTAL
+due to the following current limitations:
+
+=over 4
+
+Cons does I<not> print anything to indicate the code reference is being
+called to build the file.  The only way to give the user any indication
+is to have the code reference explicitly print some sort of "building"
+message, as in the above examples.
+
+Cons does not generate any signatures for code references, so if the
+code in the reference changes, the target will I<not> be rebuilt.
+
+Cons has no public method to allow a code reference to extract
+construction variables.  This would be good to allow generalization of
+code references based on the current construction environment, but would
+also complicate the problem of generating meaningful signatures for code
+references.
+
+=back
+
+Support for building targets via code references has been released in
+this version to encourage experimentation and the seeking of possible
+solutions to the above limitations.
+
+-->
+
+ <para>
+
+   XXX
+
+ </para>
+
+ <section>
+ <title>XXX</title>
+
+   <para>
+
+   XXX
+
+   </para>
+
+ </section>

doc/user/alias.in

+<!--
+
+  Copyright (c) 2001, 2002, 2003 Steven Knight
+
+  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.
+
+-->
+
+  <para>
+
+  We've already seen how you can use the &Alias;
+  function to create a target named <literal>install</literal>:
+
+  </para>
+
+  <scons_example name="ex1">
+     <file name="SConstruct" printme="1">
+     env = Environment()
+     hello = env.Program('hello.c')
+     env.Install('__ROOT__/usr/bin', hello)
+     env.Alias('install', '__ROOT__/usr/bin')
+     </file>
+     <file name="hello.c">
+     int main() { printf("Hello, world!\n"); }
+     </file>
+  </scons_example>
+
+  <para>
+
+  You can then use this alias on the command line
+  to tell &SCons; more naturally that you want to install files:
+
+  </para>
+
+  <scons_output example="ex1" os="posix">
+     <command>scons install</command>
+  </scons_output>
+
+  <para>
+
+  Like other &Builder; methods, though,
+  the &Alias; method returns an object
+  representing the alias being built.
+  You can then use this object as input to anothother &Builder;.
+  This is especially useful if you use such an object
+  as input to another call to the &Alias; &Builder;,
+  allowing you to create a hierarchy
+  of nested aliases:
+
+  </para>
+
+  <scons_example name="ex2">
+     <file name="SConstruct" printme="1">
+     env = Environment()
+     p = env.Program('foo.c')
+     l = env.Library('bar.c')
+     env.Install('__ROOT__/usr/bin', p)
+     env.Install('__ROOT__/usr/lib', l)
+     ib = env.Alias('install-bin', '__ROOT__/usr/bin')
+     il = env.Alias('install-lib', '__ROOT__/usr/lib')
+     env.Alias('install', [ib, il])
+     </file>
+     <file name="foo.c">
+     int main() { printf("foo.c\n"); }
+     </file>
+     <file name="bar.c">
+     void bar() { printf("bar.c\n"); }
+     </file>
+  </scons_example>
+
+  <para>
+
+  This example defines separate <literal>install</literal>,
+  <literal>install-bin</literal>,
+  and <literal>install-lib</literal> aliases,
+  allowing you finer control over what gets installed:
+
+  </para>
+
+  <scons_output example="ex2" os="posix">
+     <command>scons install-bin</command>
+     <command>scons install-lib</command>
+     <command>scons -c __ROOT__/</command>
+     <command>scons install</command>
+  </scons_output>
+<!--
+
+  Copyright (c) 2001, 2002, 2003 Steven Knight
+
+  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.
+
+-->
+
+ <para>
+
+   XXX
+
+ </para>
+
+ <section>
+ <title>Differences Between &Ant; and &SCons;</title>
+
+   <para>
+
+   XXX
+
+   </para>
+
+ </section>
+
+ <section>
+ <title>Advantages of &SCons; Over &Ant;</title>
+
+   <para>
+
+   XXX
+
+   </para>
+
+ </section>

doc/user/builders-built-in.in

+<!--
+
+  Copyright (c) 2001, 2002, 2003 Steven Knight
+
+  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.
+
+-->
+
+  <para>
+
+  &SCons; provides the ability to build a lot of different
+  types of files right "out of the box."
+  So far, we've been using &SCons;' ability to build
+  programs, objects and libraries to
+  illustrate much of the underlying functionality of &SCons;
+  This section will describe all of the different
+  types of files that you can build with &SCons;,
+  and the built-in &Builder; objects used to build them.
+
+  </para>
+
+  <section>
+  <title>Programs:  the &Program; Builder</title>
+
+    <para>
+
+    As we've seen, the &Program; Builder
+    is used to build an executable program.
+    The &source; argument is one or more
+    source-code files or object files,
+    and the &target; argument is the
+    name of the executable program name to be created.
+    For example:
+
+    </para>
+
+    <programlisting>
+      env = Environment()
+      env.Program('prog', 'file1.o')
+    </programlisting>
+
+    <para>
+
+    Will create the &prog;
+    executable on a POSIX system,
+    the &prog_exe; executable on a Windows system.
+
+    </para>
+
+    <para>
+
+    The target file's prefix and suffix may be omitted,
+    and the values from the
+    $PROGPREFIX
+    and
+    $PROGSUFFIX
+    construction variables
+    will be appended appropriately.
+    For example:
+
+    </para>
+
+    <programlisting>
+      env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx')
+      env.Program('prog', ['file1.o', 'file2.o'])
+    </programlisting>
+
+    <para>
+
+    Will create a program named
+    <filename>myprog.xxx</filename>
+    regardless of the system on which it is run.
+
+    </para>
+
+    <para>
+
+    If you omit the &target;,
+    the base of the first input
+    file name specified
+    because the base of the target
+    program created.
+    For example:
+
+    </para>
+
+    <programlisting>
+      env = Environment()
+      env.Program(['hello.c', 'goodbye.c'])
+    </programlisting>
+
+    <para>
+
+    Will create the &hello;
+    executable on a POSIX system,
+    the &hello_exe; executable on a Windows system.
+
+    </para>
+
+  </section>
+
+  <section>
+  <title>Object-File Builders</title>
+
+    <para>
+
+    &SCons; provides separate Builder objects
+    to create both static and shared object files.
+
+    </para>
+
+    <section>
+    <title>The &StaticObject; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>The &SharedObject; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>The &Object; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <para>
+
+      Creates a static object file.
+
+      </para>
+
+    </section>
+
+  </section>
+
+  <section>
+  <title>Library Builders</title>
+
+    <para>
+
+    &SCons; provides separate Builder objects
+    to create both static and shared libraries.
+
+    </para>
+
+    <section>
+    <title>The &StaticLibrary; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>The &SharedLibrary; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+    </section>
+
+    <section>
+    <title>The &Library; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+      <para>
+
+      Creates a static library file.
+
+      </para>
+
+    </section>
+
+  </section>
+
+  <section>
+  <title>Pre-Compiled Headers:  the &PCH; Builder</title>
+
+    <para>
+
+    XXX
+
+    </para>
+
+  </section>
+
+  <section>
+  <title>Microsoft Visual C++ Resource Files: the &RES; Builder</title>
+
+    <para>
+
+    XXX
+
+    </para>
+
+  </section>
+
+  <section>
+  <title>Source Files</title>
+
+    <para>
+
+    By default
+    &SCons; supports two Builder objects
+    that know how to build source files
+    from other input files.
+
+    </para>
+
+    <section>
+    <title>The &CFile; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>The &CXXFile; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+  </section>
+
+  <section>
+  <title>Documents</title>
+
+    <para>
+
+    &SCons; provides a number of Builder objects
+    for creating different types of documents.
+
+    </para>
+
+    <section>
+    <title>The &DVI; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>The &PDF; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+    </section>
+
+    <section>
+    <title>The &PostScript; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+  </section>
+
+  <section>
+  <title>Archives</title>
+
+    <para>
+
+    &SCons; provides Builder objects
+    for creating two different types of archive files.
+
+    </para>
+
+    <section>
+    <title>The &Tar; Builder</title>
+
+      <para>
+
+      The &Tar; Builder object uses the &tar;
+      utility to create archives of files
+      and/or directory trees:
+
+      </para>
+
+      <scons_example name="ex1">
+        <file name="SConstruct" printme="1">
+        env = Environment()
+        env.Tar('out1.tar', ['file1', 'file2'])
+        env.Tar('out2', 'directory')
+        </file>
+        <file name="file1">
+        file1
+        </file>
+        <file name="file2">
+        file2
+        </file>
+        <file name="directory/file3">
+        directory/file3
+        </file>
+      </scons_example>
+
+      <scons_output example="ex1" os="posix">
+        <command>scons .</command>
+      </scons_output>
+
+      <para>
+
+      One common requirement when creating a &tar; archive
+      is to create a compressed archive using the
+      <option>-z</option> option.
+      This is easily handled by specifying
+      the value of the &TARFLAGS; variable
+      when you create the construction environment.
+      Note, however, that the <option>-c</option> used to
+      to instruct &tar; to create the archive
+      is part of the default value of &TARFLAGS;,
+      so you need to set it both options:
+
+      </para>
+
+      <scons_example name="ex2">
+        <file name="SConstruct" printme="1">
+        env = Environment(TARFLAGS = '-c -z')
+        env.Tar('out.tar.gz', 'directory')
+        </file>
+        <file name="directory/file">
+        directory/file
+        </file>
+      </scons_example>
+
+      <scons_output example="ex2" os="posix">
+        <command>scons .</command>
+      </scons_output>
+
+      <para>
+
+      you may also wish to set the value of the
+      &TARSUFFIX; construction variable
+      to your desired suffix for compress &tar; archives,
+      so that &SCons; can append it to the target file name
+      without your having to specify it explicitly:
+
+      </para>
+
+      <scons_example name="ex3">
+        <file name="SConstruct" printme="1">
+        env = Environment(TARFLAGS = '-c -z',
+                          TARSUFFIX = '.tgz')
+        env.Tar('out', 'directory')
+        </file>
+        <file name="directory/file">
+        directory/file
+        </file>
+      </scons_example>
+
+      <scons_output example="ex3" os="posix">
+        <command>scons .</command>
+      </scons_output>
+
+    </section>
+
+    <section>
+    <title>The &Zip; Builder</title>
+
+      <para>
+
+      The &Zip; Builder object creates archives of files
+      and/or directory trees in the ZIP file format.
+      Python versions 1.6 or later
+      contain an internal &zipfile; module
+      that &SCons; will use.
+      In this case, given the following
+      &SConstruct; file:
+
+      </para>
+
+      <scons_example name="ex4">
+        <file name="SConstruct" printme="1">
+        env = Environment()
+        env.Zip('out', ['file1', 'file2'])
+        </file>
+        <file name="file1">
+        file1
+        </file>
+        <file name="file2">
+        file2
+        </file>
+      </scons_example>
+
+      <para>
+
+      Your output will reflect the fact
+      that an internal Python function
+      is being used to create the output ZIP archive:
+
+      </para>
+
+      <scons_output example="ex4" os="posix">
+        <command>scons .</command>
+      </scons_output>
+
+      <para>
+
+      If you're using Python version 1.5.2 to run &SCons;,
+      then &SCons; will try to use an external
+      &zip; program as follows:
+
+      </para>
+
+      <literallayout>
+        % <userinput>scons .</userinput>
+        zip /home/my/project/zip.out file1 file2
+      </literallayout>
+
+    </section>
+
+  </section>
+
+  <section>
+  <title>Java</title>
+
+    <para>
+
+    &SCons; provides Builder objects
+    for creating various types of Java output files.
+
+    </para>
+
+    <section>
+    <title>Building Class Files:  the &Java; Builder</title>
+
+      <para>
+
+      The &Java; builder takes one or more input
+      <filename>.java</filename> files
+      and turns them into one or more
+      <filename>.class</filename> files
+      Unlike most builders, however,
+      the &Java; builder takes
+      target and source <emphasis>directories</emphasis>,
+      not files, as input.
+
+      </para>
+
+      <programlisting>
+        env = Environment()
+        env.Java(target = 'classes', source = 'src')
+      </programlisting>
+
+      <para>
+
+      The &Java; builder will then
+      search the specified source directory
+      tree for all <filename>.java</filename> files,
+      and pass any out-of-date
+
+      </para>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>The &Jar; Builder</title>
+
+      <para>
+
+      The &Jar; builder object XXX
+
+      </para>
+
+      <programlisting>
+        env = Environment()
+        env.Java(target = 'classes', source = 'src')
+        env.Jar(target = '', source = 'classes')
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>Building C header and stub files:  the &JavaH; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+    <section>
+    <title>Building RMI stub and skeleton class files:  the &RMIC; Builder</title>
+
+      <para>
+
+      XXX
+
+      </para>
+
+      <programlisting>
+        XXX
+      </programlisting>
+
+      <literallayout>
+        XXX
+      </literallayout>
+
+    </section>
+
+  </section>

doc/user/builders-commands.in

+<!--
+
+  Copyright (c) 2001, 2002, 2003 Steven Knight
+
+  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 C<Command> method
+
+
+  The C<Command> method is called as follows:
+
+    Command $env <target>, <inputs>, <build action>;
+
+  The target is made dependent upon the list of input files specified, and the
+  inputs must be built successfully or Cons will not attempt to build the
+  target.
+
+  To specify a command with multiple targets, you can specify a reference to a
+  list of targets. In Perl, a list reference can be created by enclosing a
+  list in square brackets. Hence the following command:
+
+    Command $env ['foo.h', 'foo.c'], 'foo.template', q(
+  	gen %1
+    );
+
+  could be used in a case where the command C<gen> creates two files, both
+  F<foo.h> and F<foo.c>.
+
+  -->
+
+  <para>
+
+  Creating a &Builder; and attaching it to a &consenv;
+  allows for a lot of flexibility when you
+  want to re-use actions
+  to build multiple files of the same type.
+  This can, however, be cumbersome
+  if you only need to execute one specific command
+  to build a single file (or group of files).
+  For these situations, &SCons; supports a
+  &Command; &Builder; that arranges
+  for a specific action to be executed
+  to build a specific file or files.
+  This looks a lot like the other builders
+  (like &Program;, &Object;, etc.),
+  but takes as an additional argument
+  the command to be executed to build the file:
+
+  </para>
+
+  <scons_example name="ex1">
+     <file name="SConstruct" printme="1">
+     env = Environment()
+     env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET")
+     </file>
+     <file name="foo.in">
+     foo.in
+     </file>
+  </scons_example>
+
+  <scons_output example="ex1">
+    <command>scons .</command>
+  </scons_output>
+
+  <para>
+
+  This is often more convenient than
+  creating a &Builder; object
+  and adding it to the &BUILDERS; variable
+  of a &consenv;
+
+  </para>
+
+  <para>
+
+  Note that the action you 
+
+  </para>
+
+  <scons_example name="ex2">
+     <file name="SConstruct" printme="1">
+     env = Environment()
+     def build(target, source, env):
+         # Whatever it takes to build
+         return None
+     env.Command('foo.out', 'foo.in', build)
+     </file>
+     <file name="foo.in">
+     foo.in
+     </file>
+  </scons_example>
+
+  <scons_output example="ex2">
+    <command>scons .</command>
+  </scons_output>

doc/user/builders-writing.in

+<!--
+
+  Copyright (c) 2001, 2002, 2003 Steven Knight
+
+  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 &BUILDERS;
+    &consvar; in an environment.
+    The &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})
+       env.Foo('file.foo', 'file.input')
+       </file>
+       <file name="file.input">
+       file.input
+       </file>
+    </scons_example>
+
+    <programlisting>
+       bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
+       env = Environment(BUILDERS = {'Foo' : bld})
+    </programlisting>
+
+    <para>
+
+    With the &Builder; so attached to our &consenv;
+    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">
+      <command>scons</command>
+    </scons_output>
+
+    <para>
+
+    Note, however, that the default &BUILDERS;
+    variable in a &consenv;
+    comes with a default set of &Builder; objects
+    already defined:
+    &Program;, &Library;, etc.
+    And when we explicitly set the &BUILDERS; variable
+    when we create the &consenv;,
+    the default &Builder;s are no longer part of
+    the environment:
+
+    </para>
+
+    <scons_example name="ex2">
+       <file name="SConstruct" printme="1">
+       bld = Builder(action = 'foobuild < $SOURCE > $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: Reading SConscript files ...
+      other errors
+      Traceback (most recent call last):
+        File "/usr/lib/scons/SCons/Script/__init__.py", line 901, in main
+          _main()
+        File "/usr/lib/scons/SCons/Script/__init__.py", line 762, in _main
+          SCons.Script.SConscript.SConscript(script)
+        File "/usr/lib/scons/SCons/Script/SConscript.py", line 207, in SConscript
+          exec _file_ in stack[-1].globals
+        File "SConstruct", line 4, in ?
+          env.Program('hello.c')
+      scons: Environment instance has no attribute 'Program'
+    -->
+
+    <scons_output example="ex2">
+      <command>scons</command>
+    </scons_output>
+
+    <para>
+
+    To be able use both our own defined &Builder; objects
+    and the default &Builder; objects in the same &consenv;,
+    you can either add to the &BUILDERS; variable
+    using the &Append; function:
+
+    </para>
+
+    <scons_example name="ex3">
+       <file name="SConstruct">
+       env = Environment()
+       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>
+    </scons_example>
+
+    <para>
+
+    Or you can explicitly set the appropriately-named
+    key in the &BUILDERS; dictionary:
+
+    </para>
+