Allow a list of emitters to be called in sequence. (Chad Austin)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 4 Apr 2004 04:01:53 +0000 (04:01 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 4 Apr 2004 04:01:53 +0000 (04:01 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@946 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Tool/mingw.py
src/engine/SCons/Tool/mslink.py
src/engine/SCons/Tool/msvc.py
src/engine/SCons/Tool/qt.py
test/emitter.py

index 0c4674720f11bcf71b8b8b8c949db22ce7a705f2..da35784fc3d461af5f23f7e539c627521595f7fc 100644 (file)
@@ -6261,12 +6261,12 @@ any of the suffixes of the builder. Using this argument produces a
 multi-stage builder.
 
 .IP emitter
-A function to manipulate the target and source
+A function or list of functions to manipulate the target and source
 lists before dependencies are established
 and the target(s) are actually built.
 .B emitter
 can also be string containing a construction variable to expand
-to an emitter function,
+to an emitter function or list of functions,
 or a dictionary mapping source file suffixes
 to emitter functions.
 (Only the suffix of the first source file
@@ -6295,11 +6295,23 @@ def e(target, source, env):
 b = Builder("my_build < $TARGET > $SOURCE",
             emitter = e)
 
-# Calling an emitter through a construction variable.
+def e2(target, source, env):
+    return (target + ['bar.foo'], source + ['bar.src'])
+
+# Simple association of a list of emitter functions with a Builder.
+b = Builder("my_build < $TARGET > $SOURCE",
+            emitter = [e, e2])
+
+# Calling an emitter function through a construction variable.
 env = Environment(MY_EMITTER = e)
 b = Builder("my_build < $TARGET > $SOURCE",
             emitter = '$MY_EMITTER')
 
+# Calling a list of emitter functions through a construction variable.
+env = Environment(EMITTER_LIST = [e, e2])
+b = Builder("my_build < $TARGET > $SOURCE",
+            emitter = '$EMITTER_LIST')
+
 # Associating multiple emitters with different file
 # suffixes using a dictionary.
 def e_suf1(target, source, env):
index 49584a19dca48bec07553508e724e677d108d6a8..d2d933ba12932ec98f3973981253a3710a14195d 100644 (file)
@@ -19,6 +19,9 @@ RELEASE 0.96 - XXX
   - Add a $QT_AUTOBUILD_MOC_SOURCES construction variable that controls
     whether moc-generated .cpp files get compiled.
 
+  - Allow the emitter argument to a Builder() to be or expand to a list
+    of emitter functions, which will be called in sequence.
+
   From Chad Austin and Christoph Wiedemann:
 
   - Add support for a $RPATH variable to supply a list of directories
index 871a80398b1d1977d82b43e3f215ed9d6bc81e6b..0da770a3c7df2b5cbbe4e8ada6a0c4975a82f3b9 100644 (file)
@@ -45,6 +45,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import os.path
 import UserDict
+import UserList
 
 import SCons.Action
 from SCons.Debug import logInstanceCreation
@@ -116,6 +117,15 @@ class DictEmitter(SCons.Util.Selector):
             target, source = emitter(target, source, env)
         return (target, source)
 
+class ListEmitter(UserList.UserList):
+    """A callable list of emitters that calls each in sequence,
+    returning the result.
+    """
+    def __call__(self, target, source, env):
+        for e in self.data:
+            target, source = e(target, source, env)
+        return (target, source)
+
 def Builder(**kw):
     """A factory for builder objects."""
     composite = None
@@ -142,6 +152,8 @@ def Builder(**kw):
             kw['emitter'] = EmitterProxy(var)
         elif SCons.Util.is_Dict(emitter):
             kw['emitter'] = DictEmitter(emitter)
+        elif SCons.Util.is_List(emitter):
+            kw['emitter'] = ListEmitter(emitter)
 
     if kw.has_key('src_builder'):
         ret = apply(MultiStepBuilder, (), kw)
@@ -242,13 +254,16 @@ class EmitterProxy:
         # Recursively substitute the variable.
         # We can't use env.subst() because it deals only
         # in strings.  Maybe we should change that?
-        while SCons.Util.is_String(emitter) and \
-              env.has_key(emitter):
+        while SCons.Util.is_String(emitter) and env.has_key(emitter):
             emitter = env[emitter]
-        if not callable(emitter):
-            return (target, source)
+        if callable(emitter):
+            target, source = emitter(target, source, env)
+        elif SCons.Util.is_List(emitter):
+            for e in emitter:
+                target, source = e(target, source, env)
+
+        return (target, source)
 
-        return emitter(target, source, env)
 
     def __cmp__(self, other):
         return cmp(self.var, other.var)
index bd41f099b8ceaa17845d83faa58d8bbbcffbf217..e2dd0e946ebbe6c3305a9fb15c5248ff8a817ffd 100644 (file)
@@ -903,6 +903,42 @@ class BuilderTestCase(unittest.TestCase):
         tgt = builder4(env, source='ccc.4c')
         assert str(tgt) == 'emit4c-ccc', str(tgt)
 
+        # Test a list of emitter functions.
+        def emit5a(target, source, env):
+            source = map(str, source)
+            target = target + map(lambda x: 'emit5a-' + x[:-2], source)
+            return (target, source)
+        def emit5b(target, source, env):
+            source = map(str, source)
+            target = target + map(lambda x: 'emit5b-' + x[:-2], source)
+            return (target, source)
+        builder5 = SCons.Builder.Builder(action='foo',
+                                         emitter=[emit5a, emit5b],
+                                         node_factory=MyNode)
+
+        tgts = builder5(env, target='target-5', source='aaa.5')
+        tgts = map(str, tgts)
+        assert tgts == ['target-5', 'emit5a-aaa', 'emit5b-aaa'], tgts
+
+        # Test a list of emitter functions through the environment.
+        def emit6a(target, source, env):
+            source = map(str, source)
+            target = target + map(lambda x: 'emit6a-' + x[:-2], source)
+            return (target, source)
+        def emit6b(target, source, env):
+            source = map(str, source)
+            target = target + map(lambda x: 'emit6b-' + x[:-2], source)
+            return (target, source)
+        builder6 = SCons.Builder.Builder(action='foo',
+                                         emitter='$EMITTERLIST',
+                                         node_factory=MyNode)
+                                         
+        env = Environment(EMITTERLIST = [emit6a, emit6b])
+
+        tgts = builder6(env, target='target-6', source='aaa.6')
+        tgts = map(str, tgts)
+        assert tgts == ['target-6', 'emit6a-aaa', 'emit6b-aaa'], tgts
+
     def test_no_target(self):
         """Test deducing the target from the source."""
 
index 18fa79ecf36d222707ad14fa71fb03919e688776..fa46652a1125211a458de22a6fcbae5620c66992 100644 (file)
@@ -123,7 +123,7 @@ def generate(env):
     env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
     env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
     env['SHLINKCOM']   = shlib_action
-    env['SHLIBEMITTER']= shlib_emitter
+    env.Append(SHLIBEMITTER = [shlib_emitter])
     env['LINK'] = 'g++'
     env['AS'] = 'as'
     env['WIN32DEFPREFIX']        = ''
@@ -133,7 +133,7 @@ def generate(env):
 
     env['RC'] = 'windres'
     env['RCFLAGS'] = SCons.Util.CLVar('')
-    env['RCINCFLAGS'] = SCons.Util.CLVar('$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs)} $)')
+    env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs)} $)'
     env['RCINCPREFIX'] = '--include-dir '
     env['RCINCSUFFIX'] = ''
     env['RCCOM'] = '$RC $RCINCFLAGS $RCFLAGS -i $SOURCE -o $TARGET'
index fd53757a6de3db116d17ee847902db6c5ef24ed8..7e32e603b1661fa17440adf0c94cb3412e5a18b8 100644 (file)
@@ -140,12 +140,12 @@ def generate(env):
     env['_SHLINK_TARGETS'] = win32ShlinkTargets
     env['_SHLINK_SOURCES'] = win32ShlinkSources
     env['SHLINKCOM']   =  compositeLinkAction
-    env['SHLIBEMITTER']= win32LibEmitter
+    env.Append(SHLIBEMITTER = [win32LibEmitter])
     env['LINK']        = 'link'
     env['LINKFLAGS']   = SCons.Util.CLVar('/nologo')
     env['_PDB'] = pdbGenerator
     env['LINKCOM'] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $SOURCES")}'
-    env['PROGEMITTER'] = prog_emitter
+    env.Append(PROGEMITTER = [prog_emitter])
     env['LIBDIRPREFIX']='/LIBPATH:'
     env['LIBDIRSUFFIX']=''
     env['LIBLINKPREFIX']=''
index 1a2cb1c371f9a14974effec5809f803c9bbf633c..19fbf1da5ae163b545e6ad9edf032dc0857ad29a 100644 (file)
@@ -441,8 +441,8 @@ def generate(env):
     env['CPPDEFSUFFIX']  = ''
     env['INCPREFIX']  = '/I'
     env['INCSUFFIX']  = ''
-    env['OBJEMITTER'] = static_object_emitter
-    env['SHOBJEMITTER'] = shared_object_emitter
+    env.Append(OBJEMITTER = [static_object_emitter])
+    env.Append(SHOBJEMITTER = [shared_object_emitter])
     env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
 
     env['RC'] = 'rc'
index a5a53b7661a4078b471812c3c9572c7c94b927a4..f8234ba1facf645ab54babe3dcb7efd161962fb3 100644 (file)
@@ -93,7 +93,7 @@ class _Automoc:
             h=None
             for h_ext in header_extensions:
                 if os.path.exists(src_prefix + h_ext):
-                    h = FS.File(prefix + h_ext)
+                    h = FS.File(src_prefix + h_ext)
 
             if ui:
                 # file built from .ui file -> build also header from .ui
@@ -102,7 +102,7 @@ class _Automoc:
                 ui_h_suff = env.subst('$QT_UIHSUFFIX')
                 if os.path.exists(src_prefix + ui_h_suff):
                     # if a .ui.h file exists, we need to specify the dependecy ...
-                    ui_h = FS.File(prefix + ui_h_suff)
+                    ui_h = FS.File(src_prefix + ui_h_suff)
                     env.Depends(cpp, ui_h)
             if (h and q_object_search.search(h.get_contents())) or ui:
                 # h file with the Q_OBJECT macro found -> add moc_cpp
@@ -212,12 +212,12 @@ def generate(env):
     # We can't refer to the builders directly, we have to fetch them
     # as Environment attributes because that sets them up to be called
     # correctly later by our emitter.
-    env['PROGEMITTER'] = _Automoc('StaticObject',
-                                  uicDeclBld,mocFromHBld,mocFromCppBld)
-    env['SHLIBEMITTER'] = _Automoc('SharedObject',
-                                   uicDeclBld,mocFromHBld,mocFromCppBld)
-    env['LIBEMITTER'] = _Automoc('StaticObject',
-                                 uicDeclBld,mocFromHBld,mocFromCppBld)
+    env.Append(PROGEMITTER = [_Automoc('StaticObject',
+                                  uicDeclBld,mocFromHBld,mocFromCppBld)],
+               SHLIBEMITTER = [_Automoc('SharedObject',
+                                   uicDeclBld,mocFromHBld,mocFromCppBld)],
+               LIBEMITTER = [_Automoc('StaticObject',
+                                 uicDeclBld,mocFromHBld,mocFromCppBld)])
     # Of course, we need to link against the qt libraries
     env.AppendUnique(CPPPATH=[os.path.join('$QTDIR', 'include')])
     env.AppendUnique(LIBPATH=[os.path.join('$QTDIR', 'lib')])
index 70d4c8fdf3e4d4868520a8653af9a4c7e9f79059..0bdc619e5b10e79ead33375eff28a82312e1efe1 100644 (file)
@@ -49,30 +49,47 @@ def emitter(target, source, env):
     target.append(str(target[0])+".foo")
     return target,source
 
-b = Builder(action=build, emitter=emitter)
+def emit1(t, s, e): return (t + ['emit.1'], s)
+def emit2(t, s, e): return (t + ['emit.2'], s)
 
-env=Environment(BUILDERS={ 'foo': b })
+foo = Builder(action=build, emitter=emitter)
+bar = Builder(action=build, emitter='$EMITTERS')
+
+env=Environment(BUILDERS={ 'foo': foo, 'bar': bar },
+                EMITTERS=[emit1, emit2])
 env.foo('f.out', 'f.in')
 env.foo(File('g.out'), 'g.in')
+env.bar('h.out', 'h.in')
 """)
 
 test.write(['src', 'f.in'], 'f.in')
 test.write(['src', 'g.in'], 'g.in')
+test.write(['src', 'h.in'], 'h.in')
 
 test.run(arguments='.')
 
-test.fail_test(not os.path.exists(test.workpath('src', 'f.out')))
-test.fail_test(not os.path.exists(test.workpath('src', 'f.out.foo')))
-test.fail_test(not os.path.exists(test.workpath('var1', 'f.out')))
-test.fail_test(not os.path.exists(test.workpath('var1', 'f.out.foo')))
-test.fail_test(not os.path.exists(test.workpath('var2', 'f.out')))
-test.fail_test(not os.path.exists(test.workpath('var2', 'f.out.foo')))
-
-test.fail_test(not os.path.exists(test.workpath('src', 'g.out')))
-test.fail_test(not os.path.exists(test.workpath('src', 'g.out.foo')))
-test.fail_test(not os.path.exists(test.workpath('var1', 'g.out')))
-test.fail_test(not os.path.exists(test.workpath('var1', 'g.out.foo')))
-test.fail_test(not os.path.exists(test.workpath('var2', 'g.out')))
-test.fail_test(not os.path.exists(test.workpath('var2', 'g.out.foo')))
+test.must_exist(test.workpath('src', 'f.out'))
+test.must_exist(test.workpath('src', 'f.out.foo'))
+test.must_exist(test.workpath('var1', 'f.out'))
+test.must_exist(test.workpath('var1', 'f.out.foo'))
+test.must_exist(test.workpath('var2', 'f.out'))
+test.must_exist(test.workpath('var2', 'f.out.foo'))
+
+test.must_exist(test.workpath('src', 'g.out'))
+test.must_exist(test.workpath('src', 'g.out.foo'))
+test.must_exist(test.workpath('var1', 'g.out'))
+test.must_exist(test.workpath('var1', 'g.out.foo'))
+test.must_exist(test.workpath('var2', 'g.out'))
+test.must_exist(test.workpath('var2', 'g.out.foo'))
+
+test.must_exist(test.workpath('src', 'h.out'))
+test.must_exist(test.workpath('src', 'emit.1'))
+test.must_exist(test.workpath('src', 'emit.2'))
+test.must_exist(test.workpath('var1', 'h.out'))
+test.must_exist(test.workpath('var1', 'emit.1'))
+test.must_exist(test.workpath('var1', 'emit.2'))
+test.must_exist(test.workpath('var2', 'h.out'))
+test.must_exist(test.workpath('var2', 'emit.1'))
+test.must_exist(test.workpath('var2', 'emit.2'))
 
 test.pass_test()