From: stevenknight Date: Fri, 1 Apr 2005 22:58:50 +0000 (+0000) Subject: Store source file and dependency paths relative to the target's directory, not relati... X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=c4d3372b6d1a1d9ee46e659e29f651bda801c771;p=scons.git Store source file and dependency paths relative to the target's directory, not relative to the top-level SConstruct directory. git-svn-id: http://scons.tigris.org/svn/scons/trunk@1273 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/bin/restore.sh b/bin/restore.sh index 2b7a7266..d5dc6c18 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 <$/s/.*//p w q EOF diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 555139b2..779a5d63 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 095671d6..9c09e605 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 ef460efe..afe817a9 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 62c1eab8..9ff30e58 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 d2bce26e..fe014f82 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 adef880d..94e9790f 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 a8bb2faf..5f03377e 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 8cac2c83..db80985d 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 3cde7d0f..a5e07006 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 1c407028..7a24aef3 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 00000000..f6aa4f11 --- /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()