summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2005-04-01 22:58:50 (GMT)
committerSteven Knight <knight@baldmt.com>2005-04-01 22:58:50 (GMT)
commitebcc7ec5e9125cd6f69cb97df5991015239f5197 (patch)
tree57e7a2d7d0628b81827df9b05e1cfeafff1c0b04 /src/engine/SCons
parent71bf8f38553cdaf3a70937b56668f9dfd2d82476 (diff)
downloadSCons-ebcc7ec5e9125cd6f69cb97df5991015239f5197.zip
SCons-ebcc7ec5e9125cd6f69cb97df5991015239f5197.tar.gz
SCons-ebcc7ec5e9125cd6f69cb97df5991015239f5197.tar.bz2
Store source file and dependency paths relative to the target's directory, not relative to the top-level SConstruct directory.
Diffstat (limited to 'src/engine/SCons')
-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
7 files changed, 230 insertions, 97 deletions
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):