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.
SCons provides a
GetBuildFailures method that
you can use in a python
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 -j. Here's a
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)
atexit callback, to be called
before SCons exits. When that function is called,
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
The filename is not necessarily
the same file as the node; the
node is the target that was
being built when the error occurred, while the
filenameis 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.
Here is a more complete example showing how to
turn each element of
GetBuildFailures into a string:
# 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)
When this runs, you'll see the appropriate output:
% scons -Q scons: `.' is up to date. Build succeeded. % scons -Q fail=1 scons: *** [target] Source `source' not found, needed by target `target'. FAILED!!!! Failed building target: Source `source' not found, needed by target `target'.