Allow Builder objects to have both an action and a src_builder.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 5 Jan 2002 12:40:19 +0000 (12:40 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 5 Jan 2002 12:40:19 +0000 (12:40 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@189 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py

index fc04479ed987f5c5545179bbbbcb7bdc17c208ec..856d6dc0af89571cd86b3f7b578207df13c4bc01 100644 (file)
@@ -24,7 +24,8 @@ RELEASE 0.03 -
     to each individually.
 
   - Refactor to move CompositeBuilder initialization logic from the
-    factory wrapper to the __init__() method.
+    factory wrapper to the __init__() method, and allow a Builder to
+    have both an action and a src_builder (or array of them).
 
   From Anthony Roach:
 
index 27d93e940da3f9396a946248fb0e47579596bf24..0cf70e4133b2ceb45556743e016e8a3951726eb8 100644 (file)
@@ -122,11 +122,11 @@ elif os.name == 'nt':
 
 def Builder(**kw):
     """A factory for builder objects."""
-    if kw.has_key('src_builder'):
-        return apply(MultiStepBuilder, (), kw)
-    elif kw.has_key('action') and (type(kw['action']) is types.DictType or
-                                   isinstance(kw['action'], UserDict)):
+    if kw.has_key('action') and (type(kw['action']) is types.DictType or
+                                 isinstance(kw['action'], UserDict)):
         return apply(CompositeBuilder, (), kw)
+    elif kw.has_key('src_builder'):
+        return apply(MultiStepBuilder, (), kw)
     else:
         return apply(BuilderBase, (), kw)
 
@@ -263,46 +263,64 @@ class CompositeBuilder(BuilderBase):
     will examine the target's sources.  If they are all the same
     suffix, and that suffix is equal to one of the child builders'
     src_suffix, then that child builder will be used.  Otherwise,
-    UserError is thrown.
-
-    Child builders are supplied via the builders arg to the
-    constructor.  Each must have its src_suffix set."""
+    UserError is thrown."""
     def __init__(self,  name = None,
                         prefix='',
                         suffix='',
-                        action = {}):
+                        action = {},
+                        src_builder = []):
         BuilderBase.__init__(self, name=name, prefix=prefix,
                              suffix=suffix)
+        if src_builder and not type(src_builder) is types.ListType:
+            src_builder = [src_builder]
+        self.src_builder = src_builder
         self.builder_dict = {}
         for suff, act in action.items():
-             self.builder_dict[suff] = BuilderBase(name = name,
-                                                   action = act,
-                                                   src_suffix = suff)
+             # Create subsidiary builders for every suffix in the
+             # action dictionary.  If there's a src_builder that
+             # matches the suffix, add that to the initializing
+             # keywords so that a MultiStepBuilder will get created.
+             kw = {'name' : name, 'action' : act, 'src_suffix' : suff}
+             src_bld = filter(lambda x, s=suff: x.suffix == s, self.src_builder)
+             if src_bld:
+                 kw['src_builder'] = src_bld[0]
+             self.builder_dict[suff] = apply(Builder, (), kw)
 
     def __call__(self, env, target = None, source = None):
         ret = BuilderBase.__call__(self, env, target=target, source=source)
 
         builder_dict = {}
+        suff_dict = {}
         for suffix, bld in self.builder_dict.items():
             builder_dict[env.subst(bld.src_suffix)] = bld
+            suff_dict[suffix] = suffix
+            b = bld
+            while hasattr(b, 'src_builder'):
+                # Walk the chain of src_builders and add the
+                # src_suffix strings to the maps so that we know
+                # all of those suffixes are legal, too.
+                b = b.src_builder
+                s = env.subst(b.src_suffix)
+                builder_dict[s] = bld
+                suff_dict[s] = suffix
 
         if type(ret) is types.ListType:
             tlist = ret
         else:
             tlist = [ ret ]
         for tnode in tlist:
-            suflist = map(lambda x: os.path.splitext(x.path)[1],
+            suflist = map(lambda x, s=suff_dict: s[os.path.splitext(x.path)[1]],
                           tnode.sources)
             last_suffix=''
             for suffix in suflist:
                 if last_suffix and last_suffix != suffix:
-                    raise UserError, "The builder for %s is only capable of building source files of identical suffixes." % tnode.path
+                    raise UserError, "The builder for %s can only build source files of identical suffixes:  %s." % (tnode.path, str(map(lambda t: str(t.path), tnode.sources)))
                 last_suffix = suffix
             if last_suffix:
                 try:
                     tnode.builder_set(builder_dict[last_suffix])
                 except KeyError:
-                    raise UserError, "Builder not capable of building files with suffix: %s" % suffix
+                    raise UserError, "The builder for %s can not build files with suffix: %s" % (tnode.path, suffix)
         return ret
 
 print_actions = 1;
index 9f2183fd64780bda3b457bb93b26cc55562b1869..b735a0931abde57c35b3947e4ecae73264f131f9 100644 (file)
@@ -454,6 +454,57 @@ class BuilderTestCase(unittest.TestCase):
             flag = 1
         assert flag, "UserError should be thrown when we build targets with files of different suffixes."
 
+        foo_bld = SCons.Builder.Builder(action = 'a-foo',
+                                            src_suffix = '.ina',
+                                            suffix = '.foo')
+        assert isinstance(foo_bld, SCons.Builder.BuilderBase)
+        builder = SCons.Builder.Builder(action = { '.foo' : 'foo',
+                                                   '.bar' : 'bar' },
+                                        src_builder = foo_bld)
+        assert isinstance(builder, SCons.Builder.CompositeBuilder)
+
+        tgt = builder(env, target='t1', source='t1a.ina t1b.ina')
+        assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+
+        tgt = builder(env, target='t2', source='t2a.foo t2b.ina')
+        assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder), tgt.builder.__dict__
+
+        bar_bld = SCons.Builder.Builder(action = 'a-bar',
+                                        src_suffix = '.inb',
+                                        suffix = '.bar')
+        assert isinstance(bar_bld, SCons.Builder.BuilderBase)
+        builder = SCons.Builder.Builder(action = { '.foo' : 'foo',
+                                                   '.bar' : 'bar' },
+                                        src_builder = [foo_bld, bar_bld])
+        assert isinstance(builder, SCons.Builder.CompositeBuilder)
+
+        tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')
+        assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
+
+        tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')
+        assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
+
+        flag = 0
+        try:
+            tgt = builder(env, target='t5', source='test5a.foo test5b.inb')
+        except SCons.Errors.UserError:
+            flag = 1
+        assert flag, "UserError should be thrown when we build targets with files of different suffixes."
+
+        flag = 0
+        try:
+            tgt = builder(env, target='t6', source='test6a.bar test6b.ina')
+        except SCons.Errors.UserError:
+            flag = 1
+        assert flag, "UserError should be thrown when we build targets with files of different suffixes."
+
+        flag = 0
+        try:
+            tgt = builder(env, target='t4', source='test4a.ina test4b.inb')
+        except SCons.Errors.UserError:
+            flag = 1
+        assert flag, "UserError should be thrown when we build targets with files of different suffixes."
+
     def test_build_scanner(self):
         """Testing ability to set a target scanner through a builder."""
         global instanced