Search code examples
scons

SCons delete $TARGET on failure of any Action


Is there a way to emulate Make's .DELETE_ON_FAILURE behavior? If I have a builder that executes a series of Actions to produce a target, I would expect them to operate atomically. If an earlier Action produces an (incomplete) file, and a later action fails to modify it, I would like the target file to be deleted, instead of remaining in its incomplete state.

Consider this SConstruct file:

def example(target, source, env):
    raise Exception('failure')
    # more processing that never happens...

action_list = [
    Copy('$TARGET', '$SOURCE'),
    Chmod('$TARGET', 0755),
    example,
]

Command(
    action = action_list,
    target = 'foo.out',
    source = 'foo.in',
)

If the example action fails, foo.out still exists, because the first two actions were successful. However, it is incomplete.

Interestingly, running scons again causes is to again retry to build foo.out, even though it exists in the filesystem.


Solution

  • Yes, what you are looking for is GetBuildFailures.

    Expanding on your example to include this feature...

    import atexit
    import os
    
    def delete_on_failure():
        from SCons.Script import GetBuildFailures
        for bf in GetBuildFailures():
            if os.path.isfile(bf.node.abspath):
                print 'Removing %s' % bf.node.path
                os.remove(bf.node.abspath)
    atexit.register(delete_on_failure)
    
    def example(target, source, env):
        raise Exception('failure')
        # more processing that never happens...
    
    action_list = [
        Copy('$TARGET', '$SOURCE'),
        Chmod('$TARGET', 0755),
        example,
    ]
    
    Command(
        action = action_list,
        target = 'foo.out',
        source = 'foo.in',
    )
    

    Which when run produces the following...

    >> scons --version
    SCons by Steven Knight et al.:
        script: v2.3.4, 2014/09/27 12:51:43, by garyo on lubuntu
        engine: v2.3.4, 2014/09/27 12:51:43, by garyo on lubuntu
        engine path: ['/usr/lib/scons/SCons']
    Copyright (c) 2001 - 2014 The SCons Foundation
    
    >> tree 
    .
    ├── foo.in
    └── SConstruct
    
    0 directories, 2 files
    
    >> scons
    scons: Reading SConscript files ...
    scons: done reading SConscript files.
    scons: Building targets ...
    Copy("foo.out", "foo.in")
    Chmod("foo.out", 0755)
    example(["foo.out"], ["foo.in"])
    scons: *** [foo.out] Exception : failure
    Traceback (most recent call last):
      File "/usr/lib/scons/SCons/Action.py", line 1065, in execute
        result = self.execfunction(target=target, source=rsources, env=env)
      File "/path/to/SConstruct", line 13, in example
        raise Exception('failure')
    Exception: failure
    scons: building terminated because of errors.
    Removing foo.out
    
    >> tree
    .
    ├── foo.in
    └── SConstruct
    
    0 directories, 2 files