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