Add the Library() method.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 11 Oct 2001 18:38:29 +0000 (18:38 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 11 Oct 2001 18:38:29 +0000 (18:38 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@93 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Defaults.py
src/engine/SCons/Util.py
src/engine/SCons/UtilTests.py
src/script/scons.py
test/Library.py
test/Program-j.py

index 367c2ed6fce9bb04e9e99d30588aed0ea32e8503..b009fe086061cf781873e0e05ecebc42994ba866 100644 (file)
@@ -63,8 +63,8 @@ class Builder:
        return cmp(self.__dict__, other.__dict__)
 
     def __call__(self, env, target = None, source = None):
-       tlist = SCons.Util.scons_str2nodes(target)
-       slist = SCons.Util.scons_str2nodes(source)
+       tlist = SCons.Util.scons_str2nodes(target, self.node_factory)
+       slist = SCons.Util.scons_str2nodes(source, self.node_factory)
        for t in tlist:
            t.builder_set(self)
            t.env_set(env)
@@ -80,7 +80,102 @@ class Builder:
        """
        return apply(self.action.execute, (), kw)
 
+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."""
+    def __init__(self, builder):
+        self.subject = builder
 
+    def __call__(self, env, target = None, source = None):
+        return self.subject.__call__(env, target, source)
+
+    def execute(self, **kw):
+        return apply(self.subject.execute, (), kw)
+    
+    def __cmp__(self, other):
+        return cmp(self.__dict__, other.__dict__)
+
+    def __getattr__(self, name):
+        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.Util.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):
+    """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 targets of these builders then become sources for this
+    builder.
+    """
+    def __init__(self,  name = None,
+                       action = None,
+                       input_suffix = None,
+                       output_suffix = None,
+                        node_factory = SCons.Node.FS.default_fs.File,
+                        builders = []):
+        Builder.__init__(self, name, action, input_suffix, output_suffix,
+                         node_factory)
+        self.builder_dict = {}
+        for bld in builders:
+            if bld.insuffix and bld.outsuffix:
+                self.builder_dict[bld.insuffix] = bld
+
+    def __call__(self, env, target = None, source = None):
+        slist = SCons.Util.scons_str2nodes(source, self.node_factory)
+        final_sources = []
+        for snode in slist:
+            path, ext = os.path.splitext(snode.path)
+            if self.builder_dict.has_key(ext):
+                bld = self.builder_dict[ext]
+                tgt = bld(env,
+                          target=[ path+bld.outsuffix, ],
+                          source=snode)
+                if not type(tgt) is types.ListType:
+                    final_sources.append(tgt)
+                else:
+                    final_sources.extend(tgt)
+            else:
+                final_sources.append(snode)
+        return Builder.__call__(self, env, target=target,
+                                source=final_sources)
 
 print_actions = 1;
 execute_actions = 1;
index b801b4a59ab38a77918b19ec8c8fc58730996568..8554d6704a03c286b316fc837e16f941b0e9bab3 100644 (file)
@@ -49,15 +49,16 @@ sys.exit(0)
 act_py = test.workpath('act.py')
 outfile = test.workpath('outfile')
 
+class Environment:
+            def subst(self, s):
+                return s
+env = Environment()
 
 class BuilderTestCase(unittest.TestCase):
 
     def test__call__(self):
        """Test calling a builder to establish source dependencies
        """
-       class Environment:
-           pass
-       env = Environment()
        class Node:
            def __init__(self, name):
                self.name = name
@@ -197,6 +198,31 @@ class BuilderTestCase(unittest.TestCase):
        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
+
+    def test_MultiStepBuilder(self):
+        """Testing MultiStepBuilder class."""
+        builder1 = SCons.Builder.Builder(action='foo',
+                                        input_suffix='.bar',
+                                        output_suffix='.foo')
+        builder2 = SCons.Builder.MultiStepBuilder(action='foo',
+                                                  builders = [ builder1 ])
+        tgt = builder2(env, target='baz', source='test.bar test2.foo test3.txt')
+        flag = 0
+        for snode in tgt.sources:
+            if snode.path == 'test.foo':
+                flag = 1
+                assert snode.sources[0].path == 'test.bar'
+        assert flag
 
 
 if __name__ == "__main__":
index 876aa890bd5fe6e517d1025246d5cd669ef7dafe..3e1c8a8dc66827d7125c6d1fbcaf836e805127cd 100644 (file)
@@ -38,10 +38,20 @@ import SCons.Builder
 
 
 Object = SCons.Builder.Builder(name = 'Object',
-                               action = 'cc -c -o %(target)s %(source)s')
-Program = SCons.Builder.Builder(name = 'Program',
-                               action = 'cc -o %(target)s %(source)s')
-
-Builders = [Object, Program]
+                               action = 'cc -c -o %(target)s %(source)s',
+                               input_suffix='.c',
+                               output_suffix='.o')
+Program = SCons.Builder.MultiStepBuilder(name = 'Program',
+                                         action = 'cc -o %(target)s %(source)s',
+                                         builders = [ Object ])
+Library = SCons.Builder.MultiStepBuilder(name = 'Library',
+                                         action = 'ar r %(target)s %(source)s\nranlib %(target)s',
+                                         builders = [ Object ])
+
+Library = SCons.Builder.TargetNamingBuilder(builder = Library,
+                                            prefix='lib',
+                                            suffix='.a')
+
+Builders = [Object, Program, Library]
 
 ENV = { 'PATH' : '/usr/local/bin:/bin:/usr/bin' }
index 7ce7056ce5103ef92407b3a3bdecd59da397407a..61b7417e5d44f5e43fcbc98ff79b2b5c73b85d3f 100644 (file)
@@ -34,7 +34,7 @@ import types
 import string
 import SCons.Node.FS
 
-def scons_str2nodes(arg, fs=SCons.Node.FS.default_fs):
+def scons_str2nodes(arg, node_factory=SCons.Node.FS.default_fs.File):
     """This function converts a string or list into a list of Node instances.
     It follows the rules outlined in the SCons design document by accepting
     any of the following inputs:
@@ -54,7 +54,7 @@ def scons_str2nodes(arg, fs=SCons.Node.FS.default_fs):
     nodes = []
     for v in narg:
        if type(v) is types.StringType:
-           nodes.append(fs.File(v))
+           nodes.append(node_factory(v))
        # Do we enforce the following restriction?  Maybe, but it
        # also restricts what we can do for allowing people to
        # use the engine with alternate Node implementations...
index 698dfbf63427f5807ef873139dc4d90ab0df8166..135a82c93c81ccca6303f8f7837607172b356244 100644 (file)
@@ -29,7 +29,6 @@ import SCons.Node
 import SCons.Node.FS
 from SCons.Util import scons_str2nodes
 
-
 class UtilTestCase(unittest.TestCase):
     def test_str2nodes(self):
        """Test the str2nodes function."""
@@ -40,7 +39,7 @@ class UtilTestCase(unittest.TestCase):
        assert nodes[0].path == "Util.py"
        assert nodes[1].path == "UtilTests.py"
 
-       nodes = scons_str2nodes("Util.py UtilTests.py", SCons.Node.FS.FS())
+       nodes = scons_str2nodes("Util.py UtilTests.py", SCons.Node.FS.FS().File)
        assert len(nodes) == 2
        assert isinstance(nodes[0], SCons.Node.FS.File)
        assert isinstance(nodes[1], SCons.Node.FS.File)
@@ -70,7 +69,6 @@ class UtilTestCase(unittest.TestCase):
            pass
        node = scons_str2nodes(OtherNode())
 
-
 if __name__ == "__main__":
     suite = unittest.makeSuite(UtilTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
index 65b9495a5d2769e8a5bf2324f164b28c52e5e733..1a67e51ef465d6b03baa44dfad5e6c4f920d0be2 100644 (file)
@@ -47,7 +47,7 @@ from SCons.Taskmaster import Taskmaster
 #
 from SCons.Environment import Environment
 from SCons.Builder import Builder
-
+from SCons.Defaults import *
 
 
 #
index 7a44d35774462fc9025cc9193b1f2c35c9f7d28d..4941bbfff0012dc1595e7a034a12bd96991d2b13 100644 (file)
@@ -28,14 +28,16 @@ import TestSCons
 
 test = TestSCons.TestSCons()
 
-test.pass_test()       #XXX Short-circuit until this is implemented.
-
+#XXX Need to switch TestBld to Program() when LIBS variable is working.
 test.write('SConstruct', """
-env = Environment(LIBS = 'foo1 foo2 foo3')
+TestBld = Builder(name='TestBld',
+                  action='cc -o %(target)s %(source)s -L./ -lfoo1 -lfoo2 -lfoo3')
+env = Environment(BUILDERS=[ TestBld, Library ])
 env.Library(target = 'foo1', source = 'f1.c')
 env.Library(target = 'foo2', source = 'f2a.c f2b.c f2c.c')
 env.Library(target = 'foo3', source = ['f3a.c', 'f3b.c', 'f3c.c'])
-env.Program(target = 'prog', source = 'prog.c')
+env.TestBld(target = 'prog', source = 'prog.c')
+env.Depends(target = 'prog', dependency = 'libfoo1.a libfoo2.a libfoo3.a')
 """)
 
 test.write('f1.c', """
@@ -113,12 +115,13 @@ main(int argc, char *argv[])
        f3b();
        f3c();
        printf("prog.c\n");
+        return 0;
 }
 """)
 
-test.run(arguments = 'libfoo1.a libfoo2.a libfoo3.a')
+test.run(arguments = 'libfoo1.a libfoo2.a libfoo3.a prog')
 
 test.run(program = test.workpath('prog'),
-       stdout = "f1.c\nf2a.c\nf2b.c\nf2c.c\nf3a.c\nf3b.c\nf3c.c\nprog.c\n")
+         stdout = "f1.c\nf2a.c\nf2b.c\nf2c.c\nf3a.c\nf3b.c\nf3c.c\nprog.c\n")
 
 test.pass_test()
index 6b86127a54b8cbee00ea805cd5d711a5c6aa045b..95088a6c793075cd9c25630d2831f1fd8b4e5d68 100644 (file)
@@ -28,6 +28,8 @@ import TestSCons
 
 test = TestSCons.TestSCons()
 
+test.pass_test()       #XXX Short-circuit until this is supported.
+
 test.write('SConstruct', """
 env = Environment()
 env.Program(target = 'f1', source = 'f1.c')