summaryrefslogtreecommitdiff
path: root/doc/user/output.xml
diff options
context:
space:
mode:
Diffstat (limited to 'doc/user/output.xml')
-rw-r--r--doc/user/output.xml703
1 files changed, 703 insertions, 0 deletions
diff --git a/doc/user/output.xml b/doc/user/output.xml
new file mode 100644
index 0000000..89b443b
--- /dev/null
+++ b/doc/user/output.xml
@@ -0,0 +1,703 @@
+<!--
+
+ 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.
+
+-->
+
+ <para>
+
+ A key aspect of creating a usable build configuration
+ is providing good output from the build
+ so its users can readily understand
+ what the build is doing
+ and get information about how to control the build.
+ &SCons; provides several ways of
+ controlling output from the build configuration
+ to help make the build
+ more useful and understandable.
+
+ </para>
+
+ <section>
+ <title>Providing Build Help: the &Help; Function</title>
+
+ <para>
+
+ It's often very useful to be able to give
+ users some help that describes the
+ specific targets, build options, etc.,
+ that can be used for your build.
+ &SCons; provides the &Help; function
+ to allow you to specify this help text:
+
+ </para>
+
+ <programlisting>
+ Help("""
+ Type: 'scons program' to build the production program,
+ 'scons debug' to build the debug version.
+ """)
+ </programlisting>
+
+ <para>
+
+ (Note the above use of the Python triple-quote syntax,
+ which comes in very handy for
+ specifying multi-line strings like help text.)
+
+ </para>
+
+ <para>
+
+ When the &SConstruct; or &SConscript; files
+ contain such a call to the &Help; function,
+ the specified help text will be displayed in response to
+ the &SCons; <literal>-h</literal> option:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -h</userinput>
+ scons: Reading SConscript files ...
+ scons: done reading SConscript files.
+
+ Type: 'scons program' to build the production program,
+ 'scons debug' to build the debug version.
+
+ Use scons -H for help about command-line options.
+ </screen>
+
+ <para>
+
+ The &SConscript; files may contain
+ multiple calls to the &Help; function,
+ in which case the specified text(s)
+ will be concatenated when displayed.
+ This allows you to split up the
+ help text across multiple &SConscript; files.
+ In this situation, the order in
+ which the &SConscript; files are called
+ will determine the order in which the &Help; functions are called,
+ which will determine the order in which
+ the various bits of text will get concatenated.
+
+ </para>
+
+ <para>
+
+ Another use would be to make the help text conditional
+ on some variable.
+ For example, suppose you only want to display
+ a line about building a Windows-only
+ version of a program when actually
+ run on Windows.
+ The following &SConstruct; file:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+
+ Help("\nType: 'scons program' to build the production program.\n")
+
+ if env['PLATFORM'] == 'win32':
+ Help("\nType: 'scons windebug' to build the Windows debug version.\n")
+ </programlisting>
+
+ <para>
+
+ Will display the complete help text on Windows:
+
+ </para>
+
+ <screen>
+ C:\><userinput>scons -h</userinput>
+ scons: Reading SConscript files ...
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No installed VCs
+ File "&lt;stdin&gt;", line 67, in __call__
+
+ scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly
+ File "&lt;stdin&gt;", line 67, in __call__
+ scons: done reading SConscript files.
+
+ Type: 'scons program' to build the production program.
+
+ Type: 'scons windebug' to build the Windows debug version.
+
+ Use scons -H for help about command-line options.
+ </screen>
+
+ <para>
+
+ But only show the relevant option on a Linux or UNIX system:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -h</userinput>
+ scons: Reading SConscript files ...
+ scons: done reading SConscript files.
+
+ Type: 'scons program' to build the production program.
+
+ Use scons -H for help about command-line options.
+ </screen>
+
+ <para>
+
+ If there is no &Help; text in the &SConstruct; or
+ &SConscript; files,
+ &SCons; will revert to displaying its
+ standard list that describes the &SCons; command-line
+ options.
+ This list is also always displayed whenever
+ the <literal>-H</literal> option is used.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Controlling How &SCons; Prints Build Commands: the <envar>$*COMSTR</envar> Variables</title>
+
+ <para>
+
+ Sometimes the commands executed
+ to compile object files or link programs
+ (or build other targets)
+ can get very long,
+ long enough to make it difficult for users
+ to distinguish error messages or
+ other important build output
+ from the commands themselves.
+ All of the default <envar>$*COM</envar> variables
+ that specify the command lines
+ used to build various types of target files
+ have a corresponding <envar>$*COMSTR</envar> variable
+ that can be set to an alternative
+ string that will be displayed
+ when the target is built.
+
+ </para>
+
+ <para>
+
+ For example, suppose you want to
+ have &SCons; display a
+ <literal>"Compiling"</literal>
+ message whenever it's compiling an object file,
+ and a
+ <literal>"Linking"</literal>
+ when it's linking an executable.
+ You could write a &SConstruct; file
+ that looks like:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CCCOMSTR = "Compiling $TARGET",
+ LINKCOMSTR = "Linking $TARGET")
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+ Which would then yield the output:
+
+ </para>
+
+ <!--
+
+ <scons_output example="COMSTR" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Compiling foo.o
+ Linking foo
+ </screen>
+
+ <para>
+
+ &SCons; performs complete variable substitution
+ on <envar>$*COMSTR</envar> variables,
+ so they have access to all of the
+ standard variables like &cv-TARGET; &cv-SOURCES;, etc.,
+ as well as any construction variables
+ that happen to be configured in
+ the construction environment
+ used to build a specific target.
+
+ </para>
+
+ <para>
+
+ Of course, sometimes it's still important to
+ be able to see the exact command
+ that &SCons; will execute to build a target.
+ For example, you may simply need to verify
+ that &SCons; is configured to supply
+ the right options to the compiler,
+ or a developer may want to
+ cut-and-paste a compile command
+ to add a few options
+ for a custom test.
+
+ </para>
+
+ <para>
+
+ One common way to give users
+ control over whether or not
+ &SCons; should print the actual command line
+ or a short, configured summary
+ is to add support for a
+ <varname>VERBOSE</varname>
+ command-line variable to your &SConstruct; file.
+ A simple configuration for this might look like:
+
+ </para>
+
+ <programlisting>
+ env = Environment()
+ if ARGUMENTS.get('VERBOSE') != "1':
+ env['CCCOMSTR'] = "Compiling $TARGET"
+ env['LINKCOMSTR'] = "Linking $TARGET"
+ env.Program('foo.c')
+ </programlisting>
+
+ <para>
+
+
+ By only setting the appropriate
+ <envar>$*COMSTR</envar> variables
+ if the user specifies
+ <literal>VERBOSE=1</literal>
+ on the command line,
+ the user has control
+ over how &SCons;
+ displays these particular command lines:
+
+ </para>
+
+ <!--
+
+ <scons_output example="COMSTR-VERBOSE" os="posix">
+ <scons_output_command>scons -Q</scons_output_command>
+ <scons_output_command>scons -Q -c</scons_output_command>
+ <scons_output_command>scons -Q VERBOSE=1</scons_output_command>
+ </scons_output>
+
+ -->
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Compiling foo.o
+ Linking foo
+ % <userinput>scons -Q -c</userinput>
+ Removed foo.o
+ Removed foo
+ % <userinput>scons -Q VERBOSE=1</userinput>
+ cc -o foo.o -c foo.c
+ cc -o foo foo.o
+ </screen>
+
+ </section>
+
+ <section>
+ <title>Providing Build Progress Output: the &Progress; Function</title>
+
+ <para>
+
+ Another aspect of providing good build output
+ is to give the user feedback
+ about what &SCons; is doing
+ even when nothing is being built at the moment.
+ This can be especially true for large builds
+ when most of the targets are already up-to-date.
+ Because &SCons; can take a long time
+ making absolutely sure that every
+ target is, in fact, up-to-date
+ with respect to a lot of dependency files,
+ it can be easy for users to mistakenly
+ conclude that &SCons; is hung
+ or that there is some other problem with the build.
+
+ </para>
+
+ <para>
+
+ One way to deal with this perception
+ is to configure &SCons; to print something to
+ let the user know what it's "thinking about."
+ The &Progress; function
+ allows you to specify a string
+ that will be printed for every file
+ that &SCons; is "considering"
+ while it is traversing the dependency graph
+ to decide what targets are or are not up-to-date.
+
+ </para>
+
+ <programlisting>
+ Progress('Evaluating $TARGET\n')
+ Program('f1.c')
+ Program('f2.c')
+ </programlisting>
+
+ <para>
+
+ Note that the &Progress; function does not
+ arrange for a newline to be printed automatically
+ at the end of the string (as does the Python
+ <literal>print</literal> statement),
+ and we must specify the
+ <literal>\n</literal>
+ that we want printed at the end of the configured string.
+ This configuration, then,
+ will have &SCons;
+ print that it is <literal>Evaluating</literal>
+ each file that it encounters
+ in turn as it traverses the dependency graph:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ Evaluating SConstruct
+ Evaluating f1.c
+ Evaluating f1.o
+ cc -o f1.o -c f1.c
+ Evaluating f1
+ cc -o f1 f1.o
+ Evaluating f2.c
+ Evaluating f2.o
+ cc -o f2.o -c f2.c
+ Evaluating f2
+ cc -o f2 f2.o
+ Evaluating .
+ </screen>
+
+ <para>
+
+ Of course, normally you don't want to add
+ all of these additional lines to your build output,
+ as that can make it difficult for the user
+ to find errors or other important messages.
+ A more useful way to display
+ this progress might be
+ to have the file names printed
+ directly to the user's screen,
+ not to the same standard output
+ stream where build output is printed,
+ and to use a carriage return character
+ (<literal>\r</literal>)
+ so that each file name gets re-printed on the same line.
+ Such a configuration would look like:
+
+ </para>
+
+ <programlisting>
+ Progress('$TARGET\r',
+ file=open('/dev/tty', 'w'),
+ overwrite=True)
+ Program('f1.c')
+ Program('f2.c')
+ </programlisting>
+
+ <para>
+
+ Note that we also specified the
+ <literal>overwrite=True</literal> argument
+ to the &Progress; function,
+ which causes &SCons; to
+ "wipe out" the previous string with space characters
+ before printing the next &Progress; string.
+ Without the
+ <literal>overwrite=True</literal> argument,
+ a shorter file name would not overwrite
+ all of the charactes in a longer file name that
+ precedes it,
+ making it difficult to tell what the
+ actual file name is on the output.
+ Also note that we opened up the
+ <filename>/dev/tty</filename> file
+ for direct access (on POSIX) to
+ the user's screen.
+ On Windows, the equivalent would be to open
+ the <filename>con:</filename> file name.
+
+ </para>
+
+ <para>
+
+ Also, it's important to know that although you can use
+ <literal>$TARGET</literal> to substitute the name of
+ the node in the string,
+ the &Progress; function does <emphasis>not</emphasis>
+ perform general variable substitution
+ (because there's not necessarily a construction
+ environment involved in evaluating a node
+ like a source file, for example).
+
+ </para>
+
+ <para>
+
+ You can also specify a list of strings
+ to the &Progress; function,
+ in which case &SCons; will
+ display each string in turn.
+ This can be used to implement a "spinner"
+ by having &SCons; cycle through a
+ sequence of strings:
+
+ </para>
+
+ <programlisting>
+ Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5)
+ Program('f1.c')
+ Program('f2.c')
+ </programlisting>
+
+ <para>
+
+ Note that here we have also used the
+ <literal>interval=</literal>
+ keyword argument to have &SCons;
+ only print a new "spinner" string
+ once every five evaluated nodes.
+ Using an <literal>interval=</literal> count,
+ even with strings that use <literal>$TARGET</literal> like
+ our examples above,
+ can be a good way to lessen the
+ work that &SCons; expends printing &Progress; strings,
+ while still giving the user feedback
+ that indicates &SCons; is still
+ working on evaluating the build.
+
+ </para>
+
+ <para>
+
+ Lastly, you can have direct control
+ over how to print each evaluated node
+ by passing a Python function
+ (or other Python callable)
+ to the &Progress; function.
+ Your function will be called
+ for each evaluated node,
+ allowing you to
+ implement more sophisticated logic
+ like adding a counter:
+
+ </para>
+
+ <programlisting>
+ screen = open('/dev/tty', 'w')
+ count = 0
+ def progress_function(node)
+ count += 1
+ screen.write('Node %4d: %s\r' % (count, node))
+
+ Progress(progress_function)
+ </programlisting>
+
+ <para>
+
+ Of course, if you choose,
+ you could completely ignore the
+ <varname>node</varname> argument to the function,
+ and just print a count,
+ or anything else you wish.
+
+ </para>
+
+ <para>
+
+ (Note that there's an obvious follow-on question here:
+ how would you find the total number of nodes
+ that <emphasis>will be</emphasis>
+ evaluated so you can tell the user how
+ close the build is to finishing?
+ Unfortunately, in the general case,
+ there isn't a good way to do that,
+ short of having &SCons; evaluate its
+ dependency graph twice,
+ first to count the total and
+ the second time to actually build the targets.
+ This would be necessary because
+ you can't know in advance which
+ target(s) the user actually requested
+ to be built.
+ The entire build may consist of thousands of Nodes,
+ for example,
+ but maybe the user specifically requested
+ that only a single object file be built.)
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Printing Detailed Build Status: the &GetBuildFailures; Function</title>
+
+ <para>
+
+ SCons, like most build tools, returns zero status to
+ the shell on success and nonzero status on failure.
+ Sometimes it's useful to give more information about
+ the build status at the end of the run, for instance
+ to print an informative message, send an email, or
+ page the poor slob who broke the build.
+
+ </para>
+
+ <para>
+
+ SCons provides a &GetBuildFailures; method that
+ you can use in a python <function>atexit</function> function
+ to get a list of objects describing the actions that failed
+ while attempting to build targets. There can be more
+ than one if you're using <literal>-j</literal>. Here's a
+ simple example:
+
+ </para>
+
+ <programlisting>
+ import atexit
+
+ def print_build_failures():
+ from SCons.Script import GetBuildFailures
+ for bf in GetBuildFailures():
+ print "%s failed: %s" % (bf.node, bf.errstr)
+ atexit.register(print_build_failures)
+ </programlisting>
+
+ <para>
+
+ The <function>atexit.register</function> call
+ registers <function>print_build_failures</function>
+ as an <function>atexit</function> callback, to be called
+ before &SCons; exits. When that function is called,
+ it calls &GetBuildFailures; to fetch the list of failed objects.
+ See the man page
+ for the detailed contents of the returned objects;
+ some of the more useful attributes are
+ <literal>.node</literal>,
+ <literal>.errstr</literal>,
+ <literal>.filename</literal>, and
+ <literal>.command</literal>.
+ The <literal>filename</literal> is not necessarily
+ the same file as the <literal>node</literal>; the
+ <literal>node</literal> is the target that was
+ being built when the error occurred, while the
+ <literal>filename</literal>is the file or dir that
+ actually caused the error.
+ Note: only call &GetBuildFailures; at the end of the
+ build; calling it at any other time is undefined.
+
+ </para>
+
+ <para>
+
+ Here is a more complete example showing how to
+ turn each element of &GetBuildFailures; into a string:
+
+ </para>
+
+ <programlisting>
+ # Make the build fail if we pass fail=1 on the command line
+ if ARGUMENTS.get('fail', 0):
+ Command('target', 'source', ['/bin/false'])
+
+ def bf_to_str(bf):
+ """Convert an element of GetBuildFailures() to a string
+ in a useful way."""
+ import SCons.Errors
+ if bf is None: # unknown targets product None in list
+ return '(unknown tgt)'
+ elif isinstance(bf, SCons.Errors.StopError):
+ return str(bf)
+ elif bf.node:
+ return str(bf.node) + ': ' + bf.errstr
+ elif bf.filename:
+ return bf.filename + ': ' + bf.errstr
+ return 'unknown failure: ' + bf.errstr
+ import atexit
+
+ def build_status():
+ """Convert the build status to a 2-tuple, (status, msg)."""
+ from SCons.Script import GetBuildFailures
+ bf = GetBuildFailures()
+ if bf:
+ # bf is normally a list of build failures; if an element is None,
+ # it's because of a target that scons doesn't know anything about.
+ status = 'failed'
+ failures_message = "\n".join(["Failed building %s" % bf_to_str(x)
+ for x in bf if x is not None])
+ else:
+ # if bf is None, the build completed successfully.
+ status = 'ok'
+ failures_message = ''
+ return (status, failures_message)
+
+ def display_build_status():
+ """Display the build status. Called by atexit.
+ Here you could do all kinds of complicated things."""
+ status, failures_message = build_status()
+ if status == 'failed':
+ print "FAILED!!!!" # could display alert, ring bell, etc.
+ elif status == 'ok':
+ print "Build succeeded."
+ print failures_message
+
+ atexit.register(display_build_status)
+ </programlisting>
+
+ <para>
+
+ When this runs, you'll see the appropriate output:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ scons: `.' is up to date.
+ Build succeeded.
+ % <userinput>scons -Q fail=1</userinput>
+ scons: *** [target] Source `source' not found, needed by target `target'.
+ FAILED!!!!
+ Failed building target: Source `source' not found, needed by target `target'.
+ </screen>
+
+ </section>