summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/engine/SCons/Action.xml9
-rw-r--r--src/engine/SCons/Builder.py339
-rw-r--r--src/engine/SCons/BuilderTests.py397
-rw-r--r--src/engine/SCons/Defaults.xml61
-rw-r--r--src/engine/SCons/Environment.xml22
-rw-r--r--src/engine/SCons/Node/FS.py924
-rw-r--r--src/engine/SCons/Node/FSTests.py529
-rw-r--r--src/engine/SCons/Node/NodeTests.py564
-rw-r--r--src/engine/SCons/Node/__init__.py606
-rw-r--r--src/engine/SCons/Platform/__init__.xml13
-rw-r--r--src/engine/SCons/Platform/posix.xml7
-rw-r--r--src/engine/SCons/Platform/win32.xml7
-rw-r--r--src/engine/SCons/Tool/386asm.xml7
-rw-r--r--src/engine/SCons/Tool/BitKeeper.xml9
-rw-r--r--src/engine/SCons/Tool/CVS.xml7
-rw-r--r--src/engine/SCons/Tool/Perforce.xml7
-rw-r--r--src/engine/SCons/Tool/RCS.xml7
-rw-r--r--src/engine/SCons/Tool/SCCS.xml7
-rw-r--r--src/engine/SCons/Tool/Subversion.xml7
-rw-r--r--src/engine/SCons/Tool/__init__.xml46
-rw-r--r--src/engine/SCons/Tool/aixc++.xml7
-rw-r--r--src/engine/SCons/Tool/aixcc.xml7
-rw-r--r--src/engine/SCons/Tool/aixf77.xml7
-rw-r--r--src/engine/SCons/Tool/aixlink.xml7
-rw-r--r--src/engine/SCons/Tool/applelink.xml7
-rw-r--r--src/engine/SCons/Tool/ar.xml7
-rw-r--r--src/engine/SCons/Tool/as.xml7
-rw-r--r--src/engine/SCons/Tool/bcc32.xml7
-rw-r--r--src/engine/SCons/Tool/c++.xml7
-rw-r--r--src/engine/SCons/Tool/cc.xml8
-rw-r--r--src/engine/SCons/Tool/cvf.xml7
-rw-r--r--src/engine/SCons/Tool/default.xml7
-rw-r--r--src/engine/SCons/Tool/dmd.xml7
-rw-r--r--src/engine/SCons/Tool/dvipdf.xml7
-rw-r--r--src/engine/SCons/Tool/dvips.xml7
-rw-r--r--src/engine/SCons/Tool/f77.xml7
-rw-r--r--src/engine/SCons/Tool/f90.xml7
-rw-r--r--src/engine/SCons/Tool/f95.xml7
-rw-r--r--src/engine/SCons/Tool/fortran.xml21
-rw-r--r--src/engine/SCons/Tool/g++.xml7
-rw-r--r--src/engine/SCons/Tool/g77.xml7
-rw-r--r--src/engine/SCons/Tool/gas.xml7
-rw-r--r--src/engine/SCons/Tool/gcc.xml7
-rw-r--r--src/engine/SCons/Tool/gnulink.xml7
-rw-r--r--src/engine/SCons/Tool/gs.xml7
-rw-r--r--src/engine/SCons/Tool/hpc++.xml7
-rw-r--r--src/engine/SCons/Tool/hpcc.xml7
-rw-r--r--src/engine/SCons/Tool/hplink.xml7
-rw-r--r--src/engine/SCons/Tool/icc.xml7
-rw-r--r--src/engine/SCons/Tool/icl.xml7
-rw-r--r--src/engine/SCons/Tool/ifl.xml7
-rw-r--r--src/engine/SCons/Tool/ifort.xml7
-rw-r--r--src/engine/SCons/Tool/ilink.xml7
-rw-r--r--src/engine/SCons/Tool/ilink32.xml7
-rw-r--r--src/engine/SCons/Tool/intelc.xml7
-rw-r--r--src/engine/SCons/Tool/jar.xml7
-rw-r--r--src/engine/SCons/Tool/javac.xml7
-rw-r--r--src/engine/SCons/Tool/javah.xml7
-rw-r--r--src/engine/SCons/Tool/latex.xml7
-rw-r--r--src/engine/SCons/Tool/lex.xml7
-rw-r--r--src/engine/SCons/Tool/link.xml7
-rw-r--r--src/engine/SCons/Tool/linkloc.xml7
-rw-r--r--src/engine/SCons/Tool/m4.xml7
-rw-r--r--src/engine/SCons/Tool/masm.xml7
-rw-r--r--src/engine/SCons/Tool/midl.xml7
-rw-r--r--src/engine/SCons/Tool/mingw.xml7
-rw-r--r--src/engine/SCons/Tool/mslib.xml7
-rw-r--r--src/engine/SCons/Tool/mslink.xml10
-rw-r--r--src/engine/SCons/Tool/msvc.xml17
-rw-r--r--src/engine/SCons/Tool/msvs.xml62
-rw-r--r--src/engine/SCons/Tool/mwcc.xml7
-rw-r--r--src/engine/SCons/Tool/mwld.xml7
-rw-r--r--src/engine/SCons/Tool/nasm.xml7
-rw-r--r--src/engine/SCons/Tool/pdflatex.xml7
-rw-r--r--src/engine/SCons/Tool/pdftex.xml7
-rw-r--r--src/engine/SCons/Tool/qt.xml58
-rw-r--r--src/engine/SCons/Tool/rmic.xml7
-rw-r--r--src/engine/SCons/Tool/rpcgen.xml7
-rw-r--r--src/engine/SCons/Tool/sgiar.xml7
-rw-r--r--src/engine/SCons/Tool/sgic++.xml7
-rw-r--r--src/engine/SCons/Tool/sgicc.xml7
-rw-r--r--src/engine/SCons/Tool/sgilink.xml7
-rw-r--r--src/engine/SCons/Tool/sunar.xml7
-rw-r--r--src/engine/SCons/Tool/sunc++.xml7
-rw-r--r--src/engine/SCons/Tool/suncc.xml7
-rw-r--r--src/engine/SCons/Tool/sunlink.xml7
-rw-r--r--src/engine/SCons/Tool/swig.xml7
-rw-r--r--src/engine/SCons/Tool/tar.xml13
-rw-r--r--src/engine/SCons/Tool/tex.xml7
-rw-r--r--src/engine/SCons/Tool/tlib.xml7
-rw-r--r--src/engine/SCons/Tool/yacc.xml7
-rw-r--r--src/engine/SCons/Tool/zip.xml17
92 files changed, 2894 insertions, 1335 deletions
diff --git a/src/engine/SCons/Action.xml b/src/engine/SCons/Action.xml
index c225318..da2f3e3 100644
--- a/src/engine/SCons/Action.xml
+++ b/src/engine/SCons/Action.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<cvar name="PRINT_CMD_LINE_FUNC">
<summary>
A Python function used to print the command lines as they are executed
@@ -55,7 +60,7 @@ def spawn(shell, escape, cmd, args, env):
is a string naming the shell program to use.
<varname>escape</varname>
is a function that can be called to escape shell special characters in
-the command line.
+the command line.
<varname>cmd</varname>
is the path to the command to be executed.
<varname>args</varname>
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 6afff58..9083985 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -126,6 +126,7 @@ import SCons.Action
from SCons.Debug import logInstanceCreation
from SCons.Errors import InternalError, UserError
import SCons.Executor
+import SCons.Node
import SCons.Node.FS
import SCons.Util
import SCons.Warnings
@@ -151,6 +152,9 @@ class DictCmdGenerator(SCons.Util.Selector):
self[suffix] = action
def __call__(self, target, source, env, for_signature):
+ if not source:
+ return []
+
ext = None
for src in map(str, source):
my_ext = SCons.Util.splitext(src)[1]
@@ -221,6 +225,7 @@ class OverrideWarner(UserDict.UserDict):
"""
def __init__(self, dict):
UserDict.UserDict.__init__(self, dict)
+ if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
self.already_warned = None
def warn(self):
if self.already_warned:
@@ -241,12 +246,15 @@ def Builder(**kw):
if kw.has_key('generator'):
if kw.has_key('action'):
raise UserError, "You must not specify both an action and a generator."
- kw['action'] = SCons.Action.CommandGenerator(kw['generator'])
+ kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'])
del kw['generator']
- elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
- composite = DictCmdGenerator(kw['action'])
- kw['action'] = SCons.Action.CommandGenerator(composite)
- kw['src_suffix'] = composite.src_suffixes()
+ elif kw.has_key('action'):
+ if SCons.Util.is_Dict(kw['action']):
+ composite = DictCmdGenerator(kw['action'])
+ kw['action'] = SCons.Action.CommandGeneratorAction(composite)
+ kw['src_suffix'] = composite.src_suffixes()
+ else:
+ kw['action'] = SCons.Action.Action(kw['action'])
if kw.has_key('emitter'):
emitter = kw['emitter']
@@ -274,9 +282,10 @@ def Builder(**kw):
return ret
-def _init_nodes(builder, env, overrides, executor_kw, tlist, slist):
- """Initialize lists of target and source nodes with all of
- the proper Builder information.
+def _node_errors(builder, env, tlist, slist):
+ """Validate that the lists of target and source nodes are
+ legal for this builder and environment. Raise errors or
+ issue warnings as appropriate.
"""
# First, figure out if there are any errors in the way the targets
@@ -286,22 +295,17 @@ def _init_nodes(builder, env, overrides, executor_kw, tlist, slist):
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
if t.has_explicit_builder():
if not t.env is None and not t.env is env:
- t_contents = t.builder.action.get_contents(tlist, slist, t.env)
- contents = t.builder.action.get_contents(tlist, slist, env)
+ action = t.builder.action
+ t_contents = action.get_contents(tlist, slist, t.env)
+ contents = action.get_contents(tlist, slist, env)
if t_contents == contents:
SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning,
- "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), t.builder.action.strfunction(tlist, slist, t.env)))
+ "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), action.genstring(tlist, slist, t.env)))
else:
raise UserError, "Two environments with different actions were specified for the same target: %s"%str(t)
- elif t.overrides != overrides:
- raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
-
- elif builder.target_scanner and t.target_scanner and builder.target_scanner != t.target_scanner:
- raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
-
if builder.multi:
if t.builder != builder:
if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder:
@@ -317,52 +321,6 @@ def _init_nodes(builder, env, overrides, executor_kw, tlist, slist):
if len(slist) > 1:
raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
- # The targets are fine, so find or make the appropriate Executor to
- # build this particular list of targets from this particular list of
- # sources.
- executor = None
- if builder.multi:
- try:
- executor = tlist[0].get_executor(create = 0)
- except AttributeError:
- pass
- else:
- executor.add_sources(slist)
- if executor is None:
- if not builder.action:
- raise UserError, "Builder %s must have an action to build %s."%(builder.get_name(env or builder.env), map(str,tlist))
- executor = SCons.Executor.Executor(builder.action,
- env or builder.env,
- [builder.overrides, overrides],
- tlist,
- slist,
- executor_kw)
-
- # Now set up the relevant information in the target Nodes themselves.
- for t in tlist:
- t.overrides = overrides
- t.cwd = SCons.Node.FS.default_fs.getcwd()
- t.builder_set(builder)
- t.env_set(env)
- t.add_source(slist)
- t.set_executor(executor)
- if builder.target_scanner:
- t.target_scanner = builder.target_scanner
- if t.source_scanner is None:
- t.source_scanner = builder.source_scanner
-
- # Add backup source scanners from the environment to the source
- # nodes. This may not be necessary if the node will have a real
- # source scanner added later (which is why these are the "backup"
- # source scanners, not the real ones), but because source nodes may
- # be used multiple times for different targets, it ends up being
- # more efficient to do this calculation once here, as opposed to
- # delaying it until later when we potentially have to calculate it
- # over and over and over.
- for s in slist:
- if s.source_scanner is None and s.backup_source_scanner is None:
- s.backup_source_scanner = env.get_scanner(s.scanner_key())
-
class EmitterProxy:
"""This is a callable class that can act as a
Builder emitter. It holds on to a string that
@@ -398,12 +356,15 @@ class BuilderBase:
nodes (files) from input nodes (files).
"""
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
def __init__(self, action = None,
prefix = '',
suffix = '',
src_suffix = '',
- target_factory = SCons.Node.FS.default_fs.File,
- source_factory = SCons.Node.FS.default_fs.File,
+ target_factory = None,
+ source_factory = None,
target_scanner = None,
source_scanner = None,
emitter = None,
@@ -414,15 +375,14 @@ class BuilderBase:
chdir = _null,
is_explicit = 1,
**overrides):
- if __debug__: logInstanceCreation(self, 'BuilderBase')
- self.action = SCons.Action.Action(action)
+ if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
+ self.action = action
self.multi = multi
if SCons.Util.is_Dict(prefix):
prefix = CallableSelector(prefix)
self.prefix = prefix
if SCons.Util.is_Dict(suffix):
suffix = CallableSelector(suffix)
- self.suffix = suffix
self.env = env
self.single_source = single_source
if overrides.has_key('overrides'):
@@ -438,6 +398,7 @@ class BuilderBase:
del overrides['scanner']
self.overrides = overrides
+ self.set_suffix(suffix)
self.set_src_suffix(src_suffix)
self.target_factory = target_factory
@@ -470,7 +431,7 @@ class BuilderBase:
try:
index = env['BUILDERS'].values().index(self)
return env['BUILDERS'].keys()[index]
- except (AttributeError, KeyError, ValueError):
+ except (AttributeError, KeyError, TypeError, ValueError):
try:
return self.name
except AttributeError:
@@ -490,7 +451,25 @@ class BuilderBase:
return [path[:-len(suf)], path[-len(suf):]]
return SCons.Util.splitext(path)
- def _create_nodes(self, env, overwarn, target = None, source = None):
+ def get_single_executor(self, env, tlist, slist, executor_kw):
+ if not self.action:
+ raise UserError, "Builder %s must have an action to build %s."%(self.get_name(env or self.env), map(str,tlist))
+ return self.action.get_executor(env or self.env,
+ [], # env already has overrides
+ tlist,
+ slist,
+ executor_kw)
+
+ def get_multi_executor(self, env, tlist, slist, executor_kw):
+ try:
+ executor = tlist[0].get_executor(create = 0)
+ except (AttributeError, IndexError):
+ return self.get_single_executor(env, tlist, slist, executor_kw)
+ else:
+ executor.add_sources(slist)
+ return executor
+
+ def _create_nodes(self, env, target = None, source = None):
"""Create and return lists of target and source nodes.
"""
def _adjustixes(files, pre, suf):
@@ -506,14 +485,13 @@ class BuilderBase:
result.append(f)
return result
- overwarn.warn()
-
- env = env.Override(overwarn.data)
-
src_suf = self.get_src_suffix(env)
+ target_factory = env.get_factory(self.target_factory)
+ source_factory = env.get_factory(self.source_factory)
+
source = _adjustixes(source, None, src_suf)
- slist = env.arg2nodes(source, self.source_factory)
+ slist = env.arg2nodes(source, source_factory)
pre = self.get_prefix(env, slist)
suf = self.get_suffix(env, slist)
@@ -523,11 +501,14 @@ class BuilderBase:
t_from_s = slist[0].target_from_source
except AttributeError:
raise UserError("Do not know how to create a target from source `%s'" % slist[0])
- splitext = lambda S,self=self,env=env: self.splitext(S,env)
- tlist = [ t_from_s(pre, suf, splitext) ]
+ except IndexError:
+ tlist = []
+ else:
+ splitext = lambda S,self=self,env=env: self.splitext(S,env)
+ tlist = [ t_from_s(pre, suf, splitext) ]
else:
target = _adjustixes(target, pre, suf)
- tlist = env.arg2nodes(target, self.target_factory)
+ tlist = env.arg2nodes(target, target_factory)
if self.emitter:
# The emitter is going to do str(node), but because we're
@@ -538,62 +519,88 @@ class BuilderBase:
new_targets = []
for t in tlist:
if not t.is_derived():
- t.builder = self
+ t.builder_set(self)
new_targets.append(t)
target, source = self.emitter(target=tlist, source=slist, env=env)
# Now delete the temporary builders that we attached to any
- # new targets, so that _init_nodes() doesn't do weird stuff
+ # new targets, so that _node_errors() doesn't do weird stuff
# to them because it thinks they already have builders.
for t in new_targets:
if t.builder is self:
# Only delete the temporary builder if the emitter
# didn't change it on us.
- t.builder = None
+ t.builder_set(None)
# Have to call arg2nodes yet again, since it is legal for
# emitters to spit out strings as well as Node instances.
- slist = env.arg2nodes(source, self.source_factory)
- tlist = env.arg2nodes(target, self.target_factory)
+ slist = env.arg2nodes(source, source_factory)
+ tlist = env.arg2nodes(target, target_factory)
return tlist, slist
- def _execute(self, env, target=None, source=_null, overwarn={}, executor_kw={}):
- if source is _null:
- source = target
- target = None
-
- if(self.single_source and
- SCons.Util.is_List(source) and
- len(source) > 1 and
- target is None):
+ def _execute(self, env, target, source, overwarn={}, executor_kw={}):
+ # We now assume that target and source are lists or None.
+ if self.single_source and len(source) > 1 and target is None:
result = []
if target is None: target = [None]*len(source)
- for k in range(len(source)):
- t = self._execute(env, target[k], source[k], overwarn)
- if SCons.Util.is_List(t):
- result.extend(t)
- else:
- result.append(t)
+ for tgt, src in zip(target, source):
+ if not tgt is None: tgt = [tgt]
+ if not src is None: src = [src]
+ result.extend(self._execute(env, tgt, src, overwarn))
return result
+
+ overwarn.warn()
- tlist, slist = self._create_nodes(env, overwarn, target, source)
+ tlist, slist = self._create_nodes(env, target, source)
if len(tlist) == 1:
builder = self
else:
builder = ListBuilder(self, env, tlist)
- _init_nodes(builder, env, overwarn.data, executor_kw, tlist, slist)
- return tlist
+ # Check for errors with the specified target/source lists.
+ _node_errors(builder, env, tlist, slist)
- def __call__(self, env, target=None, source=_null, chdir=_null, **kw):
+ # The targets are fine, so find or make the appropriate Executor to
+ # build this particular list of targets from this particular list of
+ # sources.
+ if builder.multi:
+ get_executor = builder.get_multi_executor
+ else:
+ get_executor = builder.get_single_executor
+ executor = get_executor(env, tlist, slist, executor_kw)
+
+ # Now set up the relevant information in the target Nodes themselves.
+ for t in tlist:
+ t.cwd = env.fs.getcwd()
+ t.builder_set(builder)
+ t.env_set(env)
+ t.add_source(slist)
+ t.set_executor(executor)
+ t.set_explicit(builder.is_explicit)
+
+ return SCons.Node.NodeList(tlist)
+
+ def __call__(self, env, target=None, source=None, chdir=_null, **kw):
+ # We now assume that target and source are lists or None.
+ # The caller (typically Environment.BuilderWrapper) is
+ # responsible for converting any scalar values to lists.
if chdir is _null:
ekw = self.executor_kw
else:
ekw = self.executor_kw.copy()
ekw['chdir'] = chdir
+ if kw:
+ if self.overrides:
+ env_kw = self.overrides.copy()
+ env_kw.update(kw)
+ else:
+ env_kw = kw
+ else:
+ env_kw = self.overrides
+ env = env.Override(env_kw)
return self._execute(env, target, source, OverrideWarner(kw), ekw)
def adjust_suffix(self, suff):
@@ -607,24 +614,29 @@ class BuilderBase:
prefix = prefix(env, sources)
return env.subst(prefix)
+ def set_suffix(self, suffix):
+ if not callable(suffix):
+ suffix = self.adjust_suffix(suffix)
+ self.suffix = suffix
+
def get_suffix(self, env, sources=[]):
suffix = self.suffix
if callable(suffix):
suffix = suffix(env, sources)
- else:
- suffix = self.adjust_suffix(suffix)
return env.subst(suffix)
def src_suffixes(self, env):
- return map(lambda x, s=self, e=env: e.subst(s.adjust_suffix(x)),
- self.src_suffix)
+ "__cacheable__"
+ return map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
def set_src_suffix(self, src_suffix):
if not src_suffix:
src_suffix = []
elif not SCons.Util.is_List(src_suffix):
src_suffix = [ src_suffix ]
- self.src_suffix = src_suffix
+ adjust = lambda suf, s=self: \
+ callable(suf) and suf or s.adjust_suffix(suf)
+ self.src_suffix = map(adjust, src_suffix)
def get_src_suffix(self, env):
"""Get the first src_suffix in the list of src_suffixes."""
@@ -650,13 +662,21 @@ class BuilderBase:
"""
self.emitter[suffix] = emitter
+if SCons.Memoize.use_old_memoization():
+ _Base = BuilderBase
+ class BuilderBase(SCons.Memoize.Memoizer, _Base):
+ "Cache-backed version of BuilderBase"
+ def __init__(self, *args, **kw):
+ apply(_Base.__init__, (self,)+args, kw)
+ SCons.Memoize.Memoizer.__init__(self)
+
class ListBuilder(SCons.Util.Proxy):
"""A Proxy to support building an array of targets (for example,
foo.o and foo.h from foo.y) from a single Action execution.
"""
def __init__(self, builder, env, tlist):
- if __debug__: logInstanceCreation(self)
+ if __debug__: logInstanceCreation(self, 'Builder.ListBuilder')
SCons.Util.Proxy.__init__(self, builder)
self.builder = builder
self.target_scanner = builder.target_scanner
@@ -692,13 +712,13 @@ class MultiStepBuilder(BuilderBase):
prefix = '',
suffix = '',
src_suffix = '',
- target_factory = SCons.Node.FS.default_fs.File,
- source_factory = SCons.Node.FS.default_fs.File,
+ target_factory = None,
+ source_factory = None,
target_scanner = None,
source_scanner = None,
emitter=None,
single_source=0):
- if __debug__: logInstanceCreation(self)
+ if __debug__: logInstanceCreation(self, 'Builder.MultiStepBuilder')
BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
target_factory, source_factory,
target_scanner, source_scanner, emitter,
@@ -706,50 +726,60 @@ class MultiStepBuilder(BuilderBase):
if not SCons.Util.is_List(src_builder):
src_builder = [ src_builder ]
self.src_builder = src_builder
- self.sdict = {}
- self.cached_src_suffixes = {} # source suffixes keyed on id(env)
-
- def _execute(self, env, target = None, source = _null, overwarn={}, executor_kw={}):
- if source is _null:
- source = target
- target = None
- slist = env.arg2nodes(source, self.source_factory)
+ def _get_sdict(self, env):
+ "__cacheable__"
+ sdict = {}
+ for bld in self.src_builder:
+ if SCons.Util.is_String(bld):
+ try:
+ bld = env['BUILDERS'][bld]
+ except KeyError:
+ continue
+ for suf in bld.src_suffixes(env):
+ sdict[suf] = bld
+ return sdict
+
+ def _execute(self, env, target, source, overwarn={}, executor_kw={}):
+ # We now assume that target and source are lists or None.
+ source_factory = env.get_factory(self.source_factory)
+ slist = env.arg2nodes(source, source_factory)
final_sources = []
- try:
- sdict = self.sdict[id(env)]
- except KeyError:
- sdict = {}
- self.sdict[id(env)] = sdict
- for bld in self.src_builder:
- if SCons.Util.is_String(bld):
- try:
- bld = env['BUILDERS'][bld]
- except KeyError:
- continue
- for suf in bld.src_suffixes(env):
- sdict[suf] = bld
+ sdict = self._get_sdict(env)
src_suffixes = self.src_suffixes(env)
+ lengths_dict = {}
+ for l in map(len, src_suffixes):
+ lengths_dict[l] = None
+ lengths = lengths_dict.keys()
+
+ def match_src_suffix(node, src_suffixes=src_suffixes, lengths=lengths):
+ node_suffixes = map(lambda l, n=node: n.name[-l:], lengths)
+ for suf in src_suffixes:
+ if suf in node_suffixes:
+ return suf
+ return None
+
for snode in slist:
- for srcsuf in src_suffixes:
- if str(snode)[-len(srcsuf):] == srcsuf and sdict.has_key(srcsuf):
- tgt = sdict[srcsuf]._execute(env, None, snode, overwarn)
- # If the subsidiary Builder returned more than one target,
- # then filter out any sources that this Builder isn't
- # capable of building.
- if len(tgt) > 1:
- tgt = filter(lambda x, self=self, suf=src_suffixes, e=env:
- self.splitext(SCons.Util.to_String(x),e)[1] in suf,
- tgt)
- final_sources.extend(tgt)
- snode = None
- break
- if snode:
+ match_suffix = match_src_suffix(snode)
+ if match_suffix:
+ try:
+ bld = sdict[match_suffix]
+ except KeyError:
+ final_sources.append(snode)
+ else:
+ tlist = bld._execute(env, None, [snode], overwarn)
+ # If the subsidiary Builder returned more than one
+ # target, then filter out any sources that this
+ # Builder isn't capable of building.
+ if len(tlist) > 1:
+ tlist = filter(match_src_suffix, tlist)
+ final_sources.extend(tlist)
+ else:
final_sources.append(snode)
-
+
return BuilderBase._execute(self, env, target, final_sources, overwarn)
def get_src_builders(self, env):
@@ -772,15 +802,12 @@ class MultiStepBuilder(BuilderBase):
def src_suffixes(self, env):
"""Return a list of the src_suffix attributes for all
src_builders of this Builder.
+ __cacheable__
"""
- try:
- return self.cached_src_suffixes[id(env)]
- except KeyError:
- suffixes = BuilderBase.src_suffixes(self, env)
- for builder in self.get_src_builders(env):
- suffixes.extend(builder.src_suffixes(env))
- self.cached_src_suffixes[id(env)] = suffixes
- return suffixes
+ suffixes = BuilderBase.src_suffixes(self, env)
+ for builder in self.get_src_builders(env):
+ suffixes.extend(builder.src_suffixes(env))
+ return suffixes
class CompositeBuilder(SCons.Util.Proxy):
"""A Builder Proxy whose main purpose is to always have
@@ -789,7 +816,7 @@ class CompositeBuilder(SCons.Util.Proxy):
"""
def __init__(self, builder, cmdgen):
- if __debug__: logInstanceCreation(self)
+ if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
SCons.Util.Proxy.__init__(self, builder)
# cmdgen should always be an instance of DictCmdGenerator.
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 2075bf0..233336c 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -34,6 +34,7 @@ import os.path
import sys
import types
import unittest
+import UserList
import TestCmd
@@ -54,6 +55,9 @@ test = TestCmd.TestCmd(workdir = '')
outfile = test.workpath('outfile')
outfile2 = test.workpath('outfile2')
+infile = test.workpath('infile')
+test.write(infile, "infile\n")
+
show_string = None
scons_env = SCons.Environment.Environment()
@@ -71,6 +75,7 @@ class Environment:
global env_arg2nodes_called
env_arg2nodes_called = None
self.scanner = None
+ self.fs = SCons.Node.FS.FS()
def subst(self, s):
if not SCons.Util.is_String(s):
return s
@@ -82,6 +87,14 @@ class Environment:
except IndexError:
pass
return self.d.get(s, s)
+ def subst_target_source(self, string, raw=0, target=None,
+ source=None, dict=None, conv=None):
+ return SCons.Util.scons_subst(string, self, raw, target,
+ source, dict, conv)
+ def subst_list(self, string, raw=0, target=None,
+ source=None, dict=None, conv=None):
+ return SCons.Util.scons_subst_list(string, self, raw, target,
+ source, dict, conv)
def arg2nodes(self, args, factory):
global env_arg2nodes_called
env_arg2nodes_called = 1
@@ -93,6 +106,8 @@ class Environment:
a = factory(a)
list.append(a)
return list
+ def get_factory(self, factory):
+ return factory or self.fs.File
def get_scanner(self, ext):
return self.scanner
def Dictionary(self):
@@ -112,6 +127,7 @@ class Environment:
def Override(self, overrides):
env = apply(Environment, (), self.d)
env.d.update(overrides)
+ env.scanner = self.scanner
return env
def _update(self, dict):
self.d.update(dict)
@@ -125,23 +141,32 @@ class Environment:
d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
d['SOURCE'] = d['SOURCES'][0]
return d
+ def __cmp__(self, other):
+ return cmp(self.scanner, other.scanner) or cmp(self.d, other.d)
+
+class MyAction:
+ def __init__(self, action):
+ self.action = action
+ def get_executor(self, env, overrides, tlist, slist, executor_kw):
+ return ['executor'] + [self.action]
class MyNode_without_target_from_source:
def __init__(self, name):
self.name = name
self.sources = []
self.builder = None
+ self.is_explicit = None
self.side_effect = 0
- self.source_scanner = None
- self.backup_source_scanner = None
def __str__(self):
return self.name
def builder_set(self, builder):
self.builder = builder
def has_builder(self):
return not self.builder is None
+ def set_explicit(self, is_explicit):
+ self.is_explicit = is_explicit
def has_explicit_builder(self):
- return not self.builder is None and self.builder.is_explicit
+ return self.is_explicit
def env_set(self, env, safe=0):
self.env = env
def add_source(self, source):
@@ -206,8 +231,11 @@ class BuilderTestCase(unittest.TestCase):
target_factory=MyNode,
source_factory=MyNode)
- n1 = MyNode("n1");
- n2 = MyNode("n2");
+ tgt = builder(env, source=[])
+ assert tgt == [], tgt
+
+ n1 = MyNode("n1")
+ n2 = MyNode("n2")
builder(env, target = n1, source = n2)
assert env_arg2nodes_called
assert n1.env == env, n1.env
@@ -216,15 +244,48 @@ class BuilderTestCase(unittest.TestCase):
assert n1.executor, "no executor found"
assert not hasattr(n2, 'env')
- target = builder(env, target = 'n3', source = 'n4')[0]
+ l = [1]
+ ul = UserList.UserList([2])
+ try:
+ l.extend(ul)
+ except TypeError:
+ def mystr(l):
+ return str(map(str, l))
+ else:
+ mystr = str
+
+ nnn1 = MyNode("nnn1")
+ nnn2 = MyNode("nnn2")
+ tlist = builder(env, target = [nnn1, nnn2], source = [])
+ s = mystr(tlist)
+ assert s == "['nnn1', 'nnn2']", s
+ l = map(str, tlist)
+ assert l == ['nnn1', 'nnn2'], l
+
+ tlist = builder(env, target = 'n3', source = 'n4')
+ s = mystr(tlist)
+ assert s == "['n3']", s
+ target = tlist[0]
+ l = map(str, tlist)
+ assert l == ['n3'], l
assert target.name == 'n3'
assert target.sources[0].name == 'n4'
- target = builder(env, target = 'n4 n5', source = ['n6 n7'])[0]
+ tlist = builder(env, target = 'n4 n5', source = ['n6 n7'])
+ s = mystr(tlist)
+ assert s == "['n4 n5']", s
+ l = map(str, tlist)
+ assert l == ['n4 n5'], l
+ target = tlist[0]
assert target.name == 'n4 n5'
assert target.sources[0].name == 'n6 n7'
- target = builder(env, target = ['n8 n9'], source = 'n10 n11')[0]
+ tlist = builder(env, target = ['n8 n9'], source = 'n10 n11')
+ s = mystr(tlist)
+ assert s == "['n8 n9']", s
+ l = map(str, tlist)
+ assert l == ['n8 n9'], l
+ target = tlist[0]
assert target.name == 'n8 n9'
assert target.sources[0].name == 'n10 n11'
@@ -254,7 +315,7 @@ class BuilderTestCase(unittest.TestCase):
n20 = MyNode_without_target_from_source('n20')
flag = 0
try:
- target = builder(env, source=n20)
+ target = builder(env, None, source=n20)
except SCons.Errors.UserError, e:
flag = 1
assert flag, "UserError should be thrown if a source node can't create a target."
@@ -264,7 +325,7 @@ class BuilderTestCase(unittest.TestCase):
source_factory=MyNode,
prefix='p-',
suffix='.s')
- target = builder(env, source='n21')[0]
+ target = builder(env, None, source='n21')[0]
assert target.name == 'p-n21.s', target
builder = SCons.Builder.Builder(misspelled_action="foo",
@@ -331,6 +392,29 @@ class BuilderTestCase(unittest.TestCase):
builder = SCons.Builder.Builder(generator=generator)
assert builder.action.generator == generator
+ def test_get_name(self):
+ """Test the get_name() method
+ """
+
+ def test_get_single_executor(self):
+ """Test the get_single_executor() method
+ """
+ b = SCons.Builder.Builder(action='foo')
+ x = b.get_single_executor({}, [], [], {})
+ assert not x is None, x
+
+ def test_get_multi_executor(self):
+ """Test the get_multi_executor() method
+ """
+ b = SCons.Builder.Builder(action='foo', multi=1)
+ t1 = MyNode('t1')
+ s1 = MyNode('s1')
+ s2 = MyNode('s2')
+ x1 = b.get_multi_executor({}, [t1], [s1], {})
+ t1.executor = x1
+ x2 = b.get_multi_executor({}, [t1], [s2], {})
+ assert x1 is x2, "%s is not %s" % (repr(x1), repr(x2))
+
def test_cmp(self):
"""Test simple comparisons of Builder objects
"""
@@ -412,10 +496,10 @@ class BuilderTestCase(unittest.TestCase):
tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0]
assert tgt.path == 'libtgt2a tgt2b', \
"Target has unexpected name: %s" % tgt.path
- tgt = builder(env, source = 'src3')[0]
+ tgt = builder(env, target = None, source = 'src3')[0]
assert tgt.path == 'libsrc3', \
"Target has unexpected name: %s" % tgt.path
- tgt = builder(env, source = 'lib/src4')[0]
+ tgt = builder(env, target = None, source = 'lib/src4')[0]
assert tgt.path == os.path.join('lib', 'libsrc4'), \
"Target has unexpected name: %s" % tgt.path
tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0]
@@ -439,19 +523,35 @@ class BuilderTestCase(unittest.TestCase):
'$FOO' : 'foo-',
'.zzz' : my_emit},
action = '')
- tgt = builder(my_env, source = 'f1')[0]
+ tgt = builder(my_env, target = None, source = 'f1')[0]
assert tgt.path == 'default-f1', tgt.path
- tgt = builder(my_env, source = 'f2.c')[0]
+ tgt = builder(my_env, target = None, source = 'f2.c')[0]
assert tgt.path == 'default-f2', tgt.path
- tgt = builder(my_env, source = 'f3.in')[0]
+ tgt = builder(my_env, target = None, source = 'f3.in')[0]
assert tgt.path == 'out-f3', tgt.path
- tgt = builder(my_env, source = 'f4.x')[0]
+ tgt = builder(my_env, target = None, source = 'f4.x')[0]
assert tgt.path == 'y-f4', tgt.path
- tgt = builder(my_env, source = 'f5.foo')[0]
+ tgt = builder(my_env, target = None, source = 'f5.foo')[0]
assert tgt.path == 'foo-f5', tgt.path
- tgt = builder(my_env, source = 'f6.zzz')[0]
+ tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
assert tgt.path == 'emit-f6', tgt.path
+ def test_set_suffix(self):
+ """Test the set_suffix() method"""
+ b = SCons.Builder.Builder(action='')
+ env = Environment(XSUFFIX = '.x')
+
+ s = b.get_suffix(env)
+ assert s == '', s
+
+ b.set_suffix('.foo')
+ s = b.get_suffix(env)
+ assert s == '.foo', s
+
+ b.set_suffix('$XSUFFIX')
+ s = b.get_suffix(env)
+ assert s == '.x', s
+
def test_src_suffix(self):
"""Test Builder creation with a specified source file suffix
@@ -492,7 +592,7 @@ class BuilderTestCase(unittest.TestCase):
b6 = SCons.Builder.Builder(action = '',
src_suffix='_src.a',
suffix='.b')
- tgt = b6(env, source='foo_src.a')
+ tgt = b6(env, target=None, source='foo_src.a')
assert str(tgt[0]) == 'foo.b', str(tgt[0])
b7 = SCons.Builder.Builder(action = '',
@@ -501,16 +601,16 @@ class BuilderTestCase(unittest.TestCase):
b8 = SCons.Builder.Builder(action = '',
src_builder=b7,
suffix='.c')
- tgt = b8(env, source='foo_source.a')
+ tgt = b8(env, target=None, source='foo_source.a')
assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
- src = SCons.Node.FS.default_fs.File('foo_source.a')
- tgt = b8(env, source=src)
+ src = env.fs.File('foo_source.a')
+ tgt = b8(env, target=None, source=src)
assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
b9 = SCons.Builder.Builder(action={'_src.a' : 'srcaction'},
suffix='.c')
b9.add_action('_altsrc.b', 'altaction')
- tgt = b9(env, source='foo_altsrc.b')
+ tgt = b9(env, target=None, source='foo_altsrc.b')
assert str(tgt[0]) == 'foo.c', str(tgt[0])
def test_suffix(self):
@@ -530,7 +630,7 @@ class BuilderTestCase(unittest.TestCase):
tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0]
assert tgt.path == 'tgt4a tgt4b.o', \
"Target has unexpected name: %s" % tgt.path
- tgt = builder(env, source = 'src5')[0]
+ tgt = builder(env, target = None, source = 'src5')[0]
assert tgt.path == 'src5.o', \
"Target has unexpected name: %s" % tgt.path
@@ -551,17 +651,17 @@ class BuilderTestCase(unittest.TestCase):
'$BAR' : '.new',
'.zzz' : my_emit},
action='')
- tgt = builder(my_env, source = 'f1')[0]
+ tgt = builder(my_env, target = None, source = 'f1')[0]
assert tgt.path == 'f1.default', tgt.path
- tgt = builder(my_env, source = 'f2.c')[0]
+ tgt = builder(my_env, target = None, source = 'f2.c')[0]
assert tgt.path == 'f2.default', tgt.path
- tgt = builder(my_env, source = 'f3.in')[0]
+ tgt = builder(my_env, target = None, source = 'f3.in')[0]
assert tgt.path == 'f3.out', tgt.path
- tgt = builder(my_env, source = 'f4.x')[0]
+ tgt = builder(my_env, target = None, source = 'f4.x')[0]
assert tgt.path == 'f4.y', tgt.path
- tgt = builder(my_env, source = 'f5.bar')[0]
+ tgt = builder(my_env, target = None, source = 'f5.bar')[0]
assert tgt.path == 'f5.new', tgt.path
- tgt = builder(my_env, source = 'f6.zzz')[0]
+ tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
assert tgt.path == 'f6.emit', tgt.path
def test_single_source(self):
@@ -589,7 +689,7 @@ class BuilderTestCase(unittest.TestCase):
tgt.prepare()
tgt.build()
assert env['CNT'][0] == 2
- tgts = builder(env, infiles[2:4])
+ tgts = builder(env, None, infiles[2:4])
for t in tgts: t.prepare()
tgts[0].build()
tgts[1].build()
@@ -621,7 +721,11 @@ class BuilderTestCase(unittest.TestCase):
env = Environment()
builder = SCons.Builder.Builder(action = function2)
- tgts = builder(env, target = [outfile, outfile2], source = 'foo')
+
+ tgts = builder(env, source=[])
+ assert tgts == [], tgts
+
+ tgts = builder(env, target = [outfile, outfile2], source = infile)
for t in tgts:
t.prepare()
try:
@@ -645,7 +749,7 @@ class BuilderTestCase(unittest.TestCase):
return 1
builder = SCons.Builder.Builder(action = function3)
- tgts = builder(env, target = [sub1_out, sub2_out], source = 'foo')
+ tgts = builder(env, target = [sub1_out, sub2_out], source = infile)
for t in tgts:
t.prepare()
try:
@@ -665,23 +769,29 @@ class BuilderTestCase(unittest.TestCase):
builder1 = SCons.Builder.Builder(action='foo',
src_suffix='.bar',
suffix='.foo')
- builder2 = SCons.Builder.MultiStepBuilder(action='bar',
+ builder2 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
src_builder = builder1,
src_suffix = '.foo')
+ tgt = builder2(env, source=[])
+ assert tgt == [], tgt
+
tgt = builder2(env, target='baz',
source=['test.bar', 'test2.foo', 'test3.txt'])[0]
- assert str(tgt.sources[0]) == 'test.foo', str(tgt.sources[0])
- assert str(tgt.sources[0].sources[0]) == 'test.bar', \
- str(tgt.sources[0].sources[0])
- assert str(tgt.sources[1]) == 'test2.foo', str(tgt.sources[1])
- assert str(tgt.sources[2]) == 'test3.txt', str(tgt.sources[2])
-
- tgt = builder2(env, 'aaa.bar')[0]
- assert str(tgt) == 'aaa', str(tgt)
- assert str(tgt.sources[0]) == 'aaa.foo', str(tgt.sources[0])
- assert str(tgt.sources[0].sources[0]) == 'aaa.bar', \
- str(tgt.sources[0].sources[0])
+ s = str(tgt)
+ assert s == 'baz', s
+ s = map(str, tgt.sources)
+ assert s == ['test.foo', 'test2.foo', 'test3.txt'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['test.bar'], s
+
+ tgt = builder2(env, None, 'aaa.bar')[0]
+ s = str(tgt)
+ assert s == 'aaa', s
+ s = map(str, tgt.sources)
+ assert s == ['aaa.foo'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['aaa.bar'], s
builder3 = SCons.Builder.MultiStepBuilder(action = 'foo',
src_builder = 'xyzzy',
@@ -691,21 +801,23 @@ class BuilderTestCase(unittest.TestCase):
builder4 = SCons.Builder.Builder(action='bld4',
src_suffix='.i',
suffix='_wrap.c')
- builder5 = SCons.Builder.MultiStepBuilder(action='bld5',
+ builder5 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
src_builder=builder4,
suffix='.obj',
src_suffix='.c')
- builder6 = SCons.Builder.MultiStepBuilder(action='bld6',
+ builder6 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
src_builder=builder5,
suffix='.exe',
src_suffix='.obj')
tgt = builder6(env, 'test', 'test.i')[0]
- assert str(tgt) == 'test.exe', str(tgt)
- assert str(tgt.sources[0]) == 'test_wrap.obj', str(tgt.sources[0])
- assert str(tgt.sources[0].sources[0]) == 'test_wrap.c', \
- str(tgt.sources[0].sources[0])
- assert str(tgt.sources[0].sources[0].sources[0]) == 'test.i', \
- str(tgt.sources[0].sources[0].sources[0])
+ s = str(tgt)
+ assert s == 'test.exe', s
+ s = map(str, tgt.sources)
+ assert s == ['test_wrap.obj'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['test_wrap.c'], s
+ s = map(str, tgt.sources[0].sources[0].sources)
+ assert s == ['test.i'], s
def test_CompositeBuilder(self):
"""Testing CompositeBuilder class."""
@@ -717,15 +829,21 @@ class BuilderTestCase(unittest.TestCase):
'.bar' : func_action,
'$BAR_SUFFIX' : func_action,
'$FOO_SUFFIX' : func_action })
+
+ tgt = builder(env, source=[])
+ assert tgt == [], tgt
assert isinstance(builder, SCons.Builder.CompositeBuilder)
assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
+
tgt = builder(env, target='test1', source='test1.foo')[0]
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
assert tgt.builder.action is builder.action
+
tgt = builder(env, target='test2', source='test1.bar')[0]
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
assert tgt.builder.action is builder.action
+
flag = 0
tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
try:
@@ -852,8 +970,8 @@ class BuilderTestCase(unittest.TestCase):
source_scanner=sscan,
action='')
tgt = builder(env, target='foo2', source='bar')[0]
- assert tgt.target_scanner == tscan, tgt.target_scanner
- assert tgt.source_scanner == sscan, tgt.source_scanner
+ assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner == sscan, tgt.builder.source_scanner
builder1 = SCons.Builder.Builder(action='foo',
src_suffix='.bar',
@@ -863,8 +981,8 @@ class BuilderTestCase(unittest.TestCase):
target_scanner = tscan,
source_scanner = tscan)
tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')[0]
- assert tgt.target_scanner == tscan, tgt.target_scanner
- assert tgt.source_scanner == tscan, tgt.source_scanner
+ assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner == tscan, tgt.builder.source_scanner
def test_actual_scanner(self):
"""Test usage of actual Scanner objects."""
@@ -890,28 +1008,84 @@ class BuilderTestCase(unittest.TestCase):
return 'TestScannerkey'
def instance(self, env):
return self
+ def select(self, node):
+ return self
+ name = 'TestScanner'
+ def __str__(self):
+ return self.name
scanner = TestScanner()
builder = SCons.Builder.Builder(action='action')
# With no scanner specified, source_scanner and
# backup_source_scanner are None.
+ bar_y = MyNode('bar.y')
env1 = Environment()
tgt = builder(env1, target='foo1.x', source='bar.y')[0]
src = tgt.sources[0]
- assert tgt.target_scanner != scanner, tgt.target_scanner
- assert src.source_scanner is None, src.source_scanner
- assert src.backup_source_scanner is None, src.backup_source_scanner
-
- # Later use of the same source file with an environment that
- # has a scanner must still set the scanner.
- env2 = Environment()
- env2.scanner = scanner
- tgt = builder(env2, target='foo2.x', source='bar.y')[0]
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner is None, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y) is None, tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
+
+ # An Environment that has suffix-specified SCANNERS should
+ # provide a source scanner to the target.
+ class EnvTestScanner:
+ def key(self, env):
+ return '.y'
+ def instance(self, env):
+ return self
+ name = 'EnvTestScanner'
+ def __str__(self):
+ return self.name
+ def select(self, node):
+ return self
+ def path(self, env, dir=None):
+ return ()
+ def __call__(self, node, env, path):
+ return []
+ env3 = Environment(SCANNERS = [EnvTestScanner()])
+ env3.scanner = EnvTestScanner() # test env's version of SCANNERS
+ tgt = builder(env3, target='foo2.x', source='bar.y')[0]
src = tgt.sources[0]
- assert tgt.target_scanner != scanner, tgt.target_scanner
- assert src.source_scanner is None, src.source_scanner
- assert src.backup_source_scanner == scanner, src.backup_source_scanner
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert not tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
+
+ # Can't simply specify the scanner as a builder argument; it's
+ # global to all invocations of this builder.
+ tgt = builder(env3, target='foo3.x', source='bar.y', source_scanner = scanner)[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert not tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
+
+ # Now use a builder that actually has scanners and ensure that
+ # the target is set accordingly (using the specified scanner
+ # instead of the Environment's scanner)
+ builder = SCons.Builder.Builder(action='action',
+ source_scanner=scanner,
+ target_scanner=scanner)
+ tgt = builder(env3, target='foo4.x', source='bar.y')[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner == scanner, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.builder.source_scanner == scanner, tgt.builder.source_scanner
+ assert str(tgt.builder.source_scanner) == 'TestScanner', str(tgt.builder.source_scanner)
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert tgt.get_source_scanner(bar_y) == scanner, tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'TestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
+
+
def test_Builder_API(self):
"""Test Builder interface.
@@ -921,8 +1095,8 @@ class BuilderTestCase(unittest.TestCase):
forms of component specifications."""
builder = SCons.Builder.Builder()
-
env = Environment(BUILDERS={'Bld':builder})
+
r = builder.get_name(env)
assert r == 'Bld', r
r = builder.get_prefix(env)
@@ -937,23 +1111,34 @@ class BuilderTestCase(unittest.TestCase):
assert r == ['foo'], r
# src_suffix can be a single string or a list of strings
+ # src_suffixes() caches its return value, so we use a new
+ # Builder each time we do any of these tests
- builder.set_src_suffix('.foo')
- r = builder.get_src_suffix(env)
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix('.foo')
+ r = bld.get_src_suffix(env)
assert r == '.foo', r
- r = builder.src_suffixes(env)
+ r = bld.src_suffixes(env)
assert r == ['.foo'], r
- builder.set_src_suffix(['.foo', '.bar'])
- r = builder.get_src_suffix(env)
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix(['.foo', '.bar'])
+ r = bld.get_src_suffix(env)
assert r == '.foo', r
- r = builder.src_suffixes(env)
+ r = bld.src_suffixes(env)
assert r == ['.foo', '.bar'], r
- builder.set_src_suffix(['.bar', '.foo'])
- r = builder.get_src_suffix(env)
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix(['.bar', '.foo'])
+ r = bld.get_src_suffix(env)
assert r == '.bar', r
- r = builder.src_suffixes(env)
+ r = bld.src_suffixes(env)
assert r == ['.bar', '.foo'], r
# adjust_suffix normalizes the suffix, adding a `.' if needed
@@ -1026,8 +1211,8 @@ class BuilderTestCase(unittest.TestCase):
assert r == '.D', r
builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
-
env = Environment(BUILDERS={'Bld':builder})
+
r = builder.get_name(env)
assert r == 'Bld', r
r = builder.get_prefix(env)
@@ -1043,7 +1228,10 @@ class BuilderTestCase(unittest.TestCase):
# whose keys are the source suffix. The add_action()
# specifies a new source suffix/action binding.
+ builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+ env = Environment(BUILDERS={'Bld':builder})
builder.add_action('.src_sfx1', 'FOO')
+
r = builder.get_name(env)
assert r == 'Bld', r
r = builder.get_prefix(env)
@@ -1057,6 +1245,9 @@ class BuilderTestCase(unittest.TestCase):
r = builder.src_suffixes(env)
assert r == ['.src_sfx1'], r
+ builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+ env = Environment(BUILDERS={'Bld':builder})
+ builder.add_action('.src_sfx1', 'FOO')
builder.add_action('.src_sfx2', 'BAR')
r = builder.get_name(env)
@@ -1184,11 +1375,11 @@ class BuilderTestCase(unittest.TestCase):
'.4b':emit4b},
target_factory=MyNode,
source_factory=MyNode)
- tgt = builder4(env, source='aaa.4a')[0]
+ tgt = builder4(env, None, source='aaa.4a')[0]
assert str(tgt) == 'emit4a-aaa', str(tgt)
- tgt = builder4(env, source='bbb.4b')[0]
+ tgt = builder4(env, None, source='bbb.4b')[0]
assert str(tgt) == 'emit4b-bbb', str(tgt)
- tgt = builder4(env, source='ccc.4c')[0]
+ tgt = builder4(env, None, source='ccc.4c')[0]
assert str(tgt) == 'ccc', str(tgt)
def emit4c(target, source, env):
@@ -1196,7 +1387,7 @@ class BuilderTestCase(unittest.TestCase):
target = map(lambda x: 'emit4c-' + x[:-3], source)
return (target, source)
builder4.add_emitter('.4c', emit4c)
- tgt = builder4(env, source='ccc.4c')[0]
+ tgt = builder4(env, None, source='ccc.4c')[0]
assert str(tgt) == 'emit4c-ccc', str(tgt)
# Test a list of emitter functions.
@@ -1241,43 +1432,43 @@ class BuilderTestCase(unittest.TestCase):
env = Environment()
b = SCons.Builder.Builder(action='foo', suffix='.o')
- tgt = b(env, 'aaa')[0]
+ tgt = b(env, None, 'aaa')[0]
assert str(tgt) == 'aaa.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'aaa', map(str, tgt.sources)
- tgt = b(env, 'bbb.c')[0]
+ tgt = b(env, None, 'bbb.c')[0]
assert str(tgt) == 'bbb.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'bbb.c', map(str, tgt.sources)
- tgt = b(env, 'ccc.x.c')[0]
+ tgt = b(env, None, 'ccc.x.c')[0]
assert str(tgt) == 'ccc.x.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'ccc.x.c', map(str, tgt.sources)
- tgt = b(env, ['d0.c', 'd1.c'])[0]
+ tgt = b(env, None, ['d0.c', 'd1.c'])[0]
assert str(tgt) == 'd0.o', str(tgt)
assert len(tgt.sources) == 2, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'd0.c', map(str, tgt.sources)
assert str(tgt.sources[1]) == 'd1.c', map(str, tgt.sources)
- tgt = b(env, source='eee')[0]
+ tgt = b(env, target = None, source='eee')[0]
assert str(tgt) == 'eee.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'eee', map(str, tgt.sources)
- tgt = b(env, source='fff.c')[0]
+ tgt = b(env, target = None, source='fff.c')[0]
assert str(tgt) == 'fff.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'fff.c', map(str, tgt.sources)
- tgt = b(env, source='ggg.x.c')[0]
+ tgt = b(env, target = None, source='ggg.x.c')[0]
assert str(tgt) == 'ggg.x.o', str(tgt)
assert len(tgt.sources) == 1, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'ggg.x.c', map(str, tgt.sources)
- tgt = b(env, source=['h0.c', 'h1.c'])[0]
+ tgt = b(env, target = None, source=['h0.c', 'h1.c'])[0]
assert str(tgt) == 'h0.o', str(tgt)
assert len(tgt.sources) == 2, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'h0.c', map(str, tgt.sources)
@@ -1285,7 +1476,7 @@ class BuilderTestCase(unittest.TestCase):
w = b(env, target='i0.w', source=['i0.x'])[0]
y = b(env, target='i1.y', source=['i1.z'])[0]
- tgt = b(env, source=[w, y])[0]
+ tgt = b(env, None, source=[w, y])[0]
assert str(tgt) == 'i0.o', str(tgt)
assert len(tgt.sources) == 2, map(str, tgt.sources)
assert str(tgt.sources[0]) == 'i0.w', map(str, tgt.sources)
@@ -1316,18 +1507,31 @@ class BuilderTestCase(unittest.TestCase):
'B2': b2,
'B3': b3,
'B4': b4})
+ # With no name, get_name will return the class. Allow
+ # for caching...
+ b6_names = [
+ 'SCons.Builder.BuilderBase',
+ "<class 'SCons.Builder.BuilderBase'>",
+ 'SCons.Memoize.BuilderBase',
+ "<class 'SCons.Memoize.BuilderBase'>",
+ ]
+
assert b1.get_name(env) == 'bldr1', b1.get_name(env)
assert b2.get_name(env) == 'bldr2', b2.get_name(env)
assert b3.get_name(env) == 'bldr3', b3.get_name(env)
assert b4.get_name(env) == 'bldr4', b4.get_name(env)
assert b5.get_name(env) == 'builder5', b5.get_name(env)
- assert b6.get_name(env) == 'SCons.Builder.BuilderBase', b6.get_name(env)
+ assert b6.get_name(env) in b6_names, b6.get_name(env)
+
assert b1.get_name(env2) == 'B1', b1.get_name(env2)
assert b2.get_name(env2) == 'B2', b2.get_name(env2)
assert b3.get_name(env2) == 'B3', b3.get_name(env2)
assert b4.get_name(env2) == 'B4', b4.get_name(env2)
assert b5.get_name(env2) == 'builder5', b5.get_name(env2)
- assert b6.get_name(env2) == 'SCons.Builder.BuilderBase', b6.get_name(env2)
+ assert b6.get_name(env2) in b6_names, b6.get_name(env2)
+
+ assert b5.get_name(None) == 'builder5', b5.get_name(None)
+ assert b6.get_name(None) in b6_names, b6.get_name(None)
for B in b3.get_src_builders(env):
assert B.get_name(env) == 'bldr1'
@@ -1336,7 +1540,8 @@ class BuilderTestCase(unittest.TestCase):
tgts = b1(env, target = [outfile, outfile2], source='moo')
for t in tgts:
- assert t.builder.get_name(env) == 'ListBuilder(bldr1)'
+ name = t.builder.get_name(env)
+ assert name == 'ListBuilder(bldr1)', name
# The following are not symbolically correct, because the
# ListBuilder was only created on behalf of env, so it
# would probably be OK if better correctness
diff --git a/src/engine/SCons/Defaults.xml b/src/engine/SCons/Defaults.xml
index 1720aaa..84dfe58 100644
--- a/src/engine/SCons/Defaults.xml
+++ b/src/engine/SCons/Defaults.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<builder name="DVI">
<summary>
Builds a <filename>.dvi</filename> file
@@ -111,12 +116,24 @@ file.
</summary>
</cvar>
+<cvar name="_CPPDEFFLAGS">
+<summary>
+An automatically-generated construction variable
+containing the C preprocessor command-line options
+to define values.
+The value of &cv-_CPPDEFFLAGS; is created
+by appending &cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX;
+to the beginning and end
+of each directory in &cv-CPPDEFINES;.
+</summary>
+</cvar>
+
<cvar name="CPPDEFINES">
<summary>
A platform independent specification of C preprocessor definitions.
The definitions will be added to command lines
through the automatically-generated
-&cv-_CPPDEFFLAGS; construction variable (see below),
+&cv-_CPPDEFFLAGS; construction variable (see above),
which is constructed according to
the type of value of &cv-CPPDEFINES;:
@@ -163,7 +180,8 @@ then the name is defined without an explicit value.
Note that the resulting flags are sorted by keyword
to ensure that the order of the options on the
command line is consistent each time
-&scons; is run.
+&scons;
+is run.
<example>
# Will add -DA -DB=2 to POSIX compiler command lines,
@@ -173,18 +191,6 @@ env = Environment(CPPDEFINES={'B':2, 'A':None})
</summary>
</cvar>
-<cvar name="_CPPDEFFLAGS">
-<summary>
-An automatically-generated construction variable
-containing the C preprocessor command-line options
-to define values.
-The value of &cv-_CPPDEFFLAGS; is created
-by appending &cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX;
-to the beginning and end
-of each directory in &cv-CPPDEFINES;.
-</summary>
-</cvar>
-
<cvar name="CPPDEFPREFIX">
<summary>
The prefix used to specify preprocessor definitions
@@ -225,7 +231,7 @@ directories for include files. Don't explicitly put include directory
arguments in CCFLAGS or CXXFLAGS because the result will be non-portable
and the directories will not be searched by the dependency scanner. Note:
directory names in CPPPATH will be looked-up relative to the SConscript
-directory when they are used in a command. To force
+directory when they are used in a command. To force
&scons;
to look-up a directory relative to the root of the source tree use #:
@@ -233,7 +239,7 @@ to look-up a directory relative to the root of the source tree use #:
env = Environment(CPPPATH='#/include')
</example>
-The directory look-up can also be forced using the
+The directory look-up can also be forced using the
&Dir;()
function:
@@ -265,7 +271,7 @@ env = Environment(CCCOM="my_compiler $_CPPINCFLAGS -c -o $TARGET $SOURCE")
<cvar name="Dir">
<summary>
A function that converts a string
-into a Dir instance relative to the target being built.
+into a Dir instance relative to the target being built.
</summary>
</cvar>
@@ -291,7 +297,7 @@ The default list is:
<cvar name="File">
<summary>
A function that converts a string into a File instance relative to the
-target being built.
+target being built.
</summary>
</cvar>
@@ -354,6 +360,17 @@ in force for this file installation.
</summary>
</cvar>
+<cvar name="INSTALLSTR">
+<summary>
+The string displayed when a file is
+installed into a destination file name.
+The default is:
+<example>
+Install file: "$SOURCE" as "$TARGET"
+</example>
+</summary>
+</cvar>
+
<cvar name="LATEXSUFFIXES">
<summary>
The list of suffixes of files that will be scanned
@@ -436,7 +453,7 @@ arguments in &cv-LINKFLAGS; or &cv-SHLINKFLAGS;
because the result will be non-portable
and the directories will not be searched by the dependency scanner. Note:
directory names in LIBPATH will be looked-up relative to the SConscript
-directory when they are used in a command. To force
+directory when they are used in a command. To force
&scons;
to look-up a directory relative to the root of the source tree use #:
@@ -444,7 +461,7 @@ to look-up a directory relative to the root of the source tree use #:
env = Environment(LIBPATH='#/libs')
</example>
-The directory look-up can also be forced using the
+The directory look-up can also be forced using the
&Dir;()
function:
@@ -534,6 +551,6 @@ The suffix used for PDF file names.
<cvar name="RDirs">
<summary>
A function that converts a string into a list of Dir instances by
-searching the repositories.
+searching the repositories.
</summary>
</cvar>
diff --git a/src/engine/SCons/Environment.xml b/src/engine/SCons/Environment.xml
index d41cdaf..a861105 100644
--- a/src/engine/SCons/Environment.xml
+++ b/src/engine/SCons/Environment.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<cvar name="BUILDERS">
<summary>
A dictionary mapping the names of the builders
@@ -34,8 +39,8 @@ env['BUILDERS]['NewBuilder'] = foo
<cvar name="Dir">
<summary>
-A function that converts a file name into a Dir instance relative to the
-target being built.
+A function that converts a string
+into a Dir instance relative to the target being built.
</summary>
</cvar>
@@ -91,15 +96,8 @@ env = Environment(ENV = {'PATH' : os.environ['PATH']})
<cvar name="File">
<summary>
-A function that converts a file name into a File instance relative to the
-target being built.
-</summary>
-</cvar>
-
-<cvar name="RDirs">
-<summary>
-A function that converts a file name into a list of Dir instances by
-searching the repositories.
+A function that converts a string into a File instance relative to the
+target being built.
</summary>
</cvar>
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index b7d7af9..810ede7 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -5,9 +5,8 @@ File system nodes.
These Nodes represent the canonical external objects that people think
of when they think of building software: files and directories.
-This initializes a "default_fs" Node with an FS at the current directory
-for its own purposes, and for use by scripts or modules looking for the
-canonical default.
+This holds a "default_fs" variable that should be initialized with an FS
+that can be used by scripts or modules looking for the canonical default.
"""
@@ -50,9 +49,14 @@ from SCons.Debug import logInstanceCreation
import SCons.Errors
import SCons.Node
import SCons.Sig.MD5
+import SCons.Subst
import SCons.Util
import SCons.Warnings
+# The max_drift value: by default, use a cached signature value for
+# any file that's been untouched for more than two days.
+default_max_drift = 2*24*60*60
+
#
# We stringify these file system Nodes a lot. Turning a file system Node
# into a string is non-trivial, because the final string representation
@@ -153,7 +157,13 @@ def LinkFunc(target, source, env):
try:
func(src,dest)
break
- except OSError:
+ except (IOError, OSError):
+ # An OSError indicates something happened like a permissions
+ # problem or an attempt to symlink across file-system
+ # boundaries. An IOError indicates something like the file
+ # not existing. In either case, keeping trying additional
+ # functions in the list and only raise an error if the last
+ # one failed.
if func == Link_Funcs[-1]:
# exception of the last link method (copy) are fatal
raise
@@ -176,9 +186,8 @@ Unlink = SCons.Action.Action(UnlinkFunc, None)
def MkdirFunc(target, source, env):
t = target[0]
- p = t.abspath
- if not t.fs.exists(p):
- t.fs.mkdir(p)
+ if not t.exists():
+ t.fs.mkdir(t.abspath)
return 0
Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None)
@@ -238,10 +247,12 @@ def CachePushFunc(target, source, env):
fs.rename(tempfile, cachefile)
st = fs.stat(t.path)
fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
- except OSError:
- # It's possible someone else tried writing the file at the same
- # time we did. Print a warning but don't stop the build, since
- # it doesn't affect the correctness of the build.
+ except (IOError, OSError):
+ # It's possible someone else tried writing the file at the
+ # same time we did, or else that there was some problem like
+ # the CacheDir being on a separate file system that's full.
+ # In any case, inability to push a file to cache doesn't affect
+ # the correctness of the build, so just print a warning.
SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning,
"Unable to copy %s to cache. Cache file is %s"
% (str(target), cachefile))
@@ -281,44 +292,6 @@ def get_DefaultRCSBuilder():
name = "DefaultRCSBuilder")
return DefaultRCSBuilder
-#
-class ParentOfRoot:
- """
- An instance of this class is used as the parent of the root of a
- filesystem (POSIX) or drive (Win32). This isn't actually a node,
- but it looks enough like one so that we don't have to have
- special purpose code everywhere to deal with dir being None.
- This class is an instance of the Null object pattern.
- """
- def __init__(self):
- self.abspath = ''
- self.path = ''
- self.name=''
- self.duplicate=0
- self.srcdir=None
- self.build_dirs=[]
-
- def is_under(self, dir):
- return 0
-
- def up(self):
- return None
-
- def getRepositories(self):
- return []
-
- def get_dir(self):
- return None
-
- def src_builder(self):
- return _null
-
- def entry_abspath(self, name):
- return name
-
- def entry_path(self, name):
- return name
-
# Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem.
_is_cygwin = sys.platform == "cygwin"
if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin:
@@ -328,31 +301,96 @@ else:
def _my_normcase(x):
return string.upper(x)
+
+
+class DiskChecker:
+ def __init__(self, type, do, ignore):
+ self.type = type
+ self.do = do
+ self.ignore = ignore
+ self.set_do()
+ def set_do(self):
+ self.__call__ = self.do
+ def set_ignore(self):
+ self.__call__ = self.ignore
+ def set(self, list):
+ if self.type in list:
+ self.set_do()
+ else:
+ self.set_ignore()
+
+def do_diskcheck_match(node, predicate, errorfmt):
+ path = node.abspath
+ if predicate(path):
+ raise TypeError, errorfmt % path
+
+def ignore_diskcheck_match(node, predicate, errorfmt):
+ pass
+
+def do_diskcheck_rcs(node, name):
+ try:
+ rcs_dir = node.rcs_dir
+ except AttributeError:
+ rcs_dir = node.rcs_dir = node.Dir('RCS')
+ return rcs_dir.entry_exists_on_disk(name+',v')
+
+def ignore_diskcheck_rcs(node, name):
+ return None
+
+def do_diskcheck_sccs(node, name):
+ try:
+ sccs_dir = node.sccs_dir
+ except AttributeError:
+ sccs_dir = node.sccs_dir = node.Dir('SCCS')
+ return sccs_dir.entry_exists_on_disk('s.'+name)
+
+def ignore_diskcheck_sccs(node, name):
+ return None
+
+diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match)
+diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs)
+diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs)
+
+diskcheckers = [
+ diskcheck_match,
+ diskcheck_rcs,
+ diskcheck_sccs,
+]
+
+def set_diskcheck(list):
+ for dc in diskcheckers:
+ dc.set(list)
+
+def diskcheck_types():
+ return map(lambda dc: dc.type, diskcheckers)
+
+
+
class EntryProxy(SCons.Util.Proxy):
def __get_abspath(self):
entry = self.get()
- return SCons.Util.SpecialAttrWrapper(entry.get_abspath(),
+ return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(),
entry.name + "_abspath")
def __get_filebase(self):
name = self.get().name
- return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(name)[0],
+ return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0],
name + "_filebase")
def __get_suffix(self):
name = self.get().name
- return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(name)[1],
+ return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[1],
name + "_suffix")
def __get_file(self):
name = self.get().name
- return SCons.Util.SpecialAttrWrapper(name, name + "_file")
+ return SCons.Subst.SpecialAttrWrapper(name, name + "_file")
def __get_base_path(self):
"""Return the file's directory and file name, with the
suffix stripped."""
entry = self.get()
- return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0],
+ return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0],
entry.name + "_base")
def __get_posix_path(self):
@@ -363,7 +401,7 @@ class EntryProxy(SCons.Util.Proxy):
else:
entry = self.get()
r = string.replace(entry.get_path(), os.sep, '/')
- return SCons.Util.SpecialAttrWrapper(r, entry.name + "_posix")
+ return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix")
def __get_win32_path(self):
"""Return the path with \ as the path separator,
@@ -373,7 +411,7 @@ class EntryProxy(SCons.Util.Proxy):
else:
entry = self.get()
r = string.replace(entry.get_path(), os.sep, '\\')
- return SCons.Util.SpecialAttrWrapper(r, entry.name + "_win32")
+ return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_win32")
def __get_srcnode(self):
return EntryProxy(self.get().srcnode())
@@ -461,6 +499,11 @@ class Base(SCons.Node.Node):
self.path = name
else:
self.path = directory.entry_path(name)
+ if directory.tpath == '.':
+ self.tpath = name
+ else:
+ self.tpath = directory.entry_tpath(name)
+ self.path_elements = directory.path_elements + [self]
self.dir = directory
self.cwd = None # will hold the SConscript directory for target nodes
@@ -503,14 +546,46 @@ class Base(SCons.Node.Node):
rstr = __str__
+ def stat(self):
+ "__cacheable__"
+ try: return self.fs.stat(self.abspath)
+ except os.error: return None
+
def exists(self):
"__cacheable__"
- return self.fs.exists(self.abspath)
+ return not self.stat() is None
def rexists(self):
"__cacheable__"
return self.rfile().exists()
+ def getmtime(self):
+ st = self.stat()
+ if st: return st[stat.ST_MTIME]
+ else: return None
+
+ def getsize(self):
+ st = self.stat()
+ if st: return st[stat.ST_SIZE]
+ else: return None
+
+ def isdir(self):
+ st = self.stat()
+ return not st is None and stat.S_ISDIR(st[stat.ST_MODE])
+
+ def isfile(self):
+ st = self.stat()
+ return not st is None and stat.S_ISREG(st[stat.ST_MODE])
+
+ if hasattr(os, 'symlink'):
+ def islink(self):
+ try: st = self.fs.lstat(self.abspath)
+ except os.error: return 0
+ return stat.S_ISLNK(st[stat.ST_MODE])
+ else:
+ def islink(self):
+ return 0 # no symlinks
+
def is_under(self, dir):
if self is dir:
return 1
@@ -533,7 +608,7 @@ class Base(SCons.Node.Node):
klass=self.__class__)
return srcnode
name = dir.name + os.sep + name
- dir=dir.get_dir()
+ dir = dir.up()
return self
def get_path(self, dir=None):
@@ -541,18 +616,15 @@ class Base(SCons.Node.Node):
Node.FS.Base object that owns us."""
if not dir:
dir = self.fs.getcwd()
- path_elems = []
- d = self
- if d == dir:
- path_elems.append('.')
- else:
- while d != dir and not isinstance(d, ParentOfRoot):
- path_elems.append(d.name)
- d = d.dir
- path_elems.reverse()
- ret = string.join(path_elems, os.sep)
- return ret
-
+ if self == dir:
+ return '.'
+ path_elems = self.path_elements
+ try: i = path_elems.index(dir)
+ except ValueError: pass
+ else: path_elems = path_elems[i+1:]
+ path_elems = map(lambda n: n.name, path_elems)
+ return string.join(path_elems, os.sep)
+
def set_src_builder(self, builder):
"""Set the source code builder for this node."""
self.sbuilder = builder
@@ -599,8 +671,11 @@ class Entry(Base):
time comes, and then call the same-named method in the transformed
class."""
+ def diskcheck_match(self):
+ pass
+
def disambiguate(self):
- if self.fs.isdir(self.abspath):
+ if self.isdir():
self.__class__ = Dir
self._morph()
else:
@@ -631,18 +706,21 @@ class Entry(Base):
Since this should return the real contents from the file
system, we check to see into what sort of subclass we should
morph this Entry."""
- if self.fs.isfile(self.abspath):
+ if self.isfile():
self.__class__ = File
self._morph()
return self.get_contents()
- if self.fs.isdir(self.abspath):
+ if self.isdir():
self.__class__ = Dir
self._morph()
return self.get_contents()
- if self.fs.islink(self.abspath):
+ if self.islink():
return '' # avoid errors for dangling symlinks
raise AttributeError
+ def rel_path(self, other):
+ return self.disambiguate().rel_path(other)
+
def exists(self):
"""Return if the Entry exists. Check the file system to see
what we should turn into first. Assume a file if there's no
@@ -669,7 +747,8 @@ _classEntry = Entry
class LocalFS:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
# This class implements an abstraction layer for operations involving
# a local file system. Essentially, this wraps any function in
@@ -695,12 +774,16 @@ class LocalFS:
return os.path.exists(path)
def getmtime(self, path):
return os.path.getmtime(path)
+ def getsize(self, path):
+ return os.path.getsize(path)
def isdir(self, path):
return os.path.isdir(path)
def isfile(self, path):
return os.path.isfile(path)
def link(self, src, dst):
return os.link(src, dst)
+ def lstat(self, path):
+ return os.lstat(path)
def listdir(self, path):
return os.listdir(path)
def makedirs(self, path):
@@ -721,14 +804,11 @@ class LocalFS:
if hasattr(os, 'symlink'):
def islink(self, path):
return os.path.islink(path)
- def exists_or_islink(self, path):
- return os.path.exists(path) or os.path.islink(path)
else:
def islink(self, path):
return 0 # no symlinks
- exists_or_islink = exists
-if not SCons.Memoize.has_metaclass:
+if SCons.Memoize.use_old_memoization():
_FSBase = LocalFS
class LocalFS(SCons.Memoize.Memoizer, _FSBase):
def __init__(self, *args, **kw):
@@ -757,20 +837,22 @@ class FS(LocalFS):
The path argument must be a valid absolute path.
"""
if __debug__: logInstanceCreation(self, 'Node.FS')
- self.Top = None
- if path == None:
- self.pathTop = os.getcwd()
- else:
- self.pathTop = path
self.Root = {}
self.SConstruct_dir = None
self.CachePath = None
self.cache_force = None
self.cache_show = None
+ self.max_drift = default_max_drift
+
+ if path is None:
+ self.pathTop = os.getcwd()
+ else:
+ self.pathTop = path
- def set_toplevel_dir(self, path):
- 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
+ self.Top = self._doLookup(Dir, os.path.normpath(self.pathTop))
+ self.Top.path = '.'
+ self.Top.tpath = '.'
+ self._cwd = self.Top
def clear_cache(self):
"__cache_reset__"
@@ -778,15 +860,14 @@ class FS(LocalFS):
def set_SConstruct_dir(self, dir):
self.SConstruct_dir = dir
-
- def __setTopLevelDir(self):
- if not self.Top:
- self.Top = self._doLookup(Dir, os.path.normpath(self.pathTop))
- self.Top.path = '.'
- self._cwd = self.Top
-
+
+ def get_max_drift(self):
+ return self.max_drift
+
+ def set_max_drift(self, max_drift):
+ self.max_drift = max_drift
+
def getcwd(self):
- self.__setTopLevelDir()
return self._cwd
def __checkClass(self, node, klass):
@@ -809,9 +890,10 @@ class FS(LocalFS):
__cacheable__"""
if not name:
- # This is a stupid hack to compensate for the fact
- # that the POSIX and Win32 versions of os.path.normpath()
- # behave differently. In particular, in POSIX:
+ # This is a stupid hack to compensate for the fact that
+ # the POSIX and Win32 versions of os.path.normpath() behave
+ # differently in older versions of Python. In particular,
+ # in POSIX:
# os.path.normpath('./') == '.'
# in Win32
# os.path.normpath('./') == ''
@@ -820,75 +902,81 @@ class FS(LocalFS):
# This is a definite bug in the Python library, but we have
# to live with it.
name = '.'
- path_comp = string.split(name, os.sep)
- drive, path_first = os.path.splitdrive(path_comp[0])
- if not path_first:
- # Absolute path
+ path_orig = string.split(name, os.sep)
+ path_norm = string.split(_my_normcase(name), os.sep)
+
+ first_orig = path_orig.pop(0) # strip first element
+ first_norm = path_norm.pop(0) # strip first element
+
+ drive, path_first = os.path.splitdrive(first_orig)
+ if path_first:
+ path_orig = [ path_first, ] + path_orig
+ path_norm = [ _my_normcase(path_first), ] + path_norm
+ else:
drive = _my_normcase(drive)
+ # Absolute path
try:
directory = self.Root[drive]
except KeyError:
if not create:
raise SCons.Errors.UserError
- directory = RootDir(drive, ParentOfRoot(), self)
+ directory = RootDir(drive, self)
self.Root[drive] = directory
- path_comp = path_comp[1:]
- else:
- path_comp = [ path_first, ] + path_comp[1:]
- if not path_comp:
- path_comp = ['']
+ if not path_orig:
+ return directory
+
+ last_orig = path_orig.pop() # strip last element
+ last_norm = path_norm.pop() # strip last element
# Lookup the directory
- for path_name in path_comp[:-1]:
- path_norm = _my_normcase(path_name)
+ for orig, norm in map(None, path_orig, path_norm):
+ try:
+ entries = directory.entries
+ except AttributeError:
+ # We tried to look up the entry in either an Entry or
+ # a File. Give whatever it is a chance to do what's
+ # appropriate: morph into a Dir or raise an exception.
+ directory.must_be_a_Dir()
+ entries = directory.entries
try:
- d = directory.entries[path_norm]
+ directory = entries[norm]
except KeyError:
if not create:
raise SCons.Errors.UserError
- # look at the actual filesystem and make sure there isn't
- # a file already there
- path = directory.entry_path(path_name)
- if self.isfile(path):
- raise TypeError, \
- "File %s found where directory expected." % path
-
- dir_temp = Dir(path_name, directory, self)
- directory.entries[path_norm] = dir_temp
- directory.add_wkid(dir_temp)
- directory = dir_temp
- else:
- directory = d.must_be_a_Dir()
+ d = Dir(orig, directory, self)
+
+ # Check the file system (or not, as configured) to make
+ # sure there isn't already a file there.
+ d.diskcheck_match()
+
+ directory.entries[norm] = d
+ directory.add_wkid(d)
+ directory = d
+
+ directory.must_be_a_Dir()
- entry_norm = _my_normcase(path_comp[-1])
try:
- e = directory.entries[entry_norm]
+ e = directory.entries[last_norm]
except KeyError:
if not create:
raise SCons.Errors.UserError
- # make sure we don't create File nodes when there is actually
- # a directory at that path on the disk, and vice versa
- path = directory.entry_path(path_comp[-1])
- if fsclass == File:
- if self.isdir(path):
- raise TypeError, \
- "Directory %s found where file expected." % path
- elif fsclass == Dir:
- if self.isfile(path):
- raise TypeError, \
- "File %s found where directory expected." % path
-
- result = fsclass(path_comp[-1], directory, self)
- directory.entries[entry_norm] = result
+ result = fsclass(last_orig, directory, self)
+
+ # Check the file system (or not, as configured) to make
+ # sure there isn't already a directory at the path on
+ # disk where we just created a File node, and vice versa.
+ result.diskcheck_match()
+
+ directory.entries[last_norm] = result
directory.add_wkid(result)
else:
result = self.__checkClass(e, fsclass)
return result
- def __transformPath(self, name, directory):
+ def _transformPath(self, name, directory):
"""Take care of setting up the correct top-level directory,
usually in preparation for a call to doLookup().
@@ -898,7 +986,6 @@ class FS(LocalFS):
If directory is None, and name is a relative path,
then the same applies.
"""
- self.__setTopLevelDir()
if name and name[0] == '#':
directory = self.Top
name = name[1:]
@@ -916,7 +1003,6 @@ class FS(LocalFS):
If change_os_dir is true, we will also change the "real" cwd
to match.
"""
- self.__setTopLevelDir()
curr=self._cwd
try:
if not dir is None:
@@ -943,7 +1029,7 @@ class FS(LocalFS):
else:
if directory and not isinstance(directory, Dir):
directory = self.Dir(directory)
- name, directory = self.__transformPath(name, directory)
+ name, directory = self._transformPath(name, directory)
return self._doLookup(klass, name, directory, create)
def File(self, name, directory = None, create = 1):
@@ -976,13 +1062,10 @@ class FS(LocalFS):
"""Link the supplied build directory to the source directory
for purposes of building files."""
- self.__setTopLevelDir()
if not isinstance(src_dir, SCons.Node.Node):
src_dir = self.Dir(src_dir)
if not isinstance(build_dir, SCons.Node.Node):
build_dir = self.Dir(build_dir)
- if not src_dir.is_under(self.Top):
- raise SCons.Errors.UserError, "Source directory must be under top of build tree."
if src_dir.is_under(build_dir):
raise SCons.Errors.UserError, "Source directory cannot be under build directory."
if build_dir.srcdir:
@@ -996,118 +1079,23 @@ class FS(LocalFS):
for d in dirs:
if not isinstance(d, SCons.Node.Node):
d = self.Dir(d)
- self.__setTopLevelDir()
self.Top.addRepository(d)
- def do_Rsearch(self, path, dir, func, clazz=_classEntry):
- """Search for something in a Repository. Returns the first
- one found in the list, or None if there isn't one.
- __cacheable__
- """
- d, name = os.path.split(path)
- norm_name = _my_normcase(name)
- if d:
- dir = dir.Dir(d)
- try:
- node = dir.entries[norm_name]
- except KeyError:
- node = dir.node_on_disk(name, clazz)
- else:
- node = func(node)
- if node:
- dir = node.get_dir()
- if node:
- return node, dir
- fname = '.'
- while dir:
- for rep in dir.getRepositories():
- rdir = rep.Dir(fname)
- try:
- node = rdir.entries[norm_name]
- except KeyError:
- node = rdir.node_on_disk(name, clazz)
- else:
- node = func(node)
- if node:
- return node, dir
- fname = dir.name + os.sep + fname
- dir = dir.get_dir()
- return None, None
-
- def Rsearch(self, path, clazz=_classEntry, cwd=None):
- if isinstance(path, SCons.Node.Node):
- return path
- def func(node, clazz=clazz):
- if node.exists() and \
- (isinstance(node, clazz) or isinstance(node, Entry) \
- or not node.is_derived()):
- return node
- return None
- path, dir = self.__transformPath(path, cwd)
- return self.do_Rsearch(path, dir, func, clazz)[0]
-
- def Rsearchall(self, pathlist, must_exist=1, clazz=_classEntry, cwd=None):
- """Search for a list of somethings in the Repository list.
- __cacheable__
- """
- result = []
+ def Rfindalldirs(self, pathlist, cwd):
+ """__cacheable__"""
if SCons.Util.is_String(pathlist):
pathlist = string.split(pathlist, os.pathsep)
if not SCons.Util.is_List(pathlist):
pathlist = [pathlist]
-
- if must_exist:
- select = lambda x, clazz=clazz: isinstance(x, clazz) and x.exists()
- else:
- select = lambda x, clazz=clazz: isinstance(x, clazz)
-
+ result = []
for path in filter(None, pathlist):
if isinstance(path, SCons.Node.Node):
result.append(path)
continue
-
- path, dir = self.__transformPath(path, cwd)
- d, name = os.path.split(path)
- norm_name = _my_normcase(name)
- if d:
- dir = dir.Dir(d)
- try:
- node = dir.entries[norm_name]
- except KeyError:
- # If there's no Node on disk, we'll filter
- # out the returned None below.
- if must_exist:
- n = dir.node_on_disk(name, clazz)
- else:
- n = self._doLookup(clazz, name, dir)
- dir.srcdir_duplicate(name, clazz)
- result.append(n)
- else:
- if not must_exist or node.exists():
- result.append(node)
- fname = '.'
- while dir:
- for rep in dir.getRepositories():
- rdir = rep.Dir(fname)
- try:
- node = rdir.entries[norm_name]
- except KeyError:
- # If there's no Node on disk, we'll filter
- # out the returned None below.
- if must_exist:
- n = rdir.node_on_disk(name, clazz)
- else:
- n = self._doLookup(clazz, name, rdir)
- rdir.srcdir_duplicate(name, clazz)
- result.append(n)
- else:
- if (not must_exist or node.exists()) and \
- (isinstance(node, Dir) or not node.is_derived()):
- result.append(node)
- fname = dir.name + os.sep + fname
- dir = dir.get_dir()
-
- return filter(None, result)
+ path, dir = self._transformPath(path, cwd)
+ dir = dir.Dir(path)
+ result.extend(dir.get_all_rdirs())
+ return result
def CacheDir(self, path):
self.CachePath = path
@@ -1136,7 +1124,6 @@ class FS(LocalFS):
message = fmt % string.join(map(str, targets))
return targets, message
-
class Dir(Base):
"""A class for directories in a file system.
"""
@@ -1162,11 +1149,20 @@ class Dir(Base):
self.entries['.'] = self
self.entries['..'] = self.dir
self.cwd = self
- self.builder = get_MkdirBuilder()
self.searched = 0
self._sconsign = None
self.build_dirs = []
+ # Don't just reset the executor, replace its action list,
+ # because it might have some pre-or post-actions that need to
+ # be preserved.
+ self.builder = get_MkdirBuilder()
+ self.get_executor().set_action_list(self.builder.action)
+
+ def diskcheck_match(self):
+ diskcheck_match(self, self.fs.isfile,
+ "File %s found where directory expected.")
+
def disambiguate(self):
return self
@@ -1213,32 +1209,56 @@ class Dir(Base):
srcdir.build_dirs.append(self)
def getRepositories(self):
- """Returns a list of repositories for this directory."""
+ """Returns a list of repositories for this directory.
+ __cacheable__"""
if self.srcdir and not self.duplicate:
- try:
- return self._srcreps
- except AttributeError:
- self._srcreps = self.fs.Rsearchall(self.srcdir.path,
- clazz=Dir,
- must_exist=0,
- cwd=self.fs.Top) \
- + self.repositories
- return self._srcreps
+ return self.srcdir.get_all_rdirs() + self.repositories
return self.repositories
+ def get_all_rdirs(self):
+ """__cacheable__"""
+ result = [self]
+ fname = '.'
+ dir = self
+ while dir:
+ for rep in dir.getRepositories():
+ result.append(rep.Dir(fname))
+ fname = dir.name + os.sep + fname
+ dir = dir.up()
+ return result
+
def addRepository(self, dir):
- if not dir in self.repositories and dir != self:
+ if dir != self and not dir in self.repositories:
self.repositories.append(dir)
+ dir.tpath = '.'
self.__clearRepositoryCache()
def up(self):
return self.entries['..']
- def root(self):
- if not self.entries['..']:
- return self
+ def rel_path(self, other):
+ """Return a path to "other" relative to this directory.
+ __cacheable__"""
+ if isinstance(other, Dir):
+ name = []
else:
- return self.entries['..'].root()
+ try:
+ name = [other.name]
+ other = other.dir
+ except AttributeError:
+ return str(other)
+ if self is other:
+ return name and name[0] or '.'
+ i = 0
+ for x, y in map(None, self.path_elements, other.path_elements):
+ if not x is y:
+ break
+ i = i + 1
+ path_elems = ['..']*(len(self.path_elements)-i) \
+ + map(lambda n: n.name, other.path_elements[i:]) \
+ + name
+
+ return string.join(path_elems, os.sep)
def scan(self):
if not self.implicit is None:
@@ -1290,7 +1310,7 @@ class Dir(Base):
break
listDirs.append(parent)
p = parent.up()
- if isinstance(p, ParentOfRoot):
+ if p is None:
raise SCons.Errors.StopError, parent.path
parent = p
listDirs.reverse()
@@ -1354,12 +1374,15 @@ class Dir(Base):
def rdir(self):
"__cacheable__"
- rdir = self
if not self.exists():
- n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top)
- if n:
- rdir = n
- return rdir
+ norm_name = _my_normcase(self.name)
+ for dir in self.dir.get_all_rdirs():
+ try: node = dir.entries[norm_name]
+ except KeyError: node = dir.dir_on_disk(self.name)
+ if node and node.exists() and \
+ (isinstance(dir, Dir) or isinstance(dir, Entry)):
+ return node
+ return self
def sconsign(self):
"""Return the .sconsign file info for this directory,
@@ -1390,6 +1413,9 @@ class Dir(Base):
def entry_path(self, name):
return self.path + os.sep + name
+ def entry_tpath(self, name):
+ return self.tpath + os.sep + name
+
def must_be_a_Dir(self):
"""Called to make sure a Node is a Dir. Since we're already
one, this is a no-op for us."""
@@ -1397,15 +1423,19 @@ class Dir(Base):
def entry_exists_on_disk(self, name):
"""__cacheable__"""
- return self.fs.exists(self.entry_abspath(name))
-
- def rcs_on_disk(self, name):
- rcspath = 'RCS' + os.sep + name+',v'
- return self.entry_exists_on_disk(rcspath)
-
- def sccs_on_disk(self, name):
- sccspath = 'SCCS' + os.sep + 's.'+name
- return self.entry_exists_on_disk(sccspath)
+ try:
+ d = self.on_disk_entries
+ except AttributeError:
+ d = {}
+ try:
+ entries = os.listdir(self.abspath)
+ except OSError:
+ pass
+ else:
+ for entry in map(_my_normcase, entries):
+ d[entry] = 1
+ self.on_disk_entries = d
+ return d.has_key(_my_normcase(name))
def srcdir_list(self):
"""__cacheable__"""
@@ -1423,16 +1453,16 @@ class Dir(Base):
break
result.append(d)
dirname = dir.name + os.sep + dirname
- dir = dir.get_dir()
+ dir = dir.up()
return result
- def srcdir_duplicate(self, name, clazz):
+ def srcdir_duplicate(self, name):
for dir in self.srcdir_list():
if dir.entry_exists_on_disk(name):
- srcnode = self.fs._doLookup(clazz, name, dir)
+ srcnode = dir.File(name)
if self.duplicate:
- node = self.fs._doLookup(clazz, name, self)
+ node = self.File(name)
node.do_duplicate(srcnode)
return node
else:
@@ -1441,34 +1471,44 @@ class Dir(Base):
def srcdir_find_file(self, filename):
"""__cacheable__"""
- fs = self.fs
- do_Rsearch = fs.do_Rsearch
-
def func(node):
if (isinstance(node, File) or isinstance(node, Entry)) and \
(node.is_derived() or node.is_pseudo_derived() or node.exists()):
return node
return None
- node, d = do_Rsearch(filename, self, func, File)
- if node:
- return node, d
+ norm_name = _my_normcase(filename)
- for dir in self.srcdir_list():
- node, d = do_Rsearch(filename, dir, func, File)
+ for rdir in self.get_all_rdirs():
+ try: node = rdir.entries[norm_name]
+ except KeyError: node = rdir.file_on_disk(filename)
+ else: node = func(node)
if node:
- return File(filename, self, fs), d
+ return node, self
+
+ for srcdir in self.srcdir_list():
+ for rdir in srcdir.get_all_rdirs():
+ try: node = rdir.entries[norm_name]
+ except KeyError: node = rdir.file_on_disk(filename)
+ else: node = func(node)
+ if node:
+ return File(filename, self, self.fs), srcdir
+
return None, None
- def node_on_disk(self, name, clazz):
+ def dir_on_disk(self, name):
+ if self.entry_exists_on_disk(name):
+ try: return self.Dir(name)
+ except TypeError: pass
+ return None
+
+ def file_on_disk(self, name):
if self.entry_exists_on_disk(name) or \
- self.sccs_on_disk(name) or \
- self.rcs_on_disk(name):
- try:
- return self.fs._doLookup(clazz, name, self)
- except TypeError:
- pass
- return self.srcdir_duplicate(name, clazz)
+ diskcheck_rcs(self, name) or \
+ diskcheck_sccs(self, name):
+ try: return self.File(name)
+ except TypeError: pass
+ return self.srcdir_duplicate(name)
class RootDir(Dir):
"""A class for the root directory of a file system.
@@ -1478,30 +1518,108 @@ class RootDir(Dir):
add a separator when creating the path names of entries within
this directory.
"""
- def __init__(self, name, directory, fs):
+ def __init__(self, name, fs):
if __debug__: logInstanceCreation(self, 'Node.FS.RootDir')
- Base.__init__(self, name, directory, fs)
- self.path = self.path + os.sep
- self.abspath = self.abspath + os.sep
+ # We're going to be our own parent directory (".." entry and .dir
+ # attribute) so we have to set up some values so Base.__init__()
+ # won't gag won't it calls some of our methods.
+ self.abspath = ''
+ self.path = ''
+ self.tpath = ''
+ self.path_elements = []
+ self.duplicate = 0
+ Base.__init__(self, name, self, fs)
+
+ # Now set our paths to what we really want them to be: the
+ # initial drive letter (the name) plus the directory separator.
+ self.abspath = name + os.sep
+ self.path = name + os.sep
+ self.tpath = name + os.sep
self._morph()
+ def __str__(self):
+ return self.abspath
+
def entry_abspath(self, name):
return self.abspath + name
def entry_path(self, name):
return self.path + name
-class BuildInfo:
- bsig = None
- def __cmp__(self, other):
- try:
- return cmp(self.bsig, other.bsig)
- except AttributeError:
+ def entry_tpath(self, name):
+ return self.tpath + name
+
+ def is_under(self, dir):
+ if self is dir:
return 1
+ else:
+ return 0
+
+ def up(self):
+ return None
+
+ def get_dir(self):
+ return None
+
+ def src_builder(self):
+ return _null
+
+class NodeInfo(SCons.Node.NodeInfo):
+ def __cmp__(self, other):
+ try: return cmp(self.bsig, other.bsig)
+ except AttributeError: return 1
+ def update(self, node):
+ self.timestamp = node.get_timestamp()
+ self.size = node.getsize()
+
+class BuildInfo(SCons.Node.BuildInfo):
+ def __init__(self, node):
+ SCons.Node.BuildInfo.__init__(self, node)
+ self.node = node
+ def convert_to_sconsign(self):
+ """Convert this BuildInfo object for writing to a .sconsign file
+
+ We hung onto the node that we refer to so that we can translate
+ the lists of bsources, bdepends and bimplicit Nodes into strings
+ relative to the node, but we don't want to write out that Node
+ itself to the .sconsign file, so we delete the attribute in
+ preparation.
+ """
+ rel_path = self.node.rel_path
+ delattr(self, 'node')
+ for attr in ['bsources', 'bdepends', 'bimplicit']:
+ try:
+ val = getattr(self, attr)
+ except AttributeError:
+ pass
+ else:
+ setattr(self, attr, map(rel_path, val))
+ def convert_from_sconsign(self, dir, name):
+ """Convert a newly-read BuildInfo object for in-SCons use
+
+ An on-disk BuildInfo comes without a reference to the node
+ for which it's intended, so we have to convert the arguments
+ and add back a self.node attribute. The bsources, bdepends and
+ bimplicit lists all come from disk as paths relative to that node,
+ so convert them to actual Nodes for use by the rest of SCons.
+ """
+ self.node = dir.Entry(name)
+ Entry_func = self.node.dir.Entry
+ for attr in ['bsources', 'bdepends', 'bimplicit']:
+ try:
+ val = getattr(self, attr)
+ except AttributeError:
+ pass
+ else:
+ setattr(self, attr, map(Entry_func, val))
class File(Base):
"""A class for files in a file system.
"""
+ def diskcheck_match(self):
+ diskcheck_match(self, self.fs.isdir,
+ "Directory %s found where file expected.")
+
def __init__(self, name, directory, fs):
if __debug__: logInstanceCreation(self, 'Node.FS.File')
Base.__init__(self, name, directory, fs)
@@ -1517,6 +1635,11 @@ class File(Base):
the SConscript directory of this file."""
return self.fs.Dir(name, self.cwd)
+ def Dirs(self, pathlist):
+ """Create a list of directories relative to the SConscript
+ directory of this file."""
+ return map(lambda p, s=self: s.Dir(p), pathlist)
+
def File(self, name):
"""Create a file node named 'name' relative to
the SConscript directory of this file."""
@@ -1524,8 +1647,7 @@ class File(Base):
def RDirs(self, pathlist):
"""Search for a list of directories in the Repository list."""
- return self.fs.Rsearchall(pathlist, clazz=Dir, must_exist=0,
- cwd=self.cwd)
+ return self.fs.Rfindalldirs(pathlist, self.cwd)
def _morph(self):
"""Turn a file system node into a File object. __cache_reset__"""
@@ -1536,9 +1658,6 @@ class File(Base):
def disambiguate(self):
return self
- def root(self):
- return self.dir.root()
-
def scanner_key(self):
return self.get_suffix()
@@ -1549,7 +1668,7 @@ class File(Base):
def get_timestamp(self):
if self.rexists():
- return self.fs.getmtime(self.rfile().abspath)
+ return self.rfile().getmtime()
else:
return 0
@@ -1559,8 +1678,7 @@ class File(Base):
# in one build (SConstruct file) is a source in a different build.
# See test/chained-build.py for the use case.
entry = self.get_stored_info()
- for key, val in obj.__dict__.items():
- entry.__dict__[key] = val
+ entry.merge(obj)
self.dir.sconsign().set_entry(self.name, entry)
def get_stored_info(self):
@@ -1568,26 +1686,27 @@ class File(Base):
try:
stored = self.dir.sconsign().get_entry(self.name)
except (KeyError, OSError):
- return BuildInfo()
+ return self.new_binfo()
else:
- if isinstance(stored, BuildInfo):
- return stored
- # The stored build information isn't a BuildInfo object.
- # This probably means it's an old SConsignEntry from SCons
- # 0.95 or before. The relevant attribute names are the same,
- # though, so just copy the attributes over to an object of
- # the correct type.
- binfo = BuildInfo()
- for key, val in stored.__dict__.items():
- setattr(binfo, key, val)
- return binfo
+ if not hasattr(stored, 'ninfo'):
+ # Transition: The .sconsign file entry has no NodeInfo
+ # object, which means it's a slightly older BuildInfo.
+ # Copy over the relevant attributes.
+ ninfo = stored.ninfo = self.new_ninfo()
+ for attr in ninfo.__dict__.keys():
+ try:
+ setattr(ninfo, attr, getattr(stored, attr))
+ except AttributeError:
+ pass
+ return stored
def get_stored_implicit(self):
binfo = self.get_stored_info()
- try:
- return binfo.bimplicit
- except AttributeError:
- return None
+ try: return binfo.bimplicit
+ except AttributeError: return None
+
+ def rel_path(self, other):
+ return self.dir.rel_path(other)
def get_found_includes(self, env, scanner, path):
"""Return the included implicit dependencies in this file.
@@ -1649,13 +1768,13 @@ class File(Base):
# Push this file out to cache before the superclass Node.built()
# method has a chance to clear the build signature, which it
# will do if this file has a source scanner.
- if self.fs.CachePath and self.fs.exists(self.path):
+ if self.fs.CachePath and self.exists():
CachePush(self, [], None)
self.fs.clear_cache()
SCons.Node.Node.built(self)
def visited(self):
- if self.fs.CachePath and self.fs.cache_force and self.fs.exists(self.path):
+ if self.fs.CachePath and self.fs.cache_force and self.exists():
CachePush(self, None, None)
def has_src_builder(self):
@@ -1677,9 +1796,9 @@ class File(Base):
else:
scb = self.dir.src_builder()
if scb is _null:
- if self.dir.sccs_on_disk(self.name):
+ if diskcheck_sccs(self.dir, self.name):
scb = get_DefaultSCCSBuilder()
- elif self.dir.rcs_on_disk(self.name):
+ elif diskcheck_rcs(self.dir, self.name):
scb = get_DefaultRCSBuilder()
else:
scb = None
@@ -1720,7 +1839,7 @@ class File(Base):
def remove(self):
"""Remove this file."""
- if self.fs.exists_or_islink(self.path):
+ if self.exists() or self.islink():
self.fs.unlink(self.path)
return 1
return None
@@ -1747,28 +1866,39 @@ class File(Base):
"__cacheable__"
# Duplicate from source path if we are set up to do this.
if self.duplicate and not self.is_derived() and not self.linked:
- src=self.srcnode()
+ src = self.srcnode()
if src is self:
return Base.exists(self)
+ # At this point, src is meant to be copied in a build directory.
src = src.rfile()
- if src.abspath != self.abspath and src.exists():
- self.do_duplicate(src)
+ if src.abspath != self.abspath:
+ if src.exists():
+ self.do_duplicate(src)
+ # Can't return 1 here because the duplication might
+ # not actually occur if the -n option is being used.
+ else:
+ # The source file does not exist. Make sure no old
+ # copy remains in the build directory.
+ if Base.exists(self) or self.islink():
+ self.fs.unlink(self.path)
+ # Return None explicitly because the Base.exists() call
+ # above will have cached its value if the file existed.
+ return None
return Base.exists(self)
+ #
+ # SIGNATURE SUBSYSTEM
+ #
+
def new_binfo(self):
- return BuildInfo()
+ return BuildInfo(self)
- def del_cinfo(self):
- try:
- del self.binfo.csig
- except AttributeError:
- pass
- try:
- del self.binfo.timestamp
- except AttributeError:
- pass
+ def new_ninfo(self):
+ ninfo = NodeInfo()
+ ninfo.update(self)
+ return ninfo
- def calc_csig(self, calc=None):
+ def get_csig(self, calc=None):
"""
Generate a node's content signature, the digested signature
of its content.
@@ -1777,45 +1907,43 @@ class File(Base):
cache - alternate node to use for the signature cache
returns - the content signature
"""
- if calc is None:
- calc = self.calculator()
-
try:
- return self.binfo.csig
+ return self.binfo.ninfo.csig
except AttributeError:
pass
-
- if calc.max_drift >= 0:
- old = self.get_stored_info()
- else:
- old = BuildInfo()
- try:
- mtime = self.get_timestamp()
- except OSError:
- mtime = 0
- raise SCons.Errors.UserError, "no such %s" % self
+ if calc is None:
+ calc = self.calculator()
- try:
- if (old.timestamp and old.csig and old.timestamp == mtime):
- # use the signature stored in the .sconsign file
- csig = old.csig
- else:
- csig = calc.module.signature(self)
- except AttributeError:
- csig = calc.module.signature(self)
+ max_drift = self.fs.max_drift
+ mtime = self.get_timestamp()
+ use_stored = max_drift >= 0 and (time.time() - mtime) > max_drift
- if calc.max_drift >= 0 and (time.time() - mtime) > calc.max_drift:
+ csig = None
+ if use_stored:
+ old = self.get_stored_info().ninfo
try:
- binfo = self.binfo
+ if old.timestamp and old.csig and old.timestamp == mtime:
+ csig = old.csig
except AttributeError:
- binfo = self.binfo = self.new_binfo()
- binfo.csig = csig
- binfo.timestamp = mtime
+ pass
+ if csig is None:
+ csig = calc.module.signature(self)
+
+ binfo = self.get_binfo()
+ ninfo = binfo.ninfo
+ ninfo.csig = csig
+ ninfo.update(self)
+
+ if use_stored:
self.store_info(binfo)
return csig
+ #
+ #
+ #
+
def current(self, calc=None):
self.binfo = self.gen_binfo(calc)
return self._cur2()
@@ -1829,27 +1957,32 @@ class File(Base):
if r != self:
# ...but there is one in a Repository...
old = r.get_stored_info()
- if old == self.binfo:
+ new = self.get_binfo()
+ if new == old:
# ...and it's even up-to-date...
if self._local:
# ...and they'd like a local copy.
LocalCopy(self, r, None)
- self.store_info(self.binfo)
+ self.store_info(new)
return 1
return None
else:
old = self.get_stored_info()
- return (old == self.binfo)
+ new = self.get_binfo()
+ return (new == old)
def rfile(self):
"__cacheable__"
- rfile = self
if not self.exists():
- n = self.fs.Rsearch(self.path, clazz=File,
- cwd=self.fs.Top)
- if n:
- rfile = n
- return rfile
+ norm_name = _my_normcase(self.name)
+ for dir in self.dir.get_all_rdirs():
+ try: node = dir.entries[norm_name]
+ except KeyError: node = dir.file_on_disk(self.name)
+ if node and node.exists() and \
+ (isinstance(node, File) or isinstance(node, Entry) \
+ or not node.is_derived()):
+ return node
+ return self
def rstr(self):
return str(self.rfile())
@@ -1857,12 +1990,15 @@ class File(Base):
def cachepath(self):
if not self.fs.CachePath:
return None, None
- if self.binfo.bsig is None:
+ ninfo = self.get_binfo().ninfo
+ if not hasattr(ninfo, 'bsig'):
+ raise SCons.Errors.InternalError, "cachepath(%s) found no bsig" % self.path
+ elif ninfo.bsig is None:
raise SCons.Errors.InternalError, "cachepath(%s) found a bsig of None" % self.path
# Add the path to the cache signature, because multiple
# targets built by the same action will all have the same
# build signature, and we have to differentiate them somehow.
- cache_sig = SCons.Sig.MD5.collect([self.binfo.bsig, self.path])
+ cache_sig = SCons.Sig.MD5.collect([ninfo.bsig, self.path])
subdir = string.upper(cache_sig[0])
dir = os.path.join(self.fs.CachePath, subdir)
return dir, os.path.join(dir, cache_sig)
@@ -1875,7 +2011,7 @@ class File(Base):
File, this is a TypeError..."""
raise TypeError, "Tried to lookup File '%s' as a Dir." % self.path
-default_fs = FS()
+default_fs = None
def find_file(filename, paths, verbose=None):
"""
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index adef880..565384f 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -307,6 +307,7 @@ class BuildDirTestCase(unittest.TestCase):
try:
dir_made = []
d9.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
+ d9.reset_executor()
f9.exists()
expect = os.path.join('build', 'var2', 'new_dir')
assert dir_made[0].path == expect, dir_made[0].path
@@ -379,13 +380,10 @@ class BuildDirTestCase(unittest.TestCase):
assert (stat.S_IMODE(st[stat.ST_MODE]) & stat.S_IWRITE), \
stat.S_IMODE(st[stat.ST_MODE])
- exc_caught = 0
- try:
- fs = SCons.Node.FS.FS()
- fs.BuildDir('build', '/test/foo')
- except SCons.Errors.UserError:
- exc_caught = 1
- assert exc_caught, "Should have caught a UserError."
+ # This used to generate a UserError when we forbid the source
+ # directory from being outside the top-level SConstruct dir.
+ fs = SCons.Node.FS.FS()
+ fs.BuildDir('build', '/test/foo')
exc_caught = 0
try:
@@ -605,15 +603,170 @@ class BuildDirTestCase(unittest.TestCase):
self.failIf(errors)
-class FSTestCase(unittest.TestCase):
- def runTest(self):
+class BaseTestCase(_tempdirTestCase):
+ def test_stat(self):
+ """Test the Base.stat() method"""
+ test = self.test
+ test.write("e1", "e1\n")
+ fs = SCons.Node.FS.FS()
+
+ e1 = fs.Entry('e1')
+ s = e1.stat()
+ assert not s is None, s
+
+ e2 = fs.Entry('e2')
+ s = e2.stat()
+ assert s is None, s
+
+ def test_getmtime(self):
+ """Test the Base.getmtime() method"""
+ test = self.test
+ test.write("file", "file\n")
+ fs = SCons.Node.FS.FS()
+
+ file = fs.Entry('file')
+ assert file.getmtime()
+
+ file = fs.Entry('nonexistent')
+ mtime = file.getmtime()
+ assert mtime is None, mtime
+
+ def test_getsize(self):
+ """Test the Base.getsize() method"""
+ test = self.test
+ test.write("file", "file\n")
+ fs = SCons.Node.FS.FS()
+
+ file = fs.Entry('file')
+ size = file.getsize()
+ assert size == 5, size
+
+ file = fs.Entry('nonexistent')
+ size = file.getsize()
+ assert size is None, size
+
+ def test_isdir(self):
+ """Test the Base.isdir() method"""
+ test = self.test
+ test.subdir('dir')
+ test.write("file", "file\n")
+ fs = SCons.Node.FS.FS()
+
+ dir = fs.Entry('dir')
+ assert dir.isdir()
+
+ file = fs.Entry('file')
+ assert not file.isdir()
+
+ nonexistent = fs.Entry('nonexistent')
+ assert not nonexistent.isdir()
+
+ def test_isfile(self):
+ """Test the Base.isfile() method"""
+ test = self.test
+ test.subdir('dir')
+ test.write("file", "file\n")
+ fs = SCons.Node.FS.FS()
+
+ dir = fs.Entry('dir')
+ assert not dir.isfile()
+
+ file = fs.Entry('file')
+ assert file.isfile()
+
+ nonexistent = fs.Entry('nonexistent')
+ assert not nonexistent.isfile()
+
+ if hasattr(os, 'symlink'):
+ def test_islink(self):
+ """Test the Base.islink() method"""
+ test = self.test
+ test.subdir('dir')
+ test.write("file", "file\n")
+ test.symlink("symlink", "symlink")
+ fs = SCons.Node.FS.FS()
+
+ dir = fs.Entry('dir')
+ assert not dir.islink()
+
+ file = fs.Entry('file')
+ assert not file.islink()
+
+ symlink = fs.Entry('symlink')
+ assert symlink.islink()
+
+ nonexistent = fs.Entry('nonexistent')
+ assert not nonexistent.islink()
+
+class NodeInfoTestCase(_tempdirTestCase):
+ def test___init__(self):
+ """Test NodeInfo initialization"""
+ ni = SCons.Node.FS.NodeInfo()
+ assert not hasattr(ni, 'bsig')
+
+ def test___cmp__(self):
+ """Test comparing NodeInfo objects"""
+ ni1 = SCons.Node.FS.NodeInfo()
+ ni2 = SCons.Node.FS.NodeInfo()
+
+ msg = "cmp(%s, %s) returned %s, not %s"
+
+ c = cmp(ni1, ni2)
+ assert c == 1, msg % (ni1, ni2, c, 1)
+
+ ni1.bsig = 777
+ c = cmp(ni1, ni2)
+ assert c == 1, msg % (ni1.bsig, ni2, c, 1)
+
+ ni2.bsig = 666
+ c = cmp(ni1, ni2)
+ assert c == 1, msg % (ni1.bsig, ni2.bsig, c, 1)
+
+ ni2.bsig = 777
+ c = cmp(ni1, ni2)
+ assert c == 0, msg % (ni1.bsig, ni2.bsig, c, 0)
+
+ ni2.bsig = 888
+ c = cmp(ni1, ni2)
+ assert c == -1, msg % (ni1.bsig, ni2.bsig, c, -1)
+
+ def test_update(self):
+ """Test updating a NodeInfo with on-disk information"""
+ test = self.test
+ test.write('fff', "fff\n")
+ fff = self.fs.File('fff')
+
+ ni = SCons.Node.FS.NodeInfo()
+ assert not hasattr(ni, 'timestamp')
+ assert not hasattr(ni, 'size')
+
+ ni.update(fff)
+ assert ni.timestamp == os.path.getmtime('fff'), ni.timestamp
+ assert ni.size == os.path.getsize('fff'), ni.size
+
+class BuildInfoTestCase(_tempdirTestCase):
+ def test___init__(self):
+ """Test BuildInfo initialization"""
+ fff = self.fs.File('fff')
+ bi = SCons.Node.FS.BuildInfo(fff)
+ assert bi.node is fff, bi.node
+
+ def test_convert_to_sconsign(self):
+ """Test converting to .sconsign file format"""
+
+ def test_convert_from_sconsign(self):
+ """Test converting from .sconsign file format"""
+
+class FSTestCase(_tempdirTestCase):
+ def test_runTest(self):
"""Test FS (file system) Node operations
This test case handles all of the file system node
tests in one environment, so we don't have to set up a
complicated directory structure for each test individually.
"""
- test = TestCmd(workdir = '')
+ test = self.test
+
test.subdir('sub', ['sub', 'dir'])
wp = test.workpath('')
@@ -817,6 +970,7 @@ class FSTestCase(unittest.TestCase):
assert not built_it
d1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE
d1.builder_set(Builder(fs.File))
+ d1.reset_executor()
d1.env_set(Environment())
d1.build()
assert built_it
@@ -825,6 +979,7 @@ class FSTestCase(unittest.TestCase):
assert not built_it
f1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE
f1.builder_set(Builder(fs.File))
+ f1.reset_executor()
f1.env_set(Environment())
f1.build()
assert built_it
@@ -971,7 +1126,7 @@ class FSTestCase(unittest.TestCase):
# the reading of files in text mode. This tests that
# get_contents() returns the binary contents.
test.write("binary_file", "Foo\x1aBar")
- f1 = SCons.Node.FS.default_fs.File(test.workpath("binary_file"))
+ f1 = fs.File(test.workpath("binary_file"))
assert f1.get_contents() == "Foo\x1aBar", f1.get_contents()
def nonexistent(method, s):
@@ -1184,8 +1339,112 @@ class FSTestCase(unittest.TestCase):
t = z.target_from_source('pre-', '-suf', lambda x: x[:-1])
assert str(t) == 'pre-z-suf', str(t)
+ def test_same_name(self):
+ """Test that a local same-named file isn't found for # Dir lookup"""
+ test = self.test
+ fs = self.fs
+
+ test.subdir('subdir')
+ test.write(['subdir', 'build'], "subdir/build\n")
+
+ subdir = fs.Dir('subdir')
+ fs.chdir(subdir, change_os_dir=1)
+ path, dir = fs._transformPath('#build/file', subdir)
+ self.fs._doLookup(SCons.Node.FS.File, path, dir)
+
+ def test_above_root(self):
+ """Testing looking up a path above the root directory"""
+ test = self.test
+ fs = self.fs
+
+ d1 = fs.Dir('d1')
+ d2 = d1.Dir('d2')
+ dirs = string.split(os.path.normpath(d2.abspath), os.sep)
+ above_path = apply(os.path.join, ['..']*len(dirs) + ['above'])
+ above = d2.Dir(above_path)
+
+ def test_rel_path(self):
+ """Test the rel_path() method"""
+ test = self.test
+ fs = self.fs
+
+ d1 = fs.Dir('d1')
+ d1_f = d1.File('f')
+ d1_d2 = d1.Dir('d2')
+ d1_d2_f = d1_d2.File('f')
+
+ d3 = fs.Dir('d3')
+ d3_f = d3.File('f')
+ d3_d4 = d3.Dir('d4')
+ d3_d4_f = d3_d4.File('f')
+
+ cases = [
+ d1, d1, '.',
+ d1, d1_f, 'f',
+ d1, d1_d2, 'd2',
+ d1, d1_d2_f, 'd2/f',
+ d1, d3, '../d3',
+ d1, d3_f, '../d3/f',
+ d1, d3_d4, '../d3/d4',
+ d1, d3_d4_f, '../d3/d4/f',
+
+ d1_f, d1, '.',
+ d1_f, d1_f, 'f',
+ d1_f, d1_d2, 'd2',
+ d1_f, d1_d2_f, 'd2/f',
+ d1_f, d3, '../d3',
+ d1_f, d3_f, '../d3/f',
+ d1_f, d3_d4, '../d3/d4',
+ d1_f, d3_d4_f, '../d3/d4/f',
+
+ d1_d2, d1, '..',
+ d1_d2, d1_f, '../f',
+ d1_d2, d1_d2, '.',
+ d1_d2, d1_d2_f, 'f',
+ d1_d2, d3, '../../d3',
+ d1_d2, d3_f, '../../d3/f',
+ d1_d2, d3_d4, '../../d3/d4',
+ d1_d2, d3_d4_f, '../../d3/d4/f',
+
+ d1_d2_f, d1, '..',
+ d1_d2_f, d1_f, '../f',
+ d1_d2_f, d1_d2, '.',
+ d1_d2_f, d1_d2_f, 'f',
+ d1_d2_f, d3, '../../d3',
+ d1_d2_f, d3_f, '../../d3/f',
+ d1_d2_f, d3_d4, '../../d3/d4',
+ d1_d2_f, d3_d4_f, '../../d3/d4/f',
+ ]
+
+ d1.rel_path(d3)
+
+ failed = 0
+ while cases:
+ dir, other, expect = cases[:3]
+ expect = os.path.normpath(expect)
+ del cases[:3]
+ result = dir.rel_path(other)
+ if result != expect:
+ if failed == 0: print
+ fmt = " dir_path(%(dir)s, %(other)s) => '%(result)s' did not match '%(expect)s'"
+ print fmt % locals()
+ failed = failed + 1
+ assert failed == 0, "%d rel_path() cases failed" % failed
+
class DirTestCase(_tempdirTestCase):
+ def test__morph(self):
+ """Test handling of actions when morphing an Entry into a Dir"""
+ test = self.test
+ e = self.fs.Entry('eee')
+ x = e.get_executor()
+ x.add_pre_action('pre')
+ x.add_post_action('post')
+ e.must_be_a_Dir()
+ a = x.get_action_list()
+ assert a[0] == 'pre', a
+ assert a[2] == 'post', a
+
def test_entry_exists_on_disk(self):
"""Test the Dir.entry_exists_on_disk() method
"""
@@ -1196,11 +1455,15 @@ class DirTestCase(_tempdirTestCase):
test.subdir('d')
test.write(['d', 'exists'], "d/exists\n")
+ test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n")
d = self.fs.Dir('d')
assert d.entry_exists_on_disk('exists')
assert not d.entry_exists_on_disk('does_not_exist')
+ if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin":
+ assert d.entry_exists_on_disk('case-insensitive')
+
def test_srcdir_list(self):
"""Test the Dir.srcdir_list() method
"""
@@ -1266,11 +1529,11 @@ class DirTestCase(_tempdirTestCase):
src0 = self.fs.Dir('src0')
self.fs.BuildDir(bld0, src0, duplicate=0)
- n = bld0.srcdir_duplicate('does_not_exist', SCons.Node.FS.File)
+ n = bld0.srcdir_duplicate('does_not_exist')
assert n is None, n
assert not os.path.exists(test.workpath('bld0', 'does_not_exist'))
- n = bld0.srcdir_duplicate('exists', SCons.Node.FS.File)
+ n = bld0.srcdir_duplicate('exists')
assert str(n) == os.path.normpath('src0/exists'), str(n)
assert not os.path.exists(test.workpath('bld0', 'exists'))
@@ -1281,11 +1544,11 @@ class DirTestCase(_tempdirTestCase):
src1 = self.fs.Dir('src1')
self.fs.BuildDir(bld1, src1, duplicate=1)
- n = bld1.srcdir_duplicate('does_not_exist', SCons.Node.FS.File)
+ n = bld1.srcdir_duplicate('does_not_exist')
assert n is None, n
assert not os.path.exists(test.workpath('bld1', 'does_not_exist'))
- n = bld1.srcdir_duplicate('exists', SCons.Node.FS.File)
+ n = bld1.srcdir_duplicate('exists')
assert str(n) == os.path.normpath('bld1/exists'), str(n)
assert os.path.exists(test.workpath('bld1', 'exists'))
@@ -1321,7 +1584,7 @@ class DirTestCase(_tempdirTestCase):
exists_e.exists = return_true
def check(result, expect):
- result = map(str, result)
+ result = map(str, result)
expect = map(os.path.normpath, expect)
assert result == expect, result
@@ -1437,8 +1700,38 @@ class DirTestCase(_tempdirTestCase):
n = bld1.srcdir_find_file('on-disk-e2')
check(n, ['bld1/on-disk-e2', 'bld1'])
-class EntryTestCase(unittest.TestCase):
- def runTest(self):
+ def test_dir_on_disk(self):
+ """Test the Dir.dir_on_disk() method"""
+ self.test.subdir('sub', ['sub', 'exists'])
+ self.test.write(['sub', 'file'], "self/file\n")
+ sub = self.fs.Dir('sub')
+
+ r = sub.dir_on_disk('does_not_exist')
+ assert not r, r
+
+ r = sub.dir_on_disk('exists')
+ assert r, r
+
+ r = sub.dir_on_disk('file')
+ assert not r, r
+
+ def test_file_on_disk(self):
+ """Test the Dir.file_on_disk() method"""
+ self.test.subdir('sub', ['sub', 'dir'])
+ self.test.write(['sub', 'exists'], "self/exists\n")
+ sub = self.fs.Dir('sub')
+
+ r = sub.file_on_disk('does_not_exist')
+ assert not r, r
+
+ r = sub.file_on_disk('exists')
+ assert r, r
+
+ r = sub.file_on_disk('dir')
+ assert not r, r
+
+class EntryTestCase(_tempdirTestCase):
+ def test_runTest(self):
"""Test methods specific to the Entry sub-class.
"""
test = TestCmd(workdir='')
@@ -1527,6 +1820,57 @@ class EntryTestCase(unittest.TestCase):
# Doesn't exist, no sources, and no builder: no sig
assert sig is None, sig
+ def test_Entry_Entry_lookup(self):
+ """Test looking up an Entry within another Entry"""
+ self.fs.Entry('#topdir')
+ self.fs.Entry('#topdir/a/b/c')
+
+
+
+class FileTestCase(_tempdirTestCase):
+
+ def test_Dirs(self):
+ """Test the File.Dirs() method"""
+ fff = self.fs.File('subdir/fff')
+ # This simulates that the SConscript file that defined
+ # fff is in subdir/.
+ fff.cwd = self.fs.Dir('subdir')
+ d1 = self.fs.Dir('subdir/d1')
+ d2 = self.fs.Dir('subdir/d2')
+ dirs = fff.Dirs(['d1', 'd2'])
+ assert dirs == [d1, d2], map(str, dirs)
+
+ def test_exists(self):
+ """Test the File.exists() method"""
+ fs = self.fs
+ test = self.test
+
+ src_f1 = fs.File('src/f1')
+ assert not src_f1.exists(), "%s apparently exists?" % src_f1
+
+ test.subdir('src')
+ test.write(['src', 'f1'], "src/f1\n")
+
+ assert not src_f1.exists(), "%s did not cache previous exists() value" % src_f1
+ src_f1.clear()
+ assert src_f1.exists(), "%s apparently does not exist?" % src_f1
+
+ test.subdir('build')
+ fs.BuildDir('build', 'src')
+ build_f1 = fs.File('build/f1')
+
+ assert build_f1.exists(), "%s did not realize that %s exists" % (build_f1, src_f1)
+ assert os.path.exists(build_f1.abspath), "%s did not get duplicated on disk" % build_f1.abspath
+
+ test.unlink(['src', 'f1'])
+ src_f1.clear() # so the next exists() call will look on disk again
+
+ assert build_f1.exists(), "%s did not cache previous exists() value" % build_f1
+ build_f1.clear()
+ build_f1.linked = None
+ assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1)
+ assert not os.path.exists(build_f1.abspath), "%s did not get removed after %s was removed" % (build_f1, src_f1)
+
class RepositoryTestCase(_tempdirTestCase):
@@ -1566,6 +1910,28 @@ class RepositoryTestCase(_tempdirTestCase):
r = map(lambda x, np=os.path.normpath: np(str(x)), rep)
assert r == expect, r
+ def test_get_all_rdirs(self):
+ """Test the Dir.get_all_rdirs() method"""
+ self.fs.Repository('foo')
+ self.fs.Repository(os.path.join('foo', 'bar'))
+ self.fs.Repository('bar/foo')
+ self.fs.Repository('bar')
+
+ expect = [
+ '.',
+ self.rep1,
+ self.rep2,
+ self.rep3,
+ 'foo',
+ os.path.join('foo', 'bar'),
+ os.path.join('bar', 'foo'),
+ 'bar'
+ ]
+
+ rep = self.fs.Dir('#').get_all_rdirs()
+ r = map(lambda x, np=os.path.normpath: np(str(x)), rep)
+ assert r == expect, r
+
def test_rdir(self):
"""Test the Dir.rdir() method"""
return_true = lambda: 1
@@ -1660,95 +2026,42 @@ class RepositoryTestCase(_tempdirTestCase):
r = e2.rfile()
assert r is re2, r
- def test_Rsearches(self):
- """Test the Rsearch() methods"""
+ def test_Rfindalldirs(self):
+ """Test the Rfindalldirs() methods"""
fs = self.fs
test = self.test
- test.write([self.rep1, 'f2'], "")
- test.subdir([self.rep2, 'f3'])
- test.write([self.rep3, 'f3'], "")
-
- r = fs.Rsearch('f1')
- assert r is None, r
-
- r = fs.Rsearch('f2')
- assert r, r
-
- f3 = fs.File('f3')
- r = fs.Rsearch(f3)
- assert r is f3, r
-
- def test_Rsearchall(self):
- """Test the Rsearchall() methods"""
- fs = self.fs
- test = self.test
-
- list = fs.Rsearchall(fs.Dir('d1'))
- assert len(list) == 1, list
- assert list[0].path == 'd1', list[0].path
-
- list = fs.Rsearchall([fs.Dir('d1')])
- assert len(list) == 1, list
- assert list[0].path == 'd1', list[0].path
-
- list = fs.Rsearchall('d2')
- assert list == [], list
-
- list = fs.Rsearchall('#d2')
- assert list == [], list
-
- fs.File('d2').built() # Clear exists cache
- test.subdir(['work', 'd2'])
-
- list = fs.Rsearchall('d2')
- assert map(str, list) == ['d2'], list
-
- fs.File('../rep2/d2').built() # Clear exists cache
- test.subdir(['rep2', 'd2'])
-
- list = fs.Rsearchall('d2')
- assert map(str, list) == ['d2', test.workpath('rep2', 'd2')], list
-
- fs.File('../rep1/d2').built() # Clear exists cache
- test.subdir(['rep1', 'd2'])
-
- list = fs.Rsearchall('d2')
- assert map(str, list) == ['d2',
- test.workpath('rep1', 'd2'),
- test.workpath('rep2', 'd2')], list
-
- list = fs.Rsearchall(['d3', 'd4'])
- assert list == [], list
-
- fs.File('d3').built() # Clear exists cache
- test.subdir(['work', 'd3'])
-
- list = map(str, fs.Rsearchall(['d3', 'd4']))
- assert list == ['d3'], list
-
- fs.File('../rep3/d4').built() # Clear exists cache
- test.subdir(['rep3', 'd4'])
+ d1 = fs.Dir('d1')
+ d2 = fs.Dir('d2')
+ rep1_d1 = fs.Dir(test.workpath('rep1', 'd1'))
+ rep2_d1 = fs.Dir(test.workpath('rep2', 'd1'))
+ rep3_d1 = fs.Dir(test.workpath('rep3', 'd1'))
+ sub = fs.Dir('sub')
+ sub_d1 = sub.Dir('d1')
+ rep1_sub_d1 = fs.Dir(test.workpath('rep1', 'sub', 'd1'))
+ rep2_sub_d1 = fs.Dir(test.workpath('rep2', 'sub', 'd1'))
+ rep3_sub_d1 = fs.Dir(test.workpath('rep3', 'sub', 'd1'))
- list = map(str, fs.Rsearchall(['d3', 'd4']))
- assert list == ['d3', test.workpath('rep3', 'd4')], list
+ r = fs.Rfindalldirs(d1, fs.Top)
+ assert r == [d1], map(str, r)
- list = map(str, fs.Rsearchall(string.join(['d3', 'd4'], os.pathsep)))
- assert list == ['d3', test.workpath('rep3', 'd4')], list
+ r = fs.Rfindalldirs([d1, d2], fs.Top)
+ assert r == [d1, d2], map(str, r)
- work_d4 = fs.File(os.path.join('work', 'd4'))
+ r = fs.Rfindalldirs('d1', fs.Top)
+ assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
- list = map(str, fs.Rsearchall(['d3', work_d4]))
- assert list == ['d3', str(work_d4)], list
+ r = fs.Rfindalldirs('#d1', fs.Top)
+ assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
- list = fs.Rsearchall('')
- assert list == [], list
+ r = fs.Rfindalldirs('d1', sub)
+ assert r == [sub_d1, rep1_sub_d1, rep2_sub_d1, rep3_sub_d1], map(str, r)
- list = fs.Rsearchall([None])
- assert list == [], list
+ r = fs.Rfindalldirs('#d1', sub)
+ assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
- list = fs.Rsearchall([''])
- assert list == [], list
+ r = fs.Rfindalldirs(['d1', d2], fs.Top)
+ assert r == [d1, rep1_d1, rep2_d1, rep3_d1, d2], map(str, r)
def test_rexists(self):
"""Test the Entry.rexists() method"""
@@ -1756,17 +2069,17 @@ class RepositoryTestCase(_tempdirTestCase):
test = self.test
test.write([self.rep1, 'f2'], "")
+ test.write([self.rep2, "i_exist"], "\n")
+ test.write(["work", "i_exist_too"], "\n")
fs.BuildDir('build', '.')
f = fs.File(test.workpath("work", "i_do_not_exist"))
assert not f.rexists()
- test.write([self.rep2, "i_exist"], "\n")
f = fs.File(test.workpath("work", "i_exist"))
assert f.rexists()
- test.write(["work", "i_exist_too"], "\n")
f = fs.File(test.workpath("work", "i_exist_too"))
assert f.rexists()
@@ -1903,7 +2216,7 @@ class StringDirTestCase(unittest.TestCase):
class stored_infoTestCase(unittest.TestCase):
def runTest(self):
- """Test how storing build information"""
+ """Test how we store build information"""
test = TestCmd(workdir = '')
test.subdir('sub')
fs = SCons.Node.FS.FS(test.workpath(''))
@@ -1911,7 +2224,8 @@ class stored_infoTestCase(unittest.TestCase):
d = fs.Dir('sub')
f = fs.File('file1', d)
bi = f.get_stored_info()
- assert bi.bsig == None, bi.bsig
+ assert bi.ninfo.timestamp == 0, bi.ninfo.timestamp
+ assert bi.ninfo.size == None, bi.ninfo.size
class MySConsign:
class Null:
@@ -2023,6 +2337,7 @@ class prepareTestCase(unittest.TestCase):
dir_made = []
new_dir = fs.Dir("new_dir")
new_dir.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
+ new_dir.reset_executor()
xyz = fs.File(os.path.join("new_dir", "xyz"))
xyz.set_state(SCons.Node.up_to_date)
@@ -2161,7 +2476,7 @@ class CacheDirTestCase(unittest.TestCase):
try:
f5 = fs.File("cd.f5")
f5.binfo = f5.new_binfo()
- f5.binfo.bsig = 'a_fake_bsig'
+ f5.binfo.ninfo.bsig = 'a_fake_bsig'
cp = f5.cachepath()
dirname = os.path.join('cache', 'A')
filename = os.path.join(dirname, 'a_fake_bsig')
@@ -2196,7 +2511,7 @@ class CacheDirTestCase(unittest.TestCase):
test.write(cd_f7, "cd.f7\n")
f7 = fs.File(cd_f7)
f7.binfo = f7.new_binfo()
- f7.binfo.bsig = 'f7_bsig'
+ f7.binfo.ninfo.bsig = 'f7_bsig'
warn_caught = 0
try:
@@ -2531,9 +2846,7 @@ class SaveStringsTestCase(unittest.TestCase):
if __name__ == "__main__":
suite = unittest.TestSuite()
- suite.addTest(FSTestCase())
suite.addTest(BuildDirTestCase())
- suite.addTest(EntryTestCase())
suite.addTest(find_fileTestCase())
suite.addTest(StringDirTestCase())
suite.addTest(stored_infoTestCase())
@@ -2547,6 +2860,12 @@ if __name__ == "__main__":
suite.addTest(SpecialAttrTestCase())
suite.addTest(SaveStringsTestCase())
tclasses = [
+ BaseTestCase,
+ BuildInfoTestCase,
+ EntryTestCase,
+ FileTestCase,
+ NodeInfoTestCase,
+ FSTestCase,
DirTestCase,
RepositoryTestCase,
]
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index ce67781..8c2e6ea 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -24,9 +24,12 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
+import re
+import string
import sys
import types
import unittest
+import UserList
import SCons.Errors
import SCons.Node
@@ -39,7 +42,27 @@ built_source = None
cycle_detected = None
built_order = 0
-class MyAction:
+def _actionAppend(a1, a2):
+ all = []
+ for curr_a in [a1, a2]:
+ if isinstance(curr_a, MyAction):
+ all.append(curr_a)
+ elif isinstance(curr_a, MyListAction):
+ all.extend(curr_a.list)
+ elif type(curr_a) == type([1,2]):
+ all.extend(curr_a)
+ else:
+ raise 'Cannot Combine Actions'
+ return MyListAction(all)
+
+class MyActionBase:
+ def __add__(self, other):
+ return _actionAppend(self, other)
+
+ def __radd__(self, other):
+ return _actionAppend(other, self)
+
+class MyAction(MyActionBase):
def __init__(self):
self.order = 0
@@ -53,29 +76,36 @@ class MyAction:
self.order = built_order
return 0
- def get_actions(self):
- return [self]
-
-class MyNonGlobalAction:
- def __init__(self):
- self.order = 0
- self.built_it = None
- self.built_target = None
- self.built_source = None
-
+class MyExecutor:
+ def __init__(self, env=None, targets=[], sources=[]):
+ self.env = env
+ self.targets = targets
+ self.sources = sources
+ def get_build_env(self):
+ return self.env
+ def get_build_scanner_path(self, scanner):
+ return 'executor would call %s' % scanner
+ def cleanup(self):
+ self.cleaned_up = 1
+ def scan_targets(self, scanner):
+ if not scanner:
+ return
+ d = scanner(self.targets)
+ for t in self.targets:
+ t.implicit.extend(d)
+ def scan_sources(self, scanner):
+ if not scanner:
+ return
+ d = scanner(self.sources)
+ for t in self.targets:
+ t.implicit.extend(d)
+
+class MyListAction(MyActionBase):
+ def __init__(self, list):
+ self.list = list
def __call__(self, target, source, env, errfunc):
- # Okay, so not ENTIRELY non-global...
- global built_order
- self.built_it = 1
- self.built_target = target
- self.built_source = source
- self.built_args = env
- built_order = built_order + 1
- self.order = built_order
- return 0
-
- def get_actions(self):
- return [self]
+ for A in self.list:
+ A(target, source, env, errfunc)
class Environment:
def __init__(self, **kw):
@@ -91,13 +121,23 @@ class Environment:
return apply(Environment, (), d)
def _update(self, dict):
self._dict.update(dict)
+ def get_calculator(self):
+ return SCons.Sig.default_calc
+ def get_factory(self, factory):
+ return factory or MyNode
+ def get_scanner(self, scanner_key):
+ return self._dict['SCANNERS'][0]
class Builder:
- def __init__(self, is_explicit=1):
- self.env = Environment()
+ def __init__(self, env=None, is_explicit=1):
+ if env is None: env = Environment()
+ self.env = env
self.overrides = {}
self.action = MyAction()
+ self.source_factory = MyNode
self.is_explicit = is_explicit
+ self.target_scanner = None
+ self.source_scanner = None
def targets(self, t):
return [t]
def get_actions(self):
@@ -139,8 +179,12 @@ class Scanner:
def __call__(self, node):
self.called = 1
return node.found_includes
+ def path(self, env, dir, target=None, source=None):
+ return ()
def select(self, node):
return self
+ def recurse_nodes(self, nodes):
+ return nodes
class MyNode(SCons.Node.Node):
"""The base Node class contains a number of do-nothing methods that
@@ -156,6 +200,116 @@ class MyNode(SCons.Node.Node):
def get_found_includes(self, env, scanner, target):
return scanner(self)
+class Calculator:
+ def __init__(self, val):
+ self.max_drift = 0
+ class M:
+ def __init__(self, val):
+ self.val = val
+ def signature(self, args):
+ return self.val
+ def collect(self, args):
+ return reduce(lambda x, y: x+y, args, self.val)
+ self.module = M(val)
+
+
+
+class NodeInfoTestCase(unittest.TestCase):
+
+ def test___cmp__(self):
+ """Test comparing NodeInfo objects"""
+ ni1 = SCons.Node.NodeInfo()
+ ni2 = SCons.Node.NodeInfo()
+
+ assert ni1 == ni2, "%s != %s" % (ni1.__dict__, ni2.__dict__)
+
+ ni1.foo = 777
+ assert ni1 != ni2, "%s == %s" % (ni1.__dict__, ni2.__dict__)
+
+ ni2.foo = 888
+ assert ni1 != ni2, "%s == %s" % (ni1.__dict__, ni2.__dict__)
+
+ ni1.foo = 888
+ assert ni1 == ni2, "%s != %s" % (ni1.__dict__, ni2.__dict__)
+
+ def test_merge(self):
+ """Test merging NodeInfo attributes"""
+ ni1 = SCons.Node.NodeInfo()
+ ni2 = SCons.Node.NodeInfo()
+
+ ni1.a1 = 1
+ ni1.a2 = 2
+
+ ni2.a2 = 222
+ ni2.a3 = 333
+
+ ni1.merge(ni2)
+ assert ni1.__dict__ == {'a1':1, 'a2':222, 'a3':333}, ni1.__dict__
+
+ def test_update(self):
+ """Test the update() method"""
+ ni = SCons.Node.NodeInfo()
+ ni.update(SCons.Node.Node())
+
+
+
+class BuildInfoTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test BuildInfo initialization"""
+ bi = SCons.Node.BuildInfo(SCons.Node.Node())
+ assert hasattr(bi, 'ninfo')
+
+ class MyNode(SCons.Node.Node):
+ def new_ninfo(self):
+ return 'ninfo initialization'
+ bi = SCons.Node.BuildInfo(MyNode())
+ assert bi.ninfo == 'ninfo initialization', bi.ninfo
+
+ def test___cmp__(self):
+ """Test comparing BuildInfo objects"""
+ bi1 = SCons.Node.BuildInfo(SCons.Node.Node())
+ bi2 = SCons.Node.BuildInfo(SCons.Node.Node())
+
+ assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__)
+
+ bi1.ninfo.foo = 777
+ assert bi1 != bi2, "%s == %s" % (bi1.__dict__, bi2.__dict__)
+
+ bi2.ninfo.foo = 888
+ assert bi1 != bi2, "%s == %s" % (bi1.__dict__, bi2.__dict__)
+
+ bi1.ninfo.foo = 888
+ assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__)
+
+ bi1.foo = 999
+ assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__)
+
+ def test_merge(self):
+ """Test merging BuildInfo attributes"""
+ bi1 = SCons.Node.BuildInfo(SCons.Node.Node())
+ bi2 = SCons.Node.BuildInfo(SCons.Node.Node())
+
+ bi1.a1 = 1
+ bi1.a2 = 2
+
+ bi2.a2 = 222
+ bi2.a3 = 333
+
+ bi1.ninfo.a4 = 4
+ bi1.ninfo.a5 = 5
+ bi2.ninfo.a5 = 555
+ bi2.ninfo.a6 = 666
+
+ bi1.merge(bi2)
+ assert bi1.a1 == 1, bi1.a1
+ assert bi1.a2 == 222, bi1.a2
+ assert bi1.a3 == 333, bi1.a3
+ assert bi1.ninfo.a4 == 4, bi1.ninfo.a4
+ assert bi1.ninfo.a5 == 555, bi1.ninfo.a5
+ assert bi1.ninfo.a6 == 666, bi1.ninfo.a6
+
+
class NodeTestCase(unittest.TestCase):
@@ -236,36 +390,62 @@ class NodeTestCase(unittest.TestCase):
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())
- node.env_set(Environment())
- node.sources = ["yyy", "zzz"]
- pre1 = MyNonGlobalAction()
- pre2 = MyNonGlobalAction()
- post1 = MyNonGlobalAction()
- post2 = MyNonGlobalAction()
- node.add_pre_action(pre1)
- node.add_pre_action(pre2)
- node.add_post_action(post1)
- node.add_post_action(post2)
- node.build()
- assert built_it
- assert pre1.built_it
- assert pre2.built_it
- assert post1.built_it
- assert post2.built_it
- assert pre1.order == 1, pre1.order
- assert pre2.order == 2, pre1.order
- # The action of the builder itself is order 3...
- assert post1.order == 4, pre1.order
- assert post2.order == 5, pre1.order
-
- for act in [ pre1, pre2, post1, post2 ]:
- assert type(act.built_target[0]) == type(MyNode("bar")), type(act.built_target[0])
- assert str(act.built_target[0]) == "xxx", str(act.built_target[0])
- assert act.built_source == ["yyy", "zzz"], act.built_source
+ def test_get_build_scanner_path(self):
+ """Test the get_build_scanner_path() method"""
+ n = SCons.Node.Node()
+ x = MyExecutor()
+ n.set_executor(x)
+ p = n.get_build_scanner_path('fake_scanner')
+ assert p == "executor would call fake_scanner", p
+
+ def test_get_executor(self):
+ """Test the get_executor() method"""
+ n = SCons.Node.Node()
+
+ try:
+ n.get_executor(0)
+ except AttributeError:
+ pass
+ else:
+ self.fail("did not catch expected AttributeError")
+
+ class Builder:
+ action = 'act'
+ env = 'env1'
+ overrides = {}
+
+ n = SCons.Node.Node()
+ n.builder_set(Builder())
+ x = n.get_executor()
+ assert x.env == 'env1', x.env
+
+ n = SCons.Node.Node()
+ n.builder_set(Builder())
+ n.env_set('env2')
+ x = n.get_executor()
+ assert x.env == 'env2', x.env
+
+ def test_set_executor(self):
+ """Test the set_executor() method"""
+ n = SCons.Node.Node()
+ n.set_executor(1)
+ assert n.executor == 1, n.executor
+
+ def test_executor_cleanup(self):
+ """Test letting the executor cleanup its cache"""
+ n = SCons.Node.Node()
+ x = MyExecutor()
+ n.set_executor(x)
+ n.executor_cleanup()
+ assert x.cleaned_up
+
+ def test_reset_executor(self):
+ """Test the reset_executor() method"""
+ n = SCons.Node.Node()
+ n.set_executor(1)
+ assert n.executor == 1, n.executor
+ n.reset_executor()
+ assert not hasattr(n, 'executor'), "unexpected executor attribute"
def test_built(self):
"""Test the built() method"""
@@ -291,14 +471,6 @@ class NodeTestCase(unittest.TestCase):
n = SCons.Node.Node()
n.visited()
- def test_depends_on(self):
- """Test the depends_on() method
- """
- parent = SCons.Node.Node()
- child = SCons.Node.Node()
- parent.add_dependency([child])
- assert parent.depends_on([child])
-
def test_builder_set(self):
"""Test setting a Node's Builder
"""
@@ -320,11 +492,24 @@ class NodeTestCase(unittest.TestCase):
"""
n1 = SCons.Node.Node()
assert not n1.has_explicit_builder()
- n1.builder_set(Builder(is_explicit=1))
+ n1.set_explicit(1)
assert n1.has_explicit_builder()
- n1.builder_set(Builder(is_explicit=None))
+ n1.set_explicit(None)
assert not n1.has_explicit_builder()
+ def test_get_builder(self):
+ """Test the get_builder() method"""
+ n1 = SCons.Node.Node()
+ b = n1.get_builder()
+ assert b is None, b
+ b = n1.get_builder(777)
+ assert b == 777, b
+ n1.builder_set(888)
+ b = n1.get_builder()
+ assert b == 888, b
+ b = n1.get_builder(999)
+ assert b == 888, b
+
def test_multiple_side_effect_has_builder(self):
"""Test the multiple_side_effect_has_builder() method
"""
@@ -361,6 +546,23 @@ class NodeTestCase(unittest.TestCase):
node = SCons.Node.Node()
assert node.current() is None
+ def test_children_are_up_to_date(self):
+ """Test the children_are_up_to_date() method used by subclasses
+ """
+ n1 = SCons.Node.Node()
+ n2 = SCons.Node.Node()
+
+ calc = Calculator(111)
+
+ n1.add_source(n2)
+ assert n1.children_are_up_to_date(calc), "expected up to date"
+ n2.set_state(SCons.Node.executed)
+ assert not n1.children_are_up_to_date(calc), "expected not up to date"
+ n2.set_state(SCons.Node.up_to_date)
+ assert n1.children_are_up_to_date(calc), "expected up to date"
+ n1.always_build = 1
+ assert not n1.children_are_up_to_date(calc), "expected not up to date"
+
def test_env_set(self):
"""Test setting a Node's Environment
"""
@@ -377,85 +579,80 @@ class NodeTestCase(unittest.TestCase):
a = node.builder.get_actions()
assert isinstance(a[0], MyAction), a[0]
- def test_calc_bsig(self):
+ def test_get_bsig(self):
"""Test generic build signature calculation
"""
- class Calculator:
- def __init__(self, val):
- self.max_drift = 0
- class M:
- def __init__(self, val):
- self.val = val
- def collect(self, args):
- return reduce(lambda x, y: x+y, args, self.val)
- self.module = M(val)
node = SCons.Node.Node()
- result = node.calc_bsig(Calculator(222))
+ result = node.get_bsig(Calculator(222))
assert result == 222, result
- result = node.calc_bsig(Calculator(333))
+ result = node.get_bsig(Calculator(333))
assert result == 222, result
- def test_calc_csig(self):
+ def test_get_csig(self):
"""Test generic content signature calculation
"""
- class Calculator:
- def __init__(self, val):
- self.max_drift = 0
- class M:
- def __init__(self, val):
- self.val = val
- def signature(self, args):
- return self.val
- self.module = M(val)
node = SCons.Node.Node()
- result = node.calc_csig(Calculator(444))
+ result = node.get_csig(Calculator(444))
assert result == 444, result
- result = node.calc_csig(Calculator(555))
+ result = node.get_csig(Calculator(555))
assert result == 444, result
+ def test_get_binfo(self):
+ """Test fetching/creating a build information structure
+ """
+ node = SCons.Node.Node()
+
+ binfo = node.get_binfo()
+ assert isinstance(binfo, SCons.Node.BuildInfo), binfo
+
+ node.binfo = 777
+ binfo = node.get_binfo()
+ assert binfo == 777, binfo
+
def test_gen_binfo(self):
"""Test generating a build information structure
"""
- class Calculator:
- def __init__(self, val):
- self.max_drift = 0
- class M:
- def __init__(self, val):
- self.val = val
- def collect(self, args):
- return reduce(lambda x, y: x+y, args, self.val)
- self.module = M(val)
-
node = SCons.Node.Node()
- binfo = node.gen_binfo(Calculator(666))
+ d = SCons.Node.Node()
+ i = SCons.Node.Node()
+ node.depends = [d]
+ node.implicit = [i]
+ node.gen_binfo(Calculator(666))
+ binfo = node.binfo
assert isinstance(binfo, SCons.Node.BuildInfo), binfo
assert hasattr(binfo, 'bsources')
assert hasattr(binfo, 'bsourcesigs')
- assert hasattr(binfo, 'bdepends')
+ assert binfo.bdepends == [d]
assert hasattr(binfo, 'bdependsigs')
- assert hasattr(binfo, 'bimplicit')
+ assert binfo.bimplicit == [i]
assert hasattr(binfo, 'bimplicitsigs')
- assert binfo.bsig == 666, binfo.bsig
+ assert binfo.ninfo.bsig == 1998, binfo.ninfo.bsig
def test_explain(self):
"""Test explaining why a Node must be rebuilt
"""
- node = SCons.Node.Node()
+ class testNode(SCons.Node.Node):
+ def __str__(self): return 'xyzzy'
+ node = testNode()
node.exists = lambda: None
- node.__str__ = lambda: 'xyzzy'
+ # Can't do this with new-style classes (python bug #1066490)
+ #node.__str__ = lambda: 'xyzzy'
result = node.explain()
assert result == "building `xyzzy' because it doesn't exist\n", result
- node = SCons.Node.Node()
+ class testNode2(SCons.Node.Node):
+ def __str__(self): return 'null_binfo'
+ node = testNode2()
result = node.explain()
assert result == None, result
- class Null_BInfo:
- def __init__(self):
+ def get_null_info():
+ class Null_BInfo:
pass
+ return Null_BInfo()
- node.get_stored_info = Null_BInfo
- node.__str__ = lambda: 'null_binfo'
+ node.get_stored_info = get_null_info
+ #see above: node.__str__ = lambda: 'null_binfo'
result = node.explain()
assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result
@@ -526,6 +723,8 @@ class NodeTestCase(unittest.TestCase):
def test_prepare(self):
"""Test preparing a node to be built
+
+ By extension, this also tests the missing() method.
"""
node = SCons.Node.Node()
@@ -682,41 +881,55 @@ class NodeTestCase(unittest.TestCase):
assert deps == [], deps
s = Scanner()
- d = MyNode("ddd")
- node.found_includes = [d]
+ d1 = MyNode("d1")
+ d2 = MyNode("d2")
+ node.found_includes = [d1, d2]
# Simple return of the found includes
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d], deps
+ assert deps == [d1, d2], deps
- # No "recursive" attribute on scanner doesn't recurse
+ # By default, our fake scanner recurses
e = MyNode("eee")
- d.found_includes = [e]
+ f = MyNode("fff")
+ g = MyNode("ggg")
+ d1.found_includes = [e, f]
+ d2.found_includes = [e, f]
+ f.found_includes = [g]
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d], map(str, deps)
+ assert deps == [d1, d2, e, f, g], map(str, deps)
- # Explicit "recursive" attribute on scanner doesn't recurse
- s.recursive = None
+ # Recursive scanning eliminates duplicates
+ e.found_includes = [f]
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d], map(str, deps)
+ assert deps == [d1, d2, e, f, g], map(str, deps)
- # Explicit "recursive" attribute on scanner which does recurse
- s.recursive = 1
+ # Scanner method can select specific nodes to recurse
+ def no_fff(nodes):
+ return filter(lambda n: str(n)[0] != 'f', nodes)
+ s.recurse_nodes = no_fff
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d, e], map(str, deps)
+ assert deps == [d1, d2, e, f], map(str, deps)
- # Recursive scanning eliminates duplicates
- f = MyNode("fff")
- d.found_includes = [e, f]
- e.found_includes = [f]
+ # Scanner method can short-circuit recursing entirely
+ s.recurse_nodes = lambda nodes: []
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d, e, f], map(str, deps)
+ assert deps == [d1, d2], map(str, deps)
+
+ def test_get_scanner(self):
+ """Test fetching the environment scanner for a Node
+ """
+ node = SCons.Node.Node()
+ scanner = Scanner()
+ env = Environment(SCANNERS = [scanner])
+ s = node.get_scanner(env)
+ assert s == scanner, s
+ s = node.get_scanner(env, {'X':1})
+ assert s == scanner, s
def test_get_source_scanner(self):
"""Test fetching the source scanner for a Node
"""
- class Builder:
- pass
target = SCons.Node.Node()
source = SCons.Node.Node()
s = target.get_source_scanner(source)
@@ -726,32 +939,48 @@ class NodeTestCase(unittest.TestCase):
ts2 = Scanner()
ts3 = Scanner()
- source.backup_source_scanner = ts1
- s = target.get_source_scanner(source)
+ class Builder1(Builder):
+ def __call__(self, source):
+ r = SCons.Node.Node()
+ r.builder = self
+ return [r]
+ class Builder2(Builder1):
+ def __init__(self, scanner):
+ self.source_scanner = scanner
+
+ builder = Builder2(ts1)
+
+ targets = builder([source])
+ s = targets[0].get_source_scanner(source)
assert s is ts1, s
- target.builder = Builder()
+ target.builder_set(Builder2(ts1))
target.builder.source_scanner = ts2
s = target.get_source_scanner(source)
assert s is ts2, s
- target.source_scanner = ts3
- s = target.get_source_scanner(source)
+ builder = Builder1(env=Environment(SCANNERS = [ts3]))
+
+ targets = builder([source])
+
+ s = targets[0].get_source_scanner(source)
assert s is ts3, s
+
def test_scan(self):
"""Test Scanner functionality
"""
+ env = Environment()
node = MyNode("nnn")
node.builder = Builder()
- node.env_set(Environment())
- s = Scanner()
+ node.env_set(env)
+ x = MyExecutor(env, [node])
+ s = Scanner()
d = MyNode("ddd")
node.found_includes = [d]
- assert node.target_scanner == None, node.target_scanner
- node.target_scanner = s
+ node.builder.target_scanner = s
assert node.implicit is None
node.scan()
@@ -783,13 +1012,14 @@ class NodeTestCase(unittest.TestCase):
SCons.Node.implicit_deps_unchanged = None
try:
sn = StoredNode("eee")
- sn._children = ['fake']
- sn.target_scanner = s
+ sn.builder_set(Builder())
+ sn.builder.target_scanner = s
sn.scan()
assert sn.implicit == [], sn.implicit
- assert not hasattr(sn, '_children'), "unexpected _children attribute"
+ assert sn.children() == [], sn.children()
+
finally:
SCons.Sig.default_calc = save_default_calc
SCons.Node.implicit_cache = save_implicit_cache
@@ -864,7 +1094,7 @@ class NodeTestCase(unittest.TestCase):
"""Test setting and getting the state of a node
"""
node = SCons.Node.Node()
- assert node.get_state() == None
+ assert node.get_state() == SCons.Node.no_state
node.set_state(SCons.Node.executing)
assert node.get_state() == SCons.Node.executing
assert SCons.Node.pending < SCons.Node.executing
@@ -999,14 +1229,17 @@ class NodeTestCase(unittest.TestCase):
n.implicit = 'testimplicit'
n.waiting_parents = ['foo', 'bar']
+ x = MyExecutor()
+ n.set_executor(x)
+
n.clear()
- assert n.get_state() is None, n.get_state()
assert not hasattr(n, 'binfo'), n.bsig
assert n.includes is None, n.includes
assert n.found_includes == {}, n.found_includes
assert n.implicit is None, n.implicit
assert n.waiting_parents == [], n.waiting_parents
+ assert x.cleaned_up
def test_get_subst_proxy(self):
"""Test the get_subst_proxy method."""
@@ -1026,12 +1259,6 @@ class NodeTestCase(unittest.TestCase):
s = n.get_suffix()
assert s == '', s
- def test_generate_build_dict(self):
- """Test the base Node generate_build_dict() method"""
- n = SCons.Node.Node()
- dict = n.generate_build_dict()
- assert dict == {}, dict
-
def test_postprocess(self):
"""Test calling the base Node postprocess() method"""
n = SCons.Node.Node()
@@ -1056,9 +1283,46 @@ class NodeTestCase(unittest.TestCase):
n1.call_for_all_waiting_parents(func)
assert result == [n1, n2], result
+class NodeListTestCase(unittest.TestCase):
+ def test___str__(self):
+ """Test"""
+ n1 = MyNode("n1")
+ n2 = MyNode("n2")
+ n3 = MyNode("n3")
+ nl = SCons.Node.NodeList([n3, n2, n1])
+
+ l = [1]
+ ul = UserList.UserList([2])
+ try:
+ l.extend(ul)
+ except TypeError:
+ # An older version of Python (*cough* 1.5.2 *cough*)
+ # that doesn't allow UserList objects to extend lists.
+ pass
+ else:
+ s = str(nl)
+ assert s == "['n3', 'n2', 'n1']", s
+
+ r = repr(nl)
+ r = re.sub('at (0[xX])?[0-9a-fA-F]+', 'at 0x', r)
+ # Don't care about ancestry: just leaf value of MyNode
+ r = re.sub('<.*?\.MyNode', '<MyNode', r)
+ # New-style classes report as "object"; classic classes report
+ # as "instance"...
+ r = re.sub("object", "instance", r)
+ l = string.join(["<MyNode instance at 0x>"]*3, ", ")
+ assert r == '[%s]' % l, r
+
if __name__ == "__main__":
- suite = unittest.makeSuite(NodeTestCase, 'test_')
+ suite = unittest.TestSuite()
+ tclasses = [ BuildInfoTestCase,
+ NodeInfoTestCase,
+ NodeTestCase,
+ NodeListTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
if not unittest.TextTestRunner().run(suite).wasSuccessful():
sys.exit(1)
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 38cff92..e73e5f3 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -48,8 +48,10 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import copy
import string
+import UserList
from SCons.Debug import logInstanceCreation
+import SCons.Executor
import SCons.SConsign
import SCons.Util
@@ -60,6 +62,7 @@ import SCons.Util
# it has no builder of its own. The canonical example is a file
# system directory, which is only up to date if all of its children
# were up to date.
+no_state = 0
pending = 1
executing = 2
up_to_date = 3
@@ -67,7 +70,17 @@ executed = 4
failed = 5
stack = 6 # nodes that are in the current Taskmaster execution stack
-# controls whether implicit depedencies are cached:
+StateString = {
+ 0 : "0",
+ 1 : "pending",
+ 2 : "executing",
+ 3 : "up_to_date",
+ 4 : "executed",
+ 5 : "failed",
+ 6 : "stack",
+}
+
+# controls whether implicit dependencies are cached:
implicit_cache = 0
# controls whether implicit dep changes are ignored:
@@ -82,20 +95,69 @@ def do_nothing(node): pass
Annotate = do_nothing
-class BuildInfo:
+# Classes for signature info for Nodes.
+
+class NodeInfo:
+ """
+ A generic class for signature information for a Node.
+
+ We actually expect that modules containing Node subclasses will also
+ subclass NodeInfo, to provide their own logic for dealing with their
+ own Node-specific signature information.
+ """
+ def __init__(self):
+ """A null initializer so that subclasses have a superclass
+ initialization method to call for future use.
+ """
+ pass
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
+ def update(self, node):
+ pass
+ def merge(self, other):
+ for key, val in other.__dict__.items():
+ self.__dict__[key] = val
+
+class BuildInfo:
+ """
+ The generic build information for a Node.
+
+ This is what gets stored in a .sconsign file for each target file.
+ It contains a NodeInfo instance for this node (signature information
+ that's specific to the type of Node) and direct attributes for the
+ generic build stuff we have to track: sources, explicit dependencies,
+ implicit dependencies, and action information.
+ """
+ def __init__(self, node):
+ self.ninfo = node.new_ninfo()
+ self.bsourcesigs = []
+ self.bdependsigs = []
+ self.bimplicitsigs = []
+ self.bactsig = None
+ def __cmp__(self, other):
+ return cmp(self.ninfo, other.ninfo)
+ def merge(self, other):
+ for key, val in other.__dict__.items():
+ try:
+ merge = self.__dict__[key].merge
+ except (AttributeError, KeyError):
+ self.__dict__[key] = val
+ else:
+ merge(val)
class Node:
"""The base Node class, for entities that we know how to
build, or use to build other Nodes.
"""
+ if SCons.Memoize.use_memoizer:
+ __metaclass__ = SCons.Memoize.Memoized_Metaclass
+
class Attrs:
pass
def __init__(self):
- if __debug__: logInstanceCreation(self, 'Node')
+ if __debug__: logInstanceCreation(self, 'Node.Node')
# 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
@@ -118,17 +180,13 @@ class Node:
self.implicit = None # implicit (scanned) dependencies (None means not scanned yet)
self.waiting_parents = []
self.wkids = None # Kids yet to walk, when it's an array
- self.target_scanner = None # explicit scanner from this node's Builder
- self.source_scanner = None
- self.backup_source_scanner = None
self.env = None
- self.state = None
+ self.state = no_state
self.precious = None
self.always_build = None
self.found_includes = {}
self.includes = None
- self.overrides = {} # construction variable overrides for building this node
self.attributes = self.Attrs() # Generic place to stick information about the Node.
self.side_effect = 0 # true iff this node is a side effect
self.side_effects = [] # the side effects of building this target
@@ -144,15 +202,14 @@ class Node:
def get_suffix(self):
return ''
- def generate_build_dict(self):
- """Return an appropriate dictionary of values for building
- this Node."""
- return {}
-
def get_build_env(self):
- """Fetch the appropriate Environment to build this node."""
- executor = self.get_executor()
- return executor.get_build_env()
+ """Fetch the appropriate Environment to build this node.
+ __cacheable__"""
+ return self.get_executor().get_build_env()
+
+ def get_build_scanner_path(self, scanner):
+ """Fetch the appropriate scanner path for this node."""
+ return self.get_executor().get_build_scanner_path(scanner)
def set_executor(self, executor):
"""Set the action executor for this node."""
@@ -166,15 +223,35 @@ class Node:
except AttributeError:
if not create:
raise
- import SCons.Executor
- executor = SCons.Executor.Executor(self.builder.action,
- self.builder.env,
- [self.builder.overrides],
- [self],
- self.sources)
+ try:
+ act = self.builder.action
+ except AttributeError:
+ executor = SCons.Executor.Null(targets=[self])
+ else:
+ executor = SCons.Executor.Executor(act,
+ self.env or self.builder.env,
+ [self.builder.overrides],
+ [self],
+ self.sources)
self.executor = executor
return executor
+ def executor_cleanup(self):
+ """Let the executor clean up any cached information."""
+ try:
+ executor = self.get_executor(create=None)
+ except AttributeError:
+ pass
+ else:
+ executor.cleanup()
+
+ def reset_executor(self):
+ "Remove cached executor; forces recompute when needed."
+ try:
+ delattr(self, 'executor')
+ except AttributeError:
+ pass
+
def retrieve_from_cache(self):
"""Try to retrieve the node's content from a cache
@@ -193,15 +270,15 @@ class Node:
so only do thread safe stuff here. Do thread unsafe stuff in
built().
"""
- if not self.has_builder():
- return
- def errfunc(stat, node=self):
- raise SCons.Errors.BuildError(node=node, errstr="Error %d" % stat)
+ def exitstatfunc(stat, node=self):
+ if stat:
+ msg = "Error %d" % stat
+ raise SCons.Errors.BuildError(node=node, errstr=msg)
executor = self.get_executor()
- apply(executor, (self, errfunc), kw)
+ apply(executor, (self, exitstatfunc), kw)
def built(self):
- """Called just after this node is sucessfully built."""
+ """Called just after this node is successfully built."""
# Clear the implicit dependency caches of any Nodes
# waiting for this Node to be built.
@@ -210,30 +287,27 @@ class Node:
parent.del_binfo()
try:
- new_binfo = self.binfo
+ new = self.binfo
except AttributeError:
# Node arrived here without build info; apparently it
# doesn't need it, so don't bother calculating or storing
# it.
- new_binfo = None
+ new = None
# Reset this Node's cached state since it was just built and
# various state has changed.
- save_state = self.get_state()
self.clear()
- self.set_state(save_state)
-
- # Had build info, so it should be stored in the signature
- # cache. However, if the build info included a content
- # signature then it should be recalculated before being
- # stored.
- if new_binfo:
- if hasattr(new_binfo, 'csig'):
- new_binfo = self.gen_binfo() # sets self.binfo
+ if new:
+ # It had build info, so it should be stored in the signature
+ # cache. However, if the build info included a content
+ # signature then it must be recalculated before being stored.
+ if hasattr(new.ninfo, 'csig'):
+ self.get_csig()
else:
- self.binfo = new_binfo
- self.store_info(new_binfo)
+ new.ninfo.update(self)
+ self.binfo = new
+ self.store_info(self.binfo)
def add_to_waiting_parents(self, node):
self.waiting_parents.append(node)
@@ -246,21 +320,16 @@ class Node:
def postprocess(self):
"""Clean up anything we don't need to hang onto after we've
been built."""
- try:
- executor = self.get_executor(create=None)
- except AttributeError:
- pass
- else:
- executor.cleanup()
+ self.executor_cleanup()
def clear(self):
"""Completely clear a Node of all its cached state (so that it
can be re-evaluated by interfaces that do continuous integration
builds).
+ __reset_cache__
"""
- self.set_state(None)
+ self.executor_cleanup()
self.del_binfo()
- self.del_cinfo()
try:
delattr(self, '_calculated_sig')
except AttributeError:
@@ -276,15 +345,8 @@ class Node:
without requiring a build.."""
pass
- def depends_on(self, nodes):
- """Does this node depend on any of 'nodes'?"""
- for node in nodes:
- if node in self.children():
- return 1
-
- return 0
-
def builder_set(self, builder):
+ "__cache_reset__"
self.builder = builder
def has_builder(self):
@@ -307,6 +369,9 @@ class Node:
b = self.builder
return not b is None
+ def set_explicit(self, is_explicit):
+ self.is_explicit = is_explicit
+
def has_explicit_builder(self):
"""Return whether this Node has an explicit builder
@@ -314,7 +379,18 @@ class Node:
non-explicit, so that it can be overridden by an explicit
builder that the user supplies (the canonical example being
directories)."""
- return self.has_builder() and self.builder.is_explicit
+ try:
+ return self.is_explicit
+ except AttributeError:
+ self.is_explicit = None
+ return self.is_explicit
+
+ def get_builder(self, default_builder=None):
+ """Return the set builder, or a specified default value"""
+ try:
+ return self.builder
+ except AttributeError:
+ return default_builder
multiple_side_effect_has_builder = has_builder
@@ -327,6 +403,7 @@ class Node:
signatures when they are used as source files to other derived files. For
example: source with source builders are not derived in this sense,
and hence should not return true.
+ __cacheable__
"""
return self.has_builder() or self.side_effect
@@ -343,7 +420,7 @@ class Node:
"""
return [], None
- def get_found_includes(self, env, scanner, target):
+ def get_found_includes(self, env, scanner, path):
"""Return the scanned include lines (implicit dependencies)
found in this node.
@@ -353,7 +430,7 @@ class Node:
"""
return []
- def get_implicit_deps(self, env, scanner, target):
+ def get_implicit_deps(self, env, scanner, path):
"""Return a list of implicit dependencies for this node.
This method exists to handle recursive invocation of the scanner
@@ -367,59 +444,57 @@ class Node:
# for this Node.
scanner = scanner.select(self)
- try:
- recurse = scanner.recursive
- except AttributeError:
- recurse = None
-
nodes = [self]
seen = {}
seen[self] = 1
deps = []
while nodes:
- n = nodes.pop(0)
- d = filter(lambda x, seen=seen: not seen.has_key(x),
- n.get_found_includes(env, scanner, target))
- if d:
- deps.extend(d)
- for n in d:
- seen[n] = 1
- if recurse:
- nodes.extend(d)
+ n = nodes.pop(0)
+ d = filter(lambda x, seen=seen: not seen.has_key(x),
+ n.get_found_includes(env, scanner, path))
+ if d:
+ deps.extend(d)
+ for n in d:
+ seen[n] = 1
+ nodes.extend(scanner.recurse_nodes(d))
return deps
- # cache used to make implicit_factory fast.
- implicit_factory_cache = {}
-
- def implicit_factory(self, path):
- """
- Turn a cache implicit dependency path into a node.
- This is called so many times that doing caching
- here is a significant performance boost.
- """
- try:
- return self.implicit_factory_cache[path]
- except KeyError:
- n = self.builder.source_factory(path)
- self.implicit_factory_cache[path] = n
- return n
+ def get_scanner(self, env, kw={}):
+ return env.get_scanner(self.scanner_key())
def get_source_scanner(self, node):
"""Fetch the source scanner for the specified node
NOTE: "self" is the target being built, "node" is
the source file for which we want to fetch the scanner.
+
+ Implies self.has_builder() is true; again, expect to only be
+ called from locations where this is already verified.
+
+ This function may be called very often; it attempts to cache
+ the scanner found to improve performance.
"""
- if self.source_scanner:
- return self.source_scanner
+ scanner = None
try:
scanner = self.builder.source_scanner
- if scanner:
- return scanner
except AttributeError:
pass
- return node.backup_source_scanner or None
+ if not scanner:
+ # The builder didn't have an explicit scanner, so go look up
+ # a scanner from env['SCANNERS'] based on the node's scanner
+ # key (usually the file extension).
+ scanner = self.get_scanner(self.get_build_env())
+ if scanner:
+ scanner = scanner.select(node)
+ return scanner
+
+ def add_to_implicit(self, deps):
+ if not hasattr(self, 'implicit') or self.implicit is None:
+ self.implicit = []
+ self.implicit_dict = {}
+ self._children_reset()
+ self._add_child(self.implicit, self.implicit_dict, deps)
def scan(self):
"""Scan this node's dependents for implicit dependencies."""
@@ -439,34 +514,44 @@ class Node:
# Here's where we implement --implicit-cache.
if implicit_cache and not implicit_deps_changed:
implicit = self.get_stored_implicit()
- if implicit is not None:
- implicit = map(self.implicit_factory, implicit)
- self._add_child(self.implicit, self.implicit_dict, implicit)
+ if implicit:
+ factory = build_env.get_factory(self.builder.source_factory)
+ nodes = []
+ for i in implicit:
+ try:
+ n = factory(i)
+ except TypeError:
+ # The implicit dependency was cached as one type
+ # of Node last time, but the configuration has
+ # changed (probably) and it's a different type
+ # this time. Just ignore the mismatch and go
+ # with what our current configuration says the
+ # Node is.
+ pass
+ else:
+ nodes.append(n)
+ self._add_child(self.implicit, self.implicit_dict, nodes)
calc = build_env.get_calculator()
- if implicit_deps_unchanged or self.current(calc, scan=0):
+ if implicit_deps_unchanged or self.current(calc):
return
- else:
- # one of this node's sources has changed, so
- # we need to recalculate the implicit deps,
- # and the bsig:
- self.implicit = []
- self.implicit_dict = {}
- self._children_reset()
- self.del_binfo()
-
- for child in self.children(scan=0):
- scanner = self.get_source_scanner(child)
- if scanner:
- deps = child.get_implicit_deps(build_env, scanner, self)
- self._add_child(self.implicit, self.implicit_dict, deps)
-
- # scan this node itself for implicit dependencies
- deps = self.get_implicit_deps(build_env, self.target_scanner, self)
- self._add_child(self.implicit, self.implicit_dict, deps)
+ # one of this node's sources has changed, so
+ # we need to recalculate the implicit deps,
+ # and the bsig:
+ self.implicit = []
+ self.implicit_dict = {}
+ self._children_reset()
+ self.del_binfo()
+
+ executor = self.get_executor()
- # XXX See note above re: --implicit-cache.
- #if implicit_cache:
- # self.store_implicit()
+ # Have the executor scan the sources.
+ executor.scan_sources(self.builder.source_scanner)
+
+ # If there's a target scanner, have the executor scan the target
+ # node itself and associated targets that might be built.
+ scanner = self.builder.target_scanner
+ if scanner:
+ executor.scan_targets(scanner)
def scanner_key(self):
return None
@@ -476,6 +561,10 @@ class Node:
return
self.env = env
+ #
+ # SIGNATURE SUBSYSTEM
+ #
+
def calculator(self):
import SCons.Defaults
@@ -485,45 +574,41 @@ class Node:
def calc_signature(self, calc=None):
"""
Select and calculate the appropriate build signature for a node.
+ __cacheable__
self - the node
calc - the signature calculation module
returns - the signature
"""
- try:
- return self._calculated_sig
- except AttributeError:
- if self.is_derived():
- import SCons.Defaults
-
- env = self.env or SCons.Defaults.DefaultEnvironment()
- if env.use_build_signature():
- sig = self.calc_bsig(calc)
- else:
- sig = self.calc_csig(calc)
- elif not self.rexists():
- sig = None
- else:
- sig = self.calc_csig(calc)
- self._calculated_sig = sig
- return sig
+ if self.is_derived():
+ import SCons.Defaults
+
+ env = self.env or SCons.Defaults.DefaultEnvironment()
+ if env.use_build_signature():
+ return self.get_bsig(calc)
+ elif not self.rexists():
+ return None
+ return self.get_csig(calc)
+
+ def new_ninfo(self):
+ return NodeInfo()
def new_binfo(self):
- return BuildInfo()
+ return BuildInfo(self)
- def del_binfo(self):
- """Delete the bsig from this node."""
+ def get_binfo(self):
try:
- delattr(self, 'binfo')
+ return self.binfo
except AttributeError:
- pass
+ self.binfo = self.new_binfo()
+ return self.binfo
- def calc_bsig(self, calc=None):
+ def del_binfo(self):
+ """Delete the build info from this node."""
try:
- return self.binfo.bsig
+ delattr(self, 'binfo')
except AttributeError:
- self.binfo = self.gen_binfo(calc)
- return self.binfo.bsig
+ pass
def gen_binfo(self, calc=None, scan=1):
"""
@@ -538,68 +623,70 @@ class Node:
node's children's signatures. We expect that they're
already built and updated by someone else, if that's
what's wanted.
+ __cacheable__
"""
if calc is None:
calc = self.calculator()
- binfo = self.new_binfo()
+ binfo = self.get_binfo()
if scan:
self.scan()
- sources = self.filter_ignore(self.sources)
- depends = self.filter_ignore(self.depends)
- if self.implicit is None:
- implicit = []
- else:
- implicit = self.filter_ignore(self.implicit)
-
+ executor = self.get_executor()
def calc_signature(node, calc=calc):
return node.calc_signature(calc)
- sourcesigs = map(calc_signature, sources)
+
+ sources = executor.process_sources(None, self.ignore)
+ sourcesigs = executor.process_sources(calc_signature, self.ignore)
+
+ depends = self.depends
+ implicit = self.implicit or []
+
+ if self.ignore:
+ depends = filter(self.do_not_ignore, depends)
+ implicit = filter(self.do_not_ignore, implicit)
+
dependsigs = map(calc_signature, depends)
implicitsigs = map(calc_signature, implicit)
sigs = sourcesigs + dependsigs + implicitsigs
if self.has_builder():
- executor = self.get_executor()
- binfo.bact = executor.strfunction()
+ binfo.bact = str(executor)
binfo.bactsig = calc.module.signature(executor)
sigs.append(binfo.bactsig)
- binfo.bsources = map(str, sources)
- binfo.bdepends = map(str, depends)
- binfo.bimplicit = map(str, implicit)
+ binfo.bsources = sources
+ binfo.bdepends = depends
+ binfo.bimplicit = implicit
binfo.bsourcesigs = sourcesigs
binfo.bdependsigs = dependsigs
binfo.bimplicitsigs = implicitsigs
- binfo.bsig = calc.module.collect(filter(None, sigs))
+ binfo.ninfo.bsig = calc.module.collect(filter(None, sigs))
return binfo
- def del_cinfo(self):
+ def get_bsig(self, calc=None):
+ binfo = self.get_binfo()
try:
- del self.binfo.csig
+ return binfo.ninfo.bsig
except AttributeError:
- pass
+ self.binfo = self.gen_binfo(calc)
+ return self.binfo.ninfo.bsig
- def calc_csig(self, calc=None):
+ def get_csig(self, calc=None):
+ binfo = self.get_binfo()
try:
- binfo = self.binfo
- except AttributeError:
- binfo = self.binfo = self.new_binfo()
- try:
- return binfo.csig
+ return binfo.ninfo.csig
except AttributeError:
if calc is None:
calc = self.calculator()
- binfo.csig = calc.module.signature(self)
- self.store_info(binfo)
- return binfo.csig
+ csig = binfo.ninfo.csig = calc.module.signature(self)
+ return csig
def store_info(self, obj):
"""Make the build signature permanent (that is, store it in the
@@ -613,6 +700,10 @@ class Node:
"""Fetch the stored implicit dependencies"""
return None
+ #
+ #
+ #
+
def set_precious(self, precious = 1):
"""Set the Node's precious value."""
self.precious = precious
@@ -630,18 +721,24 @@ class Node:
"""Does this node exist locally or in a repositiory?"""
# There are no repositories by default:
return self.exists()
+
+ def missing(self):
+ """__cacheable__"""
+ return not self.is_derived() and \
+ not self.is_pseudo_derived() and \
+ not self.linked and \
+ not self.rexists()
def prepare(self):
"""Prepare for this Node to be created.
The default implemenation checks that all children either exist
or are derived.
"""
- def missing(node):
- return not node.is_derived() and \
- not node.is_pseudo_derived() and \
- not node.linked and \
- not node.rexists()
- missing_sources = filter(missing, self.children())
+ l = self.depends
+ if not self.implicit is None:
+ l = l + self.implicit
+ missing_sources = self.get_executor().get_missing_sources() \
+ + filter(lambda c: c.missing(), l)
if missing_sources:
desc = "Source `%s' not found, needed by target `%s'." % (missing_sources[0], self)
raise SCons.Errors.StopError, desc
@@ -709,33 +806,15 @@ class Node:
self.wkids.append(wkid)
def _children_reset(self):
- try:
- delattr(self, '_children')
- except AttributeError:
- pass
+ "__cache_reset__"
+ # We need to let the Executor clear out any calculated
+ # bsig info that it's cached so we can re-calculate it.
+ self.executor_cleanup()
- def filter_ignore(self, nodelist):
- ignore = self.ignore
- result = []
- for node in nodelist:
- if node not in ignore:
- result.append(node)
- return result
+ def do_not_ignore(self, node):
+ return node not in self.ignore
- def children(self, scan=1):
- """Return a list of the node's direct children, minus those
- that are ignored by this node."""
- if scan:
- self.scan()
- try:
- return self._children
- except AttributeError:
- c = self.all_children(scan=0)
- self._children = self.filter_ignore(c)
- return self._children
-
- def all_children(self, scan=1):
- """Return a list of all the node's direct children."""
+ def _all_children_get(self):
# The return list may contain duplicate Nodes, especially in
# source trees where there are a lot of repeated #includes
# of a tangle of .h files. Profiling shows, however, that
@@ -753,13 +832,31 @@ class Node:
# using dictionary keys, lose the order, and the only ordered
# dictionary patterns I found all ended up using "not in"
# internally anyway...)
- if scan:
- self.scan()
if self.implicit is None:
return self.sources + self.depends
else:
return self.sources + self.depends + self.implicit
+ def _children_get(self):
+ "__cacheable__"
+ children = self._all_children_get()
+ if self.ignore:
+ children = filter(self.do_not_ignore, children)
+ return children
+
+ def all_children(self, scan=1):
+ """Return a list of all the node's direct children."""
+ if scan:
+ self.scan()
+ return self._all_children_get()
+
+ def children(self, scan=1):
+ """Return a list of the node's direct children, minus those
+ that are ignored by this node."""
+ if scan:
+ self.scan()
+ return self._children_get()
+
def set_state(self, state):
self.state = state
@@ -779,6 +876,8 @@ class Node:
rebind their current() method to this method."""
# Allow the children to calculate their signatures.
self.binfo = self.gen_binfo(calc)
+ if self.always_build:
+ return None
state = 0
for kid in self.children(None):
s = kid.get_state()
@@ -791,16 +890,6 @@ class Node:
the command interpreter literally."""
return 1
- def add_pre_action(self, act):
- """Adds an Action performed on this Node only before
- building it."""
- self.pre_actions.append(act)
-
- def add_post_action(self, act):
- """Adds and Action performed on this Node only after
- building it."""
- self.post_actions.append(act)
-
def render_include_tree(self):
"""
Return a text representation, suitable for displaying to the
@@ -810,8 +899,9 @@ class Node:
env = self.get_build_env()
for s in self.sources:
scanner = self.get_source_scanner(s)
- def f(node, env=env, scanner=scanner, target=self):
- return node.get_found_includes(env, scanner, target)
+ path = self.get_build_scanner_path(scanner)
+ def f(node, env=env, scanner=scanner, path=path):
+ return node.get_found_includes(env, scanner, path)
return SCons.Util.render_tree(s, f, 1)
else:
return None
@@ -884,53 +974,63 @@ class Node:
result[k] = s
try:
- old_bkids = old.bsources + old.bdepends + old.bimplicit
+ osig = {}
+ dictify(osig, old.bsources, old.bsourcesigs)
+ dictify(osig, old.bdepends, old.bdependsigs)
+ dictify(osig, old.bimplicit, old.bimplicitsigs)
except AttributeError:
return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self
- osig = {}
- dictify(osig, old.bsources, old.bsourcesigs)
- dictify(osig, old.bdepends, old.bdependsigs)
- dictify(osig, old.bimplicit, old.bimplicitsigs)
-
- new_bsources = map(str, self.binfo.bsources)
- new_bdepends = map(str, self.binfo.bdepends)
- new_bimplicit = map(str, self.binfo.bimplicit)
+ new = self.get_binfo()
nsig = {}
- dictify(nsig, new_bsources, self.binfo.bsourcesigs)
- dictify(nsig, new_bdepends, self.binfo.bdependsigs)
- dictify(nsig, new_bimplicit, self.binfo.bimplicitsigs)
+ dictify(nsig, new.bsources, new.bsourcesigs)
+ dictify(nsig, new.bdepends, new.bdependsigs)
+ dictify(nsig, new.bimplicit, new.bimplicitsigs)
+
+ old_bkids = old.bsources + old.bdepends + old.bimplicit
+ new_bkids = new.bsources + new.bdepends + new.bimplicit
- new_bkids = new_bsources + new_bdepends + new_bimplicit
- lines = map(lambda x: "`%s' is no longer a dependency\n" % x,
- filter(lambda x, nk=new_bkids: not x in nk, old_bkids))
+ # The sources and dependencies we'll want to report are all stored
+ # as relative paths to this target's directory, but we want to
+ # report them relative to the top-level SConstruct directory,
+ # so we only print them after running them through this lambda
+ # to turn them into the right relative Node and then return
+ # its string.
+ stringify = lambda s, E=self.dir.Entry: str(E(s))
+
+ lines = []
+
+ removed = filter(lambda x, nk=new_bkids: not x in nk, old_bkids)
+ if removed:
+ removed = map(stringify, removed)
+ fmt = "`%s' is no longer a dependency\n"
+ lines.extend(map(lambda s, fmt=fmt: fmt % s, removed))
for k in new_bkids:
if not k in old_bkids:
- lines.append("`%s' is a new dependency\n" % k)
+ lines.append("`%s' is a new dependency\n" % stringify(k))
elif osig[k] != nsig[k]:
- lines.append("`%s' changed\n" % k)
+ lines.append("`%s' changed\n" % stringify(k))
if len(lines) == 0 and old_bkids != new_bkids:
lines.append("the dependency order changed:\n" +
- "%sold: %s\n" % (' '*15, old_bkids) +
- "%snew: %s\n" % (' '*15, new_bkids))
+ "%sold: %s\n" % (' '*15, map(stringify, old_bkids)) +
+ "%snew: %s\n" % (' '*15, map(stringify, new_bkids)))
if len(lines) == 0:
- newact, newactsig = self.binfo.bact, self.binfo.bactsig
def fmt_with_title(title, strlines):
lines = string.split(strlines, '\n')
sep = '\n' + ' '*(15 + len(title))
return ' '*15 + title + string.join(lines, sep) + '\n'
- if old.bactsig != newactsig:
- if old.bact == newact:
+ if old.bactsig != new.bactsig:
+ if old.bact == new.bact:
lines.append("the contents of the build action changed\n" +
- fmt_with_title('action: ', newact))
+ fmt_with_title('action: ', new.bact))
else:
lines.append("the build action changed:\n" +
fmt_with_title('old: ', old.bact) +
- fmt_with_title('new: ', newact))
+ fmt_with_title('new: ', new.bact))
if len(lines) == 0:
return "rebuilding `%s' for unknown reasons\n" % self
@@ -942,6 +1042,28 @@ class Node:
lines = ["%s:\n" % preamble] + lines
return string.join(lines, ' '*11)
+l = [1]
+ul = UserList.UserList([2])
+try:
+ l.extend(ul)
+except TypeError:
+ def NodeList(l):
+ return l
+else:
+ class NodeList(UserList.UserList):
+ def __str__(self):
+ return str(map(str, self.data))
+del l
+del ul
+
+if SCons.Memoize.use_old_memoization():
+ _Base = Node
+ class Node(SCons.Memoize.Memoizer, _Base):
+ def __init__(self, *args, **kw):
+ apply(_Base.__init__, (self,)+args, kw)
+ SCons.Memoize.Memoizer.__init__(self)
+
+
def get_children(node, parent): return node.children()
def ignore_cycle(node, stack): pass
def do_nothing(node, parent): pass
diff --git a/src/engine/SCons/Platform/__init__.xml b/src/engine/SCons/Platform/__init__.xml
index a6cb720..0cdd62b 100644
--- a/src/engine/SCons/Platform/__init__.xml
+++ b/src/engine/SCons/Platform/__init__.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<cvar name="ESCAPE">
<summary>
A function that will be called to escape shell special characters in
@@ -84,10 +89,10 @@ The suffix used for executable file names.
<cvar name="SHELL">
<summary>
-A string naming the shell program that will be passed to the
+A string naming the shell program that will be passed to the
&cv-SPAWN;
-function.
-See the
+function.
+See the
&cv-SPAWN;
construction variable for more information.
</summary>
diff --git a/src/engine/SCons/Platform/posix.xml b/src/engine/SCons/Platform/posix.xml
index cb05e52..a1bb7fd 100644
--- a/src/engine/SCons/Platform/posix.xml
+++ b/src/engine/SCons/Platform/posix.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<cvar name="RPATH">
<summary>
A list of paths to search for shared libraries when running programs.
diff --git a/src/engine/SCons/Platform/win32.xml b/src/engine/SCons/Platform/win32.xml
index c893f0c..f284a40 100644
--- a/src/engine/SCons/Platform/win32.xml
+++ b/src/engine/SCons/Platform/win32.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<cvar name="MAXLINELENGTH">
<summary>
The maximum number of characters allowed on an external command line.
diff --git a/src/engine/SCons/Tool/386asm.xml b/src/engine/SCons/Tool/386asm.xml
index 2a9d993..bb36048 100644
--- a/src/engine/SCons/Tool/386asm.xml
+++ b/src/engine/SCons/Tool/386asm.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="386asm">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/BitKeeper.xml b/src/engine/SCons/Tool/BitKeeper.xml
index 43841a0..3fef72d 100644
--- a/src/engine/SCons/Tool/BitKeeper.xml
+++ b/src/engine/SCons/Tool/BitKeeper.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="BitKeeper">
<summary>
XXX
@@ -14,7 +19,7 @@ The BitKeeper executable.
<cvar name="BITKEEPERCOM">
<summary>
The command line for
-fetching source files using BitKEeper.
+fetching source files using BitKeeper.
</summary>
</cvar>
diff --git a/src/engine/SCons/Tool/CVS.xml b/src/engine/SCons/Tool/CVS.xml
index 9a07294..9ad5fb5 100644
--- a/src/engine/SCons/Tool/CVS.xml
+++ b/src/engine/SCons/Tool/CVS.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="CVS">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/Perforce.xml b/src/engine/SCons/Tool/Perforce.xml
index 247330e..87565af 100644
--- a/src/engine/SCons/Tool/Perforce.xml
+++ b/src/engine/SCons/Tool/Perforce.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="Perforce">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/RCS.xml b/src/engine/SCons/Tool/RCS.xml
index 40d972f..5e3b83d 100644
--- a/src/engine/SCons/Tool/RCS.xml
+++ b/src/engine/SCons/Tool/RCS.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="RCS">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/SCCS.xml b/src/engine/SCons/Tool/SCCS.xml
index 08aa949..822547c 100644
--- a/src/engine/SCons/Tool/SCCS.xml
+++ b/src/engine/SCons/Tool/SCCS.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="SCCS">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/Subversion.xml b/src/engine/SCons/Tool/Subversion.xml
index e5560d1..c3a4f5f 100644
--- a/src/engine/SCons/Tool/Subversion.xml
+++ b/src/engine/SCons/Tool/Subversion.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="Subversion">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/__init__.xml b/src/engine/SCons/Tool/__init__.xml
index 86b830c..0cce79b 100644
--- a/src/engine/SCons/Tool/__init__.xml
+++ b/src/engine/SCons/Tool/__init__.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<builder name="CFile">
<summary>
Builds a C source file given a lex (<filename>.l</filename>)
@@ -6,7 +11,8 @@ or yacc (<filename>.y</filename>) input file.
The suffix specified by the &cv-CFILESUFFIX; construction variable
(<filename>.c</filename> by default)
is automatically added to the target
-if it is not already present. Example:
+if it is not already present.
+Example:
<example>
# builds foo.c
@@ -25,7 +31,8 @@ input file.
The suffix specified by the &cv-CXXFILESUFFIX; construction variable
(<filename>.cc</filename> by default)
is automatically added to the target
-if it is not already present. Example:
+if it is not already present.
+Example:
<example>
# builds foo.cc
@@ -44,6 +51,16 @@ builder method.
</summary>
</builder>
+<builder name="LoadableModule">
+<summary>
+On most systems,
+this is the same as
+&b-SharedLibrary;.
+On Mac OS X (Darwin) platforms,
+this creates a loadable module bundle.
+</summary>
+</builder>
+
<builder name="Object">
<summary>
A synonym for the
@@ -173,7 +190,7 @@ The target object file prefix
by default, the same as &cv-OBJPREFIX;)
and suffix
(specified by the &cv-SHOBJSUFFIX; construction variable)
-are automatically added to the target if not already present.
+are automatically added to the target if not already present.
Examples:
<example>
@@ -181,6 +198,13 @@ env.SharedObject(target = 'ddd', source = 'ddd.c')
env.SharedObject(target = 'eee.o', source = 'eee.cpp')
env.SharedObject(target = 'fff.obj', source = 'fff.for')
</example>
+
+Note that the source files will be scanned
+according to the suffix mappings in the
+<literal>SourceFileScanner</literal>
+object.
+See the section "Scanner Objects,"
+below, for a more information.
</summary>
</builder>
@@ -245,6 +269,8 @@ Source files must have one of the following extensions:
.FOR Fortran file
.fpp Fortran file + C pre-processor
.FPP Fortran file + C pre-processor
+ .m Object C file
+ .mm Object C++ file
.s assembly language file
.S WIN32: assembly language file
POSIX: assembly language file + C pre-processor
@@ -266,6 +292,13 @@ env.StaticObject(target = 'aaa', source = 'aaa.c')
env.StaticObject(target = 'bbb.o', source = 'bbb.c++')
env.StaticObject(target = 'ccc.obj', source = 'ccc.f')
</example>
+
+Note that the source files will be scanned
+according to the suffix mappings in
+<literal>SourceFileScanner</literal>
+object.
+See the section "Scanner Objects,"
+below, for a more information.
</summary>
</builder>
@@ -314,7 +347,10 @@ SCons also treats files with the suffixes
<filename>.c++</filename>,
and
<filename>.C++</filename>
-as C++ files.
+as C++ files,
+and files with
+<filename>.mm</filename>
+suffixes as Objective C++ files.
On case-sensitive systems (Linux, UNIX, and other POSIX-alikes),
SCons also treats
<filename>.C</filename>
diff --git a/src/engine/SCons/Tool/aixc++.xml b/src/engine/SCons/Tool/aixc++.xml
index d386779..2f03aa6 100644
--- a/src/engine/SCons/Tool/aixc++.xml
+++ b/src/engine/SCons/Tool/aixc++.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="aixc++">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/aixcc.xml b/src/engine/SCons/Tool/aixcc.xml
index cb27adb..a7a043c 100644
--- a/src/engine/SCons/Tool/aixcc.xml
+++ b/src/engine/SCons/Tool/aixcc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="aixcc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/aixf77.xml b/src/engine/SCons/Tool/aixf77.xml
index 7e7988c..9043b66 100644
--- a/src/engine/SCons/Tool/aixf77.xml
+++ b/src/engine/SCons/Tool/aixf77.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="aixf77">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/aixlink.xml b/src/engine/SCons/Tool/aixlink.xml
index 378dcbf..a1aa05b 100644
--- a/src/engine/SCons/Tool/aixlink.xml
+++ b/src/engine/SCons/Tool/aixlink.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="aixlink">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/applelink.xml b/src/engine/SCons/Tool/applelink.xml
index 13eea31..b9ea63d 100644
--- a/src/engine/SCons/Tool/applelink.xml
+++ b/src/engine/SCons/Tool/applelink.xml
@@ -1,4 +1,9 @@
-<!-- Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<cvar name="FRAMEWORKSFLAGS">">
<summary>
On Mac OS X with gcc,
diff --git a/src/engine/SCons/Tool/ar.xml b/src/engine/SCons/Tool/ar.xml
index de3246f..baebd27 100644
--- a/src/engine/SCons/Tool/ar.xml
+++ b/src/engine/SCons/Tool/ar.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="ar">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/as.xml b/src/engine/SCons/Tool/as.xml
index a9395df..a2447bd 100644
--- a/src/engine/SCons/Tool/as.xml
+++ b/src/engine/SCons/Tool/as.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="as">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/bcc32.xml b/src/engine/SCons/Tool/bcc32.xml
index ded614c..1cdb9c0 100644
--- a/src/engine/SCons/Tool/bcc32.xml
+++ b/src/engine/SCons/Tool/bcc32.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="bcc32">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/c++.xml b/src/engine/SCons/Tool/c++.xml
index eaa8e6c..eb0a8fe 100644
--- a/src/engine/SCons/Tool/c++.xml
+++ b/src/engine/SCons/Tool/c++.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="c++">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/cc.xml b/src/engine/SCons/Tool/cc.xml
index fb688ad..84980db 100644
--- a/src/engine/SCons/Tool/cc.xml
+++ b/src/engine/SCons/Tool/cc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="cc">
<summary>
XXX
@@ -71,6 +76,7 @@ The default list is:
[".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
".h", ".H", ".hxx", ".hpp", ".hh",
".F", ".fpp", ".FPP",
+ ".m", ".mm",
".S", ".spp", ".SPP"]
</example>
</summary>
diff --git a/src/engine/SCons/Tool/cvf.xml b/src/engine/SCons/Tool/cvf.xml
index bd848ae..9acde9b 100644
--- a/src/engine/SCons/Tool/cvf.xml
+++ b/src/engine/SCons/Tool/cvf.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="cvf">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/default.xml b/src/engine/SCons/Tool/default.xml
index 371e5bc..a4da87a 100644
--- a/src/engine/SCons/Tool/default.xml
+++ b/src/engine/SCons/Tool/default.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="default">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/dmd.xml b/src/engine/SCons/Tool/dmd.xml
index 99ca8d9..62ab78e 100644
--- a/src/engine/SCons/Tool/dmd.xml
+++ b/src/engine/SCons/Tool/dmd.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="dmd">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/dvipdf.xml b/src/engine/SCons/Tool/dvipdf.xml
index fbb4933..4d82ffe 100644
--- a/src/engine/SCons/Tool/dvipdf.xml
+++ b/src/engine/SCons/Tool/dvipdf.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="dvipdf">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/dvips.xml b/src/engine/SCons/Tool/dvips.xml
index c290469..cb0d7d8 100644
--- a/src/engine/SCons/Tool/dvips.xml
+++ b/src/engine/SCons/Tool/dvips.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="dvips">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/f77.xml b/src/engine/SCons/Tool/f77.xml
index caf959f..c077175 100644
--- a/src/engine/SCons/Tool/f77.xml
+++ b/src/engine/SCons/Tool/f77.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="f77">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/f90.xml b/src/engine/SCons/Tool/f90.xml
index b2f0857..5c252cc 100644
--- a/src/engine/SCons/Tool/f90.xml
+++ b/src/engine/SCons/Tool/f90.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="f90">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/f95.xml b/src/engine/SCons/Tool/f95.xml
index df74422..86331a4 100644
--- a/src/engine/SCons/Tool/f95.xml
+++ b/src/engine/SCons/Tool/f95.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="f95">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/fortran.xml b/src/engine/SCons/Tool/fortran.xml
index bfc34fa..c472343 100644
--- a/src/engine/SCons/Tool/fortran.xml
+++ b/src/engine/SCons/Tool/fortran.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="fortran">
<summary>
XXX
@@ -16,7 +21,7 @@ for all versions of Fortran.
<summary>
The command line used to compile a Fortran source file to an object file.
By default, any options specified
-in the &cv-FORTRANFLAGS;, &cv-CPPFLAGS;, &cv-_CPPDEFFLAGS;,
+in the &cv-FORTRANFLAGS;, &cv-CPPFLAGS;, &cv-_CPPDEFFLAGS;,
&cv-_FORTRANMODFLAG;, and &cv-_FORTRANINCFLAGS; construction variables
are included on this command line.
</summary>
@@ -51,7 +56,7 @@ for the variables that expand those options.
<summary>
An automatically-generated construction variable
containing the Fortran compiler command-line options
-for specifying directories to be searched for include
+for specifying directories to be searched for include
files and module files.
The value of &cv-_FORTRANINCFLAGS; is created
by prepending/appending &cv-INCPREFIX; and &cv-INCSUFFIX;
@@ -63,8 +68,8 @@ of each directory in &cv-FORTRANPATH;.
<cvar name="FORTRANMODDIR">
<summary>
Directory location where the Fortran compiler should place
-any module files it generates. This variable is empty, by default. Some
-Fortran compilers will internally append this directory in the search path
+any module files it generates. This variable is empty, by default. Some
+Fortran compilers will internally append this directory in the search path
for module files, as well.
</summary>
</cvar>
@@ -94,7 +99,7 @@ when the &cv-_FORTRANMODFLAG; variables is automatically generated.
An automatically-generated construction variable
containing the Fortran compiler command-line option
for specifying the directory location where the Fortran
-compiler should place any module files that happen to get
+compiler should place any module files that happen to get
generated during compilation.
The value of &cv-_FORTRANMODFLAG; is created
by prepending/appending &cv-FORTRANMODDIRPREFIX; and &cv-FORTRANMODDIRSUFFIX;
@@ -146,7 +151,7 @@ to look-up a directory relative to the root of the source tree use #:
env = Environment(FORTRANPATH='#/include')
</example>
-The directory look-up can also be forced using the
+The directory look-up can also be forced using the
&Dir;()
function:
@@ -178,7 +183,7 @@ env = Environment(FORTRANCOM="my_compiler $_FORTRANINCFLAGS -c -o $TARGET $SOURC
<cvar name="FORTRANPPCOM">
<summary>
The command line used to compile a Fortran source file to an object file
-after first running the file through the C preprocessor.
+after first running the file through the C preprocessor.
By default, any options specified in the &cv-FORTRANFLAGS;, &cv-CPPFLAGS;,
_CPPDEFFLAGS, &cv-_FORTRANMODFLAG;, and &cv-_FORTRANINCFLAGS;
construction variables are included on this command line.
diff --git a/src/engine/SCons/Tool/g++.xml b/src/engine/SCons/Tool/g++.xml
index b6fb5b0..4991811 100644
--- a/src/engine/SCons/Tool/g++.xml
+++ b/src/engine/SCons/Tool/g++.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="g++">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/g77.xml b/src/engine/SCons/Tool/g77.xml
index 94f415a..6357f94 100644
--- a/src/engine/SCons/Tool/g77.xml
+++ b/src/engine/SCons/Tool/g77.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="g77">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/gas.xml b/src/engine/SCons/Tool/gas.xml
index 29179e2..19ad1a9 100644
--- a/src/engine/SCons/Tool/gas.xml
+++ b/src/engine/SCons/Tool/gas.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="gas">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/gcc.xml b/src/engine/SCons/Tool/gcc.xml
index 51dac07..15f19cf 100644
--- a/src/engine/SCons/Tool/gcc.xml
+++ b/src/engine/SCons/Tool/gcc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="gcc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/gnulink.xml b/src/engine/SCons/Tool/gnulink.xml
index d7c1a42..d454af4 100644
--- a/src/engine/SCons/Tool/gnulink.xml
+++ b/src/engine/SCons/Tool/gnulink.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="gnulink">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/gs.xml b/src/engine/SCons/Tool/gs.xml
index 377bfc3..d4840d6 100644
--- a/src/engine/SCons/Tool/gs.xml
+++ b/src/engine/SCons/Tool/gs.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="gs">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/hpc++.xml b/src/engine/SCons/Tool/hpc++.xml
index 5c13718..ca0314c 100644
--- a/src/engine/SCons/Tool/hpc++.xml
+++ b/src/engine/SCons/Tool/hpc++.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="hpc++">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/hpcc.xml b/src/engine/SCons/Tool/hpcc.xml
index db2fccd..9f63b55 100644
--- a/src/engine/SCons/Tool/hpcc.xml
+++ b/src/engine/SCons/Tool/hpcc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="hpcc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/hplink.xml b/src/engine/SCons/Tool/hplink.xml
index 71f5e22..1bd3b78 100644
--- a/src/engine/SCons/Tool/hplink.xml
+++ b/src/engine/SCons/Tool/hplink.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="hplink">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/icc.xml b/src/engine/SCons/Tool/icc.xml
index a6fdea2..c41486e 100644
--- a/src/engine/SCons/Tool/icc.xml
+++ b/src/engine/SCons/Tool/icc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="icc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/icl.xml b/src/engine/SCons/Tool/icl.xml
index be20912..5b8c355 100644
--- a/src/engine/SCons/Tool/icl.xml
+++ b/src/engine/SCons/Tool/icl.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="icl">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/ifl.xml b/src/engine/SCons/Tool/ifl.xml
index 34dfecb..4b5b912 100644
--- a/src/engine/SCons/Tool/ifl.xml
+++ b/src/engine/SCons/Tool/ifl.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="ifl">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/ifort.xml b/src/engine/SCons/Tool/ifort.xml
index 2cb889b..0303dec 100644
--- a/src/engine/SCons/Tool/ifort.xml
+++ b/src/engine/SCons/Tool/ifort.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="ifort">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/ilink.xml b/src/engine/SCons/Tool/ilink.xml
index 4f86883..03acfa4 100644
--- a/src/engine/SCons/Tool/ilink.xml
+++ b/src/engine/SCons/Tool/ilink.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="ilink">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/ilink32.xml b/src/engine/SCons/Tool/ilink32.xml
index 0d62be4..370d046 100644
--- a/src/engine/SCons/Tool/ilink32.xml
+++ b/src/engine/SCons/Tool/ilink32.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="ilink32">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/intelc.xml b/src/engine/SCons/Tool/intelc.xml
index 47cfbec..0141c22 100644
--- a/src/engine/SCons/Tool/intelc.xml
+++ b/src/engine/SCons/Tool/intelc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="intelc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/jar.xml b/src/engine/SCons/Tool/jar.xml
index b89468d..45d9ebc 100644
--- a/src/engine/SCons/Tool/jar.xml
+++ b/src/engine/SCons/Tool/jar.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="jar">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/javac.xml b/src/engine/SCons/Tool/javac.xml
index 9cd40b5..86fd507 100644
--- a/src/engine/SCons/Tool/javac.xml
+++ b/src/engine/SCons/Tool/javac.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="javac">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/javah.xml b/src/engine/SCons/Tool/javah.xml
index 7699784..84d0068 100644
--- a/src/engine/SCons/Tool/javah.xml
+++ b/src/engine/SCons/Tool/javah.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="javah">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/latex.xml b/src/engine/SCons/Tool/latex.xml
index b8b1bb3..96c0f4a 100644
--- a/src/engine/SCons/Tool/latex.xml
+++ b/src/engine/SCons/Tool/latex.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="latex">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/lex.xml b/src/engine/SCons/Tool/lex.xml
index 3fb62e1..51401dc 100644
--- a/src/engine/SCons/Tool/lex.xml
+++ b/src/engine/SCons/Tool/lex.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="lex">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/link.xml b/src/engine/SCons/Tool/link.xml
index 420a5b7..52694ad 100644
--- a/src/engine/SCons/Tool/link.xml
+++ b/src/engine/SCons/Tool/link.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="link">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/linkloc.xml b/src/engine/SCons/Tool/linkloc.xml
index 12a9d50..58ffc48 100644
--- a/src/engine/SCons/Tool/linkloc.xml
+++ b/src/engine/SCons/Tool/linkloc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="linkloc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/m4.xml b/src/engine/SCons/Tool/m4.xml
index 1f91770..7a0f9c3 100644
--- a/src/engine/SCons/Tool/m4.xml
+++ b/src/engine/SCons/Tool/m4.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="m4">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/masm.xml b/src/engine/SCons/Tool/masm.xml
index 82c14cf..ee1ed1d 100644
--- a/src/engine/SCons/Tool/masm.xml
+++ b/src/engine/SCons/Tool/masm.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="masm">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/midl.xml b/src/engine/SCons/Tool/midl.xml
index 4d5c7e0..8885785 100644
--- a/src/engine/SCons/Tool/midl.xml
+++ b/src/engine/SCons/Tool/midl.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="midl">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/mingw.xml b/src/engine/SCons/Tool/mingw.xml
index 273a861..9f9cf1f 100644
--- a/src/engine/SCons/Tool/mingw.xml
+++ b/src/engine/SCons/Tool/mingw.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="mingw">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/mslib.xml b/src/engine/SCons/Tool/mslib.xml
index 7da4365..9d56fd5 100644
--- a/src/engine/SCons/Tool/mslib.xml
+++ b/src/engine/SCons/Tool/mslib.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="mslib">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/mslink.xml b/src/engine/SCons/Tool/mslink.xml
index 82249e7..2b901c6 100644
--- a/src/engine/SCons/Tool/mslink.xml
+++ b/src/engine/SCons/Tool/mslink.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="mslink">
<summary>
XXX
@@ -26,7 +31,8 @@ tools other than Microsoft Visual C++.
When this variable is
defined SCons will add options to the compiler and linker command line to
cause them to generate external debugging information, and will also set up the
-dependencies for the PDB file. Example:
+dependencies for the PDB file.
+Example:
<example>
env['PDB'] = 'hello.pdb'
diff --git a/src/engine/SCons/Tool/msvc.xml b/src/engine/SCons/Tool/msvc.xml
index 76529b1..252d962 100644
--- a/src/engine/SCons/Tool/msvc.xml
+++ b/src/engine/SCons/Tool/msvc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="msvc">
<summary>
XXX
@@ -12,7 +17,7 @@ Calling this builder method
returns a list of two targets: the PCH as the first element, and the object
file as the second element. Normally the object file is ignored.
This builder method is only
-provided when Microsoft Visual C++ is being used as the compiler.
+provided when Microsoft Visual C++ is being used as the compiler.
The PCH builder method is generally used in
conjuction with the PCH construction variable to force object files to use
the precompiled header:
@@ -29,11 +34,12 @@ Builds a Microsoft Visual C++ resource file.
This builder method is only provided
when Microsoft Visual C++ or MinGW is being used as the compiler. The
<filename>.res</filename>
-(or
+(or
<filename>.o</filename>
for MinGW) suffix is added to the target name if no other suffix is given.
The source
-file is scanned for implicit dependencies as though it were a C file. Example:
+file is scanned for implicit dependencies as though it were a C file.
+Example:
<example>
env.RES('resource.rc')
@@ -48,7 +54,8 @@ object files. This variable is ignored by tools other than Microsoft Visual C++.
When this variable is
defined SCons will add options to the compiler command line to
cause it to use the precompiled header, and will also set up the
-dependencies for the PCH file. Example:
+dependencies for the PCH file.
+Example:
<example>
env['PCH'] = 'StdAfx.pch'
diff --git a/src/engine/SCons/Tool/msvs.xml b/src/engine/SCons/Tool/msvs.xml
index 9af0960..babc20f 100644
--- a/src/engine/SCons/Tool/msvs.xml
+++ b/src/engine/SCons/Tool/msvs.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="msvs">
<summary>
XXX
@@ -12,7 +17,7 @@ and by default builds a solution file as well.
This builds a Visual Studio project file, based on the version of
Visual Studio that is configured (either the latest installed version,
-or the version specified by
+or the version specified by
&cv-MSVS_VERSION;
in the Environment constructor).
For Visual Studio 6, it will generate a
@@ -43,7 +48,7 @@ Builder (see below).
It takes several lists of filenames to be placed into the project
file.
-These are currently these are limited to
+These are currently limited to
<literal>srcs</literal>,
<literal>incs</literal>,
<literal>localincs</literal>,
@@ -60,7 +65,8 @@ In addition to the above lists of values (which are all optional,
although not specifying any of them results in an empty project file),
the following values may be specified:
-<literal>target</literal>: The name of the target
+<literal>target</literal>:
+The name of the target
<filename>.dsp</filename>
or
<filename>.vcproj</filename>
@@ -88,7 +94,7 @@ character:
The default target platform is Win32.
Multiple calls to
&b-MSVSProject;
- with different variants are allowed;
+with different variants are allowed;
all variants will be added to the project file with their appropriate
build targets and sources.
@@ -133,15 +139,15 @@ Builds a Microsoft Visual Studio solution file.
This builds a Visual Studio solution file,
based on the version of Visual Studio that is configured
(either the latest installed version,
-or the version specified by
+or the version specified by
&cv-MSVS_VERSION;
in the construction environment).
For Visual Studio 6, it will generate a
-<filename>dsw</filename>
+<filename>.dsw</filename>
file.
For Visual Studio 7 (.NET), it will
generate a
-<filename>sln</filename>
+<filename>.sln</filename>
file.
The following values must be specified:
@@ -185,35 +191,35 @@ local.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'],
When the Microsoft Visual Studio tools are initialized, they set up
this dictionary with the following keys:
-<envar>VERSION</envar>
+<envar>VERSION</envar>:
the version of MSVS being used (can be set via
MSVS_VERSION)
-<envar>VERSIONS</envar>
+<envar>VERSIONS</envar>:
the available versions of MSVS installed
-<envar>VCINSTALLDIR</envar>
+<envar>VCINSTALLDIR</envar>:
installed directory of Visual C++
-<envar>VSINSTALLDIR</envar>
+<envar>VSINSTALLDIR</envar>:
installed directory of Visual Studio
-<envar>FRAMEWORKDIR</envar>
+<envar>FRAMEWORKDIR</envar>:
installed directory of the .NET framework
-<envar>FRAMEWORKVERSIONS</envar>
+<envar>FRAMEWORKVERSIONS</envar>:
list of installed versions of the .NET framework, sorted latest to oldest.
-<envar>FRAMEWORKVERSION</envar>
+<envar>FRAMEWORKVERSION</envar>:
latest installed version of the .NET framework
-<envar>FRAMEWORKSDKDIR</envar>
+<envar>FRAMEWORKSDKDIR</envar>:
installed location of the .NET SDK.
-<envar>PLATFORMSDKDIR</envar>
+<envar>PLATFORMSDKDIR</envar>:
installed location of the Platform SDK.
-<envar>PLATFORMSDK_MODULES</envar>
+<envar>PLATFORMSDK_MODULES</envar>:
dictionary of installed Platform SDK modules,
where the dictionary keys are keywords for the various modules, and
the values are 2-tuples where the first is the release date, and the
@@ -239,18 +245,18 @@ minimal set of paths needed to run the tools successfully.
For VS6, the mininimal set is:
<example>
- INCLUDE:'VSDir\VC98\ATL\include;VSDir\VC98\MFC\include;VSDir\VC98\include'
- LIB:'VSDir\VC98\MFC\lib;VSDir\VC98\lib'
- PATH:'VSDir\Common\MSDev98\bin;VSDir\VC98\bin'
+ INCLUDE:'&lt;VSDir&gt;\VC98\ATL\include;&lt;VSDir&gt;\VC98\MFC\include;&lt;VSDir&gt;\VC98\include'
+ LIB:'&lt;VSDir&gt;\VC98\MFC\lib;&lt;VSDir&gt;\VC98\lib'
+ PATH:'&lt;VSDir&gt;\Common\MSDev98\bin;&lt;VSDir&gt;\VC98\bin'
</example>
For VS7, it is:
<example>
- INCLUDE:'VSDir\Vc7\atlmfc\include;VSDir\Vc7\include'
- LIB:'VSDir\Vc7\atlmfc\lib;VSDir\Vc7\lib'
- PATH:'VSDir\Common7\Tools\bin;VSDir\Common7\Tools;VSDir\Vc7\bin'
+ INCLUDE:'&lt;VSDir&gt;\Vc7\atlmfc\include;&lt;VSDir&gt;\Vc7\include'
+ LIB:'&lt;VSDir&gt;\Vc7\atlmfc\lib;&lt;VSDir&gt;\Vc7\lib'
+ PATH:'&lt;VSDir&gt;\Common7\Tools\bin;&lt;VSDir&gt;\Common7\Tools;&lt;VSDir&gt;\Vc7\bin'
</example>
-Where 'VSDir' is the installed location of Visual Studio.
+Where '&lt;VSDir&gt;' is the installed location of Visual Studio.
</summary>
</cvar>
@@ -260,7 +266,7 @@ The string
placed in a generated Microsoft Visual Studio solution file
as the value of the
<literal>SccProjectFilePathRelativizedFromConnection0</literal>
-and
+and
<literal>SccProjectFilePathRelativizedFromConnection1</literal>
attributes of the
<literal>GlobalSection(SourceCodeControl)</literal>
@@ -385,7 +391,7 @@ directory to the default
<envar>LIB</envar>
external environment variable.
The current default value is
-<literal>1</literal>
+<literal>1</literal>,
which means these directories
are added to the paths by default.
This default value is likely to change
@@ -413,7 +419,7 @@ installed on your machine.
So, if you have version 6 and version 7 (MSVS .NET) installed,
it will prefer version 7.
You can override this by
-specifying the
+specifying the
<envar>MSVS_VERSION</envar>
variable in the Environment initialization, setting it to the
appropriate version ('6.0' or '7.0', for example).
diff --git a/src/engine/SCons/Tool/mwcc.xml b/src/engine/SCons/Tool/mwcc.xml
index 83eaab3..443eda7 100644
--- a/src/engine/SCons/Tool/mwcc.xml
+++ b/src/engine/SCons/Tool/mwcc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="mwcc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/mwld.xml b/src/engine/SCons/Tool/mwld.xml
index ade9848..9943927 100644
--- a/src/engine/SCons/Tool/mwld.xml
+++ b/src/engine/SCons/Tool/mwld.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="mwld">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/nasm.xml b/src/engine/SCons/Tool/nasm.xml
index 328386e..071f537 100644
--- a/src/engine/SCons/Tool/nasm.xml
+++ b/src/engine/SCons/Tool/nasm.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="nasm">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/pdflatex.xml b/src/engine/SCons/Tool/pdflatex.xml
index 7878079..e9fe793 100644
--- a/src/engine/SCons/Tool/pdflatex.xml
+++ b/src/engine/SCons/Tool/pdflatex.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="pdflatex">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/pdftex.xml b/src/engine/SCons/Tool/pdftex.xml
index 701a280..47e4138 100644
--- a/src/engine/SCons/Tool/pdftex.xml
+++ b/src/engine/SCons/Tool/pdftex.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="pdftex">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/qt.xml b/src/engine/SCons/Tool/qt.xml
index b5335b6..6a4ac78 100644
--- a/src/engine/SCons/Tool/qt.xml
+++ b/src/engine/SCons/Tool/qt.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="qt">
<summary>
XXX
@@ -7,8 +12,8 @@ XXX
<builder name="Moc">
<summary>
-Builds an output file from a moc input file. Moc input files are either
-header files or cxx files. This builder is only available after using the
+Builds an output file from a moc input file. Moc input files are either
+header files or cxx files. This builder is only available after using the
tool 'qt'. See the &cv-QTDIR; variable for more information.
Example:
@@ -23,7 +28,7 @@ env.Moc('foo.cpp') # generates foo.moc
<summary>
Builds a header file, an implementation file and a moc file from an ui file.
and returns the corresponding nodes in the above order.
-This builder is only available after using the tool 'qt'. Note: you can
+This builder is only available after using the tool 'qt'. Note: you can
specify <filename>.ui</filename> files directly as source
files to the &b-Program;,
&b-Library; and &b-SharedLibrary; builders
@@ -63,38 +68,41 @@ Environment(tools=['default','qt'])
The qt tool supports the following operations:
-.B Automatic moc file generation from header files.
+<emphasis Role="strong">Automatic moc file generation from header files.</emphasis>
You do not have to specify moc files explicitly, the tool does it for you.
However, there are a few preconditions to do so: Your header file must have
the same filebase as your implementation file and must stay in the same
-directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. You
+directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. You
can turn off automatic moc file generation by setting QT_AUTOSCAN to 0.
See also the corresponding builder method
.B Moc()
-.B Automatic moc file generation from cxx files.
-As stated in the qt documentation, include the moc file at the end of
+<emphasis Role="strong">Automatic moc file generation from cxx files.</emphasis>
+As stated in the qt documentation, include the moc file at the end of
the cxx file. Note that you have to include the file, which is generated
-by the transformation ${QT_MOCCXXPREFIX}basename${QT_MOCCXXSUFFIX}, by default
-basename.moc. A warning is generated after building the moc file, if you
-do not include the correct file. If you are using BuildDir, you may
-need to specify duplicate=1. You can turn off automatic moc file generation
-by setting QT_AUTOSCAN to 0. See also the corresponding builder method
-.B Moc()
-
-.B Automatic handling of .ui files.
+by the transformation ${QT_MOCCXXPREFIX}&lt;basename&gt;${QT_MOCCXXSUFFIX}, by default
+&lt;basename&gt;.moc. A warning is generated after building the moc file, if you
+do not include the correct file. If you are using BuildDir, you may
+need to specify duplicate=1. You can turn off automatic moc file generation
+by setting QT_AUTOSCAN to 0. See also the corresponding
+&b-Moc;
+builder method.
+
+<emphasis Role="strong">Automatic handling of .ui files.</emphasis>
The implementation files generated from .ui files are handled much the same
as yacc or lex files. Each .ui file given as a source of Program, Library or
-SharedLibrary will generate three files, the declaration file, the
-implementation file and a moc file. Because there are also generated headers,
-you may need to specify duplicate=1 in calls to BuildDir. See also the corresponding builder method
-.B Uic()
+SharedLibrary will generate three files, the declaration file, the
+implementation file and a moc file. Because there are also generated headers,
+you may need to specify duplicate=1 in calls to BuildDir.
+See also the corresponding
+&b-Uic;
+builder method.
</summary>
</cvar>
<cvar name="QT_AUTOSCAN">
<summary>
-Turn off scanning for mocable files. Use the Moc Builder to explicitely
+Turn off scanning for mocable files. Use the Moc Builder to explicitely
specify files to run moc on.
</summary>
</cvar>
@@ -131,7 +139,7 @@ this variable to None, the tool won't change the &cv-LIBS; variable.
<cvar name="QT_LIBPATH">
<summary>
The path where the qt libraries are installed.
-The default value is '&cv-QTDIR;/lib'.
+The default value is '&cv-QTDIR;/lib'.
Note: If you set this variable to None, the tool won't change the &cv-LIBPATH;
construction variable.
</summary>
@@ -139,7 +147,7 @@ construction variable.
<cvar name="QT_MOC">
<summary>
-Default value is '&cv-QT_BINPATH;/bin/moc'.
+Default value is '&cv-QT_BINPATH;/moc'.
</summary>
</cvar>
@@ -151,7 +159,7 @@ Default value is ''. Prefix for moc output files, when source is a cxx file.
<cvar name="QT_MOCCXXSUFFIX">
<summary>
-Default value is '.moc'. Suffix for moc output files, when source is a cxx
+Default value is '.moc'. Suffix for moc output files, when source is a cxx
file.
</summary>
</cvar>
@@ -262,7 +270,7 @@ Default value is 'uic_'. Prefix for uic generated implementation files.
<cvar name="QT_UICIMPLSUFFIX">
<summary>
-Default value is '&cv-CXXFILESUFFIX;'. Suffix for uic generated implementation
+Default value is '&cv-CXXFILESUFFIX;'. Suffix for uic generated implementation
files.
</summary>
</cvar>
diff --git a/src/engine/SCons/Tool/rmic.xml b/src/engine/SCons/Tool/rmic.xml
index 390aaaf..1fe4bd8 100644
--- a/src/engine/SCons/Tool/rmic.xml
+++ b/src/engine/SCons/Tool/rmic.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="rmic">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/rpcgen.xml b/src/engine/SCons/Tool/rpcgen.xml
index 9fdf13e..74fc3be 100644
--- a/src/engine/SCons/Tool/rpcgen.xml
+++ b/src/engine/SCons/Tool/rpcgen.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="rpcgen">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/sgiar.xml b/src/engine/SCons/Tool/sgiar.xml
index cd1d414..ccea1ca 100644
--- a/src/engine/SCons/Tool/sgiar.xml
+++ b/src/engine/SCons/Tool/sgiar.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="sgiar">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/sgic++.xml b/src/engine/SCons/Tool/sgic++.xml
index 318ebbf..a507f79 100644
--- a/src/engine/SCons/Tool/sgic++.xml
+++ b/src/engine/SCons/Tool/sgic++.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="sgic++">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/sgicc.xml b/src/engine/SCons/Tool/sgicc.xml
index bd752ec..285ae8c 100644
--- a/src/engine/SCons/Tool/sgicc.xml
+++ b/src/engine/SCons/Tool/sgicc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="sgicc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/sgilink.xml b/src/engine/SCons/Tool/sgilink.xml
index 64eecb8..8fa8f7d 100644
--- a/src/engine/SCons/Tool/sgilink.xml
+++ b/src/engine/SCons/Tool/sgilink.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="sgilink">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/sunar.xml b/src/engine/SCons/Tool/sunar.xml
index b94443c..e22563d 100644
--- a/src/engine/SCons/Tool/sunar.xml
+++ b/src/engine/SCons/Tool/sunar.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="sunar">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/sunc++.xml b/src/engine/SCons/Tool/sunc++.xml
index fcd6b0a..c18dd90 100644
--- a/src/engine/SCons/Tool/sunc++.xml
+++ b/src/engine/SCons/Tool/sunc++.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="sunc++">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/suncc.xml b/src/engine/SCons/Tool/suncc.xml
index ab86294..9f6d340 100644
--- a/src/engine/SCons/Tool/suncc.xml
+++ b/src/engine/SCons/Tool/suncc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="suncc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/sunlink.xml b/src/engine/SCons/Tool/sunlink.xml
index 127651e..6cad24f 100644
--- a/src/engine/SCons/Tool/sunlink.xml
+++ b/src/engine/SCons/Tool/sunlink.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="sunlink">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/swig.xml b/src/engine/SCons/Tool/swig.xml
index 5c08412..0bdda96 100644
--- a/src/engine/SCons/Tool/swig.xml
+++ b/src/engine/SCons/Tool/swig.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="swig">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/tar.xml b/src/engine/SCons/Tool/tar.xml
index c00910a..88265dc 100644
--- a/src/engine/SCons/Tool/tar.xml
+++ b/src/engine/SCons/Tool/tar.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="tar">
<summary>
XXX
@@ -17,6 +22,12 @@ for a given target;
each additional call
adds to the list of entries
that will be built into the archive.
+Any source directories will
+be scanned for changes to
+any on-disk files,
+regardless of whether or not
+&scons;
+knows about them from other Builder or function calls.
<example>
env.Tar('src.tar', 'src')
diff --git a/src/engine/SCons/Tool/tex.xml b/src/engine/SCons/Tool/tex.xml
index df7196e..b30b36d 100644
--- a/src/engine/SCons/Tool/tex.xml
+++ b/src/engine/SCons/Tool/tex.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="tex">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/tlib.xml b/src/engine/SCons/Tool/tlib.xml
index 0351568..9415c7d 100644
--- a/src/engine/SCons/Tool/tlib.xml
+++ b/src/engine/SCons/Tool/tlib.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="tlib">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/yacc.xml b/src/engine/SCons/Tool/yacc.xml
index 97bdf9b..48bb323 100644
--- a/src/engine/SCons/Tool/yacc.xml
+++ b/src/engine/SCons/Tool/yacc.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="yacc">
<summary>
XXX
diff --git a/src/engine/SCons/Tool/zip.xml b/src/engine/SCons/Tool/zip.xml
index 4e33c15..5e28bee 100644
--- a/src/engine/SCons/Tool/zip.xml
+++ b/src/engine/SCons/Tool/zip.xml
@@ -1,4 +1,9 @@
-<!-- __COPYRIGHT__ -->
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
<tool name="zip">
<summary>
XXX
@@ -17,6 +22,12 @@ for a given target;
each additional call
adds to the list of entries
that will be built into the archive.
+Any source directories will
+be scanned for changes to
+any on-disk files,
+regardless of whether or not
+&scons;
+knows about them from other Builder or function calls.
<example>
env.Zip('src.zip', 'src')
@@ -67,11 +78,11 @@ module used by the internal Python function
to control whether the zip archive
is compressed or not.
The default value is
-<varname>zipfile.ZIP_DEFLATED</varname>,
+<literal>zipfile.ZIP_DEFLATED</literal>,
which creates a compressed zip archive.
This value has no effect when using Python 1.5.2
or if the
-<varname>zipfile</varname>
+<literal>zipfile</literal>
module is otherwise unavailable.
</summary>
</cvar>