From 1224ef13a07e6147b306039e1e3a670eb16844b3 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Sun, 2 Mar 2003 02:43:16 +0000 Subject: [PATCH] Fix using more than two targets or sources in a list. git-svn-id: http://scons.tigris.org/svn/scons/trunk@603 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/CHANGES.txt | 4 ++ src/engine/SCons/Action.py | 28 +++++++- src/engine/SCons/ActionTests.py | 102 ++++++++++++++++++++++++++- src/engine/SCons/BuilderTests.py | 16 ++++- src/engine/SCons/Environment.py | 36 +++++++++- src/engine/SCons/EnvironmentTests.py | 40 +++++++++++ 6 files changed, 220 insertions(+), 6 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 0ac801ce..22f0bdce 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -47,6 +47,10 @@ RELEASE 0.12 - XXX - Better documentation of the different ways to export variables to a subsidiary SConscript file. + - Support fetching arbitrary files from the TARGETS or SOURCES lists + (e.g. ${SOURCES[2]}) when calculating the build signature of a + command. + From Lachlan O'Dea: - Add SharedObject() support to the masm tool. diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 960ffce9..ecad9b0b 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -293,8 +293,19 @@ class CommandAction(ActionBase): def get_raw_contents(self, target, source, env): """Return the complete contents of this action's command line. """ + # We've discusssed using the real target and source names in + # a CommandAction's signature contents. This would have the + # advantage of recompiling when a file's name changes (keeping + # debug info current), but it would currently break repository + # logic that will change the file name based on whether the + # files come from a repository or locally. If we ever move to + # that scheme, though, here's how we'd do it: + #return SCons.Util.scons_subst(string.join(self.cmd_list), + # self.subst_dict(target, source, env), + # {}) return SCons.Util.scons_subst(string.join(self.cmd_list), - self._sig_dict(target, source, env), {}) + env.sig_dict(), + {}) def get_contents(self, target, source, env): """Return the signature contents of this action's command line. @@ -302,8 +313,21 @@ class CommandAction(ActionBase): This strips $(-$) and everything in between the string, since those parts don't affect signatures. """ + # We've discusssed using the real target and source names in + # a CommandAction's signature contents. This would have the + # advantage of recompiling when a file's name changes (keeping + # debug info current), but it would currently break repository + # logic that will change the file name based on whether the + # files come from a repository or locally. If we ever move to + # that scheme, though, here's how we'd do it: + #return SCons.Util.scons_subst(string.join(map(str, self.cmd_list)), + # self.subst_dict(target, source, env), + # {}, + # _remove) return SCons.Util.scons_subst(string.join(map(str, self.cmd_list)), - self._sig_dict(target, source, env), {}, _remove) + env.sig_dict(), + {}, + _remove) class CommandGeneratorAction(ActionBase): """Class for command-generator actions.""" diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 61cea537..de157ae7 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -100,6 +100,14 @@ class Environment: return self.d.get(key, value) def items(self): return self.d.items() + def sig_dict(self): + d = {} + for k,v in self.items(): d[k] = v + d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__'] + d['TARGET'] = d['TARGETS'][0] + d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__'] + d['SOURCE'] = d['SOURCES'][0] + return d if os.name == 'java': python = os.path.join(sys.prefix, 'jython') @@ -190,6 +198,8 @@ class ActionBaseTestCase(unittest.TestCase): def test_show(self): """Test the show() method """ + save_stdout = sys.stdout + save = SCons.Action.print_actions SCons.Action.print_actions = 0 @@ -209,7 +219,7 @@ class ActionBaseTestCase(unittest.TestCase): assert s == "foobar\n", s SCons.Action.print_actions = save - sys.stdout = StringIO.StringIO() + sys.stdout = save_stdout def test_get_actions(self): """Test the get_actions() method @@ -543,6 +553,51 @@ class CommandActionTestCase(unittest.TestCase): env=Environment(foo = 'FFF', bar = 'BBB')) assert c == "| $( FFF | BBB $) |", c + # We've discusssed using the real target and source names in a + # CommandAction's signature contents. This would have have the + # advantage of recompiling when a file's name changes (keeping + # debug info current), but it would currently break repository + # logic that will change the file name based on whether the + # files come from a repository or locally. If we ever move to + # that scheme, then all of the '__t1__' and '__s6__' file names + # in the asserts below would change to 't1' and 's6' and the + # like. + t = ['t1', 't2', 't3', 't4', 't5', 't6'] + s = ['s1', 's2', 's3', 's4', 's5', 's6'] + env = Environment() + + a = SCons.Action.CommandAction(["$TARGET"]) + c = a.get_raw_contents(target=t, source=s, env=env) + assert c == "__t1__", c + + a = SCons.Action.CommandAction(["$TARGETS"]) + c = a.get_raw_contents(target=t, source=s, env=env) + assert c == "__t1__ __t2__ __t3__ __t4__ __t5__ __t6__", c + + a = SCons.Action.CommandAction(["${TARGETS[2]}"]) + c = a.get_raw_contents(target=t, source=s, env=env) + assert c == "__t3__", c + + a = SCons.Action.CommandAction(["${TARGETS[3:5]}"]) + c = a.get_raw_contents(target=t, source=s, env=env) + assert c == "__t4__ __t5__", c + + a = SCons.Action.CommandAction(["$SOURCE"]) + c = a.get_raw_contents(target=t, source=s, env=env) + assert c == "__s1__", c + + a = SCons.Action.CommandAction(["$SOURCES"]) + c = a.get_raw_contents(target=t, source=s, env=env) + assert c == "__s1__ __s2__ __s3__ __s4__ __s5__ __s6__", c + + a = SCons.Action.CommandAction(["${SOURCES[2]}"]) + c = a.get_raw_contents(target=t, source=s, env=env) + assert c == "__s3__", c + + a = SCons.Action.CommandAction(["${SOURCES[3:5]}"]) + c = a.get_raw_contents(target=t, source=s, env=env) + assert c == "__s4__ __s5__", c + def test_get_contents(self): """Test fetching the contents of a command Action """ @@ -552,6 +607,51 @@ class CommandActionTestCase(unittest.TestCase): env=Environment(foo = 'FFF', bar = 'BBB')) assert c == "| |", c + # We've discusssed using the real target and source names in a + # CommandAction's signature contents. This would have have the + # advantage of recompiling when a file's name changes (keeping + # debug info current), but it would currently break repository + # logic that will change the file name based on whether the + # files come from a repository or locally. If we ever move to + # that scheme, then all of the '__t1__' and '__s6__' file names + # in the asserts below would change to 't1' and 's6' and the + # like. + t = ['t1', 't2', 't3', 't4', 't5', 't6'] + s = ['s1', 's2', 's3', 's4', 's5', 's6'] + env = Environment() + + a = SCons.Action.CommandAction(["$TARGET"]) + c = a.get_contents(target=t, source=s, env=env) + assert c == "__t1__", c + + a = SCons.Action.CommandAction(["$TARGETS"]) + c = a.get_contents(target=t, source=s, env=env) + assert c == "__t1__ __t2__ __t3__ __t4__ __t5__ __t6__", c + + a = SCons.Action.CommandAction(["${TARGETS[2]}"]) + c = a.get_contents(target=t, source=s, env=env) + assert c == "__t3__", c + + a = SCons.Action.CommandAction(["${TARGETS[3:5]}"]) + c = a.get_contents(target=t, source=s, env=env) + assert c == "__t4__ __t5__", c + + a = SCons.Action.CommandAction(["$SOURCE"]) + c = a.get_contents(target=t, source=s, env=env) + assert c == "__s1__", c + + a = SCons.Action.CommandAction(["$SOURCES"]) + c = a.get_contents(target=t, source=s, env=env) + assert c == "__s1__ __s2__ __s3__ __s4__ __s5__ __s6__", c + + a = SCons.Action.CommandAction(["${SOURCES[2]}"]) + c = a.get_contents(target=t, source=s, env=env) + assert c == "__s3__", c + + a = SCons.Action.CommandAction(["${SOURCES[3:5]}"]) + c = a.get_contents(target=t, source=s, env=env) + assert c == "__s4__ __s5__", c + class CommandGeneratorActionTestCase(unittest.TestCase): def test_init(self): diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 3ad6b238..433fadf7 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -98,6 +98,14 @@ class Environment: return env def items(self): return self.d.items() + def sig_dict(self): + d = {} + for k,v in self.items(): d[k] = v + d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__'] + d['TARGET'] = d['TARGETS'][0] + d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__'] + d['SOURCE'] = d['SOURCES'][0] + return d env = Environment() @@ -239,9 +247,13 @@ class BuilderTestCase(unittest.TestCase): """Test returning the signature contents of a Builder """ - b1 = SCons.Builder.Builder(action = "foo") + b1 = SCons.Builder.Builder(action = "foo ${TARGETS[5]}") contents = b1.get_contents([],[],Environment()) - assert contents == "foo", contents + assert contents == "foo __t6__", contents + + b1 = SCons.Builder.Builder(action = "bar ${SOURCES[3:5]}") + contents = b1.get_contents([],[],Environment()) + assert contents == "bar __s4__ __s5__", contents b2 = SCons.Builder.Builder(action = Func) contents = b2.get_contents([],[],Environment()) diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 68571097..fdfa8b2e 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -542,4 +542,38 @@ class Environment: if name[-len(old_suffix):] == old_suffix: name = name[:-len(old_suffix)] return os.path.join(dir, new_prefix+name+new_suffix) - + + def sig_dict(self): + """Supply a dictionary for use in computing signatures. + + This fills in static TARGET, TARGETS, SOURCE and SOURCES + variables so that signatures stay the same every time. + """ + class lister: + def __init__(self, fmt): + self.fmt = fmt + def _format(self, index): + # For some reason, I originally made the fake names of + # the targets and sources 1-based (['__t1__, '__t2__']), + # not 0-based. We preserve this behavior by adding one + # to the returned item names, so everyone's targets + # won't get recompiled if they were using an old + # version. + return self.fmt % (index + 1) + def __str__(self): + return self._format(0) + " " + self._format(1) + def __getitem__(self, index): + return SCons.Util.PathList([self._format(index)])[0] + def __getslice__(self, i, j): + slice = [] + for x in range(i, j): + slice.append(self._format(x)) + return SCons.Util.PathList(slice) + + dict = {} + for k,v in self.items(): dict[k] = v + dict['TARGETS'] = lister('__t%d__') + dict['TARGET'] = dict['TARGETS'][0] + dict['SOURCES'] = lister('__s%d__') + dict['SOURCE'] = dict['SOURCES'][0] + return dict diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 1163545c..e590d7d2 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -794,6 +794,46 @@ class EnvironmentTestCase(unittest.TestCase): 'PREFIX', 'SUFFIX', 'LIBPREFIX', 'LIBSUFFIX') + def test_sig_dict(self): + """Test the sig_dict() method""" + d = Environment(XYZZY = 'foo').sig_dict() + + assert d['XYZZY'] == 'foo' + + s = str(d['TARGET']) + assert s == '__t1__', s + s = str(d['TARGET'].dir) + assert s == '', s + s = str(d['TARGETS']) + assert s == '__t1__ __t2__', s + s = str(d['TARGETS'][1]) + assert s == '__t2__', s + s = str(d['TARGETS'][2]) + assert s == '__t3__', s + s = str(d['TARGETS'][87]) + assert s == '__t88__', s + s = str(d['TARGETS'][87].dir) + assert s == '', s + s = map(str, d['TARGETS'][3:5]) + assert s == ['__t4__', '__t5__'], s + + s = str(d['SOURCE']) + assert s == '__s1__', s + s = str(d['SOURCE'].dir) + assert s == '', s + s = str(d['SOURCES']) + assert s == '__s1__ __s2__', s + s = str(d['SOURCES'][1]) + assert s == '__s2__', s + s = str(d['SOURCES'][2]) + assert s == '__s3__', s + s = str(d['SOURCES'][87]) + assert s == '__s88__', s + s = str(d['SOURCES'][87].dir) + assert s == '', s + s = map(str, d['SOURCES'][3:5]) + assert s == ['__s4__', '__s5__'], s + if __name__ == "__main__": suite = unittest.makeSuite(EnvironmentTestCase, 'test_') -- 2.26.2