diff options
author | Steven Knight <knight@baldmt.com> | 2004-05-14 03:08:46 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2004-05-14 03:08:46 (GMT) |
commit | 86c11822b8f41dff8ec28e4ee8a8afeec9bfaa5f (patch) | |
tree | f4e261a94be38bdc1beff13b857c54b169113baf /src | |
parent | e2ed7aef1547812fa9ce49ae726ae1815158936d (diff) | |
download | SCons-86c11822b8f41dff8ec28e4ee8a8afeec9bfaa5f.zip SCons-86c11822b8f41dff8ec28e4ee8a8afeec9bfaa5f.tar.gz SCons-86c11822b8f41dff8ec28e4ee8a8afeec9bfaa5f.tar.bz2 |
Make the saved info opaque to the .sconsign subsystem. Lots of other cleanup.
Diffstat (limited to 'src')
-rw-r--r-- | src/CHANGES.txt | 3 | ||||
-rw-r--r-- | src/RELEASE.txt | 5 | ||||
-rw-r--r-- | src/engine/SCons/Node/Alias.py | 15 | ||||
-rw-r--r-- | src/engine/SCons/Node/FS.py | 163 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 83 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 144 | ||||
-rw-r--r-- | src/engine/SCons/Node/Python.py | 33 | ||||
-rw-r--r-- | src/engine/SCons/Node/PythonTests.py | 15 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 298 | ||||
-rw-r--r-- | src/engine/SCons/SConsign.py | 113 | ||||
-rw-r--r-- | src/engine/SCons/SConsignTests.py | 135 | ||||
-rw-r--r-- | src/engine/SCons/Script/__init__.py | 50 | ||||
-rw-r--r-- | src/script/sconsign.py | 98 |
13 files changed, 569 insertions, 586 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 215937a..f760d7b 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -107,6 +107,9 @@ RELEASE 0.96 - XXX - Have ParseConfig() recognize and supporting adding the -Wa, -Wl, and -Wp, flags to ASFLAGS, LINKFLAGS and CPPFLAGS, respectively. + - Change the .sconsign format and the checks for whether a Node is + up-to-date to make dependency checks more efficient and correct. + From Gary Oberbrunner: - Add a --debug=presub option to print actions prior to substitution. diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 4964fb2..7917a7e 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -37,6 +37,11 @@ RELEASE 0.96 - XXX import anydbm SConsignFile('.sconsign_file_name', anydbm) + - The internal format of .sconsign files has been changed. + This may cause warnings about "ignoring corrupt .sconsign files" + and rebuilds when you use SCons 0.96 for the first time in a tre + that was previously gbuilt with SCons 0.95 or earlier. + - The scan_check function that can be supplied to a custom Scanner now must take two arguments, the Node to be checked and a construction environment. It previously only used the Node as an argument. diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py index f8ce36b..89127a3 100644 --- a/src/engine/SCons/Node/Alias.py +++ b/src/engine/SCons/Node/Alias.py @@ -63,20 +63,7 @@ class Alias(SCons.Node.Node): """A "builder" for aliases.""" pass - def current(self, calc): - """If all of our children were up-to-date, then this - Alias was up-to-date, too.""" - # Allow the children to calculate their signatures. - calc.bsig(self) - state = 0 - for kid in self.children(None): - s = kid.get_state() - if s and (not state or s > state): - state = s - if state == 0 or state == SCons.Node.up_to_date: - return 1 - else: - return 0 + current = SCons.Node.Node.children_are_up_to_date def sconsign(self): """An Alias is not recorded in .sconsign files""" diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 0c6627c..15434cb 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -42,6 +42,7 @@ import shutil import stat import string import sys +import time import cStringIO import SCons.Action @@ -453,6 +454,9 @@ class Base(SCons.Node.Node): def get_suffix(self): return SCons.Util.splitext(self.name)[1] + def rfile(self): + return self + def __str__(self): """A Node.FS.Base object's string representation is its path name.""" @@ -468,6 +472,8 @@ class Base(SCons.Node.Node): self._str_val = str_val return str_val + rstr = __str__ + def exists(self): try: return self._exists @@ -488,16 +494,6 @@ class Base(SCons.Node.Node): parents.append(self.dir) return parents - def current(self, calc): - """If the underlying path doesn't exist, we know the node is - not current without even checking the signature, so return 0. - Otherwise, return None to indicate that signature calculation - should proceed as normal to find out if the node is current.""" - bsig = calc.bsig(self) - if not self.exists(): - return 0 - return calc.current(self, bsig) - def is_under(self, dir): if self is dir: return 1 @@ -1110,14 +1106,12 @@ class Dir(Base): self._morph() def _morph(self): - """Turn a file system node (either a freshly initialized - directory object or a separate Entry object) into a - proper directory object. - - Modify our paths to add the trailing slash that indicates - a directory. Set up this directory's entries and hook it - into the file system tree. Specify that directories (this - node) don't use signatures for currency calculation.""" + """Turn a file system Node (either a freshly initialized directory + object or a separate Entry object) into a proper directory object. + + Set up this directory's entries and hook it into the file + system tree. Specify that directories (this Node) don't use + signatures for calculating whether they're current.""" self.path_ = self.path + os.sep self.abspath_ = self.abspath + os.sep @@ -1269,14 +1263,6 @@ class Dir(Base): """A directory does not get scanned.""" return None - def set_binfo(self, bsig, bkids, bkidsigs, bact, bactsig): - """A directory has no signature.""" - pass - - def set_csig(self, csig): - """A directory has no signature.""" - pass - def get_contents(self): """Return aggregate contents of all our children.""" contents = cStringIO.StringIO() @@ -1345,6 +1331,14 @@ class Dir(Base): stamp = kid.get_timestamp() return stamp +class BuildInfo: + bsig = None + def __cmp__(self, other): + try: + return cmp(self.bsig, other.bsig) + except AttributeError: + return 1 + class File(Base): """A class for files in a file system. """ @@ -1404,26 +1398,14 @@ class File(Base): else: return 0 - def store_csig(self): - self.dir.sconsign().set_csig(self.name, self.get_csig()) - - def store_binfo(self): - binfo = self.get_binfo() - apply(self.dir.sconsign().set_binfo, (self.name,) + binfo) - - def get_stored_binfo(self): - return self.dir.sconsign().get_binfo(self.name) + def store_info(self, obj): + self.dir.sconsign().set_entry(self.name, obj) - def store_implicit(self): - self.dir.sconsign().set_implicit(self.name, self.implicit) - - def store_timestamp(self): - self.dir.sconsign().set_timestamp(self.name, self.get_timestamp()) - - def get_prevsiginfo(self): - """Fetch the previous signature information from the - .sconsign entry.""" - return self.dir.sconsign().get(self.name) + def get_stored_info(self): + try: + return self.dir.sconsign().get_entry(self.name) + except: + return BuildInfo() def get_stored_implicit(self): return self.dir.sconsign().get_implicit(self.name) @@ -1641,25 +1623,83 @@ class File(Base): pass return Base.exists(self) + def new_binfo(self): + return BuildInfo() + + def del_cinfo(self): + try: + del self.binfo.csig + except AttributeError: + pass + try: + del self.binfo.timestamp + except AttributeError: + pass + + def calc_csig(self, calc): + """ + 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 + """ + + try: + return self.binfo.csig + except AttributeError: + pass + + if calc.max_drift >= 0: + old = self.get_stored_info() + else: + old = BuildInfo() + + mtime = self.get_timestamp() + + try: + if (old.timestamp and old.csig and old.timestamp == mtime): + # use the signature stored in the .sconsign file + csig = old.csig + else: + csig = calc.module.signature(self) + except AttributeError: + csig = calc.module.signature(self) + + if calc.max_drift >= 0 and (time.time() - mtime) > calc.max_drift: + try: + self.binfo + except AttributeError: + self.binfo = self.new_binfo() + self.binfo.csig = csig + self.binfo.timestamp = mtime + self.store_info(self.binfo) + + return csig + def current(self, calc): - bsig = calc.bsig(self) + self.binfo = self.gen_binfo(calc) + if self.always_build: + return None if not self.exists(): # The file doesn't exist locally... r = self.rfile() if r != self: # ...but there is one in a Repository... - if calc.current(r, bsig): + old = r.get_stored_info() + if old == self.binfo: # ...and it's even up-to-date... if self._local: # ...and they'd like a local copy. LocalCopy(self, r, None) - self.set_bsig(bsig) - self.store_binfo() + self.store_info(self.binfo) return 1 self._rfile = self return None else: - return calc.current(self, bsig) + old = self.get_stored_info() + return (old == self.binfo) def rfile(self): try: @@ -1677,18 +1717,17 @@ class File(Base): return str(self.rfile()) def cachepath(self): - if self.fs.CachePath: - bsig = self.get_bsig() - if bsig is None: - raise SCons.Errors.InternalError, "cachepath(%s) found a bsig of None" % self.path - # Add the path to the cache signature, because multiple - # targets built by the same action will all have the same - # build signature, and we have to differentiate them somehow. - cache_sig = SCons.Sig.MD5.collect([bsig, self.path]) - subdir = string.upper(cache_sig[0]) - dir = os.path.join(self.fs.CachePath, subdir) - return dir, os.path.join(dir, cache_sig) - return None, None + if not self.fs.CachePath: + return None, None + if self.binfo.bsig is None: + raise SCons.Errors.InternalError, "cachepath(%s) found a bsig of None" % self.path + # Add the path to the cache signature, because multiple + # targets built by the same action will all have the same + # build signature, and we have to differentiate them somehow. + cache_sig = SCons.Sig.MD5.collect([self.binfo.bsig, self.path]) + subdir = string.upper(cache_sig[0]) + dir = os.path.join(self.fs.CachePath, subdir) + return dir, os.path.join(dir, cache_sig) def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext): return self.dir.File(prefix + splitext(self.name)[0] + suffix) diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index cebad00..fb2e0fa 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -100,7 +100,7 @@ class Builder: def targets(self, t): return [t] - + def source_factory(self, name): return self.factory(name) @@ -161,7 +161,7 @@ class BuildDirTestCase(unittest.TestCase): # A source file in the repository test.write([ 'rep1', 'src', 'test2.in' ], 'test2.in') - + # Some source files in the build directory test.write([ 'work', 'build', 'var2', 'test.in' ], 'test.old') test.write([ 'work', 'build', 'var2', 'test2.in' ], 'test2.old') @@ -173,7 +173,7 @@ class BuildDirTestCase(unittest.TestCase): # And just in case we are weird, a derived file in the source # dir. test.write([ 'work', 'src', 'test.out' ], 'test.out.src') - + # A derived file in the repository test.write([ 'rep1', 'build', 'var1', 'test2.out' ], 'test2.out_rep') test.write([ 'rep1', 'build', 'var2', 'test2.out' ], 'test2.out_rep') @@ -194,7 +194,7 @@ class BuildDirTestCase(unittest.TestCase): f2out_2 = fs.File('build/var2/test2.out') f2out_2.builder = 1 fs.Repository(test.workpath('rep1')) - + assert f1.srcnode().path == os.path.normpath('src/test.in'),\ f1.srcnode().path # str(node) returns source path for duplicate = 0 @@ -354,7 +354,7 @@ class BuildDirTestCase(unittest.TestCase): finally: SCons.Node.FS.Link = save_Link - + # Test to see if Link() works... test.subdir('src','build') test.write('src/foo', 'src/foo\n') @@ -472,7 +472,7 @@ class BuildDirTestCase(unittest.TestCase): assert 0, "Expected exception when passing an invalid duplicate to set_duplicate" except SCons.Errors.InternalError: pass - + for duplicate in SCons.Node.FS.Valid_Duplicates: simulator = LinkSimulator(duplicate) @@ -526,7 +526,7 @@ class BuildDirTestCase(unittest.TestCase): class FSTestCase(unittest.TestCase): def runTest(self): """Test FS (file system) Node operations - + This test case handles all of the file system node tests in one environment, so we don't have to set up a complicated directory structure for each test individually. @@ -813,30 +813,6 @@ class FSTestCase(unittest.TestCase): match(e5.path_, "e3/e5") match(e5.dir.path, "e3") - e8 = fs.Entry("e8") - assert e8.get_bsig() is None, e8.get_bsig() - assert e8.get_csig() is None, e8.get_csig() - e8.set_binfo('xxx', [], [], [], []) - e8.set_csig('yyy') - assert e8.get_bsig() == 'xxx', e8.get_bsig() - assert e8.get_csig() == 'yyy', e8.get_csig() - - f9 = fs.File("f9") - assert f9.get_bsig() is None, f9.get_bsig() - assert f9.get_csig() is None, f9.get_csig() - f9.set_binfo('xxx', [], [], [], []) - f9.set_csig('yyy') - assert f9.get_bsig() == 'xxx', f9.get_bsig() - assert f9.get_csig() == 'yyy', f9.get_csig() - - d10 = fs.Dir("d10") - assert d10.get_bsig() is None, d10.get_bsig() - assert d10.get_csig() is None, d10.get_csig() - d10.set_binfo('xxx', [], [], [], []) - d10.set_csig('yyy') - assert d10.get_bsig() is None, d10.get_bsig() - assert d10.get_csig() is None, d10.get_csig() - fs.chdir(fs.Dir('subdir')) f11 = fs.File("f11") match(f11.path, "subdir/f11") @@ -860,8 +836,6 @@ class FSTestCase(unittest.TestCase): f1.implicit = None f1.scan() assert f1.implicit[0].path_ == "xyz" - f1.store_implicit() - assert f1.get_stored_implicit()[0] == "xyz" # Test underlying scanning functionality in get_found_includes() env = Environment() @@ -918,7 +892,7 @@ class FSTestCase(unittest.TestCase): assert fs.getcwd().path == 'subdir', fs.getcwd().path fs.chdir(fs.Dir('../..')) assert fs.getcwd().path == test.workdir, fs.getcwd().path - + f1 = fs.File(test.workpath("do_i_exist")) assert not f1.exists() test.write("do_i_exist","\n") @@ -1047,8 +1021,6 @@ class FSTestCase(unittest.TestCase): t = d.get_timestamp() assert t == t2, "expected %f, got %f" % (t2, t) - #XXX test get_prevsiginfo() - skey = fs.Entry('eee.x').scanner_key() assert skey == '.x', skey skey = fs.Entry('eee.xyz').scanner_key() @@ -1070,7 +1042,7 @@ class FSTestCase(unittest.TestCase): test.write("i_am_not_a_directory", "\n") try: - exc_caught = 0 + exc_caught = 0 try: fs.Dir(test.workpath("i_am_not_a_directory")) except TypeError: @@ -1120,7 +1092,7 @@ class FSTestCase(unittest.TestCase): fp = open(test.workpath('can_not_remove')) f = fs.File('can_not_remove') - exc_caught = 0 + exc_caught = 0 try: r = f.remove() except OSError: @@ -1205,11 +1177,16 @@ class EntryTestCase(unittest.TestCase): class MyCalc: def __init__(self, val): - self.val = val - def csig(self, node, cache): - return self.val - def bsig(self, node, cache): - return self.val + 222 + self.max_drift = 0 + class M: + def __init__(self, val): + self.val = val + def collect(self, args): + return reduce(lambda x, y: x+y, args) + def signature(self, executor): + return self.val + 222 + self.module = M(val) + test.subdir('e5d') test.write('e5f', "e5f\n") @@ -1221,7 +1198,7 @@ class EntryTestCase(unittest.TestCase): e5f = fs.Entry('e5f') sig = e5f.calc_signature(MyCalc(666)) assert e5f.__class__ is SCons.Node.FS.File, e5f.__class__ - assert sig == 666, sig + assert sig == 888, sig e5n = fs.Entry('e5n') sig = e5n.calc_signature(MyCalc(777)) @@ -1334,14 +1311,14 @@ class RepositoryTestCase(unittest.TestCase): assert list == ['d3', str(work_d4)], list fs.BuildDir('build', '.') - + f = fs.File(test.workpath("work", "i_do_not_exist")) assert not f.rexists() - + test.write(["rep2", "i_exist"], "\n") f = fs.File(test.workpath("work", "i_exist")) assert f.rexists() - + test.write(["work", "i_exist_too"], "\n") f = fs.File(test.workpath("work", "i_exist_too")) assert f.rexists() @@ -1391,9 +1368,9 @@ class find_fileTestCase(unittest.TestCase): node_pseudo = fs.File(test.workpath('pseudo')) node_pseudo.set_src_builder(1) # Any non-zero value. paths = map(fs.Dir, ['.', './bar']) - nodes = [SCons.Node.FS.find_file('foo', paths, fs.File), + nodes = [SCons.Node.FS.find_file('foo', paths, fs.File), SCons.Node.FS.find_file('baz', paths, fs.File), - SCons.Node.FS.find_file('pseudo', paths, fs.File)] + SCons.Node.FS.find_file('pseudo', paths, fs.File)] file_names = map(str, nodes) file_names = map(os.path.normpath, file_names) assert os.path.normpath('./foo') in file_names, file_names @@ -1652,7 +1629,8 @@ class CacheDirTestCase(unittest.TestCase): SCons.Sig.MD5.collect = my_collect try: f5 = fs.File("cd.f5") - f5.set_binfo('a_fake_bsig', [], [], [], []) + f5.binfo = f5.new_binfo() + f5.binfo.bsig = 'a_fake_bsig' cp = f5.cachepath() dirname = os.path.join('cache', 'A') filename = os.path.join(dirname, 'a_fake_bsig') @@ -1662,7 +1640,7 @@ class CacheDirTestCase(unittest.TestCase): # Verify that no bsig raises an InternalERror f6 = fs.File("cd.f6") - f6.set_binfo(None, [], [], [], []) + f6.binfo = f6.new_binfo() exc_caught = 0 try: cp = f6.cachepath() @@ -1686,7 +1664,8 @@ class CacheDirTestCase(unittest.TestCase): cd_f7 = test.workpath("cd.f7") test.write(cd_f7, "cd.f7\n") f7 = fs.File(cd_f7) - f7.set_bsig('f7_bsig') + f7.binfo = f7.new_binfo() + f7.binfo.bsig = 'f7_bsig' warn_caught = 0 try: diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 7a6c39a..535c84e 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -346,73 +346,96 @@ class NodeTestCase(unittest.TestCase): a = node.builder.get_actions() assert isinstance(a[0], MyAction), a[0] - def test_set_binfo(self): - """Test setting a Node's build information - """ + def test_calc_bsig(self): + """Test generic build signature calculation + """ + class Calculator: + def __init__(self, val): + self.max_drift = 0 + class M: + def __init__(self, val): + self.val = val + def collect(self, args): + return reduce(lambda x, y: x+y, args, self.val) + self.module = M(val) node = SCons.Node.Node() - node.set_binfo('www', ['w1'], ['w2'], 'w act', 'w actsig') - assert node.bsig == 'www', node.bsig - assert node.bkids == ['w1'], node.bkdids - assert node.bkidsigs == ['w2'], node.bkidsigs - assert node.bact == 'w act', node.bkdid - assert node.bactsig == 'w actsig', node.bkidsig - - def test_get_binfo(self): - """Test fetching a Node's build information - """ + result = node.calc_bsig(Calculator(222)) + assert result == 222, result + result = node.calc_bsig(Calculator(333)) + assert result == 222, result + + def test_calc_csig(self): + """Test generic content signature calculation + """ + class Calculator: + def __init__(self, val): + self.max_drift = 0 + class M: + def __init__(self, val): + self.val = val + def signature(self, args): + return self.val + self.module = M(val) node = SCons.Node.Node() - node.set_binfo('yyy', ['y1'], ['y2'], 'y act', 'y actsig') - bsig, bkids, bkidsigs, bact, bactsig = node.get_binfo() - assert bsig == 'yyy', bsig - assert bkids == ['y1'], bkdids - assert bkidsigs == ['y2'], bkidsigs - assert bact == 'y act', bkdid - assert bactsig == 'y actsig', bkidsig - - def test_get_bsig(self): - """Test fetching a Node's signature - """ + result = node.calc_csig(Calculator(444)) + assert result == 444, result + result = node.calc_csig(Calculator(555)) + assert result == 444, result + + def test_gen_binfo(self): + """Test generating a build information structure + """ + class Calculator: + def __init__(self, val): + self.max_drift = 0 + class M: + def __init__(self, val): + self.val = val + def collect(self, args): + return reduce(lambda x, y: x+y, args, self.val) + self.module = M(val) node = SCons.Node.Node() - node.set_binfo('xxx', ['x1'], ['x2'], 'x act', 'x actsig') - assert node.get_bsig() == 'xxx' + binfo = node.gen_binfo(Calculator(666)) + assert isinstance(binfo, SCons.Node.BuildInfo), binfo + assert binfo.bsig == 666, binfo.bsig - def test_set_csig(self): - """Test setting a Node's signature + def test_explain(self): + """Test explaining why a Node must be rebuilt """ node = SCons.Node.Node() - node.set_csig('yyy') - assert node.csig == 'yyy' + node.exists = lambda: None + node.__str__ = lambda: 'xyzzy' + result = node.explain() + assert result == "building `xyzzy' because it doesn't exist\n", result - def test_get_csig(self): - """Test fetching a Node's signature - """ node = SCons.Node.Node() - node.set_csig('zzz') - assert node.get_csig() == 'zzz' + result = node.explain() + assert result == None, result - def test_store_binfo(self): - """Test calling the method to store build information - """ - node = SCons.Node.Node() - node.store_binfo() + # XXX additional tests for the guts of the functionality some day - def test_store_csig(self): - """Test calling the method to store a content signature + def test_del_binfo(self): + """Test deleting the build information from a Node """ node = SCons.Node.Node() - node.store_csig() + node.binfo = None + node.del_binfo() + assert not hasattr(node, 'binfo'), node - def test_get_timestamp(self): - """Test calling the method to fetch a Node's timestamp + def test_store_info(self): + """Test calling the method to store build information """ + class Entry: + pass node = SCons.Node.Node() - assert node.get_timestamp() == 0 + node.store_info(Entry()) - def test_store_timestamp(self): - """Test calling the method to store a timestamp + def test_get_stored_info(self): + """Test calling the method to fetch stored build information """ node = SCons.Node.Node() - node.store_timestamp() + result = node.get_stored_info() + assert result is None, result def test_set_always_build(self): """Test setting a Node's always_build value @@ -867,11 +890,6 @@ class NodeTestCase(unittest.TestCase): n = nw.next() assert nw.next() == None - def test_rstr(self): - """Test the rstr() method.""" - n1 = MyNode("n1") - assert n1.rstr() == 'n1', n1.rstr() - def test_abspath(self): """Test the get_abspath() method.""" n = MyNode("foo") @@ -925,8 +943,7 @@ class NodeTestCase(unittest.TestCase): n = SCons.Node.Node() n.set_state(3) - n.set_binfo('bbb', ['b1'], ['b2'], 'b act', 'b actsig') - n.set_csig('csig') + n.binfo = 'xyz' n.includes = 'testincludes' n.found_include = {'testkey':'testvalue'} n.implicit = 'testimplicit' @@ -934,12 +951,7 @@ class NodeTestCase(unittest.TestCase): n.clear() assert n.get_state() is None, n.get_state() - assert not hasattr(n, 'bsig'), n.bsig - assert not hasattr(n, 'bkids'), n.bkids - assert not hasattr(n, 'bkidsigs'), n.bkidsigs - assert not hasattr(n, 'bact'), n.bact - assert not hasattr(n, 'bactsig'), n.bactsig - assert not hasattr(n, 'csig'), n.csig + assert not hasattr(n, 'binfo'), n.bsig assert n.includes is None, n.includes assert n.found_includes == {}, n.found_includes assert n.implicit is None, n.implicit @@ -950,11 +962,11 @@ class NodeTestCase(unittest.TestCase): assert n.get_subst_proxy() == n, n.get_subst_proxy() - def test_get_prevsiginfo(self): - """Test the base Node get_prevsiginfo() method""" + def test_new_binfo(self): + """Test the new_binfo() method""" n = SCons.Node.Node() - siginfo = n.get_prevsiginfo() - assert siginfo == (None, None, None), siginfo + result = n.new_binfo() + assert isinstance(result, SCons.Node.BuildInfo), result def test_get_suffix(self): """Test the base Node get_suffix() method""" diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py index 312be5c..a2537ae 100644 --- a/src/engine/SCons/Node/Python.py +++ b/src/engine/SCons/Node/Python.py @@ -30,7 +30,6 @@ Python nodes. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.Node -import time class Value(SCons.Node.Node): """A class for Python variables, typically passed on the command line @@ -39,7 +38,6 @@ class Value(SCons.Node.Node): def __init__(self, value): SCons.Node.Node.__init__(self) self.value = value - self.timestamp = time.time() def __str__(self): return repr(self.value) @@ -48,20 +46,7 @@ class Value(SCons.Node.Node): """A "builder" for Values.""" pass - def current(self, calc): - """If all of our children were up-to-date, then this - Value was up-to-date, too.""" - # Allow the children to calculate their signatures. - calc.bsig(self) - state = 0 - for kid in self.children(None): - s = kid.get_state() - if s and (not state or s > state): - state = s - if state == 0 or state == SCons.Node.up_to_date: - return 1 - else: - return 0 + current = SCons.Node.Node.children_are_up_to_date def is_under(self, dir): # Make Value nodes get built regardless of @@ -77,5 +62,17 @@ class Value(SCons.Node.Node): contents = contents + kid.get_contents() return contents - def get_timestamp(self): - return self.timestamp + def calc_csig(self, calc): + """Because we're a Python value node and don't have a real + timestamp, we get to ignore the calculator and just use the + value contents.""" + try: + self.binfo + except: + self.binfo = self.new_binfo() + try: + return self.binfo.csig + except AttributeError: + self.binfo.csig = self.get_contents() + self.store_info(self.binfo) + return self.binfo.csig diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py index 0befa25..0fe22a5 100644 --- a/src/engine/SCons/Node/PythonTests.py +++ b/src/engine/SCons/Node/PythonTests.py @@ -45,6 +45,21 @@ class ValueTestCase(unittest.TestCase): assert not v1 is v2 assert v1.value == v2.value + def test_calc_csig(self): + """Test calculating the content signature of a Value() object + """ + v1 = SCons.Node.Python.Value('aaa') + csig = v1.calc_csig(None) + assert csig == 'aaa', csig + + v2 = SCons.Node.Python.Value(7) + csig = v2.calc_csig(None) + assert csig == '7', csig + + v3 = SCons.Node.Python.Value(None) + csig = v3.calc_csig(None) + assert csig == 'None', csig + if __name__ == "__main__": suite = unittest.makeSuite(ValueTestCase, 'test_') diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 6790cb5..59f2301 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -47,6 +47,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import copy +import string from SCons.Debug import logInstanceCreation import SCons.SConsign @@ -81,6 +82,10 @@ def do_nothing(node): pass Annotate = do_nothing +class BuildInfo: + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + class Node: """The base Node class, for entities that we know how to build, or use to build other Nodes. @@ -196,7 +201,12 @@ class Node: def built(self): """Called just after this node is sucessfully built.""" - self.store_binfo() + try: + new_binfo = self.binfo + except AttributeError: + pass + else: + self.store_info(new_binfo) # Clear out the implicit dependency caches: # XXX this really should somehow be made more general and put @@ -212,9 +222,9 @@ class Node: w = Walker(self, get_parents, ignore_cycle, clear_cache) while w.next(): pass - # clear out the content signature, since the contents of this - # node were presumably just changed: - self.del_csig() + # The content just changed, delete any cached info + # so it will get recalculated. + self.del_cinfo() def postprocess(self): """Clean up anything we don't need to hang onto after we've @@ -233,15 +243,11 @@ class Node: """ self.set_state(None) self.del_binfo() - self.del_csig() + self.del_cinfo() try: delattr(self, '_calculated_sig') except AttributeError: pass - try: - delattr(self, '_tempbsig') - except AttributeError: - pass self.includes = None self.found_includes = {} self.implicit = None @@ -380,22 +386,27 @@ class Node: build_env = self.get_build_env() - if implicit_cache and not implicit_deps_changed: - implicit = self.get_stored_implicit() - if implicit is not None: - implicit = map(self.implicit_factory, implicit) - self._add_child(self.implicit, self.implicit_dict, implicit) - calc = build_env.get_calculator() - if implicit_deps_unchanged or calc.current(self, calc.bsig(self)): - return - else: - # one of this node's sources has changed, so - # we need to recalculate the implicit deps, - # and the bsig: - self.implicit = [] - self.implicit_dict = {} - self._children_reset() - self.del_binfo() + # XXX Here's where we implement --implicit-cache. This doesn't + # do anything right now, but we're probably going to re-implement + # as a way to cache #include lines from source files, so I want + # to keep this code around for now. + # + #if implicit_cache and not implicit_deps_changed: + # implicit = self.get_stored_implicit() + # if implicit is not None: + # implicit = map(self.implicit_factory, implicit) + # self._add_child(self.implicit, self.implicit_dict, implicit) + # calc = build_env.get_calculator() + # if implicit_deps_unchanged or calc.current(self, calc.bsig(self)): + # return + # else: + # # one of this node's sources has changed, so + # # we need to recalculate the implicit deps, + # # and the bsig: + # self.implicit = [] + # self.implicit_dict = {} + # self._children_reset() + # self.del_binfo() for child in self.children(scan=0): scanner = child.source_scanner @@ -413,8 +424,9 @@ class Node: self.target_scanner, self)) - if implicit_cache: - self.store_implicit() + # XXX See note above re: --implicit-cache. + #if implicit_cache: + # self.store_implicit() def scanner_key(self): return None @@ -446,139 +458,92 @@ class Node: env = self.env or SCons.Defaults.DefaultEnvironment() if env.use_build_signature(): - sig = self.rfile().calc_bsig(calc, self) + sig = self.calc_bsig(calc) else: - sig = self.rfile().calc_csig(calc, self) + sig = self.calc_csig(calc) elif not self.rexists(): sig = None else: - sig = self.rfile().calc_csig(calc, self) + sig = self.calc_csig(calc) self._calculated_sig = sig return sig - def calc_bsig(self, calc, cache=None): - """Return the node's build signature, calculating it first - if necessary. + def new_binfo(self): + return BuildInfo() - Note that we don't save it in the "real" build signature - attribute if we have to calculate it here; the "real" build - signature only gets updated after a file is actually built. - """ - if cache is None: cache = self - try: - return cache.bsig - except AttributeError: - try: - return cache._tempbsig - except AttributeError: - cache._tempbsig = calc.bsig(self, cache) - return cache._tempbsig - - def get_bsig(self): - """Get the node's build signature (based on the signatures - of its dependency files and build information).""" + def del_binfo(self): + """Delete the bsig from this node.""" try: - return self.bsig + delattr(self, 'binfo') except AttributeError: - return None + pass - def set_bsig(self, bsig): - """Set the node's build signature (based on the signatures - of its dependency files and build information).""" - self.bsig = bsig - - def get_binfo(self): - """Get the node's build signature (based on the signatures - of its dependency files and build information).""" - result = [] - for attr in ['bsig', 'bkids', 'bkidsigs', 'bact', 'bactsig']: - try: - r = getattr(self, attr) - except AttributeError: - r = None - result.append(r) - return tuple(result) - - def set_binfo(self, bsig, bkids, bkidsigs, bact, bactsig): - """Set the node's build signature (based on the signatures - of its dependency files and build information).""" - self.bsig = bsig - self.bkids = bkids - self.bkidsigs = bkidsigs - self.bact = bact - self.bactsig = bactsig + def calc_bsig(self, calc): try: - delattr(self, '_tempbsig') + return self.binfo.bsig except AttributeError: - pass - - def store_binfo(self): - """Make the build signature permanent (that is, store it in the - .sconsign file or equivalent).""" - pass + self.binfo = self.gen_binfo(calc) + return self.binfo.bsig - def get_stored_binfo(self): - return (None, None, None, None, None) + def gen_binfo(self, calc): + """ + Generate a node's build signature, the digested signatures + of its dependency files and build information. - def del_binfo(self): - """Delete the bsig from this node.""" - for attr in ['bsig', 'bkids', 'bkidsigs', 'bact', 'bactsig']: - try: - delattr(self, attr) - except AttributeError: - pass - - def get_csig(self): - """Get the signature of the node's content.""" - try: - return self.csig - except AttributeError: - return None + node - the node whose sources will be collected + cache - alternate node to use for the signature cache + returns - the build signature - def calc_csig(self, calc, cache=None): - """Return the node's content signature, calculating it first - if necessary. + 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 = self - try: - return cache.csig - except AttributeError: - cache.csig = calc.csig(self, cache) - return cache.csig - def set_csig(self, csig): - """Set the signature of the node's content.""" - self.csig = csig + binfo = self.new_binfo() - def store_csig(self): - """Make the content signature permanent (that is, store it in the - .sconsign file or equivalent).""" - pass + children = self.children() + + sigs = map(lambda n, c=calc: n.calc_signature(c), children) + + binfo.bkids = map(str, children) + binfo.bkidsigs = sigs[:] + + if self.has_builder(): + executor = self.get_executor() + binfo.bact = str(executor) + binfo.bactsig = calc.module.signature(executor) + sigs.append(binfo.bactsig) - def del_csig(self): - """Delete the csig from this node.""" + binfo.bsig = calc.module.collect(filter(None, sigs)) + + return binfo + + def del_cinfo(self): try: - delattr(self, 'csig') + del self.binfo.csig except AttributeError: pass - def get_prevsiginfo(self): - """Fetch the previous signature information from the - .sconsign entry.""" - return SCons.SConsign.Base.null_siginfo - - def get_timestamp(self): - return 0 + def calc_csig(self, calc): + try: + self.binfo + except: + self.binfo = self.new_binfo() + try: + return self.binfo.csig + except AttributeError: + self.binfo.csig = calc.module.signature(self) + self.store_info(self.binfo) + return self.binfo.csig - def store_timestamp(self): - """Make the timestamp permanent (that is, store it in the + def store_info(self, obj): + """Make the build signature permanent (that is, store it in the .sconsign file or equivalent).""" pass - def store_implicit(self): - """Make the implicit deps permanent (that is, store them in the - .sconsign file or equivalent).""" - pass + def get_stored_info(self): + return None def get_stored_implicit(self): """Fetch the stored implicit dependencies""" @@ -735,13 +700,24 @@ class Node: return self.state def current(self, calc=None): + """Default check for whether the Node is current: unknown Node + subtypes are always out of date, so they will always get built.""" return None - def rfile(self): - return self - - def rstr(self): - return str(self) + def children_are_up_to_date(self, calc): + """Alternate check for whether the Node is current: If all of + our children were up-to-date, then this Node was up-to-date, too. + + The SCons.Node.Alias and SCons.Node.Python.Value subclasses + rebind their current() method to this method.""" + # Allow the children to calculate their signatures. + self.binfo = self.gen_binfo(calc) + state = 0 + for kid in self.children(None): + s = kid.get_state() + if s and (not state or s > state): + state = s + return (state == 0 or state == SCons.Node.up_to_date) def is_literal(self): """Always pass the string representation of a Node to @@ -826,7 +802,53 @@ class Node: if no new functionality is needed for Environment substitution. """ return self - + + def explain(self): + if not self.exists(): + return "building `%s' because it doesn't exist\n" % self + + old = self.get_stored_info() + if old is None: + return None + + def dictify(kids, sigs): + result = {} + for k, s in zip(kids, sigs): + result[k] = s + return result + + osig = dictify(old.bkids, old.bkidsigs) + + newkids = map(str, self.binfo.bkids) + nsig = dictify(newkids, self.binfo.bkidsigs) + + lines = map(lambda x: "`%s' is no longer a dependency\n" % x, + filter(lambda x, nk=newkids: not x in nk, old.bkids)) + + for k in newkids: + if not k in old.bkids: + lines.append("`%s' is a new dependency\n" % k) + elif osig[k] != nsig[k]: + lines.append("`%s' changed\n" % k) + + if len(lines) == 0: + newact, newactsig = self.binfo.bact, self.binfo.bactsig + if old.bact != newact: + lines.append("the build action changed:\n" + + "%sold: %s\n" % (' '*15, old.bact) + + "%snew: %s\n" % (' '*15, newact)) + + if len(lines) == 0: + lines.append("the dependency order changed:\n" + + "%sold: %s\n" % (' '*15, old.bkids) + + "%snew: %s\n" % (' '*15, newkids)) + + preamble = "rebuilding `%s' because" % self + if len(lines) == 1: + return "%s %s" % (preamble, lines[0]) + else: + lines = ["%s:\n" % preamble] + lines + return string.join(lines, ' '*11) def get_children(node, parent): return node.children() def ignore_cycle(node, stack): pass diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py index a91817b..c97f1b6 100644 --- a/src/engine/SCons/SConsign.py +++ b/src/engine/SCons/SConsign.py @@ -34,8 +34,8 @@ import os import os.path import time -import SCons.Sig import SCons.Node +import SCons.Sig import SCons.Warnings #XXX Get rid of the global array so this becomes re-entrant. @@ -48,28 +48,6 @@ def write(): for sig_file in sig_files: sig_file.write() - -class Entry: - - """Objects of this type are pickled to the .sconsign file, so it - should only contain simple builtin Python datatypes and no methods. - - This class is used to store cache information about nodes between - scons runs for efficiency, and to store the build signature for - nodes so that scons can determine if they are out of date. """ - - # setup the default value for various attributes: - # (We make the class variables so the default values won't get pickled - # with the instances, which would waste a lot of space) - timestamp = None - bsig = None - csig = None - implicit = None - bkids = [] - bkidsigs = [] - bact = None - bactsig = None - class Base: """ This is the controlling class for the signatures for the collection of @@ -88,97 +66,26 @@ class Base: self.entries = {} self.dirty = 0 - # A null .sconsign entry. We define this here so that it will - # be easy to keep this in sync if/whenever we change the type of - # information returned by the get() method, below. - null_siginfo = (None, None, None) - - def get(self, filename): - """ - Get the .sconsign entry for a file - - filename - the filename whose signature will be returned - returns - (timestamp, bsig, csig) - """ - entry = self.get_entry(filename) - return (entry.timestamp, entry.bsig, entry.csig) - def get_entry(self, filename): """ Create an entry for the filename and return it, or if one already exists, then return it. """ - try: - return self.entries[filename] - except (KeyError, AttributeError): - return Entry() + return self.entries[filename] - def set_entry(self, filename, entry): + def set_entry(self, filename, obj): """ Set the entry. """ - self.entries[filename] = entry + try: + entry = self.entries[filename] + except KeyError: + self.entries[filename] = obj + else: + for key, val in obj.__dict__.items(): + entry.__dict__[key] = val self.dirty = 1 - def set_csig(self, filename, csig): - """ - Set the csig .sconsign entry for a file - - filename - the filename whose signature will be set - csig - the file's content signature - """ - - entry = self.get_entry(filename) - entry.csig = csig - self.set_entry(filename, entry) - - def set_binfo(self, filename, bsig, bkids, bkidsigs, bact, bactsig): - """ - Set the build info .sconsign entry for a file - - filename - the filename whose signature will be set - bsig - the file's built signature - """ - - entry = self.get_entry(filename) - entry.bsig = bsig - entry.bkids = bkids - entry.bkidsigs = bkidsigs - entry.bact = bact - entry.bactsig = bactsig - self.set_entry(filename, entry) - - def set_timestamp(self, filename, timestamp): - """ - Set the timestamp .sconsign entry for a file - - filename - the filename whose signature will be set - timestamp - the file's timestamp - """ - - entry = self.get_entry(filename) - entry.timestamp = timestamp - self.set_entry(filename, entry) - - def get_implicit(self, filename): - """Fetch the cached implicit dependencies for 'filename'""" - entry = self.get_entry(filename) - return entry.implicit - - def set_implicit(self, filename, implicit): - """Cache the implicit dependencies for 'filename'.""" - entry = self.get_entry(filename) - if not SCons.Util.is_List(implicit): - implicit = [implicit] - implicit = map(str, implicit) - entry.implicit = implicit - self.set_entry(filename, entry) - - def get_binfo(self, filename): - """Fetch the cached implicit dependencies for 'filename'""" - entry = self.get_entry(filename) - return entry.bsig, entry.bkids, entry.bkidsigs, entry.bact, entry.bactsig - class DB(Base): """ A Base subclass that reads and writes signature information diff --git a/src/engine/SCons/SConsignTests.py b/src/engine/SCons/SConsignTests.py index 5f8e981..79f4387 100644 --- a/src/engine/SCons/SConsignTests.py +++ b/src/engine/SCons/SConsignTests.py @@ -23,19 +23,14 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import unittest -import TestCmd import SCons.SConsign import sys +import TestCmd +import unittest -class SConsignEntryTestCase(unittest.TestCase): - - def runTest(self): - e = SCons.SConsign.Entry() - assert e.timestamp == None - assert e.csig == None - assert e.bsig == None - assert e.implicit == None +class BuildInfo: + def __init__(self, name): + self.name = name class BaseTestCase(unittest.TestCase): @@ -50,27 +45,51 @@ class BaseTestCase(unittest.TestCase): class DummyNode: path = 'not_a_valid_path' + aaa = BuildInfo('aaa') + bbb = BuildInfo('bbb') + ccc = BuildInfo('ccc') + ccc.arg = 'ccc arg' + f = SCons.SConsign.Base() - f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig') - assert f.get('foo') == (None, 1, None) - f.set_csig('foo', 2) - assert f.get('foo') == (None, 1, 2) - f.set_timestamp('foo', 3) - assert f.get('foo') == (3, 1, 2) - f.set_implicit('foo', ['bar']) - assert f.get('foo') == (3, 1, 2) - assert f.get_implicit('foo') == ['bar'] + f.set_entry('aaa', aaa) + f.set_entry('bbb', bbb) + + e = f.get_entry('aaa') + assert e == aaa, e + assert e.name == 'aaa', e.name + + e = f.get_entry('bbb') + assert e == bbb, e + assert e.name == 'bbb', e.name + assert not hasattr(e, 'arg'), e + + f.set_entry('bbb', ccc) + e = f.get_entry('bbb') + assert e.name == 'ccc', e.name + assert e.arg == 'ccc arg', e.arg + + ddd = BuildInfo('ddd') + eee = BuildInfo('eee') + fff = BuildInfo('fff') + fff.arg = 'fff arg' f = SCons.SConsign.Base(DummyModule()) - f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig') - assert f.get('foo') == (None, 1, None) - f.set_csig('foo', 2) - assert f.get('foo') == (None, 1, 2) - f.set_timestamp('foo', 3) - assert f.get('foo') == (3, 1, 2) - f.set_implicit('foo', ['bar']) - assert f.get('foo') == (3, 1, 2) - assert f.get_implicit('foo') == ['bar'] + f.set_entry('ddd', ddd) + f.set_entry('eee', eee) + + e = f.get_entry('ddd') + assert e == ddd, e + assert e.name == 'ddd', e.name + + e = f.get_entry('eee') + assert e == eee, e + assert e.name == 'eee', e.name + assert not hasattr(e, 'arg'), e + + f.set_entry('eee', fff) + e = f.get_entry('eee') + assert e.name == 'fff', e.name + assert e.arg == 'fff arg', e.arg class SConsignDBTestCase(unittest.TestCase): @@ -82,24 +101,20 @@ class SConsignDBTestCase(unittest.TestCase): SCons.SConsign.database = {} try: d1 = SCons.SConsign.DB(DummyNode('dir1')) - d1.set_timestamp('foo', 1) - d1.set_binfo('foo', 2, ['f1'], ['f2'], 'foo act', 'foo actsig') - d1.set_csig('foo', 3) - d1.set_timestamp('bar', 4) - d1.set_binfo('bar', 5, ['b1'], ['b2'], 'bar act', 'bar actsig') - d1.set_csig('bar', 6) - assert d1.get('foo') == (1, 2, 3) - assert d1.get('bar') == (4, 5, 6) + d1.set_entry('aaa', BuildInfo('aaa name')) + d1.set_entry('bbb', BuildInfo('bbb name')) + aaa = d1.get_entry('aaa') + assert aaa.name == 'aaa name' + bbb = d1.get_entry('bbb') + assert bbb.name == 'bbb name' d2 = SCons.SConsign.DB(DummyNode('dir1')) - d2.set_timestamp('foo', 7) - d2.set_binfo('foo', 8, ['f3'], ['f4'], 'foo act', 'foo actsig') - d2.set_csig('foo', 9) - d2.set_timestamp('bar', 10) - d2.set_binfo('bar', 11, ['b3'], ['b4'], 'bar act', 'bar actsig') - d2.set_csig('bar', 12) - assert d2.get('foo') == (7, 8, 9) - assert d2.get('bar') == (10, 11, 12) + d2.set_entry('ccc', BuildInfo('ccc name')) + d2.set_entry('ddd', BuildInfo('ddd name')) + ccc = d2.get_entry('ccc') + assert ccc.name == 'ccc name' + ddd = d2.get_entry('ddd') + assert ddd.name == 'ddd name' finally: SCons.SConsign.database = save_database @@ -116,16 +131,29 @@ class SConsignDirFileTestCase(unittest.TestCase): class DummyNode: path = 'not_a_valid_path' + foo = BuildInfo('foo') + bar = BuildInfo('bar') + f = SCons.SConsign.DirFile(DummyNode(), DummyModule()) - f.set_binfo('foo', 1, ['f1'], ['f2'], 'foo act', 'foo actsig') - assert f.get('foo') == (None, 1, None) - f.set_csig('foo', 2) - assert f.get('foo') == (None, 1, 2) - f.set_timestamp('foo', 3) - assert f.get('foo') == (3, 1, 2) - f.set_implicit('foo', ['bar']) - assert f.get('foo') == (3, 1, 2) - assert f.get_implicit('foo') == ['bar'] + f.set_entry('foo', foo) + f.set_entry('bar', bar) + + e = f.get_entry('foo') + assert e == foo, e + assert e.name == 'foo', e.name + + e = f.get_entry('bar') + assert e == bar, e + assert e.name == 'bar', e.name + assert not hasattr(e, 'arg'), e + + bbb = BuildInfo('bbb') + bbb.arg = 'bbb arg' + f.set_entry('bar', bbb) + e = f.get_entry('bar') + assert e.name == 'bbb', e.name + assert e.arg == 'bbb arg', e.arg + class SConsignFileTestCase(unittest.TestCase): @@ -165,7 +193,6 @@ class SConsignFileTestCase(unittest.TestCase): def suite(): suite = unittest.TestSuite() - suite.addTest(SConsignEntryTestCase()) suite.addTest(BaseTestCase()) suite.addTest(SConsignDBTestCase()) suite.addTest(SConsignDirFileTestCase()) diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 7978629..ab76011 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -191,53 +191,9 @@ class BuildTask(SCons.Taskmaster.Task): """Make a task ready for execution""" SCons.Taskmaster.Task.make_ready(self) if self.out_of_date and print_explanations: - node = self.out_of_date[0] - if not node.exists(): - sys.stdout.write("scons: building `%s' because it doesn't exist\n" % node) - return - - oldbsig, oldkids, oldsigs, oldact, oldactsig = node.get_stored_binfo() - if oldkids is None: - return - - def dictify(kids, sigs): - result = {} - for k, s in zip(kids, sigs): - result[k] = s - return result - - osig = dictify(oldkids, oldsigs) - - newkids, newsigs = map(str, node.bkids), node.bkidsigs - nsig = dictify(newkids, newsigs) - - lines = map(lambda x: "`%s' is no longer a dependency\n" % x, - filter(lambda x, nk=newkids: not x in nk, oldkids)) - - for k in newkids: - if not k in oldkids: - lines.append("`%s' is a new dependency\n" % k) - elif osig[k] != nsig[k]: - lines.append("`%s' changed\n" % k) - - if len(lines) == 0: - newact, newactsig = node.bact, node.bactsig - if oldact != newact: - lines.append("the build action changed:\n" + - "%sold: %s\n" % (' '*15, oldact) + - "%snew: %s\n" % (' '*15, newact)) - - if len(lines) == 0: - lines.append("the dependency order changed:\n" + - "%sold: %s\n" % (' '*15, oldkids) + - "%snew: %s\n" % (' '*15, newkids)) - - preamble = "scons: rebuilding `%s' because" % node - if len(lines) == 1: - sys.stdout.write("%s %s" % (preamble, lines[0])) - else: - lines = ["%s:\n" % preamble] + lines - sys.stdout.write(string.join(lines, ' '*11)) + explanation = self.out_of_date[0].explain() + if explanation: + sys.stdout.write("scons: " + explanation) class CleanTask(SCons.Taskmaster.Task): """An SCons clean task.""" diff --git a/src/script/sconsign.py b/src/script/sconsign.py index 2204927..e4b7b07 100644 --- a/src/script/sconsign.py +++ b/src/script/sconsign.py @@ -170,43 +170,79 @@ def my_import(mname): fp, pathname, description = imp.find_module(mname) return imp.load_module(mname, fp, pathname, description) -PF_bsig = 0x1 -PF_csig = 0x2 -PF_timestamp = 0x4 -PF_implicit = 0x8 -PF_all = PF_bsig | PF_csig | PF_timestamp | PF_implicit +class Flagger: + default_value = 1 + def __setitem__(self, item, value): + self.__dict__[item] = value + self.default_value = 0 + def __getitem__(self, item): + return self.__dict__.get(item, self.default_value) Do_Call = None Print_Directories = [] Print_Entries = [] -Print_Flags = 0 +Print_Flags = Flagger() Verbose = 0 Readable = 0 -def field(name, pf, val): - if Print_Flags & pf: - if Verbose: - sep = "\n " + name + ": " - else: - sep = " " - return sep + str(val) +def default_mapper(entry, name): + try: + val = eval("entry."+name) + except: + val = None + return str(val) + +def map_timestamp(entry, name): + try: + timestamp = entry.timestamp + except AttributeError: + timestamp = None + if Readable and timestamp: + return "'" + time.ctime(timestamp) + "'" else: - return "" + return str(timestamp) + +def map_bkids(entry, name): + result = [] + try: + for i in xrange(len(entry.bkids)): + result.append("%s: %s" % (entry.bkids[i], entry.bkidsigs[i])) + except AttributeError: + return None + if result == []: + return None + return string.join(result, "\n ") + +map_field = { + 'timestamp' : map_timestamp, + 'bkids' : map_bkids, +} + +map_name = { + 'implicit' : 'bkids', +} def printfield(name, entry): - if Readable and entry.timestamp: - ts = "'" + time.ctime(entry.timestamp) + "'" - else: - ts = entry.timestamp - timestamp = field("timestamp", PF_timestamp, ts) - bsig = field("bsig", PF_bsig, entry.bsig) - csig = field("csig", PF_csig, entry.csig) - print name + ":" + timestamp + bsig + csig - if Print_Flags & PF_implicit and entry.implicit: + def field(name, verbose=Verbose, entry=entry): + if not Print_Flags[name]: + return None + fieldname = map_name.get(name, name) + mapper = map_field.get(fieldname, default_mapper) + val = mapper(entry, name) + if verbose: + val = name + ": " + val + return val + + fieldlist = ["timestamp", "bsig", "csig"] + outlist = [name+":"] + filter(None, map(field, fieldlist)) + sep = Verbose and "\n " or " " + print string.join(outlist, sep) + + outlist = field("implicit", 0) + if outlist: if Verbose: print " implicit:" - for i in entry.implicit: - print " %s" % i + print " " + outlist def printentries(entries): if Print_Entries: @@ -316,11 +352,12 @@ opts, args = getopt.getopt(sys.argv[1:], "bcd:e:f:hirtv", 'format=', 'help', 'implicit', 'readable', 'timestamp', 'verbose']) + for o, a in opts: if o in ('-b', '--bsig'): - Print_Flags = Print_Flags | PF_bsig + Print_Flags['bsig'] = 1 elif o in ('-c', '--csig'): - Print_Flags = Print_Flags | PF_csig + Print_Flags['csig'] = 1 elif o in ('-d', '--dir'): Print_Directories.append(a) elif o in ('-e', '--entry'): @@ -343,17 +380,14 @@ for o, a in opts: print helpstr sys.exit(0) elif o in ('-i', '--implicit'): - Print_Flags = Print_Flags | PF_implicit + Print_Flags['implicit'] = 1 elif o in ('-r', '--readable'): Readable = 1 elif o in ('-t', '--timestamp'): - Print_Flags = Print_Flags | PF_timestamp + Print_Flags['timestamp'] = 1 elif o in ('-v', '--verbose'): Verbose = 1 -if Print_Flags == 0: - Print_Flags = PF_all - if Do_Call: for a in args: Do_Call(a) |