[Scons-users] Modifying the provided environment in an emitter?

Marc Branchaud marcnarc at xiplink.com
Mon Apr 24 11:38:52 EDT 2017

On 2017-04-21 05:29 PM, Andrew C. Morrow wrote:
> Hi all -
> Is it legit to modify the passed in env in an emitter?

I dunno about "legit" but we do this all the time when we want to pass 
info calculated in the emitter to the builder.

Here's a simple example:  One of the things we build is a complete OS 
image in a variant subdir (which we then bundle into a tarball).  The OS 
image contains symlinks to absolute paths, which are legitimate when the 
image is written onto a root partition, but which are "dead" (the 
symlink's source doesn't exist) when they are in the image's variant subdir.

So we have a SymLink Builder that lets us create these symlinks:


Since the Builder's source isn't an actual file, the emitter sets the 
source to None, so that SCons ignores it.  But we need to save that 
source path to create the actual symlink, so we store it in a dict in 
the environment.  The dict is keyed by the target's variant-dependent 
absolute path.  (This emitter also works with non-absolute-path symlink 
sources, by stripping the path that SCons prepends to such things.)

	def emitter(target, source, env):
	    if '__SymLinkSources' not in env.Dictionary():
	        env['__SymLinkSources'] = {}
	    # The source of a symlink is just an arbitrary
	    # string, but SCons thinks it's a file and so
	    # prepends it with the path to the SConscript
	    # that uses the SymLink builder.  Undo this.
	    path = env.Dir('.').srcnode().abspath
	    src = str(source[0])
	    if src.startswith(path):
	        src = src[len(path)+1:]
	    env['__SymLinkSources'][target[0].abspath] = src
	    return (target, None)

Then the builder simply extracts the source path from the dict:

	def builder(target, source, env):
	    # SCons can't tell the difference between a
	    # symlink that points to an non-existent file
	    # and a symlink that doesn't even exist.
	    # So we need to be smart here, because we
	    # might be asked to re-create a symlink that's
	    # already there.
	    tgt = target[0].abspath
	    if os.path.lexists(tgt):
	        # If the target already exists, just remove it.
	    os.symlink(env['__SymLinkSources'][tgt], tgt)

Several of our Builders do this sort of thing.  Works great!


>     def add_lib_foo_emitter(target, source, env):
>         libs = env.get('LIBS', [])
>         libs.append('foo')
>         env['LIBS'] = libs
>         return (target, source)
>     def add_emitter(builder):
>         base_emitter = builder.emitter
>         new_emitter = SCons.Builder.ListEmitter([add_lib_foo_emitter,
> base_emitter])
>         builder.emitter = new_emitter
>     target_builders = ['Program', 'SharedLibrary', 'LoadableModule',
> 'StaticLibrary']
>     for builder in target_builders:
>         add_emitter(env['BUILDERS'][builder])
> I've found myself wanting to do this with increasing frequency, but am
> unclear on whether this is acceptable. It appears to work, but the
> documentation doesn't seem to offer any clear guidance that I've found
> on whether it is guaranteed to work. If so (which would be great!), am I
> guaranteed that the modification of env is scoped only to the passed in
> targets?
> Thanks,
> Andrew
> _______________________________________________
> Scons-users mailing list
> Scons-users at scons.org
> https://pairlist4.pair.net/mailman/listinfo/scons-users

More information about the Scons-users mailing list