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
- 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
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
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()
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):
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
# 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):
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()
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.
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()
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__"
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)
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
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
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(''))
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:
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')
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:
suite.addTest(SaveStringsTestCase())
tclasses = [
BaseTestCase,
+ BuildInfoTestCase,
+ NodeInfoTestCase,
FSTestCase,
DirTestCase,
RepositoryTestCase,
+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):
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
"""
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')
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
if __name__ == "__main__":
suite = unittest.TestSuite()
- tclasses = [ NodeTestCase,
+ tclasses = [ BuildInfoTestCase,
+ NodeInfoTestCase,
+ NodeTestCase,
NodeListTestCase ]
for tclass in tclasses:
names = unittest.getTestCaseNames(tclass, 'test_')
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."""
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
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
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
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)
"""
self.executor_cleanup()
self.del_binfo()
- self.del_cinfo()
try:
delattr(self, '_calculated_sig')
except AttributeError:
return
self.env = env
+ #
+ # SIGNATURE SUBSYSTEM
+ #
+
def calculator(self):
import SCons.Defaults
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):
"""
if calc is None:
calc = self.calculator()
- binfo = self.new_binfo()
+ binfo = self.get_binfo()
if scan:
self.scan()
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
"""Fetch the stored implicit dependencies"""
return None
+ #
+ #
+ #
+
def set_precious(self, precious = 1):
"""Set the Node's precious value."""
self.precious = precious
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)
SCons.Node.FS.BuildInfo.__init__(self, node)
self.result = result
self.string = string
- self.bsig = sig
+ self.ninfo.bsig = sig
class Streamer:
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:
import time
import SCons.dblite
-import SCons.Node
import SCons.Sig
import SCons.Warnings
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):
def suite():
suite = unittest.TestSuite()
- suite.addTest(SConsignEntryTestCase())
suite.addTest(CalculatorTestCase())
return suite
# 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
'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:"
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):
-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:
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'):
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",
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()
+++ /dev/null
-#!/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()
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+
""")
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+
""")
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+
""")
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+
""")
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+
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+
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+
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 .')
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+
""")
##############################################################################