Search code examples
cachingbuildbuilderscons

Explicitly Appending Environment State to a SCons Builder Dependency MD5 Fingerprint


I have modified my SCons Builder action to use subprocess.Popen() instead of env.Execute(). But I still need to add some md5-sum dependency check to the contents of env so that it rebuilds and caches new target files. How do I append to the SCons internal MD5 hash?


Solution

  • Suppose you have a builder that uses an environment variable:

    # Just an example. The point is that the output of this function
    # depends upon an environment variable
    def bld_func(target, source, env):
        with open(str(target[0]), 'w') as f:
            f.write(' '.join(str(t) for t in target) + '\n')
            f.write(' '.join(str(s) for s in source) + '\n')
            f.write(env.subst('$MY_VAR') + '\n')
    bld = Builder(action=bld_func)
    

    and a SConstruct that invokes it:

    env = Environment()
    env['BUILDERS']['BLD'] = bld
    env.BLD('output.txt', 'input.txt', MY_VAR=ARGUMENTS['MY_VAR'])
    

    This builder won't be re-executed if you change the value of MY_VAR:

    $ scons -Q MY_VAR=cc output.txt
    bld_func(["output.txt"], ["input.txt"])
    $ scons -Q MY_VAR=gcc output.txt
    scons: `output.txt' is up to date.
    

    But, you can add a dependency of the value of a string with env.Value:

    env.Depends('output.txt', env.Value(env.subst('$MY_VAR')))
    

    The logical place to put such a dependency is in an emitter:

    def bld_emitter(target, source, env):
        env.Depends(target, env.Value(env.subst('$MY_VAR')))
        return target, source
    bld = Builder(action=bld_func, emitter=bld_emitter)
    

    Complete SConstruct:

    # Create a builder that depends upon an environment variable
    def bld_func(target, source, env):
        with open(str(target[0]), 'w') as f:
            f.write(' '.join(str(t) for t in target) + '\n')
            f.write(' '.join(str(s) for s in source) + '\n')
            f.write(env.subst('$MY_VAR') + '\n')
    def bld_emitter(target, source, env):
        env.Depends(target, env.Value(env.subst('$MY_VAR')))
        return target, source
    bld = Builder(action=bld_func, emitter=bld_emitter)
    
    # Attach it to an environment
    env = Environment()
    env['BUILDERS']['BLD'] = bld
    
    # Invoke the builder
    env.BLD('output.txt', 'input.txt', MY_VAR=ARGUMENTS['MY_VAR'])
    

    Sample output:

    $ scons -Q MY_VAR=cc output.txt
    bld_func(["output.txt"], ["input.txt"])
    $ scons -Q MY_VAR=gcc output.txt
    bld_func(["output.txt"], ["input.txt"])
    $ scons -Q MY_VAR=gcc output.txt
    scons: `output.txt' is up to date.