Add support for fetching files from rcs.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 18 Feb 2003 08:04:29 +0000 (08:04 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 18 Feb 2003 08:04:29 +0000 (08:04 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@592 fdb21ef1-2011-0410-befe-b5e4ea1792b1

21 files changed:
doc/man/scons.1
src/CHANGES.txt
src/engine/MANIFEST.in
src/engine/SCons/Builder.py
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.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/Tool/BitKeeper.py [new file with mode: 0644]
src/engine/SCons/Tool/CVS.py [new file with mode: 0644]
src/engine/SCons/Tool/RCS.py [new file with mode: 0644]
src/engine/SCons/Tool/SCCS.py [new file with mode: 0644]
src/engine/SCons/Tool/Subversion.py [new file with mode: 0644]
src/engine/SCons/Tool/__init__.py
test/BitKeeper.py [new file with mode: 0644]
test/CVS.py [new file with mode: 0644]
test/RCS.py [new file with mode: 0644]
test/SCCS.py [new file with mode: 0644]
test/Subversion.py [new file with mode: 0644]

index 1bf91579dc57dbb04b088c458293e27cab62dcb3..f026b726e62a0446077ef385e7e68f29ed72d563 100644 (file)
@@ -1348,6 +1348,45 @@ and the lists are added together.
 env.Append(CCFLAGS = ' -g', FOO = ['foo.yyy'])
 .EE
 
+.TP
+.RI BitKeeper( repository ", " module )
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from the specified
+BitKeeper
+.IR repository .
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function.
+
+The optional specified
+.I module
+will be added to the beginning
+of all repository path names;
+this can be used, in essence,
+to strip initial directory names
+from the repository path names,
+so that you only have to
+replicate part of the repository
+directory hierarchy in your
+local build directory:
+
+.ES
+# Will fetch foo/bar/src.c
+# from /usr/local/BKsources/foo/bar/src.c.
+env.SourceCode('.', env.BitKeeper('/usr/local/BKsources'))
+
+# Will fetch bar/src.c
+# from /usr/local/BKsources/foo/bar/src.c.
+env.SourceCode('.', env.BitKeeper('/usr/local/BKsources', 'foo'))
+
+# Will fetch src.c
+# from /usr/local/BKsources/foo/bar/src.c.
+env.SourceCode('.', env.BitKeeper('/usr/local/BKsources', 'foo/bar'))
+.EE
+
 .TP
 .RI Command( target ", " source ", " commands )
 Executes a specific action
@@ -1393,6 +1432,45 @@ env2 = env.Copy()
 env3 = env.Copy(CCFLAGS = '-g')
 .EE
 
+.TP
+.RI CVS( repository ", " module )
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from the specified
+CVS
+.IR repository .
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function.
+
+The optional specified
+.I module
+will be added to the beginning
+of all repository path names;
+this can be used, in essence,
+to strip initial directory names
+from the repository path names,
+so that you only have to
+replicate part of the repository
+directory hierarchy in your
+local build directory:
+
+.ES
+# Will fetch foo/bar/src.c
+# from /usr/local/CVSROOT/foo/bar/src.c.
+env.SourceCode('.', env.CVS('/usr/local/CVSROOT'))
+
+# Will fetch bar/src.c
+# from /usr/local/CVSROOT/foo/bar/src.c.
+env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo'))
+
+# Will fetch src.c
+# from /usr/local/CVSROOT/foo/bar/src.c.
+env.SourceCode('.', env.CVS('/usr/local/CVSROOT', 'foo/bar'))
+.EE
+
 .TP
 .RI Depends( target ", " dependency )
 Specifies an explicit dependency;
@@ -1486,16 +1564,43 @@ and the lists are added together.
 env.Prepend(CCFLAGS = '-g ', FOO = ['foo.yyy'])
 .EE
 
+.TP
+.RI RCS( )
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from RCS.
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function:
+.ES
+env.SourceCode('.', env.RCS())
+.EE
+
 .TP
 .RI Replace( key = val ", [...])"
 Replaces construction variables in the Environment
 with the specified keyword arguments.
-(Note:  "Update()" is a deprecated synonym for this method.)
 
 .ES
 env.Replace(CCFLAGS = '-g', FOO = 'foo.xxx')
 .EE
 
+.TP
+.RI SCCS( )
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from SCCS.
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function:
+.ES
+env.SourceCode('.', env.SCCS())
+.EE
+
 .TP
 .RI SideEffect( side_effect , target )
 Declares
@@ -1520,6 +1625,96 @@ Consequently, you only need to use this method
 for side-effect targets that are built as a result of
 multiple build commands.
 
+.TP
+.RI SourceCode( entries , builder )
+Arrange for non-existent source files to
+be fetched from a source code system
+using the specified
+.IR builder .
+The specified
+.I entries
+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
+and use the first
+.B SourceCode
+builder it finds.
+The specified
+.I builder
+may be
+.BR None ,
+in which case
+.B scons
+will not use a builder to fetch
+source files for the specified
+.IR entries ,
+even if a
+.B SourceCode
+builder has been specified
+for a directory higher up the tree.
+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
+Builders for various popular
+source code management systems.
+Canonical examples of invocation include:
+.ES
+env.SourceCode('.', env.BitKeeper('/usr/local/BKsources'))
+env.SourceCode('src', env.CVS('/usr/local/CVSROOT'))
+env.SourceCode('/', env.RCS())
+env.SourceCode(['f1.c', 'f2.c'], env.SCCS())
+env.SourceCode('.', env.Subversion('file:///usr/local/Subversion'))
+env.SourceCode('no_source.c', None)
+.EE
+
+.TP
+.RI Subversion( repository ", " module )
+A factory function that
+returns a Builder object
+to be used to fetch source files
+from the specified Subversion
+.IR repository .
+The returned Builder
+is intended to be passed to the
+.B SourceCode
+function.
+
+The optional specified
+.I module
+will be added to the beginning
+of all repository path names;
+this can be used, in essence,
+to strip initial directory names
+from the repository path names,
+so that you only have to
+replicate part of the repository
+directory hierarchy in your
+local build directory:
+
+.ES
+# Will fetch foo/bar/src.c
+# from /usr/local/Subversion/foo/bar/src.c.
+env.SourceCode('.', env.Subversion('file:///usr/local/Subversion'))
+
+# Will fetch bar/src.c
+# from /usr/local/Subversion/foo/bar/src.c.
+env.SourceCode('.', env.Subversion('file:///usr/local/Subversion', 'foo'))
+
+# Will fetch src.c
+# from /usr/local/Subversion/foo/bar/src.c.
+env.SourceCode('.', env.Subversion('file:///usr/local/Subversion', 'foo/bar'))
+.EE
+
 .SS Construction Variables
 .\" XXX From Gary Ruben, 23 April 2002:
 .\" I think it would be good to have an example with each construction
@@ -1570,6 +1765,16 @@ after first running the file through the C preprocessor.
 Any options specified in the $ASFLAGS and $CPPFLAGS construction variables
 are included on this command line.
 
+.IP BITKEEPER
+The BitKeeper executable.
+
+.IP BITKEEPERCOM
+The command line used to
+fetch source files from a BitKeeper repository.
+
+.IP BITKEEPERFLAGS
+General options that are passed to BitKeeper.
+
 .IP BUILDERS
 A dictionary mapping the names of the builders
 available through this environment
@@ -1619,6 +1824,15 @@ SCons also treats
 (upper case) files
 as C files.
 
+.IP CO 
+The RCS "checkout" executable,
+used to fetch source files from RCS.
+See the related variables
+.B RCSCOM
+and
+.BR RCSFLAGS ,
+below.
+
 .IP _concat
 A function used to produce variables like $_CPPINCFLAGS. It takes six
 arguments: a prefix to concatenate onto each element, a list of elements, a
@@ -1631,7 +1845,6 @@ before concatenation.
 env['_CPPINCFLAGS'] = '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, locals(), globals(), RDirs)} $)',
 .EE
 
-
 .IP CPPFLAGS
 C preprocessor options.
 These will be included in any command that uses the C preprocessor,
@@ -1696,6 +1909,16 @@ include $_CPPINCFLAGS:
 env = Environment(CCCOM="my_compiler $_CPPINCFLAGS -c -o $TARGET $SOURCE")
 .EE
 
+.IP CVS
+The CVS executable.
+
+.IP CVSCOM
+The command line used to
+fetch source files from a CVS repository.
+
+.IP CVSFLAGS
+General options that are passed to CVS.
+
 .IP CXX
 The C++ compiler.
 
@@ -2135,6 +2358,21 @@ The command line used by the RES builder.
 .IP RCFLAGS
 The flags passed to the resource compiler by the RES builder.
 
+.IP RCS
+The RCS executable.
+Note that this variable is not actually used
+to fetch source files from RCS;
+see the
+.B CO
+construction variable, above.
+
+.IP RCSCOM
+The command line used to
+fetch source files from RCS.
+
+.IP RCSFLAGS
+General options that are passed to RCS.
+
 .IP RDirs
 A function that converts a file name into a list of Dir instances by
 searching the repositories. 
@@ -2142,6 +2380,16 @@ searching the repositories.
 .IP SCANNERS
 A list of the available implicit dependency scanners. [CScan] by default.
 
+.IP SCCS
+The SCCS executable.
+
+.IP SCCSCOM
+The command line used to
+fetch source files from SCCS.
+
+.IP SCCSFLAGS
+General options that are passed to SCCS.
+
 .IP SHCC
 The C compiler used for generating shared-library objects.
 
@@ -2233,6 +2481,17 @@ is that arguments to the command.
 is a dictionary of the environment variables
 in which the command should be executed.
 
+.IP SUBVERSION
+The Subversion executable (usually named
+.BR svn ).
+
+.IP SUBVERSIONCOM
+The command line used to
+fetch source files from a Subversion repository.
+
+.IP SUBVERSIONFLAGS
+General options that are passed to Subversion.
+
 .IP TAR
 The tar archiver.
 
@@ -3189,6 +3448,24 @@ and
 .I action
 arguments must not both be used for the same Builder.
 
+.IP env
+A construction environment that can be used
+to fetch source code using this Builder.
+(Note that this environment is
+.I not
+used for normal builds of normal target files,
+which use the environment that was
+used to call the Builder for the target file.)
+
+.IP overrides
+A dictionary of construction variables
+that will be set in the executing
+construction environment when this
+Builder is invoked.
+The canonical example here would be
+to set a construction variable to 
+the repository of a source code system.
+
 Any additional keyword arguments supplied
 when a Builder object is called
 will be associated with the target
index 944fcb6795fbe65bc7c7da8b37bc7ae22549bb05..dce8e349a4a5cd9b23d1c7643d4e2d629fe6af12 100644 (file)
@@ -19,6 +19,10 @@ RELEASE 0.12 - XXX
   - Remove deprecated features:  the "name" argument to Builder objects,
     and the Environment.Update() method.
 
+  - 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.
+
 
 
 RELEASE 0.11 - Tue, 11 Feb 2003 05:24:33 -0600
index 1746de93666723fdcc12e23fd8306fa567b1288d..6a6cb6a55750e3493a3b397c1031633c514636dd 100644 (file)
@@ -33,6 +33,8 @@ SCons/Taskmaster.py
 SCons/Tool/__init__.py
 SCons/Tool/386asm.py
 SCons/Tool/ar.py
+SCons/Tool/BitKeeper.py
+SCons/Tool/CVS.py
 SCons/Tool/default.py
 SCons/Tool/dvipdf.py
 SCons/Tool/dvips.py
@@ -56,11 +58,14 @@ SCons/Tool/nasm.py
 SCons/Tool/pdflatex.py
 SCons/Tool/pdftex.py
 SCons/Tool/PharLapCommon.py
+SCons/Tool/RCS.py
+SCons/Tool/SCCS.py
 SCons/Tool/sgiar.py
 SCons/Tool/sgias.py
 SCons/Tool/sgicc.py
 SCons/Tool/sgif77.py
 SCons/Tool/sgilink.py
+SCons/Tool/Subversion.py
 SCons/Tool/tar.py
 SCons/Tool/tex.py
 SCons/Tool/yacc.py
index cf85a4a4b6252384c8f6c32f4441a6585e71ad15..b6839e4d53205aec86cf05b5949d2933e76c853d 100644 (file)
@@ -216,12 +216,16 @@ class BuilderBase:
                         source_factory = None,
                         scanner = None,
                         emitter = None,
-                        multi = 0):
+                        multi = 0,
+                        env = None,
+                        overrides = {}):
         self.name = name
         self.action = SCons.Action.Action(action)
         self.multi = multi
         self.prefix = prefix
         self.suffix = suffix
+        self.env = env
+        self.overrides = overrides
 
         self.set_src_suffix(src_suffix)
 
index 97d20f93b2c876926b21635db8137b9680ac3854..c2a1ab6867e5587bcfbe6b6975f73a90d7a1932f 100644 (file)
@@ -367,6 +367,15 @@ class Environment:
             ret = ret[0]
         return ret
 
+    def SourceCode(self, entry, builder):
+        """Arrange for a source code builder for (part of) a tree."""
+        entries = SCons.Node.arg2nodes(entry, self.fs.Entry)
+        for entry in entries:
+            entry.set_src_builder(builder)
+        if len(entries) == 1:
+            return entries[0]
+        return entries
+
     def SideEffect(self, side_effect, target):
         """Tell scons that side_effects are built as side 
         effects of building targets."""
index 657f190340ae7e0e81dab4e2b066525a349e0ced..e31393948f0090276b8f1ea5c261e3641ed63d50 100644 (file)
@@ -473,6 +473,18 @@ class EnvironmentTestCase(unittest.TestCase):
         assert 'foo1.in' in map(lambda x: x.path, t.sources)
         assert 'foo2.in' in map(lambda x: x.path, t.sources)
 
+    def test_SourceCode(self):
+        """Test the SourceCode() method."""
+        env = Environment()
+        e = env.SourceCode('foo', None)
+        s = e.src_builder()
+        assert s is None, s
+
+        b = Builder()
+        env.SourceCode(e, b)
+        s = e.src_builder()
+        assert s is b, s
+
     def test_SideEffect(self):
         """Test the SideEffect() method"""
         env = Environment()
index 651d2803424304b305947ad1c23b3f1504d947de..5a8687eb72cb8ec74ff07af61b0e5db4293dd78b 100644 (file)
@@ -168,6 +168,9 @@ class ParentOfRoot:
     def get_dir(self):
         return None
 
+    def src_builder(self):
+        return None
+
 if os.path.normcase("TeSt") == os.path.normpath("TeSt"):
     def _my_normcase(x):
         return x
@@ -294,6 +297,24 @@ class Entry(SCons.Node.Node):
             self._srcnode = self
             return self._srcnode
 
+    def set_src_builder(self, builder):
+        """Set the source code builder for this node."""
+        self.sbuilder = builder
+
+    def src_builder(self):
+        """Fetch the source code builder for this node.
+
+        If there isn't one, we cache the source code builder specified
+        for the directory (which in turn will cache the value from its
+        parent directory, and so on up to the file system root).
+        """
+        try:
+            scb = self.sbuilder
+        except AttributeError:
+            scb = self.dir.src_builder()
+            self.sbuilder = scb
+        return scb
+
 # This is for later so we can differentiate between Entry the class and Entry
 # the method of the FS class.
 _classEntry = Entry
@@ -995,6 +1016,22 @@ 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):
+        """Return whether this Node has a builder or not.
+
+        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.
+        """
+        try:
+            b = self.builder
+        except AttributeError:
+            if not os.path.exists(self.path):
+                b = self.src_builder()
+            else:
+                b = None
+            self.builder = b
+        return not b is None
+
     def prepare(self):
         """Prepare for this file to be created."""
 
index fa32c25d0b468740e91b992576fa8f3b1644ea13..f1160c1b919b6b42cda0fd6cc1f6136ad8ab1593 100644 (file)
@@ -1168,6 +1168,34 @@ class StringDirTestCase(unittest.TestCase):
         assert str(f) == os.path.join('sub', 'file')
         assert not f.exists()
 
+class has_builderTestCase(unittest.TestCase):
+    def runTest(self):
+        """Test the has_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)
+
+        h = f1.has_builder()
+        assert not h, h
+
+        b1 = Builder(fs.File)
+        d.set_src_builder(b1)
+
+        test.write(['sub', 'f2'], "sub/f2\n")
+        h = f1.has_builder()    # cached from previous has_builder() call
+        assert not h, h
+        h = f2.has_builder()
+        assert not h, h
+        h = f3.has_builder()
+        assert h, h
+        assert f3.builder is b1, f3.builder
+
 class prepareTestCase(unittest.TestCase):
     def runTest(self):
         """Test the prepare() method"""
@@ -1328,6 +1356,7 @@ if __name__ == "__main__":
     suite.addTest(RepositoryTestCase())
     suite.addTest(find_fileTestCase())
     suite.addTest(StringDirTestCase())
+    suite.addTest(has_builderTestCase())
     suite.addTest(prepareTestCase())
     suite.addTest(get_actionsTestCase())
     suite.addTest(CacheDirTestCase())
index 86dcc56472315c6c40288d10c69a3d85c46b9435..b47106da97c55a24972243fcf8b5f6622863a968 100644 (file)
@@ -110,7 +110,7 @@ class ExceptBuilder2:
 class Environment:
     def Dictionary(self, *args):
         return {}
-    def Override(selv, overrides):
+    def Override(self, overrides):
         return overrides
 
 class Scanner:
@@ -166,7 +166,7 @@ class NodeTestCase(unittest.TestCase):
         node.overrides = { "foo" : 1, "bar" : 2 }
         node.build()
         assert built_it
-        assert built_target[0] == node, build_target[0]
+        assert built_target[0] == node, built_target[0]
         assert built_source == ["rrr", "sss"], built_source
         assert built_args["foo"] == 1, built_args
         assert built_args["bar"] == 2, built_args
@@ -185,6 +185,21 @@ class NodeTestCase(unittest.TestCase):
         ggg.sources = ["hhh", "iii"]
         # [Charles C. 1/7/2002] Uhhh, why are there no asserts here?
 
+        built_it = None
+        jjj = MyNode("jjj")
+        b = Builder()
+        jjj.builder_set(b)
+        # NOTE:  No env_set()!  We should pull the environment from the builder.
+        b.env = Environment()
+        b.overrides = { "on" : 3, "off" : 4 }
+        e.builder = b
+        jjj.build()
+        assert built_it
+        assert built_target[0] == jjj, built_target[0]
+        assert built_source == [], built_source
+        assert built_args["on"] == 3, built_args
+        assert built_args["off"] == 4, built_args
+
         built_it = None
         built_order = 0
         node = MyNode("xxx")
index 600c4781d444a968a97703cee95513fb7cf85d36..27ff4db2f603ddb882cc5cd336dd2e224ef0a09d 100644 (file)
@@ -89,15 +89,21 @@ class Node:
         pass
 
     def __init__(self):
+        # Note that we no longer explicitly initialize a self.builder
+        # attribute to None here.  That's because the self.builder
+        # attribute may be created on-the-fly later by a subclass (the
+        # canonical example being a builder to fetch a file from a
+        # source code system like CVS or Subversion).
+
         self.sources = []       # source files used to build node
         self.depends = []       # explicit dependencies (from Depends)
         self.implicit = None    # implicit (scanned) dependencies (None means not scanned yet)
         self.ignore = []        # dependencies to ignore
         self.parents = {}
         self.wkids = None       # Kids yet to walk, when it's an array
-        self.builder = None
         self.source_scanner = None      # implicit scanner from scanner map
         self.target_scanner = None      # explicit scanner from this node's Builder
+
         self.env = None
         self.state = None
         self.precious = None
@@ -116,12 +122,32 @@ class Node:
         Annotate(self)
 
     def generate_build_env(self):
-        return self.env.Override(self.overrides)
+        """Generate the appropriate Environment to build this node."""
+        if self.env is None:
+            # The node itself doesn't have an associated Environment
+            # (which kind of implies it's a source code file, but who
+            # knows...).  Regardless of why, use the environment (if
+            # any) associated with the Builder itself.
+            env = self.builder.env
+            overrides = self.builder.overrides
+        else:
+            # The normal case: use the Environment used to specify how
+            # this Node is to be built.
+            env = self.env
+            overrides = self.overrides
+        return env.Override(overrides)
 
     def _for_each_action(self, func):
+        """Call a function for each action required to build a node.
+
+        The purpose here is to have one place for the logic that
+        collects and executes all of the actions for a node's builder,
+        even though multiple sections of code elsewhere need this logic
+        to do different things."""
         if not self.has_builder():
-            return None
-        action_list = self.pre_actions + self.builder.get_actions() + \
+            return
+        action_list = self.pre_actions + \
+                      self.builder.get_actions() + \
                       self.post_actions
         if not action_list:
             return
@@ -193,7 +219,14 @@ class Node:
         class(es), generating a bazillion extra calls and slowing
         things down immensely.
         """
-        return not self.builder is None
+        try:
+            b = self.builder
+        except AttributeError:
+            # There was no explicit builder for this Node, so initialize
+            # the self.builder attribute to None now.
+            self.builder = None
+            b = self.builder
+        return not b is None
 
     def builder_sig_adapter(self):
         """Create an adapter for calculating a builder's signature.
diff --git a/src/engine/SCons/Tool/BitKeeper.py b/src/engine/SCons/Tool/BitKeeper.py
new file mode 100644 (file)
index 0000000..94e084f
--- /dev/null
@@ -0,0 +1,62 @@
+"""SCons.Tool.BitKeeper.py
+
+Tool-specific initialization for the BitKeeper source code control
+system.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os.path
+
+import SCons.Builder
+
+def generate(env, platform):
+    """Add a Builder factory function and construction variables for
+    BitKeeper to an Environment."""
+
+    def BitKeeperFactory(repos, module='', env=env):
+        """ """
+        # fail if repos is not an absolute path name?
+        if module != '':
+           module = os.path.join(module, '')
+        return SCons.Builder.Builder(action = "$BITKEEPERCOM",
+                                     env = env,
+                                     overrides = {'BKREPOSITORY':repos,
+                                                  'BKMODULE':module})
+
+    setattr(env, 'BitKeeper', BitKeeperFactory)
+
+    env['BITKEEPER']      = 'bk'
+    env['BITKEEPERFLAGS'] = ''
+    env['BITKEEPERCOM']   = '$BITKEEPER get $BITKEEPERFLAGS -p $BKREPOSITORY/$BKMODULE$TARGET > $TARGET'
+
+def exists(env):
+    return env.Detect('bk')
diff --git a/src/engine/SCons/Tool/CVS.py b/src/engine/SCons/Tool/CVS.py
new file mode 100644 (file)
index 0000000..c8b1f68
--- /dev/null
@@ -0,0 +1,61 @@
+"""SCons.Tool.CVS.py
+
+Tool-specific initialization for CVS.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os.path
+
+import SCons.Builder
+
+def generate(env, platform):
+    """Add a Builder factory function and construction variables for
+    CVS to an Environment."""
+
+    def CVSFactory(repos, module='', env=env):
+        """ """
+        # fail if repos is not an absolute path name?
+        if module != '':
+           module = os.path.join(module, '')
+        return SCons.Builder.Builder(action = '$CVSCOM',
+                                     env = env,
+                                     overrides = {'CVSREPOSITORY':repos,
+                                                  'CVSMODULE':module})
+
+    setattr(env, 'CVS', CVSFactory)
+
+    env['CVS']      = 'cvs'
+    env['CVSFLAGS'] = ''
+    env['CVSCOM']   = '$CVS $CVSFLAGS -d $CVSREPOSITORY co -p $CVSMODULE$TARGET > $TARGET'
+
+def exists(env):
+    return env.Detect('cvs')
diff --git a/src/engine/SCons/Tool/RCS.py b/src/engine/SCons/Tool/RCS.py
new file mode 100644 (file)
index 0000000..1f5489b
--- /dev/null
@@ -0,0 +1,54 @@
+"""SCons.Tool.RCS.py
+
+Tool-specific initialization for RCS.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import SCons.Builder
+
+def generate(env, platform):
+    """Add a Builder factory function and construction variables for
+    RCS to an Environment."""
+
+    def RCSFactory(env=env):
+        """ """
+        return SCons.Builder.Builder(action = '$RCSCOM', env = env)
+
+    setattr(env, 'RCS', RCSFactory)
+
+    env['CO']       = 'co'
+    env['RCS']      = 'rcs'
+    env['RCSFLAGS'] = ''
+    env['RCSCOM']   = '$CO $RCSFLAGS -p $TARGET,v > $TARGET'
+
+def exists(env):
+    return env.Detect('rcs')
diff --git a/src/engine/SCons/Tool/SCCS.py b/src/engine/SCons/Tool/SCCS.py
new file mode 100644 (file)
index 0000000..1b5d480
--- /dev/null
@@ -0,0 +1,53 @@
+"""SCons.Tool.SCCS.py
+
+Tool-specific initialization for SCCS.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import SCons.Builder
+
+def generate(env, platform):
+    """Add a Builder factory function and construction variables for
+    SCCS to an Environment."""
+
+    def SCCSFactory(env=env):
+        """ """
+        return SCons.Builder.Builder(action = '$SCCSCOM', env = env)
+
+    setattr(env, 'SCCS', SCCSFactory)
+
+    env['SCCS']      = 'sccs'
+    env['SCCSFLAGS'] = ''
+    env['SCCSCOM']   = '$SCCS $SCCSFLAGS get $TARGET'
+
+def exists(env):
+    return env.Detect('sccs')
diff --git a/src/engine/SCons/Tool/Subversion.py b/src/engine/SCons/Tool/Subversion.py
new file mode 100644 (file)
index 0000000..cec3eaf
--- /dev/null
@@ -0,0 +1,61 @@
+"""SCons.Tool.Subversion.py
+
+Tool-specific initialization for Subversion.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os.path
+
+import SCons.Builder
+
+def generate(env, platform):
+    """Add a Builder factory function and construction variables for
+    Subversion to an Environment."""
+
+    def SubversionFactory(repos, module='', env=env):
+        """ """
+        # fail if repos is not an absolute path name?
+        if module != '':
+            module = os.path.join(module, '')
+        return SCons.Builder.Builder(action = '$SUBVERSIONCOM',
+                                     env = env,
+                                     overrides = {'SUBVERSIONREPOSITORY':repos,
+                                                  'SUBVERSIONMODULE':module})
+
+    setattr(env, 'Subversion', SubversionFactory)
+
+    env['SUBVERSION']      = 'svn'
+    env['SUBVERSIONFLAGS'] = ''
+    env['SUBVERSIONCOM']   = '$SUBVERSION $SUBVERSIONFLAGS cat $SUBVERSIONREPOSITORY/$SUBVERSIONMODULE$TARGET > $TARGET'
+
+def exists(env):
+    return env.Detect('svn')
index d2df5bba2fe02fe20d284be1de97446d1802ccd6..b367d320edcd162e8fc190f180b00e10d9026080 100644 (file)
@@ -200,9 +200,11 @@ def tool_list(platform, env):
         else:
             cxx_compiler = FindTool(['g++'], env)
         
-    other_tools = FindAllTools(['dvipdf', 'dvips',
+    other_tools = FindAllTools(['BitKeeper', 'CVS',
+                                'dvipdf', 'dvips',
                                 'latex', 'lex',
                                 'pdflatex', 'pdftex',
+                                'RCS', 'SCCS', 'Subversion',
                                 'tar', 'tex', 'yacc'], env)
 
     tools = ([linker, c_compiler, cxx_compiler,
diff --git a/test/BitKeeper.py b/test/BitKeeper.py
new file mode 100644 (file)
index 0000000..7994a96
--- /dev/null
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test fetching source files from BitKeeper.
+"""
+
+import os
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+bk = test.where_is('bk')
+if not bk:
+    print "Could not find BitKeeper, skipping test(s)."
+    test.no_result(1)
+
+try:
+    login = os.getlogin()
+except AttributeError:
+    try:
+        login = os.environ['USER']
+    except KeyError:
+        login = 'USER'
+
+host = os.uname()[1]
+
+email = "%s@%s" % (login, host)
+
+test.subdir('BitKeeper', 'import', 'work1', 'work2', 'work3')
+
+# Set up the BitKeeper repository.
+bkroot = test.workpath('BitKeeper')
+bk_conf = test.workpath('bk.conf')
+
+# BitKeeper's licensing restrictions require a configuration file that
+# specifies you're not using it multi-user.  This seems to be the
+# minimal configuration that satisfies these requirements.
+test.write(bk_conf, """\
+description:test project 'foo'
+logging:none
+email:%s
+single_user:%s
+single_host:%s
+""" % (email, login, host))
+
+# Plus, we need to set the external environment variable that gets it to
+# shut up and not prompt us to accept the license.
+os.environ['BK_LICENSE'] = 'ACCEPTED'
+
+test.run(chdir = bkroot,
+         program = bk,
+         arguments = 'setup -f -c %s foo' % bk_conf)
+
+test.write(['import', 'aaa.in'], "import/aaa.in\n")
+test.write(['import', 'bbb.in'], "import/bbb.in\n")
+test.write(['import', 'ccc.in'], "import/ccc.in\n")
+
+test.run(chdir = 'import',
+         program = bk,
+         arguments = 'import -q -f -tplain . %s/foo' % bkroot)
+
+# Test the most straightforward BitKeeper checkouts, using the module name.
+test.write(['work1', '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', 'foo/aaa.in')
+env.Cat('bbb.out', 'foo/bbb.in')
+env.Cat('ccc.out', 'foo/ccc.in')
+env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out'])
+env.SourceCode('.', env.BitKeeper(r'%s'))
+""" % bkroot)
+
+test.subdir(['work1', 'foo'])
+test.write(['work1', 'foo', 'bbb.in'], "work1/foo/bbb.in\n")
+
+test.run(chdir = 'work1',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+bk get -p %s/foo/aaa.in > foo/aaa.in
+cat("aaa.out", "foo/aaa.in")
+cat("bbb.out", "foo/bbb.in")
+bk get -p %s/foo/ccc.in > foo/ccc.in
+cat("ccc.out", "foo/ccc.in")
+cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+""" % (bkroot, bkroot)),
+         stderr = """\
+%s/foo/aaa.in 1.1: 1 lines
+%s/foo/ccc.in 1.1: 1 lines
+""" % (bkroot, bkroot))
+
+test.fail_test(test.read(['work1', 'all']) != "import/aaa.in\nwork1/foo/bbb.in\nimport/ccc.in\n")
+
+# Test BitKeeper checkouts when the module name is specified.
+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)},
+                  BITKEEPERFLAGS='-q')
+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.BitKeeper(r'%s', 'foo'))
+""" % bkroot)
+
+test.write(['work2', 'bbb.in'], "work2/bbb.in\n")
+
+test.run(chdir = 'work2',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+bk get -q -p %s/foo/aaa.in > aaa.in
+cat("aaa.out", "aaa.in")
+cat("bbb.out", "bbb.in")
+bk get -q -p %s/foo/ccc.in > ccc.in
+cat("ccc.out", "ccc.in")
+cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+""" % (bkroot, bkroot)))
+
+test.fail_test(test.read(['work2', 'all']) != "import/aaa.in\nwork2/bbb.in\nimport/ccc.in\n")
+
+test.pass_test()
diff --git a/test/CVS.py b/test/CVS.py
new file mode 100644 (file)
index 0000000..c94ae8b
--- /dev/null
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test fetching source files from CVS.
+"""
+
+import os
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+cvs = test.where_is('cvs')
+if not cvs:
+    print "Could not find CVS, skipping test(s)."
+    test.no_result(1)
+
+test.subdir('CVS', 'import', 'work1', 'work2', 'work3')
+
+# Set up the CVS repository.
+cvsroot = test.workpath('CVS')
+
+os.environ['CVSROOT'] = cvsroot
+test.run(program = cvs, arguments = 'init')
+
+test.write(['import', 'aaa.in'], "import/aaa.in\n")
+test.write(['import', 'bbb.in'], "import/bbb.in\n")
+test.write(['import', 'ccc.in'], "import/ccc.in\n")
+
+test.run(chdir = 'import',
+         program = cvs,
+         arguments = '-q import -m "import" foo v v-r')
+
+# Test the most straightforward CVS checkouts, using the module name.
+test.write(['work1', '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)},
+                  CVSFLAGS='-Q')
+env.Cat('aaa.out', 'foo/aaa.in')
+env.Cat('bbb.out', 'foo/bbb.in')
+env.Cat('ccc.out', 'foo/ccc.in')
+env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out'])
+env.SourceCode('.', env.CVS(r'%s'))
+""" % cvsroot)
+
+test.subdir(['work1', 'foo'])
+test.write(['work1', 'foo', 'bbb.in'], "work1/foo/bbb.in\n")
+
+test.run(chdir = 'work1',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+cvs -Q -d %s co -p foo/aaa.in > foo/aaa.in
+cat("aaa.out", "foo/aaa.in")
+cat("bbb.out", "foo/bbb.in")
+cvs -Q -d %s co -p foo/ccc.in > foo/ccc.in
+cat("ccc.out", "foo/ccc.in")
+cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+""" % (cvsroot, cvsroot)))
+
+test.fail_test(test.read(['work1', 'all']) != "import/aaa.in\nwork1/foo/bbb.in\nimport/ccc.in\n")
+
+# Test CVS checkouts when the module name is specified.
+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)},
+                  CVSFLAGS='-q')
+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.CVS(r'%s', 'foo'))
+""" % cvsroot)
+
+test.write(['work2', 'bbb.in'], "work2/bbb.in\n")
+
+test.run(chdir = 'work2',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+cvs -q -d %s co -p foo/aaa.in > aaa.in
+cat("aaa.out", "aaa.in")
+cat("bbb.out", "bbb.in")
+cvs -q -d %s co -p foo/ccc.in > ccc.in
+cat("ccc.out", "ccc.in")
+cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+""" % (cvsroot, cvsroot)))
+
+test.fail_test(test.read(['work2', 'all']) != "import/aaa.in\nwork2/bbb.in\nimport/ccc.in\n")
+
+test.pass_test()
diff --git a/test/RCS.py b/test/RCS.py
new file mode 100644 (file)
index 0000000..6c0e597
--- /dev/null
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test fetching source files from RCS.
+"""
+
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+rcs = test.where_is('rcs')
+if not rcs:
+    print "Could not find RCS, skipping test(s)."
+    test.no_result(1)
+
+ci = test.where_is('ci')
+if not ci:
+    print "Could not find `ci' command, skipping test(s)."
+    test.no_result(1)
+
+# Test checkouts from local RCS files
+test.subdir('work1')
+
+for file in ['aaa.in', 'bbb.in', 'ccc.in']:
+    test.write(['work1', file], "work1/%s\n" % file)
+    args = "-f -t%s %s" % (file, file)
+    test.run(chdir = 'work1', program = ci, arguments = args, stderr = None)
+
+test.no_result(os.path.exists(test.workpath('work1', 'aaa.in')))
+test.no_result(os.path.exists(test.workpath('work1', 'bbb.in')))
+test.no_result(os.path.exists(test.workpath('work1', 'ccc.in')))
+
+test.write(['work1', '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'])
+env.SourceCode('.', env.RCS())
+""")
+
+test.write(['work1', 'bbb.in'], "checked-out work1/bbb.in\n")
+
+test.run(chdir = 'work1',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+co -p aaa.in,v > aaa.in
+cat("aaa.out", "aaa.in")
+cat("bbb.out", "bbb.in")
+co -p ccc.in,v > ccc.in
+cat("ccc.out", "ccc.in")
+cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+"""),
+         stderr = """\
+aaa.in,v  -->  standard output
+revision 1.1
+ccc.in,v  -->  standard output
+revision 1.1
+""")
+
+test.fail_test(test.read(['work1', 'all']) != "work1/aaa.in\nchecked-out work1/bbb.in\nwork1/ccc.in\n")
+
+# Test RCS checkouts from an RCS subdirectory.
+test.subdir('work2', ['work2', 'RCS'])
+
+for file in ['aaa.in', 'bbb.in', 'ccc.in']:
+    test.write(['work2', file], "work2/%s\n" % file)
+    args = "-f -t%s %s" % (file, file)
+    test.run(chdir = 'work2', program = ci, arguments = args, stderr = None)
+
+test.no_result(os.path.exists(test.workpath('work2', 'aaa.in')))
+test.no_result(os.path.exists(test.workpath('work2', 'bbb.in')))
+test.no_result(os.path.exists(test.workpath('work2', 'ccc.in')))
+
+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)},
+                  RCSFLAGS='-q')
+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())
+""")
+
+test.write(['work2', 'bbb.in'], "checked-out work2/bbb.in\n")
+
+test.run(chdir = 'work2',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+co -q -p aaa.in,v > aaa.in
+cat("aaa.out", "aaa.in")
+cat("bbb.out", "bbb.in")
+co -q -p ccc.in,v > ccc.in
+cat("ccc.out", "ccc.in")
+cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+"""))
+
+test.fail_test(test.read(['work2', 'all']) != "work2/aaa.in\nchecked-out work2/bbb.in\nwork2/ccc.in\n")
+
+test.pass_test()
diff --git a/test/SCCS.py b/test/SCCS.py
new file mode 100644 (file)
index 0000000..a25f222
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test fetching source files from SCCS.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+sccs = test.where_is('sccs')
+if not sccs:
+    print "Could not find SCCS, skipping test(s)."
+    test.no_result(1)
+
+# Test checkouts from local SCCS files.
+test.subdir('work1')
+
+test.preserve()
+
+for file in ['aaa.in', 'bbb.in', 'ccc.in']:
+    test.write(['work1', file], "work1/%s\n" % file)
+    args = "create %s" % file
+    test.run(chdir = 'work1', program = sccs, arguments = args, stderr = None)
+    test.unlink(['work1', file])
+    test.unlink(['work1', ','+file])
+
+test.write(['work1', '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'])
+env.SourceCode('.', env.SCCS())
+""")
+
+test.write(['work1', 'bbb.in'], "checked-out work1/bbb.in\n")
+
+test.run(chdir = 'work1',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+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"])
+"""), stderr = """\
+aaa.in 1.1: 1 lines
+ccc.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.pass_test()
diff --git a/test/Subversion.py b/test/Subversion.py
new file mode 100644 (file)
index 0000000..648a131
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test fetching source files from Subversion.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+svn = test.where_is('svn')
+if not svn:
+    print "Could not find Subversion, skipping test(s)."
+    test.no_result(1)
+
+svnadmin = test.where_is('svnadmin')
+if not svn:
+    print "Could not find Subversion, skipping test(s)."
+    test.no_result(1)
+
+test.subdir('Subversion', 'import', 'work1', 'work2', 'work3')
+
+# Set up the Subversion repository.
+svnrootpath = test.workpath('Subversion')
+svnrooturl = 'file://' + svnrootpath
+
+test.run(program = svnadmin, arguments = 'create %s' % svnrootpath)
+
+test.write(['import', 'aaa.in'], "import/aaa.in\n")
+test.write(['import', 'bbb.in'], "import/bbb.in\n")
+test.write(['import', 'ccc.in'], "import/ccc.in\n")
+
+test.run(chdir = 'import',
+         program = svn,
+         arguments = 'import %s . foo -m"import foo"' % svnrooturl)
+
+# Test the most straightforward Subversion checkouts, using the module name.
+test.write(['work1', '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', 'foo/aaa.in')
+env.Cat('bbb.out', 'foo/bbb.in')
+env.Cat('ccc.out', 'foo/ccc.in')
+env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out'])
+env.SourceCode('.', env.Subversion(r'%s'))
+""" % svnrooturl)
+
+test.subdir(['work1', 'foo'])
+test.write(['work1', 'foo', 'bbb.in'], "work1/foo/bbb.in\n")
+
+test.run(chdir = 'work1',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+svn cat %s/foo/aaa.in > foo/aaa.in
+cat("aaa.out", "foo/aaa.in")
+cat("bbb.out", "foo/bbb.in")
+svn cat %s/foo/ccc.in > foo/ccc.in
+cat("ccc.out", "foo/ccc.in")
+cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+""" % (svnrooturl, svnrooturl)))
+
+test.fail_test(test.read(['work1', 'all']) != "import/aaa.in\nwork1/foo/bbb.in\nimport/ccc.in\n")
+
+# Test Subversion checkouts when the module name is specified.
+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'])
+env.SourceCode('.', env.Subversion(r'%s', 'foo'))
+""" % svnrooturl)
+
+test.write(['work2', 'bbb.in'], "work2/bbb.in\n")
+
+test.run(chdir = 'work2',
+         arguments = '.',
+         stdout = test.wrap_stdout("""\
+svn cat %s/foo/aaa.in > aaa.in
+cat("aaa.out", "aaa.in")
+cat("bbb.out", "bbb.in")
+svn cat %s/foo/ccc.in > ccc.in
+cat("ccc.out", "ccc.in")
+cat("all", ["aaa.out", "bbb.out", "ccc.out"])
+""" % (svnrooturl, svnrooturl)))
+
+test.fail_test(test.read(['work2', 'all']) != "import/aaa.in\nwork2/bbb.in\nimport/ccc.in\n")
+
+test.pass_test()