summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-05-14 03:08:46 (GMT)
committerSteven Knight <knight@baldmt.com>2004-05-14 03:08:46 (GMT)
commit86c11822b8f41dff8ec28e4ee8a8afeec9bfaa5f (patch)
treef4e261a94be38bdc1beff13b857c54b169113baf /src
parente2ed7aef1547812fa9ce49ae726ae1815158936d (diff)
downloadSCons-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.txt3
-rw-r--r--src/RELEASE.txt5
-rw-r--r--src/engine/SCons/Node/Alias.py15
-rw-r--r--src/engine/SCons/Node/FS.py163
-rw-r--r--src/engine/SCons/Node/FSTests.py83
-rw-r--r--src/engine/SCons/Node/NodeTests.py144
-rw-r--r--src/engine/SCons/Node/Python.py33
-rw-r--r--src/engine/SCons/Node/PythonTests.py15
-rw-r--r--src/engine/SCons/Node/__init__.py298
-rw-r--r--src/engine/SCons/SConsign.py113
-rw-r--r--src/engine/SCons/SConsignTests.py135
-rw-r--r--src/engine/SCons/Script/__init__.py50
-rw-r--r--src/script/sconsign.py98
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)