diff options
author | Steven Knight <knight@baldmt.com> | 2003-03-22 08:31:26 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2003-03-22 08:31:26 (GMT) |
commit | 3be8585d1be52d25020b93d99c333d9c9d577b51 (patch) | |
tree | 1841b7b005e680ba16d9a897e759774390a1583c /src/engine/SCons | |
parent | 5ea9b7416ae70c3a4678bcf337bebd41b944cf86 (diff) | |
download | SCons-3be8585d1be52d25020b93d99c333d9c9d577b51.zip SCons-3be8585d1be52d25020b93d99c333d9c9d577b51.tar.gz SCons-3be8585d1be52d25020b93d99c333d9c9d577b51.tar.bz2 |
Make RCS/SCCS/BitKeeper support more transparent.
Diffstat (limited to 'src/engine/SCons')
-rw-r--r-- | src/engine/SCons/Builder.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/BuilderTests.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Defaults.py | 6 | ||||
-rw-r--r-- | src/engine/SCons/Node/FS.py | 120 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 65 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 21 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 5 | ||||
-rw-r--r-- | src/engine/SCons/Script/SConscript.py | 9 | ||||
-rw-r--r-- | src/engine/SCons/Script/__init__.py | 12 | ||||
-rw-r--r-- | src/engine/SCons/Taskmaster.py | 25 | ||||
-rw-r--r-- | src/engine/SCons/TaskmasterTests.py | 44 |
11 files changed, 208 insertions, 103 deletions
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 5047a44..aceadbc 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -144,7 +144,7 @@ def _init_nodes(builder, env, overrides, tlist, slist): for t in tlist: if t.side_effect: raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t) - if t.has_builder(fetch = 0): + if t.has_builder(): if t.env != env: raise UserError, "Two different environments were specified for the same target: %s"%str(t) elif t.overrides != overrides: diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 2f17227..190c9e8 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -148,7 +148,7 @@ class BuilderTestCase(unittest.TestCase): return self.name def builder_set(self, builder): self.builder = builder - def has_builder(self, fetch=1): + def has_builder(self): return not self.builder is None def env_set(self, env, safe=0): self.env = env diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index f4bd637..07a388e 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -50,6 +50,12 @@ import SCons.Scanner.C import SCons.Scanner.Fortran import SCons.Scanner.Prog +# A placeholder for a default Environment (for fetching source files +# from source code management systems and the like). This must be +# initialized later, after the top-level directory is set by the calling +# interface. +_default_env = None + def alias_builder(env, target, source): pass diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index e5182cd..89566b2 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -154,6 +154,32 @@ def CachePushFunc(target, source, env): CachePush = SCons.Action.Action(CachePushFunc, None) +class _Null: + pass + +_null = _Null() + +DefaultSCCSBuilder = None +DefaultRCSBuilder = None + +def get_DefaultSCCSBuilder(): + global DefaultSCCSBuilder + if DefaultSCCSBuilder is None: + import SCons.Builder + import SCons.Defaults + DefaultSCCSBuilder = SCons.Builder.Builder(action = '$SCCSCOM', + env = SCons.Defaults._default_env) + return DefaultSCCSBuilder + +def get_DefaultRCSBuilder(): + global DefaultRCSBuilder + if DefaultRCSBuilder is None: + import SCons.Builder + import SCons.Defaults + DefaultRCSBuilder = SCons.Builder.Builder(action = '$RCSCOM', + env = SCons.Defaults._default_env) + return DefaultRCSBuilder + # class ParentOfRoot: """ @@ -188,7 +214,7 @@ class ParentOfRoot: return path_elems def src_builder(self): - return None + return _null if os.path.normcase("TeSt") == os.path.normpath("TeSt"): def _my_normcase(x): @@ -265,7 +291,7 @@ class Entry(SCons.Node.Node): try: return self._exists except AttributeError: - self._exists = os.path.exists(self.abspath) + self._exists = _existsp(self.abspath) return self._exists def rexists(self): @@ -382,7 +408,7 @@ class FS: self.pathTop = path self.Root = {} self.Top = None - self.SConstruct = None + self.SConstruct_dir = None self.CachePath = None self.cache_force = None self.cache_show = None @@ -391,8 +417,8 @@ class FS: assert not self.Top, "You can only set the top-level path on an FS object that has not had its File, Dir, or Entry methods called yet." self.pathTop = path - def set_SConstruct(self, path): - self.SConstruct = self.File(path) + def set_SConstruct_dir(self, dir): + self.SConstruct_dir = dir def __setTopLevelDir(self): if not self.Top: @@ -636,7 +662,6 @@ class FS: # Go up one directory d = d.get_dir() return None - def Rsearchall(self, pathlist, must_exist=1, clazz=_classEntry, cwd=None): """Search for a list of somethings in the Repository list.""" @@ -661,16 +686,20 @@ class FS: d = n.get_dir() name = n.name - # Search repositories of all directories that this file is under. + # Search repositories of all directories that this file + # is under. while d: for rep in d.getRepositories(): try: rnode = self.__doLookup(clazz, name, rep) - # Only find the node if it exists (or must_exist is zero) - # and it is not a derived file. If for some reason, we - # are explicitly building a file IN a Repository, we don't - # want it to show up in the build tree. This is usually the - # case with BuildDir(). We only want to find pre-existing files. + # Only find the node if it exists (or + # must_exist is zero) and it is not a + # derived file. If for some reason, we + # are explicitly building a file IN a + # Repository, we don't want it to show up in + # the build tree. This is usually the case + # with BuildDir(). We only want to find + # pre-existing files. if (not must_exist or rnode.exists()) and \ (not rnode.has_builder() or isinstance(rnode, Dir)): ret.append(rnode) @@ -851,6 +880,17 @@ class Dir(Entry): else: return 0 + def rdir(self): + try: + return self._rdir + except AttributeError: + self._rdir = self + if not self.exists(): + n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top) + if n: + self._rdir = n + return self._rdir + def sconsign(self): """Return the .sconsign file info for this directory, creating it first if necessary.""" @@ -1039,9 +1079,10 @@ class File(Entry): so only do thread safe stuff here. Do thread unsafe stuff in built(). """ - if not self.has_builder(): + b = self.has_builder() + if not b and not self.has_src_builder(): return - if self.fs.CachePath: + if b and self.fs.CachePath: if self.fs.cache_show: if CacheRetrieveSilent(self, None, None) == 0: def do_print(action, targets, sources, env, self=self): @@ -1074,27 +1115,56 @@ class File(Entry): if self.fs.CachePath and self.fs.cache_force and os.path.exists(self.path): CachePush(self, None, None) - def has_builder(self, fetch = 1): - """Return whether this Node has a builder or not. + def has_src_builder(self): + """Return whether this Node has a source builder or not. + + If this Node doesn't have an explicit source code builder, this + is where we figure out, on the fly, if there's a transparent + source code builder for it. - If this Node doesn't have an explicit builder, this is where we - figure out, on the fly, if there's a source code builder for it. + Note that if we found a source builder, we also set the + self.builder attribute, so that all of the methods that actually + *build* this file don't have to do anything different. """ try: - b = self.builder + scb = self.sbuilder except AttributeError: - if fetch and not os.path.exists(self.path): - b = self.src_builder() + if self.rexists(): + scb = None else: - b = None - self.builder = b - return not b is None + scb = self.dir.src_builder() + if scb is _null: + scb = None + dir = self.dir.path + sccspath = os.path.join('SCCS', 's.' + self.name) + if dir != '.': + sccspath = os.path.join(dir, sccspath) + if os.path.exists(sccspath): + scb = get_DefaultSCCSBuilder() + else: + rcspath = os.path.join('RCS', self.name + ',v') + if dir != '.': + rcspath = os.path.join(dir, rcspath) + if os.path.exists(rcspath): + scb = get_DefaultRCSBuilder() + self.builder = scb + self.sbuilder = scb + return not scb is None + + def is_derived(self): + """Return whether this file is a derived file or not. + + This overrides the base class method to account for the fact + that a file may be derived transparently from a source code + builder. + """ + return self.has_builder() or self.side_effect or self.has_src_builder() def prepare(self): """Prepare for this file to be created.""" def missing(node): - return not node.has_builder() and not node.linked and not node.rexists() + return not node.has_builder() and not node.linked and not node.rexists() and not node.has_src_builder() missing_sources = filter(missing, self.children()) if missing_sources: desc = "No Builder for target `%s', needed by `%s'." % (missing_sources[0], self) diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 294a4bb..d460c1e 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -1182,47 +1182,46 @@ class StringDirTestCase(unittest.TestCase): assert str(f) == os.path.join('sub', 'file') assert not f.exists() -class has_builderTestCase(unittest.TestCase): +class has_src_builderTestCase(unittest.TestCase): def runTest(self): - """Test the has_builder() method""" + """Test the has_src_builder() method""" test = TestCmd(workdir = '') fs = SCons.Node.FS.FS(test.workpath('')) os.chdir(test.workpath('')) - test.subdir('sub') - - d = fs.Dir('sub', '.') - f1 = fs.File('f1', d) - f2 = fs.File('f2', d) - f3 = fs.File('f3', d) - f4 = fs.File('f4', d) - f5 = fs.File('f5', d) - f6 = fs.File('f6', d) - f7 = fs.File('f7', d) - - h = f1.has_builder() + test.subdir('sub1') + test.subdir('sub2', ['sub2', 'SCCS'], ['sub2', 'RCS']) + + sub1 = fs.Dir('sub1', '.') + f1 = fs.File('f1', sub1) + f2 = fs.File('f2', sub1) + f3 = fs.File('f3', sub1) + sub2 = fs.Dir('sub2', '.') + f4 = fs.File('f4', sub2) + f5 = fs.File('f5', sub2) + f6 = fs.File('f6', sub2) + + h = f1.has_src_builder() assert not h, h b1 = Builder(fs.File) - d.set_src_builder(b1) + sub1.set_src_builder(b1) - test.write(['sub', 'f2'], "sub/f2\n") - h = f1.has_builder() # cached from previous has_builder() call + test.write(['sub1', 'f2'], "sub1/f2\n") + h = f1.has_src_builder() # cached from previous call assert not h, h - h = f2.has_builder() + h = f2.has_src_builder() assert not h, h - h = f3.has_builder() + h = f3.has_src_builder() assert h, h assert f3.builder is b1, f3.builder - test.write(['sub', 'f4'], "sub/f4\n") - test.write(['sub', 'f6'], "sub/f6\n") - h = f4.has_builder(fetch = 0) + test.write(['sub2', 'SCCS', 's.f5'], "sub2/SCCS/s.f5\n") + test.write(['sub2', 'RCS', 'f6,v'], "sub2/RCS/f6,v\n") + h = f4.has_src_builder() assert not h, h - h = f5.has_builder(fetch = 0) - assert not h, h - h = f6.has_builder(fetch = 1) - assert not h, h - h = f7.has_builder(fetch = 1) + h = f5.has_src_builder() + assert h, h + h = f6.has_src_builder() assert h, h class prepareTestCase(unittest.TestCase): @@ -1267,13 +1266,13 @@ class get_actionsTestCase(unittest.TestCase): a = dir.get_actions() assert a == [], a -class SConstructTestCase(unittest.TestCase): +class SConstruct_dirTestCase(unittest.TestCase): def runTest(self): - """Test setting the SConstruct file""" + """Test setting the SConstruct directory""" fs = SCons.Node.FS.FS() - fs.set_SConstruct('xxx') - assert fs.SConstruct.path == 'xxx' + fs.set_SConstruct_dir(fs.Dir('xxx')) + assert fs.SConstruct_dir.path == 'xxx' class CacheDirTestCase(unittest.TestCase): def runTest(self): @@ -1437,10 +1436,10 @@ if __name__ == "__main__": suite.addTest(RepositoryTestCase()) suite.addTest(find_fileTestCase()) suite.addTest(StringDirTestCase()) - suite.addTest(has_builderTestCase()) + suite.addTest(has_src_builderTestCase()) suite.addTest(prepareTestCase()) suite.addTest(get_actionsTestCase()) - suite.addTest(SConstructTestCase()) + suite.addTest(SConstruct_dirTestCase()) suite.addTest(CacheDirTestCase()) if not unittest.TextTestRunner().run(suite).wasSuccessful(): sys.exit(1) diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index a9fa361..91b35d0 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -259,20 +259,23 @@ class NodeTestCase(unittest.TestCase): """Test the has_builder() method """ n1 = SCons.Node.Node() + assert n1.has_builder() == 0 + n1.builder_set(Builder()) + assert n1.has_builder() == 1 + + def test_is_derived(self): + """Test the is_derived() method + """ + n1 = SCons.Node.Node() n2 = SCons.Node.Node() n3 = SCons.Node.Node() - assert n1.has_builder() == 0 - assert n2.has_builder(fetch = 0) == 0 - assert n3.has_builder(fetch = 1) == 0 - - n1.builder_set(Builder()) n2.builder_set(Builder()) - n3.builder_set(Builder()) + n3.side_effect = 1 - assert n1.has_builder() == 1 - assert n2.has_builder(fetch = 0) == 1 - assert n3.has_builder(fetch = 1) == 1 + assert n1.is_derived() == 0 + assert n2.is_derived() == 1 + assert n3.is_derived() == 1 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 0a64586..326aee3 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -208,7 +208,7 @@ class Node: def builder_set(self, builder): self.builder = builder - def has_builder(self, fetch = 1): + def has_builder(self): """Return whether this Node has a builder or not. In Boolean tests, this turns out to be a *lot* more efficient @@ -228,6 +228,9 @@ class Node: b = self.builder return not b is None + def is_derived(self): + return self.has_builder() or self.side_effect + def builder_sig_adapter(self): """Create an adapter for calculating a builder's signature. diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 8dd4ef3..0fe0d64 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -38,6 +38,7 @@ import SCons.Errors import SCons.Node import SCons.Node.FS import SCons.Platform +import SCons.Script import SCons.Tool import SCons.Util import SCons.Options @@ -184,7 +185,7 @@ def SConscript(*ls, **kw): default_fs = SCons.Node.FS.default_fs top = default_fs.Top - sd = default_fs.SConstruct.rfile().dir + sd = default_fs.SConstruct_dir.rdir() # evaluate each SConscript file results = [] @@ -210,7 +211,7 @@ def SConscript(*ls, **kw): default_fs.chdir(top, change_os_dir=1) if f.rexists(): _file_ = open(f.rstr(), "r") - elif f.has_builder(): + elif f.has_src_builder(): # The SConscript file apparently exists in a source # code management system. Build it, but then clear # the builder so that it doesn't get built *again* @@ -364,7 +365,6 @@ def SetBuildSignatureType(type): raise SCons.Errors.UserError, "Unknown build signature type '%s'"%type def SetContentSignatureType(type): - import SCons.Script if type == 'MD5': import SCons.Sig.MD5 SCons.Script.sig_module = SCons.Sig.MD5 @@ -448,10 +448,11 @@ def Exit(value=0): def BuildDefaultGlobals(): """ Create a dictionary containing all the default globals for - SConscruct and SConscript files. + SConstruct and SConscript files. """ globals = {} + globals['_default_env'] = SCons.Defaults._default_env globals['Action'] = SCons.Action.Action globals['AddPostAction'] = AddPostAction globals['AddPreAction'] = AddPreAction diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 1f9daf5..760f207 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -55,6 +55,8 @@ import traceback # 'lib', # 'scons-%d' % SCons.__version__)] + sys.path[1:] +import SCons.Defaults +import SCons.Environment import SCons.Errors import SCons.Job import SCons.Node @@ -722,6 +724,10 @@ def _main(): SCons.Node.FS.default_fs.set_toplevel_dir(os.getcwd()) + # Now that the top-level directory has been set, + # we can initialize the default Environment. + SCons.Defaults._default_env = SCons.Environment.Environment() + scripts = [] if options.file: scripts.extend(options.file) @@ -742,7 +748,11 @@ def _main(): if not scripts: raise SCons.Errors.UserError, "No SConstruct file found." - SCons.Node.FS.default_fs.set_SConstruct(scripts[0]) + if scripts[0] == "-": + d = SCons.Node.FS.default_fs.getcwd() + else: + d = SCons.Node.FS.default_fs.File(scripts[0]).dir + SCons.Node.FS.default_fs.set_SConstruct_dir(d) class Unbuffered: def __init__(self, file): diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 2ab076b..93df569 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -63,6 +63,12 @@ class Task: This unlinks all targets and makes all directories before building anything.""" + + # Now that it's the appropriate time, give the TaskMaster a + # chance to raise any exceptions it encountered while preparing + # this task. + self.tm.exception_raise() + if self.targets[0].get_state() != SCons.Node.up_to_date: for t in self.targets: t.prepare() @@ -74,11 +80,6 @@ class Task: so only do thread safe stuff here. Do thread unsafe stuff in prepare(), executed() or failed().""" - # Now that it's the appropriate time, give the TaskMaster a - # chance to raise any exceptions it encountered while preparing - # this task. - self.tm.exception_raise() - try: self.targets[0].build() except KeyboardInterrupt: @@ -245,8 +246,18 @@ class Taskmaster: # Add derived files that have not been built # to the candidates list: def derived(node): - return (node.has_builder() or node.side_effect) and node.get_state() == None - derived = filter(derived, children) + return node.is_derived() and node.get_state() == None + try: + derived = filter(derived, 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." + 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)) diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index ea9b8b4..c6543e3 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -61,6 +61,9 @@ class Node: def has_builder(self): return not self.builder is None + def is_derived(self): + return self.has_builder or self.side_effect + def built(self): global built_text built_text = built_text + " really" @@ -532,6 +535,26 @@ class TaskmasterTestCase(unittest.TestCase): assert n1.prepared assert n2.prepared + # If the Node has had an exception recorded while it was getting + # prepared, then prepare() should raise that exception. + class MyException(Exception): + pass + + built_text = None + n5 = Node("n5") + tm = SCons.Taskmaster.Taskmaster([n5]) + tm.exc_type = MyException + tm.exc_value = "exception value" + t = tm.next_task() + exc_caught = None + try: + t.prepare() + except MyException, v: + assert str(v) == "exception value", v + exc_caught = 1 + assert exc_caught, "did not catch expected MyException" + assert built_text is None, built_text + def test_execute(self): """Test executing a task @@ -591,27 +614,6 @@ class TaskmasterTestCase(unittest.TestCase): else: raise TestFailed, "did not catch expected BuildError" - # If the Node has had an exception recorded (during - # preparation), then execute() should raise that exception, - # not build the Node. - class MyException(Exception): - pass - - built_text = None - n5 = Node("n5") - tm = SCons.Taskmaster.Taskmaster([n5]) - tm.exc_type = MyException - tm.exc_value = "exception value" - t = tm.next_task() - exc_caught = None - try: - t.execute() - except MyException, v: - assert str(v) == "exception value", v - exc_caught = 1 - assert exc_caught, "did not catch expected MyException" - assert built_text is None, built_text - def test_exception(self): """Test generic Taskmaster exception handling |