X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=src%2Fengine%2FSCons%2FBuilder.py;h=bbf503c07d7b22f31a41ac40e1d442b7ca57d049;hb=6a218d30e5fa1a14835a31129881b4288db7dc1d;hp=45bd99a1e947ed4209e8e9d3474d00da1738e74f;hpb=8452039c43b7a520df6edd12a38cf24ce3c77624;p=scons.git diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 45bd99a1..bbf503c0 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -97,13 +97,11 @@ There are the following methods for internal use within this module: # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import SCons.compat - -import UserDict -import UserList +import collections import SCons.Action from SCons.Debug import logInstanceCreation @@ -120,6 +118,14 @@ class _Null: _null = _Null +def match_splitext(path, suffixes = []): + if suffixes: + matchsuf = [S for S in suffixes if path[-len(S):] == S] + if matchsuf: + suf = max(list(map(None, list(map(len, matchsuf)), matchsuf)))[1] + return [path[:-len(suf)], path[-len(suf):]] + return SCons.Util.splitext(path) + class DictCmdGenerator(SCons.Util.Selector): """This is a callable class that can be used as a command generator function. It holds on to a dictionary @@ -144,25 +150,30 @@ class DictCmdGenerator(SCons.Util.Selector): return [] if self.source_ext_match: + suffixes = self.src_suffixes() ext = None for src in map(str, source): - my_ext = SCons.Util.splitext(src)[1] + my_ext = match_splitext(src, suffixes)[1] if ext and my_ext != ext: - raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext)) + raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" + % (repr(list(map(str, target))), src, ext, my_ext)) ext = my_ext else: - ext = SCons.Util.splitext(str(source[0]))[1] + ext = match_splitext(str(source[0]), self.src_suffixes())[1] if not ext: - raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source)))) + #return ext + raise UserError("While building `%s': " + "Cannot deduce file extension from source files: %s" + % (repr(list(map(str, target))), repr(list(map(str, source))))) try: - ret = SCons.Util.Selector.__call__(self, env, source) + ret = SCons.Util.Selector.__call__(self, env, source, ext) except KeyError, e: raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2])) if ret is None: raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ - (repr(map(str, target)), repr(map(str, source)), ext, repr(self.keys()))) + (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(self.keys()))) return ret class CallableSelector(SCons.Util.Selector): @@ -188,7 +199,7 @@ class DictEmitter(SCons.Util.Selector): target, source = emitter(target, source, env) return (target, source) -class ListEmitter(UserList.UserList): +class ListEmitter(collections.UserList): """A callable list of emitters that calls each in sequence, returning the result. """ @@ -206,7 +217,7 @@ misleading_keywords = { 'sources' : 'source', } -class OverrideWarner(UserDict.UserDict): +class OverrideWarner(collections.UserDict): """A class for warning about keyword arguments that we use as overrides in a Builder call. @@ -215,14 +226,14 @@ class OverrideWarner(UserDict.UserDict): warnings once, no matter how many Builders are invoked. """ def __init__(self, dict): - UserDict.UserDict.__init__(self, dict) + collections.UserDict.__init__(self, dict) if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner') self.already_warned = None def warn(self): if self.already_warned: return for k in self.keys(): - if misleading_keywords.has_key(k): + if k in misleading_keywords: alt = misleading_keywords[k] msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) @@ -231,23 +242,23 @@ class OverrideWarner(UserDict.UserDict): def Builder(**kw): """A factory for builder objects.""" composite = None - 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.CommandGeneratorAction(kw['generator']) + if 'generator' in kw: + if 'action' in kw: + raise UserError("You must not specify both an action and a generator.") + kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) del kw['generator'] - elif kw.has_key('action'): + elif 'action' in kw: source_ext_match = kw.get('source_ext_match', 1) - if kw.has_key('source_ext_match'): + if 'source_ext_match' in kw: del kw['source_ext_match'] if SCons.Util.is_Dict(kw['action']): composite = DictCmdGenerator(kw['action'], source_ext_match) - kw['action'] = SCons.Action.CommandGeneratorAction(composite) + 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'): + if 'emitter' in kw: emitter = kw['emitter'] if SCons.Util.is_String(emitter): # This allows users to pass in an Environment @@ -256,14 +267,14 @@ def Builder(**kw): # a callable to use as the actual emitter. var = SCons.Util.get_environment_var(emitter) if not var: - raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter + raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) kw['emitter'] = EmitterProxy(var) elif SCons.Util.is_Dict(emitter): kw['emitter'] = DictEmitter(emitter) elif SCons.Util.is_List(emitter): kw['emitter'] = ListEmitter(emitter) - result = apply(BuilderBase, (), kw) + result = BuilderBase(**kw) if not composite is None: result = CompositeBuilder(result, composite) @@ -280,7 +291,7 @@ def _node_errors(builder, env, tlist, slist): # were specified. for t in tlist: if t.side_effect: - raise UserError, "Multiple ways to build the same target were specified for: %s" % t + raise UserError("Multiple ways to build the same target were specified for: %s" % t) if t.has_explicit_builder(): if not t.env is None and not t.env is env: action = t.builder.action @@ -292,21 +303,22 @@ def _node_errors(builder, env, tlist, slist): SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) else: msg = "Two environments with different actions were specified for the same target: %s" % t - raise UserError, msg + raise UserError(msg) if builder.multi: if t.builder != builder: msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) - raise UserError, msg - if t.get_executor().targets != tlist: - msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, map(str, t.get_executor().targets), map(str, tlist)) - raise UserError, msg + raise UserError(msg) + # TODO(batch): list constructed each time! + if t.get_executor().get_all_targets() != tlist: + msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist))) + raise UserError(msg) elif t.sources != slist: - msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, map(str, t.sources), map(str, slist)) - raise UserError, msg + msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist))) + raise UserError(msg) if builder.single_source: 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)) + raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist)))) class EmitterProxy: """This is a callable class that can act as a @@ -324,7 +336,7 @@ class EmitterProxy: # Recursively substitute the variable. # We can't use env.subst() because it deals only # in strings. Maybe we should change that? - while SCons.Util.is_String(emitter) and env.has_key(emitter): + while SCons.Util.is_String(emitter) and emitter in env: emitter = env[emitter] if callable(emitter): target, source = emitter(target, source, env) @@ -377,13 +389,13 @@ class BuilderBase: suffix = CallableSelector(suffix) self.env = env self.single_source = single_source - if overrides.has_key('overrides'): + if 'overrides' in overrides: SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ "\tspecify the items as keyword arguments to the Builder() call instead.") overrides.update(overrides['overrides']) del overrides['overrides'] - if overrides.has_key('scanner'): + if 'scanner' in overrides: SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, "The \"scanner\" keyword to Builder() creation has been deprecated;\n" "\tuse: source_scanner or target_scanner as appropriate.") @@ -417,7 +429,7 @@ class BuilderBase: self.src_builder = src_builder def __nonzero__(self): - raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead" + raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead") def get_name(self, env): """Attempts to get the name of the Builder. @@ -443,30 +455,10 @@ class BuilderBase: if not env: env = self.env if env: - matchsuf = filter(lambda S,path=path: path[-len(S):] == S, - self.src_suffixes(env)) - if matchsuf: - suf = max(map(None, map(len, matchsuf), matchsuf))[1] - return [path[:-len(suf)], path[-len(suf):]] - return SCons.Util.splitext(path) - - 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) + suffixes = self.src_suffixes(env) else: - executor.add_sources(slist) - return executor + suffixes = [] + return match_splitext(path, suffixes) def _adjustixes(self, files, pre, suf, ensure_suffix=False): if not files: @@ -503,11 +495,11 @@ class BuilderBase: except IndexError: tlist = [] else: - splitext = lambda S,self=self,env=env: self.splitext(S,env) + splitext = lambda S: self.splitext(S,env) tlist = [ t_from_s(pre, suf, splitext) ] else: target = self._adjustixes(target, pre, suf, self.ensure_suffix) - tlist = env.arg2nodes(target, target_factory) + tlist = env.arg2nodes(target, target_factory, target=target, source=source) if self.emitter: # The emitter is going to do str(node), but because we're @@ -568,11 +560,37 @@ class BuilderBase: # 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 + key = None + if self.multi: - get_executor = self.get_multi_executor - else: - get_executor = self.get_single_executor - executor = get_executor(env, tlist, slist, executor_kw) + try: + executor = tlist[0].get_executor(create = 0) + except (AttributeError, IndexError): + pass + else: + executor.add_sources(slist) + + if executor is None: + if not self.action: + fmt = "Builder %s must have an action to build %s." + raise UserError(fmt % (self.get_name(env or self.env), + list(map(str,tlist)))) + key = self.action.batch_key(env or self.env, tlist, slist) + if key: + try: + executor = SCons.Executor.GetBatchExecutor(key) + except KeyError: + pass + else: + executor.add_batch(tlist, slist) + + if executor is None: + executor = SCons.Executor.Executor(self.action, env, [], + tlist, slist, executor_kw) + if key: + SCons.Executor.AddBatchExecutor(key, executor) # Now set up the relevant information in the target Nodes themselves. for t in tlist: @@ -595,7 +613,7 @@ class BuilderBase: ekw = self.executor_kw.copy() ekw['chdir'] = chdir if kw: - if kw.has_key('srcdir'): + if 'srcdir' in kw: def prependDirIfRelative(f, srcdir=kw['srcdir']): import os.path if SCons.Util.is_String(f) and not os.path.isabs(f): @@ -603,7 +621,7 @@ class BuilderBase: return f if not SCons.Util.is_List(source): source = [source] - source = map(prependDirIfRelative, source) + source = list(map(prependDirIfRelative, source)) del kw['srcdir'] if self.overrides: env_kw = self.overrides.copy() @@ -642,9 +660,7 @@ class BuilderBase: src_suffix = [] elif not SCons.Util.is_List(src_suffix): 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) + self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix] def get_src_suffix(self, env): """Get the first src_suffix in the list of src_suffixes.""" @@ -707,7 +723,7 @@ class BuilderBase: lengths = list(set(map(len, src_suffixes))) def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): - node_suffixes = map(lambda l, n=name: n[-l:], lengths) + node_suffixes = [name[-l:] for l in lengths] for suf in src_suffixes: if suf in node_suffixes: return suf @@ -733,8 +749,7 @@ class BuilderBase: # target, then filter out any sources that this # Builder isn't capable of building. if len(tlist) > 1: - mss = lambda t, m=match_src_suffix: m(t.name) - tlist = filter(mss, tlist) + tlist = [t for t in tlist if match_src_suffix(t.name)] result.extend(tlist) else: result.append(s) @@ -803,7 +818,7 @@ class BuilderBase: return memo_dict[memo_key] except KeyError: pass - suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix) + suffixes = [env.subst(x) for x in self.src_suffix] memo_dict[memo_key] = suffixes return suffixes @@ -822,7 +837,7 @@ class BuilderBase: sdict[s] = 1 for builder in self.get_src_builders(env): for s in builder.src_suffixes(env): - if not sdict.has_key(s): + if s not in sdict: sdict[s] = 1 suffixes.append(s) return suffixes @@ -844,3 +859,19 @@ class CompositeBuilder(SCons.Util.Proxy): def add_action(self, suffix, action): self.cmdgen.add_action(suffix, action) self.set_src_suffix(self.cmdgen.src_suffixes()) + +def is_a_Builder(obj): + """"Returns True iff the specified obj is one of our Builder classes. + + The test is complicated a bit by the fact that CompositeBuilder + is a proxy, not a subclass of BuilderBase. + """ + return (isinstance(obj, BuilderBase) + or isinstance(obj, CompositeBuilder) + or callable(obj)) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: