summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/man/scons.1281
-rw-r--r--src/CHANGES.txt4
-rw-r--r--src/engine/MANIFEST.in5
-rw-r--r--src/engine/SCons/Builder.py6
-rw-r--r--src/engine/SCons/Environment.py9
-rw-r--r--src/engine/SCons/EnvironmentTests.py12
-rw-r--r--src/engine/SCons/Node/FS.py37
-rw-r--r--src/engine/SCons/Node/FSTests.py29
-rw-r--r--src/engine/SCons/Node/NodeTests.py19
-rw-r--r--src/engine/SCons/Node/__init__.py43
-rw-r--r--src/engine/SCons/Tool/BitKeeper.py62
-rw-r--r--src/engine/SCons/Tool/CVS.py61
-rw-r--r--src/engine/SCons/Tool/RCS.py54
-rw-r--r--src/engine/SCons/Tool/SCCS.py53
-rw-r--r--src/engine/SCons/Tool/Subversion.py61
-rw-r--r--src/engine/SCons/Tool/__init__.py4
-rw-r--r--test/BitKeeper.py157
-rw-r--r--test/CVS.py125
-rw-r--r--test/RCS.py140
-rw-r--r--test/SCCS.py86
-rw-r--r--test/Subversion.py126
21 files changed, 1363 insertions, 11 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 1bf9157..f026b72 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -1349,6 +1349,45 @@ 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
(or list of actions)
@@ -1394,6 +1433,45 @@ 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;
the target file(s) will be rebuilt
@@ -1487,16 +1565,43 @@ 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
.I side_effect
@@ -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
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 944fcb6..dce8e34 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -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
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index 1746de9..6a6cb6a 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -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
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index cf85a4a..b6839e4 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -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)
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 97d20f9..c2a1ab6 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -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."""
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 657f190..e313939 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -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()
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 651d280..5a8687e 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -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."""
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index fa32c25..f1160c1 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -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())
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 86dcc56..b47106d 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -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
@@ -186,6 +186,21 @@ class NodeTestCase(unittest.TestCase):
# [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")
node.builder_set(Builder())
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 600c478..27ff4db 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -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
index 0000000..94e084f
--- /dev/null
+++ b/src/engine/SCons/Tool/BitKeeper.py
@@ -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
index 0000000..c8b1f68
--- /dev/null
+++ b/src/engine/SCons/Tool/CVS.py
@@ -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
index 0000000..1f5489b
--- /dev/null
+++ b/src/engine/SCons/Tool/RCS.py
@@ -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
index 0000000..1b5d480
--- /dev/null
+++ b/src/engine/SCons/Tool/SCCS.py
@@ -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
index 0000000..cec3eaf
--- /dev/null
+++ b/src/engine/SCons/Tool/Subversion.py
@@ -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')
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
index d2df5bb..b367d32 100644
--- a/src/engine/SCons/Tool/__init__.py
+++ b/src/engine/SCons/Tool/__init__.py
@@ -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
index 0000000..7994a96
--- /dev/null
+++ b/test/BitKeeper.py
@@ -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
index 0000000..c94ae8b
--- /dev/null
+++ b/test/CVS.py
@@ -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
index 0000000..6c0e597
--- /dev/null
+++ b/test/RCS.py
@@ -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
index 0000000..a25f222
--- /dev/null
+++ b/test/SCCS.py
@@ -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
index 0000000..648a131
--- /dev/null
+++ b/test/Subversion.py
@@ -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()