18.6. Builders That Modify the Target or Source Lists Using an Emitter

SCons supports the ability for a Builder to modify the lists of target(s) from the specified source(s). You do this by defining an emitter function that takes as its arguments the list of the targets passed to the builder, the list of the sources passed to the builder, and the construction environment. The emitter function should return the modified lists of targets that should be built and sources from which the targets will be built.

For example, suppose you want to define a Builder that always calls a foobuild program, and you want to automatically add a new target file named new_target and a new source file named new_source whenever it's called. The SConstruct file might look like this:


       def modify_targets(target, source, env):
           target.append('new_target')
           source.append('new_source')
           return target, source
       bld = Builder(action = 'foobuild $TARGETS - $SOURCES',
                     suffix = '.foo',
                     src_suffix = '.input',
                     emitter = modify_targets)
       env = Environment(BUILDERS = {'Foo' : bld})
       env.Foo('file')
    

And would yield the following output:


      % scons -Q
      foobuild file.foo new_target - file.input new_source
    

One very flexible thing that you can do is use a construction variable to specify different emitter functions for different construction variable. To do this, specify a string containing a construction variable expansion as the emitter when you call the Builder function, and set that construction variable to the desired emitter function in different construction environments:


        bld = Builder(action = 'my_command $SOURCES > $TARGET',
                      suffix = '.foo',
                      src_suffix = '.input',
                      emitter = '$MY_EMITTER')
        def modify1(target, source, env):
            return target, source + ['modify1.in']
        def modify2(target, source, env):
            return target, source + ['modify2.in']
        env1 = Environment(BUILDERS = {'Foo' : bld},
                           MY_EMITTER = modify1)
        env2 = Environment(BUILDERS = {'Foo' : bld},
                           MY_EMITTER = modify2)
        env1.Foo('file1')
        env2.Foo('file2')
        import os
        env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd()
        env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd()

    

      bld = Builder(action = 'my_command $SOURCES > $TARGET',
                    suffix = '.foo',
                    src_suffix = '.input',
                    emitter = '$MY_EMITTER')
      def modify1(target, source, env):
          return target, source + ['modify1.in']
      def modify2(target, source, env):
          return target, source + ['modify2.in']
      env1 = Environment(BUILDERS = {'Foo' : bld},
                         MY_EMITTER = modify1)
      env2 = Environment(BUILDERS = {'Foo' : bld},
                         MY_EMITTER = modify2)
      env1.Foo('file1')
      env2.Foo('file2')
    

In this example, the modify1.in and modify2.in files get added to the source lists of the different commands:


      % scons -Q
      my_command file1.input modify1.in > file1.foo
      my_command file2.input modify2.in > file2.foo