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 atexit
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 -j
. Here's a
simple example:
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)
The atexit.register
call
registers print_build_failures
as an atexit
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
.node
,
.errstr
,
.filename
, and
.command
.
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
filename
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.
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'.