diff options
Diffstat (limited to 'src/engine')
-rw-r--r-- | src/engine/SCons/Builder.py | 99 | ||||
-rw-r--r-- | src/engine/SCons/BuilderTests.py | 32 | ||||
-rw-r--r-- | src/engine/SCons/Defaults.py | 20 | ||||
-rw-r--r-- | src/engine/SCons/Util.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/UtilTests.py | 4 |
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(): |