summaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine')
-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
5 files changed, 144 insertions, 15 deletions
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 367c2ed..b009fe0 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 b801b4a..8554d67 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 876aa89..3e1c8a8 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 7ce7056..61b7417 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 698dfbf..135a82c 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():