diff options
author | Steven Knight <knight@baldmt.com> | 2005-04-01 22:58:50 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2005-04-01 22:58:50 (GMT) |
commit | ebcc7ec5e9125cd6f69cb97df5991015239f5197 (patch) | |
tree | 57e7a2d7d0628b81827df9b05e1cfeafff1c0b04 /src/engine/SCons | |
parent | 71bf8f38553cdaf3a70937b56668f9dfd2d82476 (diff) | |
download | SCons-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.py | 23 | ||||
-rw-r--r-- | src/engine/SCons/ExecutorTests.py | 51 | ||||
-rw-r--r-- | src/engine/SCons/Node/FS.py | 59 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 79 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 16 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 90 | ||||
-rw-r--r-- | src/engine/SCons/SConfTests.py | 9 |
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): |