summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bin/restore.sh29
-rw-r--r--src/CHANGES.txt6
-rw-r--r--src/RELEASE.txt17
-rw-r--r--src/engine/SCons/Executor.py23
-rw-r--r--src/engine/SCons/ExecutorTests.py51
-rw-r--r--src/engine/SCons/Node/FS.py59
-rw-r--r--src/engine/SCons/Node/FSTests.py79
-rw-r--r--src/engine/SCons/Node/NodeTests.py16
-rw-r--r--src/engine/SCons/Node/__init__.py90
-rw-r--r--src/engine/SCons/SConfTests.py9
-rw-r--r--test/sconsign-script.py390
-rw-r--r--test/subdivide.py97
12 files changed, 524 insertions, 342 deletions
diff --git a/bin/restore.sh b/bin/restore.sh
index 2b7a726..d5dc6c1 100644
--- a/bin/restore.sh
+++ b/bin/restore.sh
@@ -5,10 +5,33 @@
# send in diffs based on the released source.
#
-for i in `find src test -name '*.py'`; do
+if test "X$*" = "X"; then
+ DIRS="src test"
+else
+ DIRS="$*"
+fi
+
+for i in `find $DIRS -name '*.py'`; do
+ed $i <<EOF
+/^__revision__ = /s/= .*/= "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"/p
+/Copyright (c) 2001.*SCons Foundation/s/.*/__COPYRIGHT__/p
+w
+q
+EOF
+done
+
+for i in `find $DIRS -name '*.txt'`; do
+ed $i <<EOF
+/# [^ ]* 0.96.[CD][0-9]* [0-9\/]* [0-9:]* knight$/s/.*/# __FILE__ __REVISION__ __DATE__ __DEVELOPER__/p
+/Copyright (c) 2001.*SCons Foundation/s/.*/__COPYRIGHT__/p
+w
+q
+EOF
+done
+
+for i in `find $DIRS -name '*.xml'`; do
ed $i <<EOF
-/__revision__ = /s/= .*/= "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"/p
-/# Copyright (c) 2001/s/.*/# __COPYRIGHT__/p
+/^<!-- Copyright (c) 2001.*SCons Foundation -->$/s/.*/<!-- __COPYRIGHT__ -->/p
w
q
EOF
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 555139b..779a5d6 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -256,6 +256,12 @@ RELEASE 0.97 - XXX
to all of the targets in a multiple-target builder call, which
could cause out-of-order builds when the -j option is used.
+ - Store the paths of source files and dependencies in the .sconsign*
+ file(s) relative to the target's directory, not relative to the
+ top-level SConstruct directory. This starts to make it possible to
+ subdivide the dependency tree arbitrarily by putting an SConstruct
+ file in every directory and using content signatures.
+
From Wayne Lee:
- Avoid "maximum recursion limit" errors when removing $(-$) pairs
diff --git a/src/RELEASE.txt b/src/RELEASE.txt
index 095671d..9c09e60 100644
--- a/src/RELEASE.txt
+++ b/src/RELEASE.txt
@@ -73,6 +73,23 @@ RELEASE 0.97 - XXX
the elements. This may cause rebuilds on Windows systems
with hierarchical configurations.
+ -- STORED DEPENDENCY PATHS ARE NOW RELATIVE TO THE TARGET
+
+ SCons used to store the paths of all source files and
+ dependencies relative to the top-level SConstruct directory.
+ It now stores them relative to the directory of the
+ associated target file. This makes it possible to use
+ content signatures to subdivide a dependency tree without
+ causing unnecessary rebuilds due to an intermediate file in
+ one build being treated as a source file in a nother build.
+
+ This a step towards making it possible to write a hierarchy
+ of SConstruct files that allow developers to build just
+ one portion of a tree wherever there's an SConstruct file.
+ (Note that this would still require some specific code at
+ the top of each SConstruct file, but we hope to make this
+ an easier/more naturally supported thing in the future.)
+
-- 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/Executor.py b/src/engine/SCons/Executor.py
index ef460ef..afe817a 100644
--- a/src/engine/SCons/Executor.py
+++ b/src/engine/SCons/Executor.py
@@ -196,19 +196,16 @@ class Executor:
"""
return filter(lambda s: s.missing(), self.sources)
- def get_source_binfo(self, calc, ignore=[]):
- """
- Return three lists, one of the source files, one of their
- calculated signatures, and one of their strings (path names).
- __cacheable__
- """
+ def get_unignored_sources(self, ignore):
+ """__cacheable__"""
sourcelist = self.sources
if ignore:
sourcelist = filter(lambda s, i=ignore: not s in i, sourcelist)
- calc_signature = lambda node, calc=calc: node.calc_signature(calc)
- return (sourcelist,
- map(calc_signature, sourcelist),
- map(str, sourcelist))
+ return sourcelist
+
+ def process_sources(self, func, ignore=[]):
+ """__cacheable__"""
+ return map(func, self.get_unignored_sources(ignore))
@@ -235,8 +232,10 @@ class Null:
pass
def get_missing_sources(self):
return []
- def get_source_binfo(self, calc, ignore=[]):
- return ([], [], [])
+ def get_unignored_sources(self, ignore=[]):
+ return []
+ def process_sources(self, func, ignore=[]):
+ return []
diff --git a/src/engine/SCons/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py
index 62c1eab..9ff30e5 100644
--- a/src/engine/SCons/ExecutorTests.py
+++ b/src/engine/SCons/ExecutorTests.py
@@ -358,28 +358,41 @@ class ExecutorTestCase(unittest.TestCase):
missing = x.get_missing_sources()
assert missing == [sources[0]], missing
- def test_get_source_binfo(self):
- """Test fetching the build signature info for the sources"""
+ def test_get_unignored_sources(self):
+ """Test fetching the unignored source list"""
env = MyEnvironment()
- t1 = MyNode('t')
s1 = MyNode('s1')
s2 = MyNode('s2')
- x = SCons.Executor.Executor('b', env, [{}], [t1], [s1, s2])
-
- b = x.get_source_binfo('C')
- assert b == ([s1, s2],
- ['cs-C-s1', 'cs-C-s2'],
- ['s1', 's2']), b
-
- b = x.get_source_binfo('C', [s1])
- assert b == ([s2],
- ['cs-C-s2'],
- ['s2']), b
-
- b = x.get_source_binfo('C', [s2])
- assert b == ([s1],
- ['cs-C-s1'],
- ['s1']), b
+ s3 = MyNode('s3')
+ x = SCons.Executor.Executor('b', env, [{}], [], [s1, s2, s3])
+
+ r = x.get_unignored_sources([])
+ assert r == [s1, s2, s3], map(str, r)
+
+ r = x.get_unignored_sources([s2])
+ assert r == [s1, s3], map(str, r)
+
+ r = x.get_unignored_sources([s1, s3])
+ assert r == [s2], map(str, r)
+
+ def test_process_sources(self):
+ """Test processing the source list through a function"""
+ env = MyEnvironment()
+ s1 = MyNode('s1')
+ s2 = MyNode('s2')
+ s3 = MyNode('s3')
+ x = SCons.Executor.Executor('b', env, [{}], [], [s1, s2, s3])
+
+ r = x.process_sources(str)
+ assert r == ['s1', 's2', 's3'], r
+
+ r = x.process_sources(str, [s2])
+ assert r == ['s1', 's3'], r
+
+ def xxx(x):
+ return 'xxx-' + str(x)
+ r = x.process_sources(xxx, [s1, s3])
+ assert r == ['xxx-s2'], r
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index d2bce26..fe014f8 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -297,6 +297,7 @@ class ParentOfRoot:
self.duplicate=0
self.srcdir=None
self.build_dirs=[]
+ self.path_elements=[]
def is_under(self, dir):
return 0
@@ -461,6 +462,7 @@ class Base(SCons.Node.Node):
self.path = name
else:
self.path = directory.entry_path(name)
+ self.path_elements = directory.path_elements + [self]
self.dir = directory
self.cwd = None # will hold the SConscript directory for target nodes
@@ -541,18 +543,15 @@ class Base(SCons.Node.Node):
Node.FS.Base object that owns us."""
if not dir:
dir = self.fs.getcwd()
- path_elems = []
- d = self
- if d == dir:
- path_elems.append('.')
- else:
- while d != dir and not isinstance(d, ParentOfRoot):
- path_elems.append(d.name)
- d = d.dir
- path_elems.reverse()
- ret = string.join(path_elems, os.sep)
- return ret
-
+ if self == dir:
+ return '.'
+ path_elems = self.path_elements
+ try: i = path_elems.index(dir)
+ except ValueError: pass
+ else: path_elems = path_elems[i+1:]
+ path_elems = map(lambda n: n.name, path_elems)
+ return string.join(path_elems, os.sep)
+
def set_src_builder(self, builder):
"""Set the source code builder for this node."""
self.sbuilder = builder
@@ -643,6 +642,9 @@ class Entry(Base):
return '' # avoid errors for dangling symlinks
raise AttributeError
+ def rel_path(self, other):
+ return self.disambiguate().rel_path(other)
+
def exists(self):
"""Return if the Entry exists. Check the file system to see
what we should turn into first. Assume a file if there's no
@@ -1252,6 +1254,29 @@ class Dir(Base):
else:
return self.entries['..'].root()
+ def rel_path(self, other):
+ """Return a path to "other" relative to this directory."""
+ if isinstance(other, Dir):
+ name = []
+ else:
+ try:
+ name = [other.name]
+ other = other.dir
+ except AttributeError:
+ return str(other)
+ if self is other:
+ return name and name[0] or '.'
+ i = 0
+ for x, y in map(None, self.path_elements, other.path_elements):
+ if not x is y:
+ break
+ i = i + 1
+ path_elems = ['..']*(len(self.path_elements)-i) \
+ + map(lambda n: n.name, other.path_elements[i:]) \
+ + name
+
+ return string.join(path_elems, os.sep)
+
def scan(self):
if not self.implicit is None:
return
@@ -1596,10 +1621,12 @@ class File(Base):
def get_stored_implicit(self):
binfo = self.get_stored_info()
- try:
- return binfo.bimplicit
- except AttributeError:
- return None
+ try: implicit = binfo.bimplicit
+ except AttributeError: return None
+ else: return map(self.dir.Entry, implicit)
+
+ def rel_path(self, other):
+ return self.dir.rel_path(other)
def get_found_includes(self, env, scanner, path):
"""Return the included implicit dependencies in this file.
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index adef880..94e9790 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -605,15 +605,16 @@ class BuildDirTestCase(unittest.TestCase):
self.failIf(errors)
-class FSTestCase(unittest.TestCase):
- def runTest(self):
+class FSTestCase(_tempdirTestCase):
+ def test_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.
"""
- test = TestCmd(workdir = '')
+ test = self.test
+
test.subdir('sub', ['sub', 'dir'])
wp = test.workpath('')
@@ -1184,6 +1185,74 @@ class FSTestCase(unittest.TestCase):
t = z.target_from_source('pre-', '-suf', lambda x: x[:-1])
assert str(t) == 'pre-z-suf', str(t)
+ def test_rel_path(self):
+ """Test the rel_path() method"""
+ test = self.test
+ fs = self.fs
+
+ d1 = fs.Dir('d1')
+ d1_f = d1.File('f')
+ d1_d2 = d1.Dir('d2')
+ d1_d2_f = d1_d2.File('f')
+
+ d3 = fs.Dir('d3')
+ d3_f = d3.File('f')
+ d3_d4 = d3.Dir('d4')
+ d3_d4_f = d3_d4.File('f')
+
+ cases = [
+ d1, d1, '.',
+ d1, d1_f, 'f',
+ d1, d1_d2, 'd2',
+ d1, d1_d2_f, 'd2/f',
+ d1, d3, '../d3',
+ d1, d3_f, '../d3/f',
+ d1, d3_d4, '../d3/d4',
+ d1, d3_d4_f, '../d3/d4/f',
+
+ d1_f, d1, '.',
+ d1_f, d1_f, 'f',
+ d1_f, d1_d2, 'd2',
+ d1_f, d1_d2_f, 'd2/f',
+ d1_f, d3, '../d3',
+ d1_f, d3_f, '../d3/f',
+ d1_f, d3_d4, '../d3/d4',
+ d1_f, d3_d4_f, '../d3/d4/f',
+
+ d1_d2, d1, '..',
+ d1_d2, d1_f, '../f',
+ d1_d2, d1_d2, '.',
+ d1_d2, d1_d2_f, 'f',
+ d1_d2, d3, '../../d3',
+ d1_d2, d3_f, '../../d3/f',
+ d1_d2, d3_d4, '../../d3/d4',
+ d1_d2, d3_d4_f, '../../d3/d4/f',
+
+ d1_d2_f, d1, '..',
+ d1_d2_f, d1_f, '../f',
+ d1_d2_f, d1_d2, '.',
+ d1_d2_f, d1_d2_f, 'f',
+ d1_d2_f, d3, '../../d3',
+ d1_d2_f, d3_f, '../../d3/f',
+ d1_d2_f, d3_d4, '../../d3/d4',
+ d1_d2_f, d3_d4_f, '../../d3/d4/f',
+ ]
+
+ d1.rel_path(d3)
+
+ failed = 0
+ while cases:
+ dir, other, expect = cases[:3]
+ expect = os.path.normpath(expect)
+ del cases[:3]
+ result = dir.rel_path(other)
+ if result != expect:
+ if failed == 0: print
+ fmt = " dir_path(%(dir)s, %(other)s) => '%(result)s' did not match '%(expect)s'"
+ print fmt % locals()
+ failed = failed + 1
+ assert failed == 0, "%d rel_path() cases failed" % failed
+
class DirTestCase(_tempdirTestCase):
def test_entry_exists_on_disk(self):
@@ -1321,7 +1390,7 @@ class DirTestCase(_tempdirTestCase):
exists_e.exists = return_true
def check(result, expect):
- result = map(str, result)
+ result = map(str, result)
expect = map(os.path.normpath, expect)
assert result == expect, result
@@ -2531,7 +2600,6 @@ class SaveStringsTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.TestSuite()
- suite.addTest(FSTestCase())
suite.addTest(BuildDirTestCase())
suite.addTest(EntryTestCase())
suite.addTest(find_fileTestCase())
@@ -2547,6 +2615,7 @@ if __name__ == "__main__":
suite.addTest(SpecialAttrTestCase())
suite.addTest(SaveStringsTestCase())
tclasses = [
+ FSTestCase,
DirTestCase,
RepositoryTestCase,
]
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index a8bb2fa..5f03377 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -560,6 +560,15 @@ class NodeTestCase(unittest.TestCase):
assert hasattr(binfo, 'bimplicitsigs')
assert binfo.bsig == 666, binfo.bsig
+ def test_rel_path(self):
+ """Test the rel_path() method
+ """
+ node = SCons.Node.Node()
+ other = SCons.Node.Node()
+ other.__str__ = lambda: "xyzzy"
+ r = node.rel_path(other)
+ assert r == "xyzzy", r
+
def test_explain(self):
"""Test explaining why a Node must be rebuilt
"""
@@ -578,11 +587,12 @@ class NodeTestCase(unittest.TestCase):
result = node.explain()
assert result == None, result
- class Null_BInfo:
- def __init__(self):
+ def get_null_info():
+ class Null_BInfo:
pass
+ return Null_BInfo()
- node.get_stored_info = Null_BInfo
+ node.get_stored_info = get_null_info
#see above: node.__str__ = lambda: 'null_binfo'
result = node.explain()
assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 8cac2c8..db80985 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -480,20 +480,19 @@ class Node:
# Here's where we implement --implicit-cache.
if implicit_cache and not implicit_deps_changed:
implicit = self.get_stored_implicit()
- if implicit is not None:
+ if implicit:
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 self.current(calc):
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()
+ # 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()
executor = self.get_executor()
@@ -581,8 +580,12 @@ class Node:
self.scan()
executor = self.get_executor()
+ def calc_signature(node, calc=calc):
+ return node.calc_signature(calc)
+
+ bsources = executor.process_sources(self.rel_path, self.ignore)
+ sourcesigs = executor.process_sources(calc_signature, self.ignore)
- sourcelist, sourcesigs, bsources = executor.get_source_binfo(calc, self.ignore)
depends = self.depends
implicit = self.implicit or []
@@ -590,8 +593,6 @@ class Node:
depends = filter(self.do_not_ignore, depends)
implicit = filter(self.do_not_ignore, implicit)
- def calc_signature(node, calc=calc):
- return node.calc_signature(calc)
dependsigs = map(calc_signature, depends)
implicitsigs = map(calc_signature, implicit)
@@ -603,8 +604,8 @@ class Node:
sigs.append(binfo.bactsig)
binfo.bsources = bsources
- binfo.bdepends = map(str, depends)
- binfo.bimplicit = map(str, implicit)
+ binfo.bdepends = map(self.rel_path, depends)
+ binfo.bimplicit = map(self.rel_path, implicit)
binfo.bsourcesigs = sourcesigs
binfo.bdependsigs = dependsigs
@@ -614,6 +615,9 @@ class Node:
return binfo
+ def rel_path(self, other):
+ return str(other)
+
def del_cinfo(self):
try:
del self.binfo.csig
@@ -930,53 +934,63 @@ class Node:
result[k] = s
try:
- old_bkids = old.bsources + old.bdepends + old.bimplicit
+ osig = {}
+ dictify(osig, old.bsources, old.bsourcesigs)
+ dictify(osig, old.bdepends, old.bdependsigs)
+ dictify(osig, old.bimplicit, old.bimplicitsigs)
except AttributeError:
return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self
- osig = {}
- dictify(osig, old.bsources, old.bsourcesigs)
- dictify(osig, old.bdepends, old.bdependsigs)
- dictify(osig, old.bimplicit, old.bimplicitsigs)
-
- new_bsources = map(str, self.binfo.bsources)
- new_bdepends = map(str, self.binfo.bdepends)
- new_bimplicit = map(str, self.binfo.bimplicit)
+ new = self.binfo
nsig = {}
- dictify(nsig, new_bsources, self.binfo.bsourcesigs)
- dictify(nsig, new_bdepends, self.binfo.bdependsigs)
- dictify(nsig, new_bimplicit, self.binfo.bimplicitsigs)
+ dictify(nsig, new.bsources, new.bsourcesigs)
+ dictify(nsig, new.bdepends, new.bdependsigs)
+ dictify(nsig, new.bimplicit, new.bimplicitsigs)
+
+ old_bkids = old.bsources + old.bdepends + old.bimplicit
+ new_bkids = new.bsources + new.bdepends + new.bimplicit
+
+ # The sources and dependencies we'll want to report are all stored
+ # as relative paths to this target's directory, but we want to
+ # report them relative to the top-level SConstruct directory,
+ # so we only print them after running them through this lambda
+ # to turn them into the right relative Node and then return
+ # its string.
+ stringify = lambda s, E=self.dir.Entry: str(E(s))
+
+ lines = []
- new_bkids = new_bsources + new_bdepends + new_bimplicit
- lines = map(lambda x: "`%s' is no longer a dependency\n" % x,
- filter(lambda x, nk=new_bkids: not x in nk, old_bkids))
+ removed = filter(lambda x, nk=new_bkids: not x in nk, old_bkids)
+ if removed:
+ removed = map(stringify, removed)
+ fmt = "`%s' is no longer a dependency\n"
+ lines.extend(map(lambda s, fmt=fmt: fmt % s, removed))
for k in new_bkids:
if not k in old_bkids:
- lines.append("`%s' is a new dependency\n" % k)
+ lines.append("`%s' is a new dependency\n" % stringify(k))
elif osig[k] != nsig[k]:
- lines.append("`%s' changed\n" % k)
+ lines.append("`%s' changed\n" % stringify(k))
if len(lines) == 0 and old_bkids != new_bkids:
lines.append("the dependency order changed:\n" +
- "%sold: %s\n" % (' '*15, old_bkids) +
- "%snew: %s\n" % (' '*15, new_bkids))
+ "%sold: %s\n" % (' '*15, map(stringify, old_bkids)) +
+ "%snew: %s\n" % (' '*15, map(stringify, new_bkids)))
if len(lines) == 0:
- newact, newactsig = self.binfo.bact, self.binfo.bactsig
def fmt_with_title(title, strlines):
lines = string.split(strlines, '\n')
sep = '\n' + ' '*(15 + len(title))
return ' '*15 + title + string.join(lines, sep) + '\n'
- if old.bactsig != newactsig:
- if old.bact == newact:
+ if old.bactsig != new.bactsig:
+ if old.bact == new.bact:
lines.append("the contents of the build action changed\n" +
- fmt_with_title('action: ', newact))
+ fmt_with_title('action: ', new.bact))
else:
lines.append("the build action changed:\n" +
fmt_with_title('old: ', old.bact) +
- fmt_with_title('new: ', newact))
+ fmt_with_title('new: ', new.bact))
if len(lines) == 0:
return "rebuilding `%s' for unknown reasons\n" % self
diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py
index 3cde7d0..a5e0700 100644
--- a/src/engine/SCons/SConfTests.py
+++ b/src/engine/SCons/SConfTests.py
@@ -122,9 +122,9 @@ class SConfTestCase(unittest.TestCase):
log = self.test.read( self.test.workpath('config.log') )
expr = re.compile( ".*failed in a previous run and all", re.DOTALL )
firstOcc = expr.match( log )
- assert firstOcc != None
+ assert firstOcc != None, log
secondOcc = expr.match( log, firstOcc.end(0) )
- assert secondOcc == None
+ assert secondOcc == None, log
# 2.2 test the error caching mechanism (dependencies have changed)
self._resetSConfState()
@@ -235,6 +235,7 @@ int main() {
assert not res[1][0] and res[1][1] == ""
finally:
sconf.Finish()
+ log = self.test.read( self.test.workpath('config.log') )
# test the caching mechanism
self._resetSConfState()
@@ -251,9 +252,9 @@ int main() {
log = self.test.read( self.test.workpath('config.log') )
expr = re.compile( ".*failed in a previous run and all", re.DOTALL )
firstOcc = expr.match( log )
- assert firstOcc != None
+ assert firstOcc != None, log
secondOcc = expr.match( log, firstOcc.end(0) )
- assert secondOcc == None
+ assert secondOcc == None, log
def test_TryAction(self):
diff --git a/test/sconsign-script.py b/test/sconsign-script.py
index 1c40702..7a24aef 100644
--- a/test/sconsign-script.py
+++ b/test/sconsign-script.py
@@ -51,7 +51,21 @@ def sort_match(test, lines, expect):
def re_sep(*args):
return string.replace(apply(os.path.join, args), '\\', '\\\\')
-test = TestSCons.TestSCons(match = TestCmd.match_re)
+
+
+class MyTestSCons(TestSCons.TestSCons):
+ # subclass with a method for running the sconsign script
+ def __init__(self, *args, **kw):
+ apply(TestSCons.TestSCons.__init__, (self,)+args, kw)
+ self.my_kw = {
+ 'interpreter' : TestSCons.python,
+ 'program' : sconsign,
+ }
+ def run_sconsign(self, *args, **kw):
+ kw.update(self.my_kw)
+ return apply(self.run, args, kw)
+
+test = MyTestSCons(match = TestCmd.match_re)
@@ -96,41 +110,33 @@ test.write(['work1', 'sub2', 'inc2.h'], r"""\
#define STRING2 "inc2.h"
""")
-test.run(chdir = 'work1', arguments = '--implicit-cache .')
+test.run(chdir = 'work1', arguments = '--debug=stacktrace --implicit-cache .')
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "work1/sub1/.sconsign",
+test.run_sconsign(arguments = "work1/sub1/.sconsign",
stdout = """\
hello.exe: None \S+ None
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-v work1/sub1/.sconsign",
+test.run_sconsign(arguments = "-v work1/sub1/.sconsign",
stdout = """\
hello.exe:
timestamp: None
bsig: \S+
csig: None
implicit:
- %s: \S+
+ hello.obj: \S+
hello.obj:
timestamp: None
bsig: \S+
csig: None
implicit:
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-b -v work1/sub1/.sconsign",
+test.run_sconsign(arguments = "-b -v work1/sub1/.sconsign",
stdout = """\
hello.exe:
bsig: \S+
@@ -138,9 +144,7 @@ hello.obj:
bsig: \S+
""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-c -v work1/sub1/.sconsign",
+test.run_sconsign(arguments = "-c -v work1/sub1/.sconsign",
stdout = """\
hello.exe:
csig: None
@@ -148,74 +152,57 @@ hello.obj:
csig: None
""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.obj work1/sub1/.sconsign",
+test.run_sconsign(arguments = "-e hello.obj work1/sub1/.sconsign",
stdout = """\
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.obj -e hello.exe -e hello.obj work1/sub1/.sconsign",
+test.run_sconsign(arguments = "-e hello.obj -e hello.exe -e hello.obj work1/sub1/.sconsign",
stdout = """\
hello.obj: None \S+ None
- %s: \S+
+ hello.c: \S+
hello.exe: None \S+ None
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.c'),
- re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
-
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "work1/sub2/.sconsign",
+ hello.c: \S+
+""")
+
+# XXX NOT SURE IF THIS IS RIGHT!
+sub2_inc1_h = re_sep('sub2', 'inc1.h')
+sub2_inc2_h = re_sep('sub2', 'inc2.h')
+
+test.run_sconsign(arguments = "work1/sub2/.sconsign",
stdout = """\
hello.exe: None \S+ None
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
- %s: \S+
- %s: \S+
-""" % (re_sep('sub2', 'hello.obj'),
- re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h')))
-
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-i -v work1/sub2/.sconsign",
+ hello.c: \S+
+ inc1.h: \S+
+ inc2.h: \S+
+""")
+
+test.run_sconsign(arguments = "-i -v work1/sub2/.sconsign",
stdout = """\
hello.exe:
implicit:
- %s: \S+
+ hello.obj: \S+
hello.obj:
implicit:
- %s: \S+
- %s: \S+
- %s: \S+
-""" % (re_sep('sub2', 'hello.obj'),
- re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h')))
-
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.obj work1/sub2/.sconsign work1/sub1/.sconsign",
+ hello.c: \S+
+ inc1.h: \S+
+ inc2.h: \S+
+""")
+
+test.run_sconsign(arguments = "-e hello.obj work1/sub2/.sconsign work1/sub1/.sconsign",
stdout = """\
hello.obj: None \S+ None
- %s: \S+
- %s: \S+
- %s: \S+
+ hello.c: \S+
+ inc1.h: \S+
+ inc2.h: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
test.run(chdir = 'work1', arguments = '--clean .')
@@ -230,29 +217,23 @@ env2.Program('sub2/hello.c')
time.sleep(1)
-test.run(chdir = 'work1', arguments = '. --max-drift=1')
+test.run(chdir = 'work1', arguments = '. --max-drift=1 --debug=stacktrace')
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.exe -e hello.obj work1/sub1/.sconsign",
+test.run_sconsign(arguments = "-e hello.exe -e hello.obj work1/sub1/.sconsign",
stdout = """\
hello.exe: None \d+ None
- %s: \d+
+ hello.obj: \d+
hello.obj: None \d+ None
- %s: \d+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \d+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.exe -e hello.obj -r work1/sub1/.sconsign",
+test.run_sconsign(arguments = "-e hello.exe -e hello.obj -r work1/sub1/.sconsign",
stdout = """\
hello.exe: None \d+ None
- %s: \d+
+ hello.obj: \d+
hello.obj: None \d+ None
- %s: \d+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \d+
+""")
##############################################################################
@@ -297,36 +278,25 @@ test.write(['work2', 'sub2', 'inc2.h'], r"""\
test.run(chdir = 'work2', arguments = '--implicit-cache .')
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "work2/.sconsign")
+test.run_sconsign(arguments = "work2/.sconsign")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "work2/.sconsign",
+test.run_sconsign(arguments = "work2/.sconsign",
stdout = """\
=== sub1:
hello.exe: None \S+ None
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
+ hello.c: \S+
=== sub2:
hello.exe: None \S+ None
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
- %s: \S+
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c'),
- re_sep('sub2', 'hello.obj'),
- re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h')))
-
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-v work2/.sconsign",
+ hello.c: \S+
+ inc1.h: \S+
+ inc2.h: \S+
+""")
+
+test.run_sconsign(arguments = "-v work2/.sconsign",
stdout = """\
=== sub1:
hello.exe:
@@ -334,38 +304,31 @@ hello.exe:
bsig: \S+
csig: None
implicit:
- %s: \S+
+ hello.obj: \S+
hello.obj:
timestamp: None
bsig: \S+
csig: None
implicit:
- %s: \S+
+ hello.c: \S+
=== sub2:
hello.exe:
timestamp: None
bsig: \S+
csig: None
implicit:
- %s: \S+
+ hello.obj: \S+
hello.obj:
timestamp: None
bsig: \S+
csig: None
implicit:
- %s: \S+
- %s: \S+
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c'),
- re_sep('sub2', 'hello.obj'),
- re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h')))
-
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-b -v work2/.sconsign",
+ hello.c: \S+
+ inc1.h: \S+
+ inc2.h: \S+
+""")
+
+test.run_sconsign(arguments = "-b -v work2/.sconsign",
stdout = """\
=== sub1:
hello.exe:
@@ -379,9 +342,7 @@ hello.obj:
bsig: \S+
""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-c -v work2/.sconsign",
+test.run_sconsign(arguments = "-c -v work2/.sconsign",
stdout = """\
=== sub1:
hello.exe:
@@ -395,82 +356,59 @@ hello.obj:
csig: None
""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.obj work2/.sconsign",
+test.run_sconsign(arguments = "-e hello.obj work2/.sconsign",
stdout = """\
=== sub1:
hello.obj: None \S+ None
- %s: \S+
+ hello.c: \S+
=== sub2:
hello.obj: None \S+ None
- %s: \S+
- %s: \S+
- %s: \S+
-""" % (re_sep('sub1', 'hello.c'),
- re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h')))
-
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.obj -e hello.exe -e hello.obj work2/.sconsign",
+ 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
- %s: \S+
+ hello.c: \S+
hello.exe: None \S+ None
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
+ hello.c: \S+
=== sub2:
hello.obj: None \S+ None
- %s: \S+
- %s: \S+
- %s: \S+
+ hello.c: \S+
+ inc1.h: \S+
+ inc2.h: \S+
hello.exe: None \S+ None
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
- %s: \S+
- %s: \S+
-""" % (re_sep('sub1', 'hello.c'),
- re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c'),
- re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h'),
- re_sep('sub2', 'hello.obj'),
- re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h')))
-
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-i -v work2/.sconsign",
+ hello.c: \S+
+ inc1.h: \S+
+ inc2.h: \S+
+""")
+
+test.run_sconsign(arguments = "-i -v work2/.sconsign",
stdout = """\
=== sub1:
hello.exe:
implicit:
- %s: \S+
+ hello.obj: \S+
hello.obj:
implicit:
- %s: \S+
+ hello.c: \S+
=== sub2:
hello.exe:
implicit:
- %s: \S+
+ hello.obj: \S+
hello.obj:
implicit:
- %s: \S+
- %s: \S+
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c'),
- re_sep('sub2', 'hello.obj'),
- re_sep('sub2', 'hello.c'),
- re_sep('sub2', 'inc1.h'),
- re_sep('sub2', 'inc2.h')))
+ hello.c: \S+
+ inc1.h: \S+
+ inc2.h: \S+
+""")
test.run(chdir = 'work2', arguments = '--clean .')
@@ -493,81 +431,63 @@ expect = """\
hello.c: \d+ None \d+
"""
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign",
+test.run_sconsign(arguments = "-e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign",
stdout = """\
=== sub1:
hello.exe: None \S+ None
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign.dblite",
+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
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.c -e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign",
+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
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.c -e hello.exe -e hello.obj -d sub1 -f dblite work2/my_sconsign.dblite",
+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
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.c -e hello.exe -e hello.obj -r -d sub1 -f dblite work2/my_sconsign",
+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
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-e hello.c -e hello.exe -e hello.obj -r -d sub1 -f dblite work2/my_sconsign.dblite",
+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
- %s: \S+
+ hello.obj: \S+
hello.obj: None \S+ None
- %s: \S+
-""" % (re_sep('sub1', 'hello.obj'),
- re_sep('sub1', 'hello.c')))
+ hello.c: \S+
+""")
##############################################################################
@@ -575,39 +495,25 @@ test.write('bad1', "bad1\n")
test.write('bad2.dblite', "bad2.dblite\n")
test.write('bad3', "bad3\n")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-f dblite no_sconsign",
+test.run_sconsign(arguments = "-f dblite no_sconsign",
stderr = "sconsign: \[Errno 2\] No such file or directory: 'no_sconsign'\n")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-f dblite bad1",
+test.run_sconsign(arguments = "-f dblite bad1",
stderr = "sconsign: \[Errno 2\] No such file or directory: 'bad1.dblite'\n")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-f dblite bad1.dblite",
+test.run_sconsign(arguments = "-f dblite bad1.dblite",
stderr = "sconsign: \[Errno 2\] No such file or directory: 'bad1.dblite'\n")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-f dblite bad2",
+test.run_sconsign(arguments = "-f dblite bad2",
stderr = "sconsign: ignoring invalid `dblite' file `bad2'\n")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-f dblite bad2.dblite",
+test.run_sconsign(arguments = "-f dblite bad2.dblite",
stderr = "sconsign: ignoring invalid `dblite' file `bad2.dblite'\n")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-f sconsign no_sconsign",
+test.run_sconsign(arguments = "-f sconsign no_sconsign",
stderr = "sconsign: \[Errno 2\] No such file or directory: 'no_sconsign'\n")
-test.run(interpreter = TestSCons.python,
- program = sconsign,
- arguments = "-f sconsign bad3",
+test.run_sconsign(arguments = "-f sconsign bad3",
stderr = "sconsign: ignoring invalid .sconsign file `bad3'\n")
test.pass_test()
diff --git a/test/subdivide.py b/test/subdivide.py
new file mode 100644
index 0000000..f6aa4f1
--- /dev/null
+++ b/test/subdivide.py
@@ -0,0 +1,97 @@
+#!/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__"
+
+"""
+Verify that rebuilds do not occur when TargetSignatures()
+content is used to subdivide a dependency tree.
+"""
+
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+#if os.path.exists('sconsign.py'):
+# sconsign = 'sconsign.py'
+#elif os.path.exists('sconsign'):
+# sconsign = 'sconsign'
+#else:
+# print "Can find neither 'sconsign.py' nor 'sconsign' scripts."
+# test.no_result(1)
+
+test.subdir('src', ['src', 'sub'])
+
+test.write('SConstruct', """\
+TargetSignatures('content')
+env = Environment()
+env.SConscript('src/SConstruct', exports=['env'])
+env.Object('foo.c')
+""")
+
+test.write(['src', 'SConstruct'], """\
+TargetSignatures('content')
+env = Environment()
+p = env.Program('prog', ['main.c', '../foo%s', 'sub/bar.c'])
+env.Default(p)
+""" % TestSCons._obj)
+
+test.write('foo.c', """\
+void
+foo(void) {
+ printf("foo.c\\n");
+}
+""")
+
+test.write(['src', 'main.c'], """\
+extern void foo(void);
+extern void bar(void);
+int
+main(int argc, char *argv[]) {
+ foo();
+ bar();
+ printf("src/main.c\\n");
+ exit (0);
+}
+""")
+
+test.write(['src', 'sub', 'bar.c'], """\
+void
+bar(void) {
+ printf("bar.c\\n");
+}
+""")
+
+test.run()
+
+test.run(program=test.workpath('src', 'prog'),
+ stdout="foo.c\nbar.c\nsrc/main.c\n")
+
+test.up_to_date(chdir='src', arguments = test.workpath())
+
+test.up_to_date(arguments = '.')
+
+test.pass_test()