summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Builder.py
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2005-11-17 14:23:07 (GMT)
committerSteven Knight <knight@baldmt.com>2005-11-17 14:23:07 (GMT)
commit62e2f021af43f2bd0422fabdd0b6129ae3695946 (patch)
treeb69a29b3c5e16f0b6743947b59e7084221f1e8a1 /src/engine/SCons/Builder.py
parent9cc468f75539541734366b5e3bb9f36346ee5cda (diff)
downloadSCons-62e2f021af43f2bd0422fabdd0b6129ae3695946.zip
SCons-62e2f021af43f2bd0422fabdd0b6129ae3695946.tar.gz
SCons-62e2f021af43f2bd0422fabdd0b6129ae3695946.tar.bz2
Bring CVS back in sync.
Diffstat (limited to 'src/engine/SCons/Builder.py')
-rw-r--r--src/engine/SCons/Builder.py339
1 files changed, 183 insertions, 156 deletions
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.