Make RCS/SCCS/BitKeeper support more transparent.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 22 Mar 2003 08:31:26 +0000 (08:31 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 22 Mar 2003 08:31:26 +0000 (08:31 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@616 fdb21ef1-2011-0410-befe-b5e4ea1792b1

15 files changed:
doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Defaults.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Script/SConscript.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
test/RCS.py
test/SCCS.py

index 6d84638329ca48d3a41d79c688e7acefa38f953f..bd0dce71a1d2787290d4dbe1f7d9df50022f67f7 100644 (file)
@@ -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
index 70ac65546d085a8100339ba7aeae14ea6666cfe8..b858dd8d7374b9e02349ac049cf69fc4e1a78bf9 100644 (file)
@@ -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.
 
index 5047a449054baa6b9b48e7d096fa5087318ba979..aceadbc57030e17fab1a419e1dedc61c05e09497 100644 (file)
@@ -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:
index 2f1722780782247242d3fe2f6d4dd8ebd23b536e..190c9e845d452a8196ab582ced10981ce538f139 100644 (file)
@@ -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
index f4bd637c77d59f9fea13e062bd0cf85e8f97f00c..07a388eb5766efac985a5195d360f5acd5dcea70 100644 (file)
@@ -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
 
index e5182cd884ae23dfede003a17a67a09ba086f9bc..89566b2a9861283a6301ff74309b51e1eaf137d6 100644 (file)
@@ -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)
index 294a4bb51eb6b5a19bc583dfcfda0d33e3cabaca..d460c1ed98c88fbad12dfd45566e490286ea1b19 100644 (file)
@@ -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)
index a9fa361d89e0e4539306f1da57347a475eed0228..91b35d036a252e3bc30402fb730fbcee77d2f09f 100644 (file)
@@ -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
index 0a64586fcc5411f92aef32b3d5bf7c8a59471bf9..326aee396b51fbc822f899742d21845224369d7a 100644 (file)
@@ -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.
 
index 8dd4ef34d37d80a9c7727c422ca8955cd86320c1..0fe0d64df77036d346e65dd7197ee82afae3ce55 100644 (file)
@@ -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
index 1f9daf5b07b4bf5a53ed03f1f7f3f2892d22eb1d..760f2076ad96ceea9c5e5f30482f98f2f2cb0bd1 100644 (file)
@@ -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):
index 2ab076bb50391c195aff692491363fd6776c3fe5..93df5697521e7051a152e817a211ccf4a99c4245 100644 (file)
@@ -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))
index ea9b8b4480f5d644f6240afe9abdf39421287900..c6543e3746223d1b9901b1ec6e43caaf75c1c40e 100644 (file)
@@ -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
 
index 55e16f8662b0ed0c3fa533eb0137b36ef6d57574..52371566a083f54fd8c7ab118ca825c3b960c533 100644 (file)
@@ -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")
 
index 00e33371e36680d02d407a34c68bd617c8f169d6..f309c70061945b3a7691066ea2a24d5b3e52b44c 100644 (file)
@@ -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()