summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/engine/SCons/Builder.py99
-rw-r--r--src/engine/SCons/BuilderTests.py32
-rw-r--r--src/engine/SCons/Defaults.py20
-rw-r--r--src/engine/SCons/Util.py4
-rw-r--r--src/engine/SCons/UtilTests.py4
-rw-r--r--src/script/scons.py2
-rw-r--r--test/Library.py15
-rw-r--r--test/Program-j.py2
8 files changed, 156 insertions, 22 deletions
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 367c2ed6..b009fe08 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -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;
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index b801b4a5..8554d670 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -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__":
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index 876aa890..3e1c8a8d 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -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' }
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 7ce7056c..61b7417e 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -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...
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index 698dfbf6..135a82c9 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -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():
diff --git a/src/script/scons.py b/src/script/scons.py
index 65b9495a..1a67e51e 100644
--- a/src/script/scons.py
+++ b/src/script/scons.py
@@ -47,7 +47,7 @@ from SCons.Taskmaster import Taskmaster
#
from SCons.Environment import Environment
from SCons.Builder import Builder
-
+from SCons.Defaults import *
#
diff --git a/test/Library.py b/test/Library.py
index 7a44d357..4941bbff 100644
--- a/test/Library.py
+++ b/test/Library.py
@@ -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()
diff --git a/test/Program-j.py b/test/Program-j.py
index 6b86127a..95088a6c 100644
--- a/test/Program-j.py
+++ b/test/Program-j.py
@@ -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')