9.4. Printing Detailed Build Status: the `GetBuildFailures` Function
Prev | Chapter 9. Controlling Build Output | Next

## 9.4. Printing Detailed Build Status: the `GetBuildFailures` Function

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'.

Prev | Up | Next
9.3. Providing Build Progress Output: the `Progress` Function | Home | Chapter 10. Controlling a Build From the Command Line