From 064c8e0e9c55f2bdca9fbb5d562877b7da5cbdc8 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Wed, 26 Mar 2003 15:57:50 +0000 Subject: [PATCH] Add warnings when we switch to BuildDir() targets when using -u or -U. git-svn-id: http://scons.tigris.org/svn/scons/trunk@622 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/engine/SCons/Node/FS.py | 33 ++++++++ src/engine/SCons/Node/FSTests.py | 15 ++++ src/engine/SCons/Node/NodeTests.py | 8 ++ src/engine/SCons/Node/__init__.py | 5 ++ src/engine/SCons/Script/__init__.py | 3 + src/engine/SCons/Taskmaster.py | 49 ++++++++--- src/engine/SCons/TaskmasterTests.py | 16 ++++ test/option-u.py | 122 +++++++++++++++++++++------- 8 files changed, 208 insertions(+), 43 deletions(-) diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 57fd1c29..d6b7b532 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -197,6 +197,7 @@ class ParentOfRoot: self.name='' self.duplicate=0 self.srcdir=None + self.build_dirs=[] def is_under(self, dir): return 0 @@ -714,6 +715,24 @@ class FS: def CacheDir(self, path): self.CachePath = path + def build_dir_target_climb(self, dir, tail): + """Create targets in corresponding build directories + + Climb the directory tree, and look up path names + relative to any linked build directories we find. + """ + targets = [] + message = None + while dir: + for bd in dir.build_dirs: + p = apply(os.path.join, [bd.path] + tail) + targets.append(self.Entry(p)) + tail = [dir.name] + tail + dir = dir.up() + if targets: + message = "building associated BuildDir targets: %s" % string.join(map(str, targets)) + return targets, message + # XXX TODO? # Annotate with the creator # rel_path @@ -749,6 +768,7 @@ class Dir(Entry): self.cwd = self self.builder = 1 self._sconsign = None + self.build_dirs = [] def __clearRepositoryCache(self, duplicate=None): """Called when we change the repository(ies) for a directory. @@ -801,6 +821,7 @@ class Dir(Entry): self.srcdir = srcdir self.duplicate = duplicate self.__clearRepositoryCache(duplicate) + srcdir.build_dirs.append(self) def getRepositories(self): """Returns a list of repositories for this directory.""" @@ -850,6 +871,11 @@ class Dir(Entry): """A null "builder" for directories.""" pass + def alter_targets(self): + """Return any corresponding targets in a build directory. + """ + return self.fs.build_dir_target_climb(self, []) + def calc_signature(self, calc): """A directory has no signature.""" return None @@ -1160,6 +1186,13 @@ class File(Entry): """ return self.has_builder() or self.side_effect or self.has_src_builder() + def alter_targets(self): + """Return any corresponding targets in a build directory. + """ + if self.has_builder(): + return [], None + return self.fs.build_dir_target_climb(self.dir, [self.name]) + def prepare(self): """Prepare for this file to be created.""" diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index d460c1ed..11146eb5 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -282,6 +282,21 @@ class BuildDirTestCase(unittest.TestCase): assert f10.exists() assert f10.get_contents() == 'stuff', f10.get_contents() + f11 = fs.File('src/file11') + t, m = f11.alter_targets() + bdt = map(lambda n: n.path, t) + assert bdt == ['build/var1/file11', 'build/var2/file11'], bdt + + f12 = fs.File('src/file12') + f12.builder = 1 + bdt, m = f12.alter_targets() + assert bdt == [], map(lambda n: n.path, bdt) + + d13 = fs.Dir('src/new_dir') + t, m = d13.alter_targets() + bdt = map(lambda n: n.path, t) + assert bdt == ['build/var1/new_dir', 'build/var2/new_dir'], bdt + save_Mkdir = SCons.Node.FS.Mkdir dir_made = [] def mkdir_func(target, source, env, dir_made=dir_made): diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 91b35d03..f2828609 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -277,6 +277,14 @@ class NodeTestCase(unittest.TestCase): assert n2.is_derived() == 1 assert n3.is_derived() == 1 + def test_alter_targets(self): + """Test the alter_targets() method + """ + n = SCons.Node.Node() + t, m = n.alter_targets() + assert t == [], t + assert m == None, m + def test_builder_sig_adapter(self): """Test the node's adapter for builder signatures """ diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 326aee39..222cd1f1 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -231,6 +231,11 @@ class Node: def is_derived(self): return self.has_builder() or self.side_effect + def alter_targets(self): + """Return a list of alternate targets for this Node. + """ + return [], None + def builder_sig_adapter(self): """Create an adapter for calculating a builder's signature. diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 60d299d2..d2c60aa4 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -73,6 +73,9 @@ import SCons.Warnings # class BuildTask(SCons.Taskmaster.Task): """An SCons build task.""" + def display(self, message): + display('scons: ' + message) + def execute(self): target = self.targets[0] if target.get_state() == SCons.Node.up_to_date: diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 93df5697..bc53024d 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -58,6 +58,11 @@ class Task: self.top = top self.node = node + def display(self, message): + """Allow the calling interface to display a message + """ + pass + def prepare(self): """Called just before the task is executed. @@ -69,6 +74,10 @@ class Task: # this task. self.tm.exception_raise() + if self.tm.message: + self.display(self.tm.message) + self.tm.message = None + if self.targets[0].get_state() != SCons.Node.up_to_date: for t in self.targets: t.prepare() @@ -197,6 +206,7 @@ class Taskmaster: self.calc = calc self.order = order self.exception_set(None, None) + self.message = None def _find_next_ready_node(self): """Find the next node that is ready to be built""" @@ -243,24 +253,39 @@ class Taskmaster: desc = "Dependency cycle: " + string.join(map(str, nodes), " -> ") raise SCons.Errors.UserError, desc - # Add derived files that have not been built - # to the candidates list: - def derived(node): - return node.is_derived() and node.get_state() == None + # Find all of the derived dependencies (that is, + # children who have builders or are side effects): try: - derived = filter(derived, children) + def derived_nodes(node): return node.is_derived() + derived = filter(derived_nodes, children) except: - # We had a problem just trying to figure out the - # children (like a child couldn't be linked in to a - # BuildDir, or a Scanner threw something). Arrange to - # raise the exception when the Task is "executed." + # We had a problem just trying to figure out if any of + # the kids are derived (like a child couldn't be linked + # from a repository). Arrange to raise the exception + # when the Task is "executed." self.exception_set(sys.exc_type, sys.exc_value) self.candidates.pop() self.ready = node break - if derived: - derived.reverse() - self.candidates.extend(self.order(derived)) + + # If there aren't any children with builders and this + # was a top-level argument, then see if we can find any + # corresponding targets in linked build directories: + if not derived and node in self.targets: + alt, message = node.alter_targets() + if alt: + self.message = message + self.candidates.pop() + self.candidates.extend(alt) + continue + + # Add derived files that have not been built + # to the candidates list: + def unbuilt_nodes(node): return node.get_state() == None + not_built = filter(unbuilt_nodes, derived) + if not_built: + not_built.reverse() + self.candidates.extend(self.order(not_built)) continue # Skip this node if it has side-effects that are diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index c6543e37..89c53c74 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -50,6 +50,7 @@ class Node: self.parents = [] self.side_effect = 0 self.side_effects = [] + self.alttargets = [] for kid in kids: kid.parents.append(self) @@ -64,6 +65,9 @@ class Node: def is_derived(self): return self.has_builder or self.side_effect + def alter_targets(self): + return self.alttargets, None + def built(self): global built_text built_text = built_text + " really" @@ -376,6 +380,18 @@ class TaskmasterTestCase(unittest.TestCase): assert t.get_target() == n4, t.get_target() t.executed() + n5 = Node("n5") + n6 = Node("n6") + n7 = Node("n7") + n6.alttargets = [n7] + tm = SCons.Taskmaster.Taskmaster([n5]) + t = tm.next_task() + assert t.get_target() == n5 + t.executed() + tm = SCons.Taskmaster.Taskmaster([n6]) + t = tm.next_task() + assert t.get_target() == n7 + t.executed() def test_make_ready_exception(self): diff --git a/test/option-u.py b/test/option-u.py index 1f7df7ae..9cb00550 100644 --- a/test/option-u.py +++ b/test/option-u.py @@ -24,6 +24,11 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +""" +Test that the -u option only builds targets at or below +the current directory. +""" + import os.path import sys @@ -31,49 +36,104 @@ import TestSCons test = TestSCons.TestSCons() -python = TestSCons.python - -test.subdir('sub1', 'sub2', 'sub3') - -test.write('build.py', r""" -import sys -contents = open(sys.argv[2], 'rb').read() -file = open(sys.argv[1], 'wb') -file.write(contents) -file.close() -""") +test.subdir('sub1', + 'sub2', ['sub2', 'dir'], + 'sub3', + 'sub4', ['sub4', 'dir']) test.write('SConstruct', """ +def cat(env, source, target): + target = str(target[0]) + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() env = Environment() -env['BUILDERS']['B'] = Builder(action=r'%s build.py $TARGET $SOURCES') -env.B(target = 'sub1/foo.out', source = 'sub1/foo.in') +env.Append(BUILDERS = {'Cat' : Builder(action=cat)}) +env.Cat(target = 'sub1/f1a.out', source = 'sub1/f1a.in') +env.Cat(target = 'sub1/f1b.out', source = 'sub1/f1b.in') Export('env') SConscript('sub2/SConscript') -env.Alias('baz', env.B(target = 'sub3/baz.out', source = 'sub3/baz.in')) -""" % python) +f3 = env.Cat(target = 'sub3/f3.out', source = 'sub3/f3.in') +env.Alias('my_alias', f3) +BuildDir('build', 'sub4') +SConscript('build/SConscript') +""") test.write(['sub2', 'SConscript'], """ Import('env') -env.B(target = 'bar.out', source = 'bar.in') +env.Cat(target = 'f2a.out', source = 'f2a.in') +env.Cat(target = 'dir/f2b.out', source = 'dir/f2b.in') """) -test.write(['sub1', 'foo.in'], "sub1/foo.in") -test.write(['sub2', 'bar.in'], "sub2/bar.in") -test.write(['sub3', 'baz.in'], "sub3/baz.in") - -test.run(arguments = '-u foo.out', chdir = 'sub1') - -test.fail_test(test.read(['sub1', 'foo.out']) != "sub1/foo.in") -test.fail_test(os.path.exists(test.workpath('sub2', 'bar.out'))) -test.fail_test(os.path.exists(test.workpath('sub3', 'baz.out'))) +test.write(['sub4', 'SConscript'], """ +Import('env') +env.Cat(target = 'f4a.out', source = 'f4a.in') +env.Cat(target = 'dir/f4b.out', source = 'dir/f4b.in') +""") +test.write(['sub1', 'f1a.in'], "sub1/f1a.in") +test.write(['sub1', 'f1b.in'], "sub1/f1b.in") +test.write(['sub2', 'f2a.in'], "sub2/f2a.in") +test.write(['sub2', 'dir', 'f2b.in'], "sub2/dir/f2b.in") +test.write(['sub3', 'f3.in'], "sub3/f3.in") +test.write(['sub4', 'f4a.in'], "sub4/f4a.in") +test.write(['sub4', 'dir', 'f4b.in'], "sub4/dir/f4b.in") + +# Verify that we only build the specified local argument. +test.run(chdir = 'sub1', arguments = '-u f1a.out') + +test.fail_test(test.read(['sub1', 'f1a.out']) != "sub1/f1a.in") +test.fail_test(os.path.exists(test.workpath('sub1', 'sub1/f1b.out'))) +test.fail_test(os.path.exists(test.workpath('sub2', 'f2a.out'))) +test.fail_test(os.path.exists(test.workpath('sub2', 'dir', 'f2b.out'))) +test.fail_test(os.path.exists(test.workpath('sub3', 'f3.out'))) +test.fail_test(os.path.exists(test.workpath('sub4', 'f4a.out'))) +test.fail_test(os.path.exists(test.workpath('sub4', 'dir', 'f4b.out'))) +test.fail_test(os.path.exists(test.workpath('build', 'f4a.out'))) +test.fail_test(os.path.exists(test.workpath('build', 'dir', 'f4b.out'))) + +# Verify that we build everything at or below our current directory. test.run(chdir = 'sub2', arguments = '-u') -test.fail_test(test.read(['sub2', 'bar.out']) != "sub2/bar.in") -test.fail_test(os.path.exists(test.workpath('sub3', 'baz.out'))) - -test.run(chdir = 'sub2', arguments = '-u baz') -test.fail_test(test.read(['sub3', 'baz.out']) != "sub3/baz.in") +test.fail_test(os.path.exists(test.workpath('sub1', 'sub1/f1b.out'))) +test.fail_test(test.read(['sub2', 'f2a.out']) != "sub2/f2a.in") +test.fail_test(test.read(['sub2', 'dir', 'f2b.out']) != "sub2/dir/f2b.in") +test.fail_test(os.path.exists(test.workpath('sub3', 'f3.out'))) +test.fail_test(os.path.exists(test.workpath('sub4', 'f4a.out'))) +test.fail_test(os.path.exists(test.workpath('sub4', 'dir', 'f4b.out'))) +test.fail_test(os.path.exists(test.workpath('build', 'f4a.out'))) +test.fail_test(os.path.exists(test.workpath('build', 'dir', 'f4b.out'))) + +# Verify that we build a specified alias, regardless of where. +test.run(chdir = 'sub2', arguments = '-u my_alias') + +test.fail_test(os.path.exists(test.workpath('sub1', 'sub1/f1b.out'))) +test.fail_test(test.read(['sub3', 'f3.out']) != "sub3/f3.in") +test.fail_test(os.path.exists(test.workpath('sub4', 'f4a.out'))) +test.fail_test(os.path.exists(test.workpath('sub4', 'dir', 'f4b.out'))) +test.fail_test(os.path.exists(test.workpath('build', 'f4a.out'))) +test.fail_test(os.path.exists(test.workpath('build', 'dir', 'f4b.out'))) + +# Verify that we build things in a linked BuildDir. +f4a_in = os.path.join('build', 'f4a.in') +f4a_out = os.path.join('build', 'f4a.out') +f4b_in = os.path.join('build', 'dir', 'f4b.in') +f4b_out = os.path.join('build', 'dir', 'f4b.out') +test.run(chdir = 'sub4', + arguments = '-u', + stdout = "scons: Entering directory %s\n" % test.workpath() + \ + test.wrap_stdout("""\ +scons: building associated BuildDir targets: build +cat("%s", "%s") +cat("%s", "%s") +""" % (f4b_out, f4b_in, f4a_out, f4a_in))) + +test.fail_test(os.path.exists(test.workpath('sub1', 'sub1/f1b.out'))) +test.fail_test(os.path.exists(test.workpath('sub4', 'f4a.out'))) +test.fail_test(os.path.exists(test.workpath('sub4', 'dir', 'f4b.out'))) +test.fail_test(test.read(['build', 'f4a.out']) != "sub4/f4a.in") +test.fail_test(test.read(['build', 'dir', 'f4b.out']) != "sub4/dir/f4b.in") test.pass_test() - -- 2.26.2