From 9dcb1ca499a2f114bf12cb3349983c3c44f60bde Mon Sep 17 00:00:00 2001 From: stevenknight Date: Sat, 22 Mar 2003 08:31:26 +0000 Subject: [PATCH] Make RCS/SCCS/BitKeeper support more transparent. git-svn-id: http://scons.tigris.org/svn/scons/trunk@616 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- doc/man/scons.1 | 50 ++++++++++- src/CHANGES.txt | 3 +- src/engine/SCons/Builder.py | 2 +- src/engine/SCons/BuilderTests.py | 2 +- src/engine/SCons/Defaults.py | 6 ++ src/engine/SCons/Node/FS.py | 120 ++++++++++++++++++++------ src/engine/SCons/Node/FSTests.py | 65 +++++++------- src/engine/SCons/Node/NodeTests.py | 21 +++-- src/engine/SCons/Node/__init__.py | 5 +- src/engine/SCons/Script/SConscript.py | 9 +- src/engine/SCons/Script/__init__.py | 12 ++- src/engine/SCons/Taskmaster.py | 25 ++++-- src/engine/SCons/TaskmasterTests.py | 44 +++++----- test/RCS.py | 66 +++++++------- test/SCCS.py | 86 +++++++++++++++++- 15 files changed, 374 insertions(+), 142 deletions(-) diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 6d846383..bd0dce71 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -130,6 +130,11 @@ whenever any "included" input file changes. supports the ability to define new scanners for unknown input file types. +.B scons +knows how to fetch files automatically from +SCCS or RCS subdirectories +using SCCS, RCS or BitKeeper. + .B scons is normally executed in a top-level directory containing a .I SConstruct @@ -1650,6 +1655,19 @@ function: .ES env.SourceCode('.', env.RCS()) .EE +Note that +.B scons +will fetch source files +from RCS subdirectories automatically, +so configuring RCS +as demonstrated in the above example +should only be necessary if +you are fetching from +RCS,v +files in the same +directory as the source files, +or if you need to explicitly specify RCS +for a specific subdirectory. .TP .RI Replace( key = val ", [...])" @@ -1673,6 +1691,19 @@ function: .ES env.SourceCode('.', env.SCCS()) .EE +Note that +.B scons +will fetch source files +from SCCS subdirectories automatically, +so configuring SCCS +as demonstrated in the above example +should only be necessary if +you are fetching from +.I s.SCCS +files in the same +directory as the source files, +or if you need to explicitly specify SCCS +for a specific subdirectory. .TP .RI SideEffect( side_effect , target ) @@ -1701,7 +1732,7 @@ multiple build commands. .TP .RI SourceCode( entries , builder ) Arrange for non-existent source files to -be fetched from a source code system +be fetched from a source code management system using the specified .IR builder . The specified @@ -1710,6 +1741,7 @@ may be a Node, string or list of both, and may represent either individual source files or directories in which source files can be found. + For any non-existent source files, .B scons will search up the directory tree @@ -1729,12 +1761,28 @@ even if a .B SourceCode builder has been specified for a directory higher up the tree. + +.B scons +will, by default, +fetch files from SCCS or RCS subdirectories +without explicit configuration. +This takes some extra processing time +to search for the necessary +source code management files on disk. +You can avoid these extra searches +and speed up your build a little +by disabling these searches as follows: +.ES +env.SourceCode('.', None) +.EE + Note that if the specified .I builder is one you create by hand, it must have an associated construction environment to use when fetching a source file. + .B scons provides a set of canned factory functions that return appropriate diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 70ac6554..b858dd8d 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -35,7 +35,8 @@ RELEASE 0.12 - XXX - Add an Environment.SourceCode() method to support fetching files from source code systems. Add factory methods that create Builders - to support BitKeeper, CVS, RCS, SCCS and Subversion. + to support BitKeeper, CVS, RCS, SCCS and Subversion. Add support + for fetching files from RCS or SCCS transparently (like GNU Make). - Make the internal to_String() function more efficient. diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 5047a449..aceadbc5 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 2f172278..190c9e84 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 f4bd637c..07a388eb 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 e5182cd8..89566b2a 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 294a4bb5..d460c1ed 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 a9fa361d..91b35d03 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 0a64586f..326aee39 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 8dd4ef34..0fe0d64d 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 1f9daf5b..760f2076 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 2ab076bb..93df5697 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 ea9b8b44..c6543e37 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 diff --git a/test/RCS.py b/test/RCS.py index 55e16f86..52371566 100644 --- a/test/RCS.py +++ b/test/RCS.py @@ -44,7 +44,7 @@ if not ci: print "Could not find `ci' command, skipping test(s)." test.pass_test(1) -# Test checkouts from local RCS files +# Test explicit checkouts from local RCS files test.subdir('work1', ['work1', 'sub']) for file in ['aaa.in', 'bbb.in', 'ccc.in']: @@ -85,7 +85,8 @@ def cat(env, source, target): for src in source: f.write(open(src, "rb").read()) f.close() -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env = Environment(BUILDERS={'Cat':Builder(action=cat)}, + RCSFLAGS='-q') env.Cat('aaa.out', 'aaa.in') env.Cat('bbb.out', 'bbb.in') env.Cat('ccc.out', 'ccc.in') @@ -101,45 +102,28 @@ test.write(['work1', 'sub', 'eee.in'], "checked-out work1/sub/eee.in\n") test.run(chdir = 'work1', arguments = '.', stdout = test.wrap_stdout(read_str = """\ -co sub/SConscript +co -q sub/SConscript """, build_str = """\ -co aaa.in +co -q aaa.in cat("aaa.out", "aaa.in") cat("bbb.out", "bbb.in") -co ccc.in +co -q ccc.in cat("ccc.out", "ccc.in") cat("all", ["aaa.out", "bbb.out", "ccc.out"]) -co sub/ddd.in +co -q sub/ddd.in cat("sub/ddd.out", "sub/ddd.in") cat("sub/eee.out", "sub/eee.in") -co sub/fff.in +co -q sub/fff.in cat("sub/fff.out", "sub/fff.in") cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"]) -"""), - stderr = """\ -sub/SConscript,v --> sub/SConscript -revision 1.1 -done -aaa.in,v --> aaa.in -revision 1.1 -done -ccc.in,v --> ccc.in -revision 1.1 -done -sub/ddd.in,v --> sub/ddd.in -revision 1.1 -done -sub/fff.in,v --> sub/fff.in -revision 1.1 -done -""") +""")) test.fail_test(test.read(['work1', 'all']) != "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n") test.fail_test(test.read(['work1', 'sub', 'all']) != "work1/sub/ddd.in\nchecked-out work1/sub/eee.in\nwork1/sub/fff.in\n") -# Test RCS checkouts from an RCS subdirectory. +# Test transparent RCS checkouts from an RCS subdirectory. test.subdir('work2', ['work2', 'RCS'], ['work2', 'sub'], ['work2', 'sub', 'RCS']) @@ -187,7 +171,6 @@ env.Cat('aaa.out', 'aaa.in') env.Cat('bbb.out', 'bbb.in') env.Cat('ccc.out', 'ccc.in') env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -env.SourceCode('.', env.RCS()) SConscript('sub/SConscript', "env") """) @@ -198,22 +181,39 @@ test.write(['work2', 'sub', 'eee.in'], "checked-out work2/sub/eee.in\n") test.run(chdir = 'work2', arguments = '.', stdout = test.wrap_stdout(read_str = """\ -co -q sub/SConscript +co sub/SConscript """, build_str = """\ -co -q aaa.in +co aaa.in cat("aaa.out", "aaa.in") cat("bbb.out", "bbb.in") -co -q ccc.in +co ccc.in cat("ccc.out", "ccc.in") cat("all", ["aaa.out", "bbb.out", "ccc.out"]) -co -q sub/ddd.in +co sub/ddd.in cat("sub/ddd.out", "sub/ddd.in") cat("sub/eee.out", "sub/eee.in") -co -q sub/fff.in +co sub/fff.in cat("sub/fff.out", "sub/fff.in") cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"]) -""")) +"""), + stderr = """\ +sub/RCS/SConscript,v --> sub/SConscript +revision 1.1 +done +RCS/aaa.in,v --> aaa.in +revision 1.1 +done +RCS/ccc.in,v --> ccc.in +revision 1.1 +done +sub/RCS/ddd.in,v --> sub/ddd.in +revision 1.1 +done +sub/RCS/fff.in,v --> sub/fff.in +revision 1.1 +done +""") test.fail_test(test.read(['work2', 'all']) != "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n") diff --git a/test/SCCS.py b/test/SCCS.py index 00e33371..f309c700 100644 --- a/test/SCCS.py +++ b/test/SCCS.py @@ -37,11 +37,9 @@ if not sccs: print "Could not find SCCS, skipping test(s)." test.pass_test(1) -# Test checkouts from local SCCS files. +# Test explicit checkouts from local SCCS files. test.subdir('work1', ['work1', 'sub']) -test.preserve() - for file in ['aaa.in', 'bbb.in', 'ccc.in']: test.write(['work1', file], "work1/%s\n" % file) args = "create %s" % file @@ -107,7 +105,8 @@ cat("sub/eee.out", "sub/eee.in") sccs get sub/fff.in cat("sub/fff.out", "sub/fff.in") cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"]) -"""), stderr = """\ +"""), + stderr = """\ sub/SConscript 1.1: 5 lines aaa.in 1.1: 1 lines ccc.in 1.1: 1 lines @@ -117,4 +116,83 @@ sub/fff.in 1.1: 1 lines test.fail_test(test.read(['work1', 'all']) != "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n") +# Test transparent checkouts from SCCS files in an SCCS subdirectory. +test.subdir('work2', ['work2', 'SCCS'], + ['work2', 'sub'], ['work2', 'sub', 'SCCS']) + +for file in ['aaa.in', 'bbb.in', 'ccc.in']: + test.write(['work2', file], "work2/%s\n" % file) + args = "create %s" % file + test.run(chdir = 'work2', program = sccs, arguments = args, stderr = None) + test.unlink(['work2', file]) + test.unlink(['work2', ','+file]) + +test.write(['work2', 'sub', 'SConscript'], """\ +Import("env") +env.Cat('ddd.out', 'ddd.in') +env.Cat('eee.out', 'eee.in') +env.Cat('fff.out', 'fff.in') +env.Cat('all', ['ddd.out', 'eee.out', 'fff.out']) +""") +args = "create SConscript" +test.run(chdir = 'work2/sub', program = sccs, arguments = args, stderr = None) +test.unlink(['work2', 'sub', 'SConscript']) +test.unlink(['work2', 'sub', ',SConscript']) + +for file in ['ddd.in', 'eee.in', 'fff.in']: + test.write(['work2', 'sub', file], "work2/sub/%s\n" % file) + args = "create %s" % file + test.run(chdir = 'work2/sub', program = sccs, arguments = args, stderr = None) + test.unlink(['work2', 'sub', file]) + test.unlink(['work2', 'sub', ','+file]) + +test.write(['work2', '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(BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +SConscript('sub/SConscript', "env") +""") + +test.write(['work2', 'bbb.in'], "checked-out work2/bbb.in\n") + +test.write(['work2', 'sub', 'eee.in'], "checked-out work2/sub/eee.in\n") + +test.run(chdir = 'work2', + arguments = '.', + stdout = test.wrap_stdout(read_str = """\ +sccs get sub/SConscript +""", + build_str = """\ +sccs get aaa.in +cat("aaa.out", "aaa.in") +cat("bbb.out", "bbb.in") +sccs get ccc.in +cat("ccc.out", "ccc.in") +cat("all", ["aaa.out", "bbb.out", "ccc.out"]) +sccs get sub/ddd.in +cat("sub/ddd.out", "sub/ddd.in") +cat("sub/eee.out", "sub/eee.in") +sccs get sub/fff.in +cat("sub/fff.out", "sub/fff.in") +cat("sub/all", ["sub/ddd.out", "sub/eee.out", "sub/fff.out"]) +"""), + stderr = """\ +sub/SConscript 1.1: 5 lines +aaa.in 1.1: 1 lines +ccc.in 1.1: 1 lines +sub/ddd.in 1.1: 1 lines +sub/fff.in 1.1: 1 lines +""") + +test.fail_test(test.read(['work2', 'all']) != "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n") + test.pass_test() -- 2.26.2