Use the prefix and suffix arguments in the Builder base class.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 12 Oct 2001 00:56:40 +0000 (00:56 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 12 Oct 2001 00:56:40 +0000 (00:56 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@95 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/design/engine.sgml
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Defaults.py

index 8c7babf7f596ccd2c8c7261ec3bbe4cded21e7a8..d325c9a0b2b9f4d1c684978d4d05b3b32b3e55b0 100644 (file)
@@ -691,8 +691,8 @@ Feedback?
 
        <programlisting>
        WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target',
-                       input_suffix = '.in',
-                       output_suffix = '.html')
+                         suffix = '.html',
+                         src_suffix = '.in')
        </programlisting>
 
   <para>
@@ -708,7 +708,7 @@ Feedback?
                return 1
 
        OtherBuilder1 = Builder(function = update,
-                       input_suffix = ['.in', '.input'])
+                               src_suffix = ['.in', '.input'])
        </programlisting>
 
    <para>
@@ -723,8 +723,8 @@ Feedback?
                return 1
 
        OtherBuilder2 = Builder(function = update_arg,
-                       function_arg = 'xyzzy',
-                       input_suffix = ['.in', '.input'])
+                               function_arg = 'xyzzy',
+                               src_suffix = ['.in', '.input'])
        </programlisting>
 
   <para>
@@ -748,8 +748,8 @@ Feedback?
 
        <programlisting>
        WebPage = Builder(command = 'htmlgen $HTMLGENFLAGS $sources > $target',
-                       input_suffix = '.in',
-                       output_suffix = '.html')
+                         suffix = '.html',
+                         src_suffix = '.in')
        env = Environment(BUILDERS = ['WebPage'])
        env.WebPage(target = 'foo.html', source = 'foo.in')
        # Builds 'bar.html' on UNIX, 'bar.htm' on Windows NT:
@@ -765,12 +765,42 @@ Feedback?
 
   <para>
 
-   A &Builder; object may optionally be initialized with a list of
-   the expected suffixes of input files for this object.  It may also
-   be initialized with an output suffix for the files that this
-   &Builder; object builds.  These arguments are used in automatic
-   dependency analysis and in generating output file names that don't
-   have suffixes supplied explicitly.
+   A &Builder; object may optionally be initialized with a list of:
+
+  </para>
+
+   <itemizedlist>
+     <listitem>
+     <para>
+
+       the prefix of the target file (e.g., 'lib' for libraries)
+
+     </para>
+     </listitem>
+
+     <listitem>
+     <para>
+
+       the suffix of the target file (e.g., '.a' for libraries)
+
+     </para>
+     </listitem>
+
+     <listitem>
+     <para>
+
+       the expected suffixes of the input files
+       (e.g., '.o' for object files)
+
+     </para>
+     </listitem>
+   </itemizedlist>
+
+   <para>
+
+    These arguments are used in automatic
+    dependency analysis and to generate output file names that don't
+    have suffixes supplied explicitly.
 
   </para>
  </section>
@@ -788,7 +818,7 @@ Feedback?
 
        <programlisting>
        build = Builder(function = my_build)
-       build_out = build.Copy(output_suffix = '.out')
+       build_out = build.Copy(suffix = '.out')
        </programlisting>
 
   <para>
@@ -891,8 +921,8 @@ Feedback?
 <REMARK>
 Do we even need this anymore?
 Now that the individual builders
-have specified <literal>input_suffix</literal>
-and <literal>output_suffix</literal> values,
+have specified <literal>suffix</literal>
+and <literal>src_suffix</literal> values,
 all of the information we need to support
 the &MakeBuilder; builder is right there in the environment.
 I think this is a holdover from before I
@@ -1237,7 +1267,7 @@ you set it up with another environment...
 <REMARK>
 If the &BUILDERMAP; proves unnecessary,
 we could/should get rid of this one, too,
-by adding a parallel <literal>input_suffix</literal>
+by adding a parallel <literal>src_suffix</literal>
 argument to the &Scanner; factory...
 Comments?
 </REMARK>
index 65d4d41368d6cfa4a6df1e047fae3f0b36602df6..9e84a7d8d25870f4cf3762e78a2991e5ec148545 100644 (file)
@@ -39,32 +39,60 @@ import types
 
 
 
-class Builder:
+def Builder(**kw):
+    """A factory for builder objects."""
+    if kw.has_key('builders'):
+        return apply(MultiStepBuilder, (), kw)
+    else:
+        return apply(BuilderBase, (), kw)
+
+
+
+class BuilderBase:
     """Base class for Builders, objects that create output
     nodes (files) from input nodes (files).
     """
 
     def __init__(self, name = None,
                        action = None,
-                       input_suffix = None,
-                       output_suffix = None,
+                       prefix = None,
+                       suffix = None,
+                       src_suffix = None,
                        node_factory = SCons.Node.FS.default_fs.File):
        self.name = name
        self.action = Action(action)
-       self.insuffix = input_suffix
-       self.outsuffix = output_suffix
+       self.prefix = prefix
+       self.suffix = suffix
+       self.src_suffix = src_suffix
        self.node_factory = node_factory
-       if not self.insuffix is None and self.insuffix[0] != '.':
-           self.insuffix = '.' + self.insuffix
-       if not self.outsuffix is None and self.outsuffix[0] != '.':
-           self.outsuffix = '.' + self.outsuffix
+       if not self.suffix is None and self.suffix[0] != '.':
+           self.suffix = '.' + self.suffix
+       if not self.src_suffix is None and self.src_suffix[0] != '.':
+           self.src_suffix = '.' + self.src_suffix
 
     def __cmp__(self, other):
        return cmp(self.__dict__, other.__dict__)
 
     def __call__(self, env, target = None, source = None):
-       tlist = scons_str2nodes(target, self.node_factory)
-       slist = scons_str2nodes(source, self.node_factory)
+       def adjustixes(files, pre, suf):
+           ret = []
+           if not type(files) is type([]):
+               files = [files]
+           for f in files:
+               if type(f) == type(""):
+                   if pre and f[:len(pre)] != pre:
+                       f = pre + f
+                   if suf:
+                       if f[-len(suf):] != suf:
+                           f = f + suf
+               ret.append(f)
+           return ret
+           
+
+       tlist = scons_str2nodes(adjustixes(target, self.prefix, self.suffix),
+                                self.node_factory)
+       slist = scons_str2nodes(adjustixes(source, None, self.src_suffix),
+                                self.node_factory)
        for t in tlist:
            t.builder_set(self)
            t.env_set(env)
@@ -83,7 +111,8 @@ class Builder:
 class BuilderProxy:
     """This base class serves as a proxy to a builder object,
     exposing the same interface, but just forwarding calls to
-    the underlying object."""
+    the underlying object.  Use it for subclass Builders that
+    need to wrap or decorate another Builder class."""
     def __init__(self, builder):
         self.subject = builder
 
@@ -100,62 +129,30 @@ class BuilderProxy:
         assert 'subject' in self.__dict__.keys(), \
                "You must call __init__() on the BuilderProxy base."
         return getattr(self.subject, name)
-    
-class TargetNamingBuilder(BuilderProxy):
-    """This is a simple Builder Proxy that decorates the names
-    of a Builder's targets prior to passing them to the underlying
-    Builder.  You can use this to simplify the syntax of a target
-    file.  For instance, you might want to call a Library builder
-    with a target of "foo" and expect to get "libfoo.a" back."""
-
-    def __init__(self, builder, prefix='', suffix=''):
-        """
-        builder - The underlying Builder object
-        
-        prefix - text to prepend to target names (may contain
-        environment variables)
-
-        suffix - text to append to target names (may contain
-        environment variables)
-        """
-        BuilderProxy.__init__(self, builder)
-        self.prefix=prefix
-        self.suffix=suffix
 
-    def __call__(self, env, target = None, source = None):
-        tlist = scons_str2nodes(target, self.subject.node_factory)
-        tlist_decorated = []
-        for tnode in tlist:
-            path, fn = os.path.split(tnode.path)
-            tlist_decorated.append(self.subject.
-                                   node_factory(os.path.join(path,
-                                                             env.subst(self.prefix) +
-                                                             fn +
-                                                             env.subst(self.suffix))))
-        return self.subject.__call__(env, target=tlist_decorated, source=source)
-
-class MultiStepBuilder(Builder):
+class MultiStepBuilder(BuilderBase):
     """This is a builder subclass that can build targets in
     multiple steps according to the suffixes of the source files.
     Given one or more "subordinate" builders in its constructor,
     this class will apply those builders to any files matching
-    the builder's input_suffix, using a file of the same name
-    as the source, but with input_suffix changed to output_suffix.
+    the builder's src_suffix, using a file of the same name
+    as the source, but with src_suffix changed to suffix.
     The targets of these builders then become sources for this
     builder.
     """
     def __init__(self,  name = None,
                        action = None,
-                       input_suffix = None,
-                       output_suffix = None,
+                       prefix = None,
+                       suffix = None,
+                       src_suffix = None,
                         node_factory = SCons.Node.FS.default_fs.File,
                         builders = []):
-        Builder.__init__(self, name, action, input_suffix, output_suffix,
-                         node_factory)
+        BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
+                             node_factory)
         self.builder_dict = {}
         for bld in builders:
-            if bld.insuffix and bld.outsuffix:
-                self.builder_dict[bld.insuffix] = bld
+            if bld.suffix and bld.src_suffix:
+                self.builder_dict[bld.src_suffix] = bld
 
     def __call__(self, env, target = None, source = None):
         slist = scons_str2nodes(source, self.node_factory)
@@ -165,7 +162,7 @@ class MultiStepBuilder(Builder):
             if self.builder_dict.has_key(ext):
                 bld = self.builder_dict[ext]
                 tgt = bld(env,
-                          target=[ path+bld.outsuffix, ],
+                          target=[ path+bld.suffix, ],
                           source=snode)
                 if not type(tgt) is types.ListType:
                     final_sources.append(tgt)
@@ -173,8 +170,8 @@ class MultiStepBuilder(Builder):
                     final_sources.extend(tgt)
             else:
                 final_sources.append(snode)
-        return Builder.__call__(self, env, target=target,
-                                source=final_sources)
+        return BuilderBase.__call__(self, env, target=target,
+                                    source=final_sources)
 
 print_actions = 1;
 execute_actions = 1;
index 7db69ef2f34137f68d962185a367f697ac50869d..46e926c3b0a9d5543c5dd10cf20f212aafe0f8de 100644 (file)
@@ -59,12 +59,13 @@ class BuilderTestCase(unittest.TestCase):
     def test__call__(self):
        """Test calling a builder to establish source dependencies
        """
-       env = Environment()
        class Node:
            def __init__(self, name):
                self.name = name
                self.sources = []
                self.derived = 0
+           def __str__(self):
+               return self.name
            def builder_set(self, builder):
                self.builder = builder
            def env_set(self, env):
@@ -91,10 +92,10 @@ class BuilderTestCase(unittest.TestCase):
     def test_cmp(self):
        """Test simple comparisons of Builder objects
        """
-       b1 = SCons.Builder.Builder(input_suffix = '.o')
-       b2 = SCons.Builder.Builder(input_suffix = '.o')
+       b1 = SCons.Builder.Builder(src_suffix = '.o')
+       b2 = SCons.Builder.Builder(src_suffix = '.o')
        assert b1 == b2
-       b3 = SCons.Builder.Builder(input_suffix = '.x')
+       b3 = SCons.Builder.Builder(src_suffix = '.x')
        assert b1 != b3
        assert b2 != b3
 
@@ -198,17 +199,6 @@ class BuilderTestCase(unittest.TestCase):
        c = test.read(outfile, 'r')
        assert c == "act.py: syzygy\nfunction2\nclass2a\nclass2b\n", c
 
-    def test_insuffix(self):
-       """Test Builder creation with a specified input suffix
-       
-       Make sure that the '.' separator is appended to the
-       beginning if it isn't already present.
-       """
-       builder = SCons.Builder.Builder(input_suffix = '.c')
-       assert builder.insuffix == '.c'
-       builder = SCons.Builder.Builder(input_suffix = 'c')
-       assert builder.insuffix == '.c'
-
     def test_name(self):
        """Test Builder creation with a specified name
        """
@@ -225,33 +215,52 @@ class BuilderTestCase(unittest.TestCase):
        builder = SCons.Builder.Builder(node_factory = FooFactory)
        assert builder.node_factory is FooFactory
 
-    def test_outsuffix(self):
-       """Test Builder creation with a specified output suffix
+    def test_prefix(self):
+       """Test Builder creation with a specified target prefix
+
+       Make sure that there is no '.' separator appended.
+       """
+       builder = SCons.Builder.Builder(prefix = 'lib.')
+       assert builder.prefix == 'lib.'
+       builder = SCons.Builder.Builder(prefix = 'lib')
+       assert builder.prefix == 'lib'
+       tgt = builder(env, target = 'tgt1', source = 'src1')
+       assert tgt.path == 'libtgt1', \
+               "Target has unexpected name: %s" % tgt[0].path
+
+    def test_src_suffix(self):
+       """Test Builder creation with a specified source file suffix
+       
+       Make sure that the '.' separator is appended to the
+       beginning if it isn't already present.
+       """
+       builder = SCons.Builder.Builder(src_suffix = '.c')
+       assert builder.src_suffix == '.c'
+       builder = SCons.Builder.Builder(src_suffix = 'c')
+       assert builder.src_suffix == '.c'
+       tgt = builder(env, target = 'tgt2', source = 'src2')
+       assert tgt.sources[0].path == 'src2.c', \
+               "Source has unexpected name: %s" % tgt.sources[0].path
+
+    def test_suffix(self):
+       """Test Builder creation with a specified target suffix
 
        Make sure that the '.' separator is appended to the
        beginning if it isn't already present.
        """
-       builder = SCons.Builder.Builder(input_suffix = '.o')
-       assert builder.insuffix == '.o'
-       builder = SCons.Builder.Builder(input_suffix = 'o')
-       assert builder.insuffix == '.o'
-
-    def test_TargetNamingBuilder(self):
-        """Testing the TargetNamingBuilder class."""
-        builder = SCons.Builder.Builder(action='foo')
-        proxy = SCons.Builder.TargetNamingBuilder(builder=builder,
-                                                  prefix='foo',
-                                                  suffix='bar')
-        tgt = proxy(env, target='baz', source='bleh')
-        assert tgt.path == 'foobazbar', \
-               "Target has unexpected name: %s" % tgt[0].path
-        assert tgt.builder == builder
+       builder = SCons.Builder.Builder(suffix = '.o')
+       assert builder.suffix == '.o'
+       builder = SCons.Builder.Builder(suffix = 'o')
+       assert builder.suffix == '.o'
+       tgt = builder(env, target = 'tgt3', source = 'src3')
+       assert tgt.path == 'tgt3.o', \
+               "Target has unexpected name: %s" % tgt[0].path
 
     def test_MultiStepBuilder(self):
         """Testing MultiStepBuilder class."""
         builder1 = SCons.Builder.Builder(action='foo',
-                                        input_suffix='.bar',
-                                        output_suffix='.foo')
+                                        src_suffix='.bar',
+                                        suffix='.foo')
         builder2 = SCons.Builder.MultiStepBuilder(action='foo',
                                                   builders = [ builder1 ])
         tgt = builder2(env, target='baz', source='test.bar test2.foo test3.txt')
index d9120de6f7be99a8123ddd6e5b34815482a90d64..230f432ceda9ea5af21e5cd45bc0894fe6d9e539 100644 (file)
@@ -39,18 +39,18 @@ import SCons.Builder
 
 Object = SCons.Builder.Builder(name = 'Object',
                                action = 'cc -c -o $target $sources',
-                               input_suffix='.c',
-                               output_suffix='.o')
-Program = SCons.Builder.MultiStepBuilder(name = 'Program',
-                                         action = 'cc -o $target $sources',
-                                         builders = [ Object ])
-Library = SCons.Builder.MultiStepBuilder(name = 'Library',
-                                         action = 'ar r $target $sources\nranlib $target',
-                                         builders = [ Object ])
-  
-Library = SCons.Builder.TargetNamingBuilder(builder = Library,
-                                            prefix='lib',
-                                            suffix='.a')
+                               src_suffix='.c',
+                               suffix='.o')
+
+Program = SCons.Builder.Builder(name = 'Program',
+                                action = 'cc -o $target $sources',
+                                builders = [ Object ])
+
+Library = SCons.Builder.Builder(name = 'Library',
+                                action = 'ar r $target $sources\nranlib $target',
+                                prefix = 'lib',
+                                suffix = '.a',
+                                builders = [ Object ])
 
 Builders = [Object, Program, Library]