Commits

Anonymous committed a8f0a0c

Add the Library() method.

Comments (0)

Files changed (8)

src/engine/SCons/Builder.py

 	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)
 	"""
 	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;

src/engine/SCons/BuilderTests.py

 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
 	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__":

src/engine/SCons/Defaults.py

 
 
 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')
+                               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 ])
 
-Builders = [Object, Program]
+Library = SCons.Builder.TargetNamingBuilder(builder = Library,
+                                            prefix='lib',
+                                            suffix='.a')
+
+Builders = [Object, Program, Library]
 
 ENV = { 'PATH' : '/usr/local/bin:/bin:/usr/bin' }

src/engine/SCons/Util.py

 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:
     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...

src/engine/SCons/UtilTests.py

 import SCons.Node.FS
 from SCons.Util import scons_str2nodes
 
-
 class UtilTestCase(unittest.TestCase):
     def test_str2nodes(self):
 	"""Test the str2nodes function."""
 	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)
 	    pass
 	node = scons_str2nodes(OtherNode())
 
-
 if __name__ == "__main__":
     suite = unittest.makeSuite(UtilTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():

src/script/scons.py

 #
 from SCons.Environment import Environment
 from SCons.Builder import Builder
-
+from SCons.Defaults import *
 
 
 #
 
 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', """
 	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()

test/Program-j.py

 
 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')