From: stevenknight Date: Sun, 4 Apr 2004 04:01:53 +0000 (+0000) Subject: Allow a list of emitters to be called in sequence. (Chad Austin) X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=34b82ad5d3f7fe342b5e258aacce42d40d2b41b3;p=scons.git Allow a list of emitters to be called in sequence. (Chad Austin) git-svn-id: http://scons.tigris.org/svn/scons/trunk@946 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 0c467472..da35784f 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -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): diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 49584a19..d2d933ba 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -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 diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 871a8039..0da770a3 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -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) diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index bd41f099..e2dd0e94 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -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.""" diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index 18fa79ec..fa46652a 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -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' diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index fd53757a..7e32e603 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -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']='' diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index 1a2cb1c3..19fbf1da 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -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' diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index a5a53b76..f8234ba1 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -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')]) diff --git a/test/emitter.py b/test/emitter.py index 70d4c8fd..0bdc619e 100644 --- a/test/emitter.py +++ b/test/emitter.py @@ -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()