From 78b110985d41c80a1803e95f3f9fe7280a0c1cb1 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Sun, 30 May 2004 19:17:38 +0000 Subject: Eliminate the refactored-out-of-existence Sig.Calculator methods (whose jobs are now handled by Node methods). Stop tying calculators to Taskmaster objects, and eliminate the unused Taskmaster.Calc class. --- src/CHANGES.txt | 6 + src/engine/SCons/SConf.py | 4 +- src/engine/SCons/Script/__init__.py | 2 +- src/engine/SCons/Sig/SigTests.py | 372 +----------------------------------- src/engine/SCons/Sig/__init__.py | 127 +----------- src/engine/SCons/Taskmaster.py | 54 ++---- src/engine/SCons/TaskmasterTests.py | 44 ++--- 7 files changed, 54 insertions(+), 555 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 70556c6..e2f4964 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -130,6 +130,9 @@ RELEASE 0.96 - XXX .h file changes. This eliminates incorrect circular dependencies with .h files generated from other source files. + - Slim down the internal Sig.Calculator class by eliminating methods + whose functionality is now covered by Node methods. + From Gary Oberbrunner: - Add a --debug=presub option to print actions prior to substitution. @@ -142,6 +145,9 @@ RELEASE 0.96 - XXX - Try to find the ICL license file path name in the external environment and the registry before resorting to the hard-coded path name. + - Add support for fetching command-line keyword=value arguments in + order from an ARGLIST list. + From Simon Perkins: - Fix a bug introduced in building shared libraries under MinGW. diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index 196b426..6280f75 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -219,9 +219,7 @@ class SConf: buildTask = SConfDryRunTask else: buildTask = SConfBuildTask - tm = SCons.Taskmaster.Taskmaster( nodes, - buildTask, - self.calc ) + tm = SCons.Taskmaster.Taskmaster( nodes, buildTask ) # we don't want to build tests in parallel jobs = SCons.Job.Jobs(1, tm ) try: diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index e77297d..db2b7c4 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -1045,7 +1045,7 @@ def _main(args, parser): return dependencies progress_display("scons: " + opening_message) - taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, None, order) + taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order) nj = ssoptions.get('num_jobs') jobs = SCons.Job.Jobs(nj, taskmaster) diff --git a/src/engine/SCons/Sig/SigTests.py b/src/engine/SCons/Sig/SigTests.py index 4390a60..8964c5c 100644 --- a/src/engine/SCons/Sig/SigTests.py +++ b/src/engine/SCons/Sig/SigTests.py @@ -24,393 +24,31 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import unittest -import TestCmd import SCons.Sig -import SCons.Sig.MD5 -import SCons.Sig.TimeStamp import sys - -class DummyFile: - """A class that simulates a file for testing purposes""" - def __init__(self, path, contents, timestamp, builder): - self.path = path - self.contents = contents - self.timestamp = timestamp - self.builder = builder - - def modify(self, contents, timestamp): - self.contents = contents - self.timestamp = timestamp - -class DummyNode: - """A node workalike for testing purposes""" - - def __init__(self, file): - self.file = file - self.path = file.path - self.builder = file.builder - self.depends = [] - self.ignore = [] - self.use_signature = 1 - self.bsig = None - self.csig = None - self.oldtime = 0 - self.oldbsig = 0 - self.oldcsig = 0 - self.always_build = 0 - - def has_builder(self): - return self.builder - - def get_contents(self): - # a file that doesn't exist has no contents: - assert self.exists() - - return self.file.contents - - def get_timestamp(self): - # a file that doesn't exist has no timestamp: - assert self.exists() - - return self.file.timestamp - - def exists(self): - return not self.file.contents is None - - def cached_exists(self): - try: - return self.exists_cache - except AttributeError: - self.exists_cache = self.exists() - return self.exists_cache - - def rexists(self): - return not self.file.contents is None - - def children(self): - return filter(lambda x, i=self.ignore: x not in i, - self.sources + self.depends) - - def all_children(self): - return self.sources + self.depends - - def current(self): - if not self.exists(): - return 0 - return None - - def calc_signature(self, calc): - if self.has_builder(): - return calc.bsig(self) - else: - return calc.csig(self) - - def set_binfo(self, bsig, bkids, bkidsigs, bact, bactsig): - self.bsig = bsig - self.bkids = bkids - self.bkidsigs = bkidsigs - self.bact = bact - self.bactsig = bactsig - - def get_bsig(self): - return self.bsig - - def store_bsig(self): - pass - - def set_csig(self, csig): - self.csig = csig - - def get_csig(self): - return self.csig - - def store_csig(self): - pass - - def get_prevsiginfo(self): - return (self.oldtime, self.oldbsig, self.oldcsig) - - def get_stored_implicit(self): - return None - - def store_timestamp(self): - pass - - def get_executor(self): - class Adapter: - def get_contents(self): - return 111 - def get_timestamp(self): - return 222 - return Adapter() - - -def create_files(test): - args = [(test.workpath('f1.c'), 'blah blah', 111, 0), #0 - (test.workpath('f1'), None, 0, 1), #1 - (test.workpath('d1/f1.c'), 'blah blah', 111, 0), #2 - (test.workpath('d1/f1.h'), 'blah blah', 111, 0), #3 - (test.workpath('d1/f2.c'), 'blah blah', 111, 0), #4 - (test.workpath('d1/f3.h'), 'blah blah', 111, 0), #5 - (test.workpath('d1/f4.h'), 'blah blah', 111, 0), #6 - (test.workpath('d1/f1'), None, 0, 1), #7 - (test.workpath('d1/test.c'), 'blah blah', 111, 0),#8 - (test.workpath('d1/test.o'), None, 0, 1), #9 - (test.workpath('d1/test'), None, 0, 1)] #10 - - files = map(lambda x: apply(DummyFile, x), args) - - return files - -def create_nodes(files): - nodes = map(DummyNode, files) - - nodes[0].sources = [] - nodes[1].sources = [nodes[0]] - nodes[2].sources = [] - nodes[3].sources = [] - nodes[4].sources = [] - nodes[5].sources = [nodes[6]] - nodes[6].sources = [nodes[5]] - nodes[7].sources = [nodes[2], nodes[4], nodes[3], nodes[5]] - nodes[8].sources = [] - nodes[9].sources = [nodes[8]] - nodes[10].sources = [nodes[9]] - - return nodes - -def current(calc, node): - return calc.current(node, node.calc_signature(calc)) - -def write(calc, nodes): - for node in nodes: - node.oldtime = node.file.timestamp - node.oldbsig = calc.bsig(node) - node.oldcsig = calc.csig(node) - -def clear(nodes): - for node in nodes: - node.csig = None - node.bsig = None - class SConsignEntryTestCase(unittest.TestCase): def runTest(self): - se = SCons.Sig.SConsignEntry() assert hasattr(se, 'timestamp'), "no timestamp attribute" assert hasattr(se, 'bsig'), "no bsig attribute" assert hasattr(se, 'csig'), "no csig attribute" assert hasattr(se, 'implicit'), "no implicit attribute" -class SigTestBase: - - def runTest(self): - - test = TestCmd.TestCmd(workdir = '') - test.subdir('d1') - - self.files = create_files(test) - self.test_initial() - self.test_built() - self.test_modify() - self.test_modify_same_time() - - def test_initial(self): - - nodes = create_nodes(self.files) - calc = SCons.Sig.Calculator(self.module) - - for node in nodes: - self.failUnless(not current(calc, node), - "node %s should not be current" % node.path) - - # simulate a build: - self.files[1].modify('built', 222) - self.files[7].modify('built', 222) - self.files[9].modify('built', 222) - self.files[10].modify('built', 222) - - def test_built(self): - - nodes = create_nodes(self.files) - - calc = SCons.Sig.Calculator(self.module) - - write(calc, nodes) - - for node in nodes: - self.failUnless(current(calc, node), - "node %s should be current" % node.path) - - def test_modify(self): - - nodes = create_nodes(self.files) - - calc = SCons.Sig.Calculator(self.module) - - write(calc, nodes) - - #simulate a modification of some files - self.files[0].modify('blah blah blah', 333) - self.files[3].modify('blah blah blah', 333) - self.files[6].modify('blah blah blah', 333) - self.files[8].modify('blah blah blah', 333) - - clear(nodes) - - self.failUnless(not current(calc, nodes[0]), "modified directly") - self.failUnless(not current(calc, nodes[1]), "direct source modified") - self.failUnless(current(calc, nodes[2])) - self.failUnless(not current(calc, nodes[3]), "modified directly") - self.failUnless(current(calc, nodes[4])) - self.failUnless(current(calc, nodes[5])) - self.failUnless(not current(calc, nodes[6]), "modified directly") - self.failUnless(not current(calc, nodes[7]), "indirect source modified") - self.failUnless(not current(calc, nodes[8]), "modified directory") - self.failUnless(not current(calc, nodes[9]), "direct source modified") - self.failUnless(not current(calc, nodes[10]), "indirect source modified") - - def test_modify_same_time(self): - - nodes = create_nodes(self.files) - - calc = SCons.Sig.Calculator(self.module, 0) - - write(calc, nodes) - - #simulate a modification of some files without changing the timestamp: - self.files[0].modify('blah blah blah blah', 333) - self.files[3].modify('blah blah blah blah', 333) - self.files[6].modify('blah blah blah blah', 333) - self.files[8].modify('blah blah blah blah', 333) - - clear(nodes) - - for node in nodes: - self.failUnless(current(calc, node), - "node %s should be current" % node.path) - - -class MD5TestCase(unittest.TestCase, SigTestBase): - """Test MD5 signatures""" - - module = SCons.Sig.MD5 - -class TimeStampTestCase(unittest.TestCase, SigTestBase): - """Test timestamp signatures""" - - module = SCons.Sig.TimeStamp - -class CalcTestCase(unittest.TestCase): +class CalculatorTestCase(unittest.TestCase): def runTest(self): class MySigModule: - def collect(self, signatures): - return reduce(lambda x, y: x + y, signatures) - def current(self, newsig, oldsig): - return newsig == oldsig - def signature(self, node): - return node.get_csig() - - class MyNode: - def __init__(self, name, bsig, csig): - self.name = name - self.bsig = bsig - self.csig = csig - self.kids = [] - self.ignore = [] - self.builder = None - self.use_signature = 1 - def has_builder(self): - return not self.builder is None - def children(self): - return filter(lambda x, i=self.ignore: x not in i, self.kids) - def all_children(self): - return self.kids - def exists(self): - return 1 - def cached_exists(self): - return 1 - def get_bsig(self): - return self.bsig - def set_binfo(self, bsig, bkids, bkidsig, bact, bactsig): - self.bsig = bsig - self.bkids = bkids - self.bkidsigs = bkidsigs - self.bact = bact - self.bactsig = bactsig - def get_csig(self): - return self.csig - def set_csig(self, csig): - self.csig = csig - def store_csig(self): - pass - def store_timestamp(self): - pass - def get_prevsiginfo(self): - return 0, self.bsig, self.csig - def get_stored_implicit(self): - return None - def get_timestamp(self): - return 1 - def builder_sig_adapter(self): - class MyAdapter: - def get_csig(self): - return 333 - def get_timestamp(self): - return 444 - return MyAdapter() - - self.module = MySigModule() - self.nodeclass = MyNode - self.test_Calc___init__() - self.test_Calc_bsig() - self.test_Calc_current() - - def test_Calc___init__(self): - self.calc = SCons.Sig.Calculator(self.module) - assert self.calc.module == self.module - - def test_Calc_bsig(self): - n1 = self.nodeclass('n1', 11, 12) - n2 = self.nodeclass('n2', 22, 23) - n3 = self.nodeclass('n3', 33, 34) - n1.builder = 1 - n1.kids = [n2, n3] - - assert self.calc.bsig(n1) == 55 - - n1.ignore = [n2] - - assert self.calc.bsig(n1) == 33 - - def test_Calc_bsig(self): - n = self.nodeclass('n', 11, 12) - - assert self.calc.csig(n) == 12 - - def test_Calc_current(self): - class NN(self.nodeclass): - always_build = 0 - def current(self): - return None - - nn = NN('nn', 33, 34) - assert not self.calc.current(nn, 30) - assert self.calc.current(nn, 33) - nn.always_build = 1 - assert not self.calc.current(nn, 33) - + pass + calc = SCons.Sig.Calculator(MySigModule) + assert calc.module == MySigModule def suite(): suite = unittest.TestSuite() suite.addTest(SConsignEntryTestCase()) - suite.addTest(MD5TestCase()) - suite.addTest(TimeStampTestCase()) - suite.addTest(CalcTestCase()) + suite.addTest(CalculatorTestCase()) return suite if __name__ == "__main__": diff --git a/src/engine/SCons/Sig/__init__.py b/src/engine/SCons/Sig/__init__.py index 6626571..2a9680e 100644 --- a/src/engine/SCons/Sig/__init__.py +++ b/src/engine/SCons/Sig/__init__.py @@ -29,14 +29,6 @@ The Signature package for the scons software construction utility. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import cPickle -import os -import os.path -import time - -import SCons.Node -import SCons.Warnings - try: import MD5 default_module = MD5 @@ -44,6 +36,8 @@ except ImportError: import TimeStamp default_module = TimeStamp +# XXX We should move max_drift into Node/FS.py, +# since it's really something about files. default_max_drift = 2*24*60*60 class SConsignEntry: @@ -72,121 +66,4 @@ class Calculator: self.module = module self.max_drift = max_drift - def bsig(self, node, cache=None): - """ - Generate a node's build signature, the digested signatures - of its dependency files and build information. - - node - the node whose sources will be collected - cache - alternate node to use for the signature cache - returns - the build signature - - This no longer handles the recursive descent of the - node's children's signatures. We expect that they're - already built and updated by someone else, if that's - what's wanted. - """ - - if cache is None: cache = node - - bsig = cache.get_bsig() - if bsig is not None: - return bsig - - children = node.children() - bkids = map(str, children) - - # double check bsig, because the call to children() above may - # have set it: - bsig = cache.get_bsig() - if bsig is not None: - return bsig - - sigs = map(lambda n, c=self: n.calc_signature(c), children) - - if node.has_builder(): - executor = node.get_executor() - bact = str(executor) - bactsig = self.module.signature(executor) - sigs.append(bactsig) - else: - bact = "" - bactsig = "" - - bsig = self.module.collect(filter(None, sigs)) - - cache.set_binfo(bsig, bkids, sigs, bact, bactsig) - - # don't store the bsig here, because it isn't accurate until - # the node is actually built. - - return bsig - - def csig(self, node, cache=None): - """ - Generate a node's content signature, the digested signature - of its content. - - node - the node - cache - alternate node to use for the signature cache - returns - the content signature - """ - - if cache is None: cache = node - - csig = cache.get_csig() - if csig is not None: - return csig - - if self.max_drift >= 0: - oldtime, oldbsig, oldcsig = node.get_prevsiginfo() - else: - import SCons.SConsign - oldtime, oldbsig, oldcsig = SCons.SConsign.Base.null_siginfo - - mtime = node.get_timestamp() - - if (oldtime and oldcsig and oldtime == mtime): - # use the signature stored in the .sconsign file - csig = oldcsig - # Set the csig here so it doesn't get recalculated unnecessarily - # and so it's set when the .sconsign file gets written - cache.set_csig(csig) - else: - csig = self.module.signature(node) - # Set the csig here so it doesn't get recalculated unnecessarily - # and so it's set when the .sconsign file gets written - cache.set_csig(csig) - - if self.max_drift >= 0 and (time.time() - mtime) > self.max_drift: - node.store_csig() - node.store_timestamp() - - return csig - - def current(self, node, newsig): - """ - Check if a signature is up to date with respect to a node. - - node - the node whose signature will be checked - newsig - the (presumably current) signature of the file - - returns - 1 if the file is current with the specified signature, - 0 if it isn't - """ - - if node.always_build: - return 0 - - oldtime, oldbsig, oldcsig = node.get_prevsiginfo() - - if type(newsig) != type(oldbsig): - return 0 - - if not node.has_builder() and node.get_timestamp() == oldtime: - return 1 - - return self.module.current(newsig, oldbsig) - - default_calc = Calculator() diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index a253ddf..b09a589 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -165,6 +165,16 @@ class Task: self.tm.executed(self.node) + def mark_targets(self, state): + for t in self.targets: + t.set_state(state) + + def mark_targets_and_side_effects(self, state): + for t in self.targets: + for side_effect in t.side_effects: + side_effect.set_state(state) + t.set_state(state) + def make_ready_all(self): """Mark all targets in a task ready for execution. @@ -172,11 +182,7 @@ class Task: visited--the canonical example being the "scons -c" option. """ self.out_of_date = self.targets[:] - state = SCons.Node.executing - for t in self.targets: - for side_effect in t.side_effects: - side_effect.set_state(state) - t.set_state(state) + self.mark_targets_and_side_effects(SCons.Node.executing) def make_ready_current(self): """Mark all targets in a task ready for execution if any target @@ -185,25 +191,13 @@ class Task: This is the default behavior for building only what's necessary. """ self.out_of_date = [] - calc = self.tm.calc - if calc: - for t in self.targets: - if not t.current(calc): - self.out_of_date.append(t) - else: - for t in self.targets: - if not t.current(t.calculator()): - self.out_of_date.append(t) + for t in self.targets: + if not t.current(t.calculator()): + self.out_of_date.append(t) if self.out_of_date: - state = SCons.Node.executing - for t in self.targets: - for side_effect in t.side_effects: - side_effect.set_state(state) - t.set_state(state) + self.mark_targets_and_side_effects(SCons.Node.executing) else: - state = SCons.Node.up_to_date - for t in self.targets: - t.set_state(state) + self.mark_targets(SCons.Node.up_to_date) make_ready = make_ready_current @@ -218,19 +212,6 @@ def order(dependencies): """Re-order a list of dependencies (if we need to).""" return dependencies -class Calc: - def bsig(self, node): - """ - """ - return None - - def current(self, node, sig): - """Default SCons build engine is-it-current function. - - This returns "always out of date," so every node is always - built/visited. - """ - return 0 class Taskmaster: """A generic Taskmaster for handling a bunch of targets. @@ -239,7 +220,7 @@ class Taskmaster: the base class method, so this class can do its thing. """ - def __init__(self, targets=[], tasker=Task, calc=Calc(), order=order): + def __init__(self, targets=[], tasker=Task, order=order): self.targets = targets # top level targets self.candidates = targets[:] # nodes that might be ready to be executed self.candidates.reverse() @@ -247,7 +228,6 @@ class Taskmaster: self.pending = [] # nodes that depend on a currently executing node self.tasker = tasker self.ready = None # the next task that is ready to be executed - self.calc = calc self.order = order self.exception_set(None, None) self.message = None diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index 7010edb..5955ecf 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -55,6 +55,8 @@ class Node: self.side_effects = [] self.alttargets = [] self.postprocessed = None + self._bsig_val = None + self._current_val = 0 def retrieve_from_cache(self): global cache_text @@ -127,6 +129,14 @@ class Node: def store_bsig(self): pass + def calculator(self): + class Calc: + def bsig(self, node): + return node._bsig_val + def current(self, node, sig): + return node._current_val + return Calc() + def current(self, calc): return calc.current(self, calc.bsig(self)) @@ -194,10 +204,6 @@ class TaskmasterTestCase(unittest.TestCase): built_text = "up to date: " top_node = n3 - class MyCalc(SCons.Taskmaster.Calc): - def current(self, node, sig): - return 1 - class MyTask(SCons.Taskmaster.Task): def execute(self): global built_text @@ -210,10 +216,12 @@ class TaskmasterTestCase(unittest.TestCase): self.targets[0].build() n1.set_state(None) + n1._current_val = 1 n2.set_state(None) + n2._current_val = 1 n3.set_state(None) - tm = SCons.Taskmaster.Taskmaster(targets = [n3], - tasker = MyTask, calc = MyCalc()) + n3._current_val = 1 + tm = SCons.Taskmaster.Taskmaster(targets = [n3], tasker = MyTask) t = tm.next_task() t.prepare() @@ -413,11 +421,6 @@ class TaskmasterTestCase(unittest.TestCase): def test_make_ready_out_of_date(self): """Test the Task.make_ready() method's list of out-of-date Nodes """ - class MyCalc(SCons.Taskmaster.Calc): - def current(self, node, sig): - n = str(node) - return n[0] == 'c' - ood = [] def TaskGen(tm, targets, top, node, ood=ood): class MyTask(SCons.Taskmaster.Task): @@ -430,11 +433,12 @@ class TaskmasterTestCase(unittest.TestCase): n1 = Node("n1") c2 = Node("c2") + c2._current_val = 1 n3 = Node("n3") c4 = Node("c4") + c4._current_val = 1 tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4], - tasker = TaskGen, - calc = MyCalc()) + tasker = TaskGen) del ood[:] t = tm.next_task() @@ -471,21 +475,18 @@ class TaskmasterTestCase(unittest.TestCase): def test_make_ready_all(self): - class MyCalc(SCons.Taskmaster.Calc): - def current(self, node, sig): - n = str(node) - return n[0] == 'c' - + """Test the make_ready_all() method""" class MyTask(SCons.Taskmaster.Task): make_ready = SCons.Taskmaster.Task.make_ready_all n1 = Node("n1") c2 = Node("c2") + c2._current_val = 1 n3 = Node("n3") c4 = Node("c4") + c4._current_val = 1 - tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4], - calc = MyCalc()) + tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4]) t = tm.next_task() target = t.get_target() @@ -512,8 +513,7 @@ class TaskmasterTestCase(unittest.TestCase): c4 = Node("c4") tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4], - tasker = MyTask, - calc = MyCalc()) + tasker = MyTask) t = tm.next_task() target = t.get_target() -- cgit v0.12