From ec123584c01358ecdd441cdc908fc65fdfbdc2d5 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Thu, 7 Oct 2004 15:55:52 +0000 Subject: [PATCH] Clear out dependent-child caches when a node is rebuilt. (Kevin Quick) git-svn-id: http://scons.tigris.org/svn/scons/trunk@1115 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/CHANGES.txt | 2 + src/engine/SCons/Node/FSTests.py | 5 ++ src/engine/SCons/Node/NodeTests.py | 10 +++ src/engine/SCons/Node/__init__.py | 41 +++++++---- src/engine/SCons/Script/__init__.py | 2 +- test/srcchange.py | 110 ++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 16 deletions(-) create mode 100644 test/srcchange.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 0167581d..e98f08f0 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -188,6 +188,8 @@ RELEASE 0.97 - XXX - Improve the --debug=explain message when the build action changes. + - Properly reset cached state when a Node subclass has been built. + From Christoph Wiedemann: - Add an Environment.SetDefault() method that only sets values if diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index ca14c3b7..2259b7b6 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -1169,16 +1169,21 @@ class EntryTestCase(unittest.TestCase): e5d = fs.Entry('e5d') sig = e5d.calc_signature(MyCalc(555)) assert e5d.__class__ is SCons.Node.FS.Dir, e5d.__class__ + # Node has builder (MkDirBuilder), so executor will calculate + # the build signature. assert sig == 777, sig e5f = fs.Entry('e5f') sig = e5f.calc_signature(MyCalc(666)) assert e5f.__class__ is SCons.Node.FS.File, e5f.__class__ + # This node has no builder, so it just calculates the + # signature once: the source content signature. assert sig == 888, sig e5n = fs.Entry('e5n') sig = e5n.calc_signature(MyCalc(777)) assert e5n.__class__ is SCons.Node.FS.File, e5n.__class__ + # Doesn't exist, no sources, and no builder: no sig assert sig is None, sig diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index e380318e..f8cd62b7 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -266,6 +266,16 @@ class NodeTestCase(unittest.TestCase): assert str(act.built_target[0]) == "xxx", str(act.built_target[0]) assert act.built_source == ["yyy", "zzz"], act.built_source + def test_built(self): + """Test the built() method""" + class SubNode(SCons.Node.Node): + def clear(self): + self.cleared = 1 + + n = SubNode() + n.built() + assert n.cleared, n.cleared + def test_retrieve_from_cache(self): """Test the base retrieve_from_cache() method""" n = SCons.Node.Node() diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index a4e12667..f994c674 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -202,27 +202,38 @@ class Node: def built(self): """Called just after this node is sucessfully built.""" - try: - new_binfo = self.binfo - except AttributeError: - pass - else: - self.store_info(new_binfo) - - # Clear our scanned included files. - self.found_includes = {} - self.includes = None # Clear the implicit dependency caches of any Nodes # waiting for this Node to be built. for parent in self.waiting_parents: parent.implicit = None parent.del_binfo() - self.waiting_parents = [] - - # The content just changed, delete any cached info - # so it will get recalculated. - self.del_cinfo() + + try: + new_binfo = self.binfo + except AttributeError: + # Node arrived here without build info; apparently it + # doesn't need it, so don't bother calculating or storing + # it. + new_binfo = None + + # Reset this Node's cached state since it was just built and + # various state has changed. + save_state = self.get_state() + self.clear() + self.set_state(save_state) + + # Had build info, so it should be stored in the signature + # cache. However, if the build info included a content + # signature then it should be recalculated before being + # stored. + + if new_binfo: + if hasattr(new_binfo, 'csig'): + new_binfo = self.gen_binfo() # sets self.binfo + else: + self.binfo = new_binfo + self.store_info(new_binfo) def add_to_waiting_parents(self, node): self.waiting_parents.append(node) diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index c40f27ef..712b4bab 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -258,7 +258,7 @@ num_jobs = 1 # this is modifed by SConscript.SetJobs() # utility functions -def get_all_children(node): return node.all_children(None) +def get_all_children(node): return node.all_children() def get_derived_children(node): children = node.all_children(None) diff --git a/test/srcchange.py b/test/srcchange.py new file mode 100644 index 00000000..2188bcc5 --- /dev/null +++ b/test/srcchange.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test changing the C source files based on an always-executed revision +extraction and substitution. +""" + +import os.path + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('getrevision', """ +#!/usr/bin/env python +import string +print string.strip(open('revnum.in','rb').read()) +""") + +test.write('SConstruct', """ +import re +import string + +def subrevision(target, source ,env): + orig = target[0].get_contents() + new = re.sub('\$REV.*?\$', + '$REV: %%s$'%%string.strip(source[0].get_contents()), + target[0].get_contents()) + outf = open(str(target[0]),'wb') + outf.write(new) + outf.close() + +SubRevision = Action(subrevision) + +env=Environment() +content_env=env.Copy() +content_env.TargetSignatures('content') +content_env.Command('revision.in', [], '%(python)s getrevision > $TARGET') +content_env.AlwaysBuild('revision.in') +env.Precious('main.cpp') +env.Command('main.cpp', 'revision.in', SubRevision) +exe = env.Program('main.cpp') +env.Default(exe) +""" % {'python':TestSCons.python}) + +test.write('main.cpp', """\ +#include +int +main(int argc, char *argv[]) +{ + std::cout << "Revision $REV$" << std::endl; +} +""") + +test.write('revnum.in', '3.2\n') + +prog = 'main' + TestSCons._exe + +full_build=test.wrap_stdout("""\ +%(python)s getrevision > revision.in +subrevision(["main.cpp"], ["revision.in"]) +g++ -c -o main.o main.cpp +g++ -o main main.o +""" % {'python':TestSCons.python}) + +light_build=test.wrap_stdout("""\ +%(python)s getrevision > revision.in +""" % {'python':TestSCons.python}) + +test.run(arguments='.', stdout=full_build) +test.must_exist(prog) +test.run(program=test.workpath(prog), stdout='Revision $REV: 3.2$\n') + +test.run(arguments='.', stdout=light_build) +test.must_exist(prog) + +test.run(arguments='.', stdout=light_build) +test.must_exist(prog) + +test.write('revnum.in', '3.3\n') + +test.run(arguments='.', stdout=full_build) +test.must_exist(prog) +test.run(program=test.workpath(prog), stdout='Revision $REV: 3.3$\n') + +test.pass_test() -- 2.26.2