From: stevenknight Date: Tue, 7 Jun 2005 11:32:40 +0000 (+0000) Subject: Split Node-specific stuff from BuildInfo into a separate NodeInfo class. Add size... X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=9074326b92131d8f89b425a8589dac2c0c3b52c7;p=scons.git Split Node-specific stuff from BuildInfo into a separate NodeInfo class. Add size info to the File information we collect. git-svn-id: http://scons.tigris.org/svn/scons/trunk@1308 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 6e95311c..072f80ab 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -765,7 +765,8 @@ modification time of at least two days ago in order to have its cached content signature used. A negative value means to never cache the content signature and to ignore the cached value if there already is one. A value -of 0 means to always cache the signature, no matter how old the file is. +of 0 means to always use the cached signature, +no matter how old the file is. .TP -n, --just-print, --dry-run, --recon diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 8abf25b5..d246daeb 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -291,6 +291,11 @@ RELEASE 0.97 - XXX - Print various --debug= stats even if we exit early (e.g. using -h). + - Really only use the cached content signature value if the file + is older than --max-drift, not just if --max-drift is set. + + - Remove support for conversion from old (pre 0.96) .sconsign formats. + From Wayne Lee: - Avoid "maximum recursion limit" errors when removing $(-$) pairs diff --git a/src/RELEASE.txt b/src/RELEASE.txt index bbf0a3a2..5b95aa78 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -130,6 +130,13 @@ RELEASE 0.97 - XXX Python function Actions (including various pre-supplied SCons Actions) be rebuilt. + -- REMOVED CONVERSION FROM PRE-0.96 .sconsign FORMATS + + Because this release involves so many other signature + changes that cause rebuilds, the support for automatically + converting signature information from .sconsign files + written by SCons versions prior to 0.96 has been removed. + -- CACHED Configure() RESULTS ARE STORED IN A DIFFERENT FILE The Configure() subsystem now stores its cached results in a diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index c728eceb..3b4e77ff 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -482,7 +482,18 @@ class Base(SCons.Node.Node): return self.rfile().exists() def getmtime(self): - return self.stat()[stat.ST_MTIME] + st = self.stat() + if st: + return self.stat()[stat.ST_MTIME] + else: + return None + + def getsize(self): + st = self.stat() + if st: + return self.stat()[stat.ST_SIZE] + else: + return None def isdir(self): st = self.stat() @@ -686,6 +697,8 @@ class LocalFS: return os.path.exists(path) def getmtime(self, path): return os.path.getmtime(path) + def getsize(self, path): + return os.path.getsize(path) def isdir(self, path): return os.path.isdir(path) def isfile(self, path): @@ -1464,17 +1477,21 @@ class RootDir(Dir): def src_builder(self): return _null -class BuildInfo: - # bsig needs to stay here, if it's initialized in __init__() then - # the assignment overwrites any values read from .sconsign files. +class NodeInfo(SCons.Node.NodeInfo): + # The bsig attributes needs to stay here, if it's initialized in + # __init__() then the assignment seems to overwrite any values + # unpickled from .sconsign files. bsig = None + def __cmp__(self, other): + return cmp(self.bsig, other.bsig) + def update(self, node): + self.timestamp = node.get_timestamp() + self.size = node.getsize() + +class BuildInfo(SCons.Node.BuildInfo): def __init__(self, node): + SCons.Node.BuildInfo.__init__(self, node) self.node = node - def __cmp__(self, other): - try: - return cmp(self.bsig, other.bsig) - except AttributeError: - return 1 def convert_to_sconsign(self): """Convert this BuildInfo object for writing to a .sconsign file @@ -1568,8 +1585,7 @@ class File(Base): # in one build (SConstruct file) is a source in a different build. # See test/chained-build.py for the use case. entry = self.get_stored_info() - for key, val in obj.__dict__.items(): - entry.__dict__[key] = val + entry.merge(obj) self.dir.sconsign().set_entry(self.name, entry) def get_stored_info(self): @@ -1579,17 +1595,17 @@ class File(Base): except (KeyError, OSError): return self.new_binfo() else: - if isinstance(stored, BuildInfo): - return stored - # The stored build information isn't a BuildInfo object. - # This probably means it's an old SConsignEntry from SCons - # 0.95 or before. The relevant attribute names are the same, - # though, so just copy the attributes over to an object of - # the correct type. - binfo = self.new_binfo() - for key, val in stored.__dict__.items(): - setattr(binfo, key, val) - return binfo + if not hasattr(stored, 'ninfo'): + # Transition: The .sconsign file entry has no NodeInfo + # object, which means it's a slightly older BuildInfo. + # Copy over the relevant attributes. + ninfo = stored.ninfo = self.new_ninfo() + for attr in ninfo.__dict__.keys(): + try: + setattr(ninfo, attr, getattr(stored, attr)) + except AttributeError: + pass + return stored def get_stored_implicit(self): binfo = self.get_stored_info() @@ -1765,20 +1781,19 @@ class File(Base): self.do_duplicate(src) return Base.exists(self) + # + # SIGNATURE SUBSYSTEM + # + def new_binfo(self): return BuildInfo(self) - def del_cinfo(self): - try: - del self.binfo.csig - except AttributeError: - pass - try: - del self.binfo.timestamp - except AttributeError: - pass + def new_ninfo(self): + ninfo = NodeInfo() + ninfo.update(self) + return ninfo - def calc_csig(self, calc=None): + def get_csig(self, calc=None): """ Generate a node's content signature, the digested signature of its content. @@ -1787,45 +1802,44 @@ class File(Base): cache - alternate node to use for the signature cache returns - the content signature """ - if calc is None: - calc = self.calculator() - try: - return self.binfo.csig + return self.binfo.ninfo.csig except AttributeError: pass - - if calc.max_drift >= 0: - old = self.get_stored_info() - else: - old = self.new_binfo() - try: - mtime = self.get_timestamp() - except OSError: - mtime = 0 - raise SCons.Errors.UserError, "no such %s" % self + if calc is None: + calc = self.calculator() - 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) + mtime = self.get_timestamp() - if calc.max_drift >= 0 and (time.time() - mtime) > calc.max_drift: + use_stored = calc.max_drift >= 0 and \ + (time.time() - mtime) > calc.max_drift + + csig = None + if use_stored: + old = self.get_stored_info().ninfo try: - binfo = self.binfo + if old.timestamp and old.csig and old.timestamp == mtime: + csig = old.csig except AttributeError: - binfo = self.binfo = self.new_binfo() - binfo.csig = csig - binfo.timestamp = mtime + pass + if csig is None: + csig = calc.module.signature(self) + + binfo = self.get_binfo() + ninfo = binfo.ninfo + ninfo.csig = csig + ninfo.update(self) + + if use_stored: self.store_info(binfo) return csig + # + # + # + def current(self, calc=None): self.binfo = self.gen_binfo(calc) return self._cur2() @@ -1839,17 +1853,19 @@ class File(Base): if r != self: # ...but there is one in a Repository... old = r.get_stored_info() - if old == self.binfo: + new = self.get_binfo() + if new == old: # ...and it's even up-to-date... if self._local: # ...and they'd like a local copy. LocalCopy(self, r, None) - self.store_info(self.binfo) + self.store_info(new) return 1 return None else: old = self.get_stored_info() - return (self.binfo == old) + new = self.get_binfo() + return (new == old) def rfile(self): "__cacheable__" @@ -1870,12 +1886,15 @@ class File(Base): def cachepath(self): if not self.fs.CachePath: return None, None - if self.binfo.bsig is None: + ninfo = self.get_binfo().ninfo + if not hasattr(ninfo, 'bsig'): + raise SCons.Errors.InternalError, "cachepath(%s) found no bsig" % self.path + elif ninfo.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]) + cache_sig = SCons.Sig.MD5.collect([ninfo.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) diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 6185de85..f7a2a636 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -627,6 +627,24 @@ class BaseTestCase(_tempdirTestCase): file = fs.Entry('file') assert file.getmtime() + file = fs.Entry('nonexistent') + mtime = file.getmtime() + assert mtime is None, mtime + + def test_getsize(self): + """Test the Base.getsize() method""" + test = self.test + test.write("file", "file\n") + fs = SCons.Node.FS.FS() + + file = fs.Entry('file') + size = file.getsize() + assert size == 5, size + + file = fs.Entry('nonexistent') + size = file.getsize() + assert size is None, size + def test_isdir(self): """Test the Base.isdir() method""" test = self.test @@ -680,6 +698,52 @@ class BaseTestCase(_tempdirTestCase): nonexistent = fs.Entry('nonexistent') assert not nonexistent.islink() +class NodeInfoTestCase(_tempdirTestCase): + def test___init__(self): + """Test NodeInfo initialization""" + ni = SCons.Node.FS.NodeInfo() + assert hasattr(ni, 'bsig') + + def test___cmp__(self): + """Test comparing NodeInfo objects""" + ni1 = SCons.Node.FS.NodeInfo() + ni2 = SCons.Node.FS.NodeInfo() + + assert cmp(ni1, ni2) == 0, "ni1 %s != ni2 %s" % (ni1, ni2) + + ni1.bsig = 777 + assert cmp(ni1, ni2) != 0, "ni1 %s == ni2 %s" % (ni1, ni2) + + ni2.bsig = 777 + assert cmp(ni1, ni2) == 0, "ni1 %s != ni2 %s" % (ni1, ni2) + + def test_update(self): + """Test updating a NodeInfo with on-disk information""" + test = self.test + test.write('fff', "fff\n") + fff = self.fs.File('fff') + + ni = SCons.Node.FS.NodeInfo() + assert not hasattr(ni, 'timestamp') + assert not hasattr(ni, 'size') + + ni.update(fff) + assert ni.timestamp == os.path.getmtime('fff'), ni.timestamp + assert ni.size == os.path.getsize('fff'), ni.size + +class BuildInfoTestCase(_tempdirTestCase): + def test___init__(self): + """Test BuildInfo initialization""" + fff = self.fs.File('fff') + bi = SCons.Node.FS.BuildInfo(fff) + assert bi.node is fff, bi.node + + def test_convert_to_sconsign(self): + """Test converting to .sconsign file format""" + + def test_convert_from_sconsign(self): + """Test converting from .sconsign file format""" + class FSTestCase(_tempdirTestCase): def test_runTest(self): """Test FS (file system) Node operations @@ -2084,7 +2148,7 @@ class StringDirTestCase(unittest.TestCase): class stored_infoTestCase(unittest.TestCase): def runTest(self): - """Test how storing build information""" + """Test how we store build information""" test = TestCmd(workdir = '') test.subdir('sub') fs = SCons.Node.FS.FS(test.workpath('')) @@ -2092,7 +2156,8 @@ class stored_infoTestCase(unittest.TestCase): d = fs.Dir('sub') f = fs.File('file1', d) bi = f.get_stored_info() - assert bi.bsig == None, bi.bsig + assert bi.ninfo.timestamp == 0, bi.ninfo.timestamp + assert bi.ninfo.size == None, bi.ninfo.size class MySConsign: class Null: @@ -2343,7 +2408,7 @@ class CacheDirTestCase(unittest.TestCase): try: f5 = fs.File("cd.f5") f5.binfo = f5.new_binfo() - f5.binfo.bsig = 'a_fake_bsig' + f5.binfo.ninfo.bsig = 'a_fake_bsig' cp = f5.cachepath() dirname = os.path.join('cache', 'A') filename = os.path.join(dirname, 'a_fake_bsig') @@ -2378,7 +2443,7 @@ class CacheDirTestCase(unittest.TestCase): test.write(cd_f7, "cd.f7\n") f7 = fs.File(cd_f7) f7.binfo = f7.new_binfo() - f7.binfo.bsig = 'f7_bsig' + f7.binfo.ninfo.bsig = 'f7_bsig' warn_caught = 0 try: @@ -2729,6 +2794,8 @@ if __name__ == "__main__": suite.addTest(SaveStringsTestCase()) tclasses = [ BaseTestCase, + BuildInfoTestCase, + NodeInfoTestCase, FSTestCase, DirTestCase, RepositoryTestCase, diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 3be1d6b6..8c2e6ea6 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -214,6 +214,104 @@ class Calculator: +class NodeInfoTestCase(unittest.TestCase): + + def test___cmp__(self): + """Test comparing NodeInfo objects""" + ni1 = SCons.Node.NodeInfo() + ni2 = SCons.Node.NodeInfo() + + assert ni1 == ni2, "%s != %s" % (ni1.__dict__, ni2.__dict__) + + ni1.foo = 777 + assert ni1 != ni2, "%s == %s" % (ni1.__dict__, ni2.__dict__) + + ni2.foo = 888 + assert ni1 != ni2, "%s == %s" % (ni1.__dict__, ni2.__dict__) + + ni1.foo = 888 + assert ni1 == ni2, "%s != %s" % (ni1.__dict__, ni2.__dict__) + + def test_merge(self): + """Test merging NodeInfo attributes""" + ni1 = SCons.Node.NodeInfo() + ni2 = SCons.Node.NodeInfo() + + ni1.a1 = 1 + ni1.a2 = 2 + + ni2.a2 = 222 + ni2.a3 = 333 + + ni1.merge(ni2) + assert ni1.__dict__ == {'a1':1, 'a2':222, 'a3':333}, ni1.__dict__ + + def test_update(self): + """Test the update() method""" + ni = SCons.Node.NodeInfo() + ni.update(SCons.Node.Node()) + + + +class BuildInfoTestCase(unittest.TestCase): + + def test___init__(self): + """Test BuildInfo initialization""" + bi = SCons.Node.BuildInfo(SCons.Node.Node()) + assert hasattr(bi, 'ninfo') + + class MyNode(SCons.Node.Node): + def new_ninfo(self): + return 'ninfo initialization' + bi = SCons.Node.BuildInfo(MyNode()) + assert bi.ninfo == 'ninfo initialization', bi.ninfo + + def test___cmp__(self): + """Test comparing BuildInfo objects""" + bi1 = SCons.Node.BuildInfo(SCons.Node.Node()) + bi2 = SCons.Node.BuildInfo(SCons.Node.Node()) + + assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__) + + bi1.ninfo.foo = 777 + assert bi1 != bi2, "%s == %s" % (bi1.__dict__, bi2.__dict__) + + bi2.ninfo.foo = 888 + assert bi1 != bi2, "%s == %s" % (bi1.__dict__, bi2.__dict__) + + bi1.ninfo.foo = 888 + assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__) + + bi1.foo = 999 + assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__) + + def test_merge(self): + """Test merging BuildInfo attributes""" + bi1 = SCons.Node.BuildInfo(SCons.Node.Node()) + bi2 = SCons.Node.BuildInfo(SCons.Node.Node()) + + bi1.a1 = 1 + bi1.a2 = 2 + + bi2.a2 = 222 + bi2.a3 = 333 + + bi1.ninfo.a4 = 4 + bi1.ninfo.a5 = 5 + bi2.ninfo.a5 = 555 + bi2.ninfo.a6 = 666 + + bi1.merge(bi2) + assert bi1.a1 == 1, bi1.a1 + assert bi1.a2 == 222, bi1.a2 + assert bi1.a3 == 333, bi1.a3 + assert bi1.ninfo.a4 == 4, bi1.ninfo.a4 + assert bi1.ninfo.a5 == 555, bi1.ninfo.a5 + assert bi1.ninfo.a6 == 666, bi1.ninfo.a6 + + + + class NodeTestCase(unittest.TestCase): def test_build(self): @@ -481,24 +579,36 @@ class NodeTestCase(unittest.TestCase): a = node.builder.get_actions() assert isinstance(a[0], MyAction), a[0] - def test_calc_bsig(self): + def test_get_bsig(self): """Test generic build signature calculation """ node = SCons.Node.Node() - result = node.calc_bsig(Calculator(222)) + result = node.get_bsig(Calculator(222)) assert result == 222, result - result = node.calc_bsig(Calculator(333)) + result = node.get_bsig(Calculator(333)) assert result == 222, result - def test_calc_csig(self): + def test_get_csig(self): """Test generic content signature calculation """ node = SCons.Node.Node() - result = node.calc_csig(Calculator(444)) + result = node.get_csig(Calculator(444)) assert result == 444, result - result = node.calc_csig(Calculator(555)) + result = node.get_csig(Calculator(555)) assert result == 444, result + def test_get_binfo(self): + """Test fetching/creating a build information structure + """ + node = SCons.Node.Node() + + binfo = node.get_binfo() + assert isinstance(binfo, SCons.Node.BuildInfo), binfo + + node.binfo = 777 + binfo = node.get_binfo() + assert binfo == 777, binfo + def test_gen_binfo(self): """Test generating a build information structure """ @@ -507,7 +617,8 @@ class NodeTestCase(unittest.TestCase): i = SCons.Node.Node() node.depends = [d] node.implicit = [i] - binfo = node.gen_binfo(Calculator(1998)) + node.gen_binfo(Calculator(666)) + binfo = node.binfo assert isinstance(binfo, SCons.Node.BuildInfo), binfo assert hasattr(binfo, 'bsources') assert hasattr(binfo, 'bsourcesigs') @@ -515,7 +626,7 @@ class NodeTestCase(unittest.TestCase): assert hasattr(binfo, 'bdependsigs') assert binfo.bimplicit == [i] assert hasattr(binfo, 'bimplicitsigs') - assert binfo.bsig == 5994, binfo.bsig + assert binfo.ninfo.bsig == 1998, binfo.ninfo.bsig def test_explain(self): """Test explaining why a Node must be rebuilt @@ -1206,7 +1317,9 @@ class NodeListTestCase(unittest.TestCase): if __name__ == "__main__": suite = unittest.TestSuite() - tclasses = [ NodeTestCase, + tclasses = [ BuildInfoTestCase, + NodeInfoTestCase, + NodeTestCase, NodeListTestCase ] for tclass in tclasses: names = unittest.getTestCaseNames(tclass, 'test_') diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py index cabc1624..0e1c985a 100644 --- a/src/engine/SCons/Node/Python.py +++ b/src/engine/SCons/Node/Python.py @@ -62,7 +62,7 @@ class Value(SCons.Node.Node): contents = contents + kid.get_contents() return contents - def calc_csig(self, calc=None): + def get_csig(self, calc=None): """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.""" @@ -71,8 +71,8 @@ class Value(SCons.Node.Node): except AttributeError: binfo = self.binfo = self.new_binfo() try: - return binfo.csig + return binfo.ninfo.csig except AttributeError: - binfo.csig = self.get_contents() + binfo.ninfo.csig = self.get_contents() self.store_info(binfo) - return binfo.csig + return binfo.ninfo.csig diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py index 0fe22a52..78cbf68a 100644 --- a/src/engine/SCons/Node/PythonTests.py +++ b/src/engine/SCons/Node/PythonTests.py @@ -45,19 +45,19 @@ class ValueTestCase(unittest.TestCase): assert not v1 is v2 assert v1.value == v2.value - def test_calc_csig(self): + def test_get_csig(self): """Test calculating the content signature of a Value() object """ v1 = SCons.Node.Python.Value('aaa') - csig = v1.calc_csig(None) + csig = v1.get_csig(None) assert csig == 'aaa', csig v2 = SCons.Node.Python.Value(7) - csig = v2.calc_csig(None) + csig = v2.get_csig(None) assert csig == '7', csig v3 = SCons.Node.Python.Value(None) - csig = v3.calc_csig(None) + csig = v3.get_csig(None) assert csig == 'None', csig diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 627d0201..b04284d3 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -95,9 +95,55 @@ def do_nothing(node): pass Annotate = do_nothing -class BuildInfo: +# Classes for signature info for Nodes. + +class NodeInfo: + """ + A generic class for signature information for a Node. + + We actually expect that modules containing Node subclasses will also + subclass NodeInfo, to provide their own logic for dealing with their + own Node-specific signature information. + """ + def __init__(self): + """A null initializer so that subclasses have a superclass + initialization method to call for future use. + """ + pass def __cmp__(self, other): return cmp(self.__dict__, other.__dict__) + def update(self, node): + pass + def merge(self, other): + for key, val in other.__dict__.items(): + self.__dict__[key] = val + +class BuildInfo: + """ + The generic build information for a Node. + + This is what gets stored in a .sconsign file for each target file. + It contains a NodeInfo instance for this node (signature information + that's specific to the type of Node) and direct attributes for the + generic build stuff we have to track: sources, explicit dependencies, + implicit dependencies, and action information. + """ + def __init__(self, node): + self.ninfo = node.new_ninfo() + self.bsourcesigs = [] + self.bdependsigs = [] + self.bimplicitsigs = [] + self.bactsig = None + def __cmp__(self, other): + return cmp(self.ninfo, other.ninfo) + def merge(self, other): + for key, val in other.__dict__.items(): + try: + merge = self.__dict__[key].merge + except (AttributeError, KeyError): + self.__dict__[key] = val + else: + merge(val) class Node: """The base Node class, for entities that we know how to @@ -241,28 +287,27 @@ class Node: parent.del_binfo() try: - new_binfo = self.binfo + new = 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 + new = None # Reset this Node's cached state since it was just built and # various state has changed. self.clear() - - # 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 + if new: + # It had build info, so it should be stored in the signature + # cache. However, if the build info included a content + # signature then it must be recalculated before being stored. + if hasattr(new.ninfo, 'csig'): + self.get_csig() else: - self.binfo = new_binfo - self.store_info(new_binfo) + new.ninfo.update(self) + self.binfo = new + self.store_info(self.binfo) def add_to_waiting_parents(self, node): self.waiting_parents.append(node) @@ -285,7 +330,6 @@ class Node: """ self.executor_cleanup() self.del_binfo() - self.del_cinfo() try: delattr(self, '_calculated_sig') except AttributeError: @@ -504,6 +548,10 @@ class Node: return self.env = env + # + # SIGNATURE SUBSYSTEM + # + def calculator(self): import SCons.Defaults @@ -524,27 +572,30 @@ class Node: env = self.env or SCons.Defaults.DefaultEnvironment() if env.use_build_signature(): - return self.calc_bsig(calc) + return self.get_bsig(calc) elif not self.rexists(): return None - return self.calc_csig(calc) + return self.get_csig(calc) + + def new_ninfo(self): + return NodeInfo() def new_binfo(self): - return BuildInfo() + return BuildInfo(self) - def del_binfo(self): - """Delete the bsig from this node.""" + def get_binfo(self): try: - delattr(self, 'binfo') + return self.binfo except AttributeError: - pass + self.binfo = self.new_binfo() + return self.binfo - def calc_bsig(self, calc=None): + def del_binfo(self): + """Delete the build info from this node.""" try: - return self.binfo.bsig + delattr(self, 'binfo') except AttributeError: - self.binfo = self.gen_binfo(calc) - return self.binfo.bsig + pass def gen_binfo(self, calc=None, scan=1): """ @@ -565,7 +616,7 @@ class Node: if calc is None: calc = self.calculator() - binfo = self.new_binfo() + binfo = self.get_binfo() if scan: self.scan() @@ -602,29 +653,27 @@ class Node: binfo.bdependsigs = dependsigs binfo.bimplicitsigs = implicitsigs - binfo.bsig = calc.module.collect(filter(None, sigs)) + binfo.ninfo.bsig = calc.module.collect(filter(None, sigs)) return binfo - def del_cinfo(self): + def get_bsig(self, calc=None): + binfo = self.get_binfo() try: - del self.binfo.csig + return binfo.ninfo.bsig except AttributeError: - pass + self.binfo = self.gen_binfo(calc) + return self.binfo.ninfo.bsig - def calc_csig(self, calc=None): - try: - binfo = self.binfo - except AttributeError: - binfo = self.binfo = self.new_binfo() + def get_csig(self, calc=None): + binfo = self.get_binfo() try: - return binfo.csig + return binfo.ninfo.csig except AttributeError: if calc is None: calc = self.calculator() - binfo.csig = calc.module.signature(self) - self.store_info(binfo) - return binfo.csig + csig = binfo.ninfo.csig = calc.module.signature(self) + return csig def store_info(self, obj): """Make the build signature permanent (that is, store it in the @@ -638,6 +687,10 @@ class Node: """Fetch the stored implicit dependencies""" return None + # + # + # + def set_precious(self, precious = 1): """Set the Node's precious value.""" self.precious = precious @@ -915,7 +968,7 @@ class Node: except AttributeError: return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self - new = self.binfo + new = self.get_binfo() nsig = {} dictify(nsig, new.bsources, new.bsourcesigs) diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index 3d1a1452..168add96 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -163,7 +163,7 @@ class SConfBuildInfo(SCons.Node.FS.BuildInfo): SCons.Node.FS.BuildInfo.__init__(self, node) self.result = result self.string = string - self.bsig = sig + self.ninfo.bsig = sig class Streamer: @@ -253,9 +253,9 @@ class SConfBuildTask(SCons.Taskmaster.Task): else: new_bsig = t.calc_signature(sconf_global.calc) if t.env.use_build_signature(): - old_bsig = bi.bsig + old_bsig = bi.ninfo.bsig else: - old_bsig = bi.csig + old_bsig = bi.ninfo.csig is_up_to_date = (is_up_to_date and new_bsig == old_bsig) cached_error = cached_error or bi.result else: diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py index 073cfc45..10bb98dc 100644 --- a/src/engine/SCons/SConsign.py +++ b/src/engine/SCons/SConsign.py @@ -36,7 +36,6 @@ import string import time import SCons.dblite -import SCons.Node import SCons.Sig import SCons.Warnings diff --git a/src/engine/SCons/Sig/SigTests.py b/src/engine/SCons/Sig/SigTests.py index 8964c5c3..6b7c83fd 100644 --- a/src/engine/SCons/Sig/SigTests.py +++ b/src/engine/SCons/Sig/SigTests.py @@ -27,15 +27,6 @@ import unittest import SCons.Sig import sys -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 CalculatorTestCase(unittest.TestCase): def runTest(self): @@ -47,7 +38,6 @@ class CalculatorTestCase(unittest.TestCase): def suite(): suite = unittest.TestSuite() - suite.addTest(SConsignEntryTestCase()) suite.addTest(CalculatorTestCase()) return suite diff --git a/src/engine/SCons/Sig/__init__.py b/src/engine/SCons/Sig/__init__.py index d746dfa4..00c2b83a 100644 --- a/src/engine/SCons/Sig/__init__.py +++ b/src/engine/SCons/Sig/__init__.py @@ -40,18 +40,6 @@ except ImportError: # since it's really something about files. default_max_drift = 2*24*60*60 -class SConsignEntry: - """The old SConsignEntry format. - We keep this around to handle conversions from old .sconsign files.""" - timestamp = None - bsig = None - csig = None - implicit = None - def convert_to_sconsign(self): - pass - def convert_from_sconsign(self, dir, name): - pass - class Calculator: """ Encapsulates signature calculations and .sconsign file generating diff --git a/src/script/sconsign.py b/src/script/sconsign.py index 3303916c..6145ad66 100644 --- a/src/script/sconsign.py +++ b/src/script/sconsign.py @@ -241,23 +241,30 @@ map_name = { 'implicit' : 'bkids', } -def printfield(name, entry): - 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) +def field(name, entry, verbose=Verbose): + 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 + +def nodeinfo_string(name, entry, prefix=""): + fieldlist = ["bsig", "csig", "timestamp", "size"] + f = lambda x, e=entry, v=Verbose: field(x, e, v) + outlist = [name+":"] + filter(None, map(f, fieldlist)) + if Verbose: + sep = "\n " + prefix + else: + sep = " " + return string.join(outlist, sep) + +def printfield(name, entry, prefix=""): + print nodeinfo_string(name, entry.ninfo, prefix) + + outlist = field("implicit", entry, 0) if outlist: if Verbose: print " implicit:" @@ -273,8 +280,10 @@ def printentries(entries): else: printfield(name, entry) else: - for name, e in entries.items(): - printfield(name, e) + names = entries.keys() + names.sort() + for name in names: + printfield(name, entries[name]) class Do_SConsignDB: def __init__(self, dbm_name, dbm): @@ -362,14 +371,15 @@ Options: -h, --help Print this message and exit. -i, --implicit Print implicit dependency information. -r, --readable Print timestamps in human-readable form. + -s, --size Print file sizes human-readable form. -t, --timestamp Print timestamp information. -v, --verbose Verbose, describe each field. """ -opts, args = getopt.getopt(sys.argv[1:], "bcd:e:f:hirtv", +opts, args = getopt.getopt(sys.argv[1:], "bcd:e:f:hirstv", ['bsig', 'csig', 'dir=', 'entry=', 'format=', 'help', 'implicit', - 'readable', 'timestamp', 'verbose']) + 'readable', 'size', 'timestamp', 'verbose']) for o, a in opts: @@ -402,6 +412,8 @@ for o, a in opts: Print_Flags['implicit'] = 1 elif o in ('-r', '--readable'): Readable = 1 + elif o in ('-s', '--size'): + Print_Flags['size'] = 1 elif o in ('-t', '--timestamp'): Print_Flags['timestamp'] = 1 elif o in ('-v', '--verbose'): diff --git a/test/chained-build.py b/test/chained-build.py index d2772e27..9d873475 100644 --- a/test/chained-build.py +++ b/test/chained-build.py @@ -76,9 +76,11 @@ test.write(['w1', 'foo.in'], "foo.in 2") test.run(chdir='w1', arguments="--max-drift=0 -f SConstruct1 foo.mid", stdout = test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n')) -test.run(chdir='w1', - arguments="--max-drift=0 -f SConstruct2 foo.out", - stdout = test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n')) +# Because we're using --max-drift=0, we use the cached csig value +# and think that foo.mid hasn't changed even though it has on disk. +test.up_to_date(chdir='w1', + options="--max-drift=0 -f SConstruct2", + arguments="foo.out") test.up_to_date(chdir='w1', options="--max-drift=0 -f SConstruct1", @@ -87,4 +89,14 @@ test.up_to_date(chdir='w1', options="--max-drift=0 -f SConstruct2", arguments="foo.out") +# Now try with --max-drift disabled. The build of foo.mid should still +# be considered up-to-date, but the build of foo.out now detects the +# change and rebuilds, too. +test.up_to_date(chdir='w1', + options="--max-drift=-1 -f SConstruct1", + arguments="foo.mid") +test.run(chdir='w1', + arguments="--max-drift=-1 -f SConstruct2 foo.out", + stdout = test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n')) + test.pass_test() diff --git a/test/sconsign/old.py b/test/sconsign/old.py deleted file mode 100644 index 703a924c..00000000 --- a/test/sconsign/old.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/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 the transition from the old .sconsign format(s). -""" - -import os -import os.path -import string -import sys -import TestSCons - -python = TestSCons.python - -test = TestSCons.TestSCons() - -test.subdir('src1', ['src1', 'subdir'], - 'src2', ['src2', 'subdir']) - -convert = test.workpath('convert.py') -convert_dblite = test.workpath('convert_dblite.py') - -test.write(convert, """\ -import cPickle -import sys -import SCons.SConsign -import SCons.Sig - -try: - SConsignEntry = SCons.Sig.SConsignEntry -except AttributeError: - class SConsignEntry: - timestamp = None - bsig = None - csig = None - implicit = None - -filename = sys.argv[1] - -sconsign = SCons.SConsign.Dir(open(filename, 'rb')) - -old_entries = {} -for name, entry in sconsign.entries.items(): - oe = SConsignEntry() - for attr in ['timestamp', 'bsig', 'csig', 'implicit']: - try: setattr(oe, attr, getattr(entry, attr)) - except AttributeError: pass - old_entries[name] = oe - -cPickle.dump(old_entries, open(filename, 'wb'), 1) -""") - -test.write(convert_dblite, """\ -import cPickle -import SCons.dblite -import sys -import SCons.SConsign -import SCons.Sig - -try: - SConsignEntry = SCons.Sig.SConsignEntry -except AttributeError: - class SConsignEntry: - timestamp = None - bsig = None - csig = None - implicit = None - -filename = sys.argv[1] - -db = SCons.dblite.open(filename, "r") - -old_db = {} -for dir in db.keys(): - #self.printentries(dir, db[dir]) - - new_entries = cPickle.loads(db[dir]) - old_db[dir] = old_entries = {} - for name, entry in new_entries.items(): - oe = SConsignEntry() - for attr in ['timestamp', 'bsig', 'csig', 'implicit']: - try: setattr(oe, attr, getattr(entry, attr)) - except AttributeError: pass - old_entries[name] = oe - -db = SCons.dblite.open(filename, "c") -for key, val in old_db.items(): - db[key] = cPickle.dumps(val) -db.sync() -""") - - - -# Now generate a simple .sconsign file for a simple build. -test.write(['src1', 'SConstruct'], """\ -SConsignFile(None) -import os -def cat(env, source, target): - target = str(target[0]) - source = map(str, source) - f = open(target, "wb") - for src in source: - f.write(open(src, "rb").read()) - f.close() - -env = Environment() -env.Append(BUILDERS={'Cat':Builder(action=cat)}) - -Export("env") -SConscript('SConscript') -""") - -test.write(['src1', 'SConscript'], """\ -Import("env") -env.Cat('file1', 'file1.in') -env.Cat('subdir/file2', 'subdir/file2.in') -""") - -test.write(['src1', 'file1.in'], "file1.in 1\n") -test.write(['src1', 'subdir', 'file2.in'], "subdir/file2.in 1\n") - -test.run(chdir='src1', arguments='.') - -test.up_to_date(chdir='src1', arguments='.') - -sconsign_list = [ - test.workpath('src1', '.sconsign'), - test.workpath('src1', 'subdir', '.sconsign'), -] - -for sconsign in sconsign_list: - test.run(interpreter=python, program=convert, arguments=sconsign) - -test.up_to_date(chdir='src1', arguments='.') - - - -# Now do the same with SConsignFile(). -test.write(['src2', 'SConstruct'], """\ -import os -def cat(env, source, target): - target = str(target[0]) - source = map(str, source) - f = open(target, "wb") - for src in source: - f.write(open(src, "rb").read()) - f.close() - -env = Environment() -env.Append(BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('file1', 'file1.in') -env.Cat('subdir/file2', 'subdir/file2.in') -""") - -test.write(['src2', 'file1.in'], "file1.in 1\n") -test.write(['src2', 'subdir', 'file2.in'], "subdir/file2.in 1\n") - -test.run(chdir='src2', arguments='.') - -test.up_to_date(chdir='src2', arguments='.') - -sconsign_list = [ - test.workpath('src2', '.sconsign'), -] - -for sconsign in sconsign_list: - test.run(interpreter=python, program=convert_dblite, arguments=sconsign) - -test.up_to_date(chdir='src2', arguments='.') - - - -test.pass_test() diff --git a/test/sconsign/script.py b/test/sconsign/script.py index 9758d5a3..2c54e401 100644 --- a/test/sconsign/script.py +++ b/test/sconsign/script.py @@ -115,24 +115,26 @@ test.run(chdir = 'work1', arguments = '--debug=stacktrace --implicit-cache .') test.run_sconsign(arguments = "work1/sub1/.sconsign", stdout = """\ -hello.exe: None \S+ None +hello.exe: \S+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ """) test.run_sconsign(arguments = "-v work1/sub1/.sconsign", stdout = """\ hello.exe: - timestamp: None bsig: \S+ csig: None + timestamp: \d+ + size: \d+ implicit: hello.obj: \S+ hello.obj: - timestamp: None bsig: \S+ csig: None + timestamp: \d+ + size: \d+ implicit: hello.c: \S+ """) @@ -153,19 +155,35 @@ hello.obj: csig: None """) +test.run_sconsign(arguments = "-s -v work1/sub1/.sconsign", + stdout = """\ +hello.exe: + size: \d+ +hello.obj: + size: \d+ +""") + +test.run_sconsign(arguments = "-t -v work1/sub1/.sconsign", + stdout = """\ +hello.exe: + timestamp: \d+ +hello.obj: + timestamp: \d+ +""") + test.run_sconsign(arguments = "-e hello.obj work1/sub1/.sconsign", stdout = """\ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ """) test.run_sconsign(arguments = "-e hello.obj -e hello.exe -e hello.obj work1/sub1/.sconsign", stdout = """\ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ -hello.exe: None \S+ None +hello.exe: \S+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ """) @@ -175,33 +193,33 @@ sub2_inc2_h = re_sep('sub2', 'inc2.h') test.run_sconsign(arguments = "work1/sub2/.sconsign", stdout = """\ -hello.exe: None \S+ None +hello.exe: \S+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ inc1.h: \S+ inc2.h: \S+ """) -test.run_sconsign(arguments = "-i -v work1/sub2/.sconsign", - stdout = """\ -hello.exe: - implicit: - hello.obj: \S+ -hello.obj: - implicit: - hello.c: \S+ - inc1.h: \S+ - inc2.h: \S+ -""") +#test.run_sconsign(arguments = "-i -v work1/sub2/.sconsign", +# stdout = """\ +#hello.exe: +# implicit: +# hello.obj: \S+ None \d+ \d+ +#hello.obj: +# implicit: +# hello.c: None \S+ \d+ \d+ +# inc1.h: None \S+ \d+ \d+ +# inc2.h: None \S+ \d+ \d+ +#""") test.run_sconsign(arguments = "-e hello.obj work1/sub2/.sconsign work1/sub1/.sconsign", stdout = """\ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ inc1.h: \S+ inc2.h: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ """) @@ -222,17 +240,17 @@ test.run(chdir = 'work1', arguments = '. --max-drift=1 --debug=stacktrace') test.run_sconsign(arguments = "-e hello.exe -e hello.obj work1/sub1/.sconsign", stdout = """\ -hello.exe: None \S+ None +hello.exe: \S+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ """) test.run_sconsign(arguments = "-e hello.exe -e hello.obj -r work1/sub1/.sconsign", stdout = """\ -hello.exe: None \S+ None +hello.exe: \S+ None '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' \d+ hello.c: \S+ """) @@ -284,14 +302,14 @@ test.run_sconsign(arguments = "work2/.sconsign") test.run_sconsign(arguments = "work2/.sconsign", stdout = """\ === sub1: -hello.exe: None \S+ None +hello.exe: \S+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ === sub2: -hello.exe: None \S+ None +hello.exe: \S+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ inc1.h: \S+ inc2.h: \S+ @@ -301,28 +319,32 @@ test.run_sconsign(arguments = "-v work2/.sconsign", stdout = """\ === sub1: hello.exe: - timestamp: None bsig: \S+ csig: None + timestamp: \d+ + size: \d+ implicit: hello.obj: \S+ hello.obj: - timestamp: None bsig: \S+ csig: None + timestamp: \d+ + size: \d+ implicit: hello.c: \S+ === sub2: hello.exe: - timestamp: None bsig: \S+ csig: None + timestamp: \d+ + size: \d+ implicit: hello.obj: \S+ hello.obj: - timestamp: None bsig: \S+ csig: None + timestamp: \d+ + size: \d+ implicit: hello.c: \S+ inc1.h: \S+ @@ -357,13 +379,41 @@ hello.obj: csig: None """) +test.run_sconsign(arguments = "-s -v work2/.sconsign", + stdout = """\ +=== sub1: +hello.exe: + size: \d+ +hello.obj: + size: \d+ +=== sub2: +hello.exe: + size: \d+ +hello.obj: + size: \d+ +""") + +test.run_sconsign(arguments = "-t -v work2/.sconsign", + stdout = """\ +=== sub1: +hello.exe: + timestamp: \d+ +hello.obj: + timestamp: \d+ +=== sub2: +hello.exe: + timestamp: \d+ +hello.obj: + timestamp: \d+ +""") + test.run_sconsign(arguments = "-e hello.obj work2/.sconsign", stdout = """\ === sub1: -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ === sub2: -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ inc1.h: \S+ inc2.h: \S+ @@ -372,44 +422,44 @@ hello.obj: None \S+ None test.run_sconsign(arguments = "-e hello.obj -e hello.exe -e hello.obj work2/.sconsign", stdout = """\ === sub1: -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ -hello.exe: None \S+ None +hello.exe: \S+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ === sub2: -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ inc1.h: \S+ inc2.h: \S+ -hello.exe: None \S+ None +hello.exe: \S+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \S+ None \d+ \d+ hello.c: \S+ inc1.h: \S+ inc2.h: \S+ """) -test.run_sconsign(arguments = "-i -v work2/.sconsign", - stdout = """\ -=== sub1: -hello.exe: - implicit: - hello.obj: \S+ -hello.obj: - implicit: - hello.c: \S+ -=== sub2: -hello.exe: - implicit: - hello.obj: \S+ -hello.obj: - implicit: - hello.c: \S+ - inc1.h: \S+ - inc2.h: \S+ -""") +#test.run_sconsign(arguments = "-i -v work2/.sconsign", +# stdout = """\ +#=== sub1: +#hello.exe: +# implicit: +# hello.obj: \S+ +#hello.obj: +# implicit: +# hello.c: \S+ +#=== sub2: +#hello.exe: +# implicit: +# hello.obj: \S+ +#hello.obj: +# implicit: +# hello.c: \S+ +# inc1.h: \S+ +# inc2.h: \S+ +#""") test.run(chdir = 'work2', arguments = '--clean .') @@ -429,65 +479,65 @@ test.run(chdir = 'work2', arguments = '. --max-drift=1') expect = """\ === sub1: -hello.c: \d+ None \d+ +hello.c: None \S+ \d+ \d+ """ test.run_sconsign(arguments = "-e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign", stdout = """\ === sub1: -hello.exe: None \S+ None - hello.obj: \S+ -hello.obj: None \S+ None - hello.c: \S+ +hello.exe: \d+ None \d+ \d+ + hello.obj: \d+ +hello.obj: \d+ None \d+ \d+ + hello.c: \d+ """) test.run_sconsign(arguments = "-e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign.dblite", stdout = """\ === sub1: -hello.exe: None \S+ None +hello.exe: \d+ None \d+ \d+ hello.obj: \S+ -hello.obj: None \S+ None +hello.obj: \d+ None \d+ \d+ hello.c: \S+ """) test.run_sconsign(arguments = "-e hello.c -e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign", stdout = """\ === sub1: -hello.c: \d+ None \d+ -hello.exe: None \S+ None - hello.obj: \S+ -hello.obj: None \S+ None - hello.c: \S+ +hello.c: None \d+ \d+ \d+ +hello.exe: \d+ None \d+ \d+ + hello.obj: \d+ +hello.obj: \d+ None \d+ \d+ + hello.c: \d+ """) test.run_sconsign(arguments = "-e hello.c -e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign.dblite", stdout = """\ === sub1: -hello.c: \d+ None \d+ -hello.exe: None \S+ None - hello.obj: \S+ -hello.obj: None \S+ None - hello.c: \S+ +hello.c: None \d+ \d+ \d+ +hello.exe: \S+ None \d+ \d+ + hello.obj: \d+ +hello.obj: \S+ None \d+ \d+ + hello.c: \d+ """) test.run_sconsign(arguments = "-e hello.c -e hello.exe -e hello.obj -r -d sub1 -f dblite work2/my_sconsign", stdout = """\ === sub1: -hello.c: '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' None \d+ -hello.exe: None \S+ None - hello.obj: \S+ -hello.obj: None \S+ None - hello.c: \S+ +hello.c: None \d+ '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' \d+ +hello.exe: \d+ None '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' \d+ + hello.obj: \d+ +hello.obj: \d+ None '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' \d+ + hello.c: \d+ """) test.run_sconsign(arguments = "-e hello.c -e hello.exe -e hello.obj -r -d sub1 -f dblite work2/my_sconsign.dblite", stdout = """\ === sub1: -hello.c: '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' None \d+ -hello.exe: None \S+ None - hello.obj: \S+ -hello.obj: None \S+ None - hello.c: \S+ +hello.c: None \d+ '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' \d+ +hello.exe: \d+ None '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' \d+ + hello.obj: \d+ +hello.obj: \d+ None '\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d' \d+ + hello.c: \d+ """) ##############################################################################