Toplevel directory cleanup
[senf.git] / tools / scons-1.2.0 / engine / SCons / Builder.py
1 """SCons.Builder
2
3 Builder object subsystem.
4
5 A Builder object is a callable that encapsulates information about how
6 to execute actions to create a target Node (file) from source Nodes
7 (files), and how to create those dependencies for tracking.
8
9 The main entry point here is the Builder() factory method.  This provides
10 a procedural interface that creates the right underlying Builder object
11 based on the keyword arguments supplied and the types of the arguments.
12
13 The goal is for this external interface to be simple enough that the
14 vast majority of users can create new Builders as necessary to support
15 building new types of files in their configurations, without having to
16 dive any deeper into this subsystem.
17
18 The base class here is BuilderBase.  This is a concrete base class which
19 does, in fact, represent the Builder objects that we (or users) create.
20
21 There is also a proxy that looks like a Builder:
22
23     CompositeBuilder
24
25         This proxies for a Builder with an action that is actually a
26         dictionary that knows how to map file suffixes to a specific
27         action.  This is so that we can invoke different actions
28         (compilers, compile options) for different flavors of source
29         files.
30
31 Builders and their proxies have the following public interface methods
32 used by other modules:
33
34     __call__()
35         THE public interface.  Calling a Builder object (with the
36         use of internal helper methods) sets up the target and source
37         dependencies, appropriate mapping to a specific action, and the
38         environment manipulation necessary for overridden construction
39         variable.  This also takes care of warning about possible mistakes
40         in keyword arguments.
41
42     add_emitter()
43         Adds an emitter for a specific file suffix, used by some Tool
44         modules to specify that (for example) a yacc invocation on a .y
45         can create a .h *and* a .c file.
46
47     add_action()
48         Adds an action for a specific file suffix, heavily used by
49         Tool modules to add their specific action(s) for turning
50         a source file into an object file to the global static
51         and shared object file Builders.
52
53 There are the following methods for internal use within this module:
54
55     _execute()
56         The internal method that handles the heavily lifting when a
57         Builder is called.  This is used so that the __call__() methods
58         can set up warning about possible mistakes in keyword-argument
59         overrides, and *then* execute all of the steps necessary so that
60         the warnings only occur once.
61
62     get_name()
63         Returns the Builder's name within a specific Environment,
64         primarily used to try to return helpful information in error
65         messages.
66
67     adjust_suffix()
68     get_prefix()
69     get_suffix()
70     get_src_suffix()
71     set_src_suffix()
72         Miscellaneous stuff for handling the prefix and suffix
73         manipulation we use in turning source file names into target
74         file names.
75
76 """
77
78 #
79 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
80 #
81 # Permission is hereby granted, free of charge, to any person obtaining
82 # a copy of this software and associated documentation files (the
83 # "Software"), to deal in the Software without restriction, including
84 # without limitation the rights to use, copy, modify, merge, publish,
85 # distribute, sublicense, and/or sell copies of the Software, and to
86 # permit persons to whom the Software is furnished to do so, subject to
87 # the following conditions:
88 #
89 # The above copyright notice and this permission notice shall be included
90 # in all copies or substantial portions of the Software.
91 #
92 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
93 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
94 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
95 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
96 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
97 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
98 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
99 #
100
101 __revision__ = "src/engine/SCons/Builder.py 3842 2008/12/20 22:59:52 scons"
102
103 import UserDict
104 import UserList
105
106 import SCons.Action
107 from SCons.Debug import logInstanceCreation
108 from SCons.Errors import InternalError, UserError
109 import SCons.Executor
110 import SCons.Memoize
111 import SCons.Node
112 import SCons.Node.FS
113 import SCons.Util
114 import SCons.Warnings
115
116 class _Null:
117     pass
118
119 _null = _Null
120
121 class DictCmdGenerator(SCons.Util.Selector):
122     """This is a callable class that can be used as a
123     command generator function.  It holds on to a dictionary
124     mapping file suffixes to Actions.  It uses that dictionary
125     to return the proper action based on the file suffix of
126     the source file."""
127
128     def __init__(self, dict=None, source_ext_match=1):
129         SCons.Util.Selector.__init__(self, dict)
130         self.source_ext_match = source_ext_match
131
132     def src_suffixes(self):
133         return self.keys()
134
135     def add_action(self, suffix, action):
136         """Add a suffix-action pair to the mapping.
137         """
138         self[suffix] = action
139
140     def __call__(self, target, source, env, for_signature):
141         if not source:
142             return []
143
144         if self.source_ext_match:
145             ext = None
146             for src in map(str, source):
147                 my_ext = SCons.Util.splitext(src)[1]
148                 if ext and my_ext != ext:
149                     raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
150                 ext = my_ext
151         else:
152             ext = SCons.Util.splitext(str(source[0]))[1]
153
154         if not ext:
155             raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
156
157         try:
158             ret = SCons.Util.Selector.__call__(self, env, source)
159         except KeyError, e:
160             raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
161         if ret is None:
162             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." % \
163                             (repr(map(str, target)), repr(map(str, source)), ext, repr(self.keys())))
164         return ret
165
166 class CallableSelector(SCons.Util.Selector):
167     """A callable dictionary that will, in turn, call the value it
168     finds if it can."""
169     def __call__(self, env, source):
170         value = SCons.Util.Selector.__call__(self, env, source)
171         if callable(value):
172             value = value(env, source)
173         return value
174
175 class DictEmitter(SCons.Util.Selector):
176     """A callable dictionary that maps file suffixes to emitters.
177     When called, it finds the right emitter in its dictionary for the
178     suffix of the first source file, and calls that emitter to get the
179     right lists of targets and sources to return.  If there's no emitter
180     for the suffix in its dictionary, the original target and source are
181     returned.
182     """
183     def __call__(self, target, source, env):
184         emitter = SCons.Util.Selector.__call__(self, env, source)
185         if emitter:
186             target, source = emitter(target, source, env)
187         return (target, source)
188
189 class ListEmitter(UserList.UserList):
190     """A callable list of emitters that calls each in sequence,
191     returning the result.
192     """
193     def __call__(self, target, source, env):
194         for e in self.data:
195             target, source = e(target, source, env)
196         return (target, source)
197
198 # These are a common errors when calling a Builder;
199 # they are similar to the 'target' and 'source' keyword args to builders,
200 # so we issue warnings when we see them.  The warnings can, of course,
201 # be disabled.
202 misleading_keywords = {
203     'targets'   : 'target',
204     'sources'   : 'source',
205 }
206
207 class OverrideWarner(UserDict.UserDict):
208     """A class for warning about keyword arguments that we use as
209     overrides in a Builder call.
210
211     This class exists to handle the fact that a single Builder call
212     can actually invoke multiple builders.  This class only emits the
213     warnings once, no matter how many Builders are invoked.
214     """
215     def __init__(self, dict):
216         UserDict.UserDict.__init__(self, dict)
217         if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
218         self.already_warned = None
219     def warn(self):
220         if self.already_warned:
221             return
222         for k in self.keys():
223             if misleading_keywords.has_key(k):
224                 alt = misleading_keywords[k]
225                 msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
226                 SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
227         self.already_warned = 1
228
229 def Builder(**kw):
230     """A factory for builder objects."""
231     composite = None
232     if kw.has_key('generator'):
233         if kw.has_key('action'):
234             raise UserError, "You must not specify both an action and a generator."
235         kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {})
236         del kw['generator']
237     elif kw.has_key('action'):
238         source_ext_match = kw.get('source_ext_match', 1)
239         if kw.has_key('source_ext_match'):
240             del kw['source_ext_match']
241         if SCons.Util.is_Dict(kw['action']):
242             composite = DictCmdGenerator(kw['action'], source_ext_match)
243             kw['action'] = SCons.Action.CommandGeneratorAction(composite, {})
244             kw['src_suffix'] = composite.src_suffixes()
245         else:
246             kw['action'] = SCons.Action.Action(kw['action'])
247
248     if kw.has_key('emitter'):
249         emitter = kw['emitter']
250         if SCons.Util.is_String(emitter):
251             # This allows users to pass in an Environment
252             # variable reference (like "$FOO") as an emitter.
253             # We will look in that Environment variable for
254             # a callable to use as the actual emitter.
255             var = SCons.Util.get_environment_var(emitter)
256             if not var:
257                 raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter
258             kw['emitter'] = EmitterProxy(var)
259         elif SCons.Util.is_Dict(emitter):
260             kw['emitter'] = DictEmitter(emitter)
261         elif SCons.Util.is_List(emitter):
262             kw['emitter'] = ListEmitter(emitter)
263
264     result = apply(BuilderBase, (), kw)
265
266     if not composite is None:
267         result = CompositeBuilder(result, composite)
268
269     return result
270
271 def _node_errors(builder, env, tlist, slist):
272     """Validate that the lists of target and source nodes are
273     legal for this builder and environment.  Raise errors or
274     issue warnings as appropriate.
275     """
276
277     # First, figure out if there are any errors in the way the targets
278     # were specified.
279     for t in tlist:
280         if t.side_effect:
281             raise UserError, "Multiple ways to build the same target were specified for: %s" % t
282         if t.has_explicit_builder():
283             if not t.env is None and not t.env is env:
284                 action = t.builder.action
285                 t_contents = action.get_contents(tlist, slist, t.env)
286                 contents = action.get_contents(tlist, slist, env)
287
288                 if t_contents == contents:
289                     msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env))
290                     SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
291                 else:
292                     msg = "Two environments with different actions were specified for the same target: %s" % t
293                     raise UserError, msg
294             if builder.multi:
295                 if t.builder != builder:
296                     msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
297                     raise UserError, msg
298                 if t.get_executor().targets != tlist:
299                     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))
300                     raise UserError, msg
301             elif t.sources != slist:
302                 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))
303                 raise UserError, msg
304
305     if builder.single_source:
306         if len(slist) > 1:
307             raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
308
309 class EmitterProxy:
310     """This is a callable class that can act as a
311     Builder emitter.  It holds on to a string that
312     is a key into an Environment dictionary, and will
313     look there at actual build time to see if it holds
314     a callable.  If so, we will call that as the actual
315     emitter."""
316     def __init__(self, var):
317         self.var = SCons.Util.to_String(var)
318
319     def __call__(self, target, source, env):
320         emitter = self.var
321
322         # Recursively substitute the variable.
323         # We can't use env.subst() because it deals only
324         # in strings.  Maybe we should change that?
325         while SCons.Util.is_String(emitter) and env.has_key(emitter):
326             emitter = env[emitter]
327         if callable(emitter):
328             target, source = emitter(target, source, env)
329         elif SCons.Util.is_List(emitter):
330             for e in emitter:
331                 target, source = e(target, source, env)
332
333         return (target, source)
334
335
336     def __cmp__(self, other):
337         return cmp(self.var, other.var)
338
339 class BuilderBase:
340     """Base class for Builders, objects that create output
341     nodes (files) from input nodes (files).
342     """
343
344     if SCons.Memoize.use_memoizer:
345         __metaclass__ = SCons.Memoize.Memoized_Metaclass
346
347     memoizer_counters = []
348
349     def __init__(self,  action = None,
350                         prefix = '',
351                         suffix = '',
352                         src_suffix = '',
353                         target_factory = None,
354                         source_factory = None,
355                         target_scanner = None,
356                         source_scanner = None,
357                         emitter = None,
358                         multi = 0,
359                         env = None,
360                         single_source = 0,
361                         name = None,
362                         chdir = _null,
363                         is_explicit = 1,
364                         src_builder = None,
365                         ensure_suffix = False,
366                         **overrides):
367         if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
368         self._memo = {}
369         self.action = action
370         self.multi = multi
371         if SCons.Util.is_Dict(prefix):
372             prefix = CallableSelector(prefix)
373         self.prefix = prefix
374         if SCons.Util.is_Dict(suffix):
375             suffix = CallableSelector(suffix)
376         self.env = env
377         self.single_source = single_source
378         if overrides.has_key('overrides'):
379             SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
380                 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
381                 "\tspecify the items as keyword arguments to the Builder() call instead.")
382             overrides.update(overrides['overrides'])
383             del overrides['overrides']
384         if overrides.has_key('scanner'):
385             SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
386                                 "The \"scanner\" keyword to Builder() creation has been deprecated;\n"
387                                 "\tuse: source_scanner or target_scanner as appropriate.")
388             del overrides['scanner']
389         self.overrides = overrides
390
391         self.set_suffix(suffix)
392         self.set_src_suffix(src_suffix)
393         self.ensure_suffix = ensure_suffix
394
395         self.target_factory = target_factory
396         self.source_factory = source_factory
397         self.target_scanner = target_scanner
398         self.source_scanner = source_scanner
399
400         self.emitter = emitter
401
402         # Optional Builder name should only be used for Builders
403         # that don't get attached to construction environments.
404         if name:
405             self.name = name
406         self.executor_kw = {}
407         if not chdir is _null:
408             self.executor_kw['chdir'] = chdir
409         self.is_explicit = is_explicit
410
411         if src_builder is None:
412             src_builder = []
413         elif not SCons.Util.is_List(src_builder):
414             src_builder = [ src_builder ]
415         self.src_builder = src_builder
416
417     def __nonzero__(self):
418         raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
419
420     def get_name(self, env):
421         """Attempts to get the name of the Builder.
422
423         Look at the BUILDERS variable of env, expecting it to be a
424         dictionary containing this Builder, and return the key of the
425         dictionary.  If there's no key, then return a directly-configured
426         name (if there is one) or the name of the class (by default)."""
427
428         try:
429             index = env['BUILDERS'].values().index(self)
430             return env['BUILDERS'].keys()[index]
431         except (AttributeError, KeyError, TypeError, ValueError):
432             try:
433                 return self.name
434             except AttributeError:
435                 return str(self.__class__)
436
437     def __cmp__(self, other):
438         return cmp(self.__dict__, other.__dict__)
439
440     def splitext(self, path, env=None):
441         if not env:
442             env = self.env
443         if env:
444             matchsuf = filter(lambda S,path=path: path[-len(S):] == S,
445                               self.src_suffixes(env))
446             if matchsuf:
447                 suf = max(map(None, map(len, matchsuf), matchsuf))[1]
448                 return [path[:-len(suf)], path[-len(suf):]]
449         return SCons.Util.splitext(path)
450
451     def get_single_executor(self, env, tlist, slist, executor_kw):
452         if not self.action:
453             raise UserError, "Builder %s must have an action to build %s."%(self.get_name(env or self.env), map(str,tlist))
454         return self.action.get_executor(env or self.env,
455                                         [],  # env already has overrides
456                                         tlist,
457                                         slist,
458                                         executor_kw)
459
460     def get_multi_executor(self, env, tlist, slist, executor_kw):
461         try:
462             executor = tlist[0].get_executor(create = 0)
463         except (AttributeError, IndexError):
464             return self.get_single_executor(env, tlist, slist, executor_kw)
465         else:
466             executor.add_sources(slist)
467             return executor
468
469     def _adjustixes(self, files, pre, suf, ensure_suffix=False):
470         if not files:
471             return []
472         result = []
473         if not SCons.Util.is_List(files):
474             files = [files]
475
476         for f in files:
477             if SCons.Util.is_String(f):
478                 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
479             result.append(f)
480         return result
481
482     def _create_nodes(self, env, target = None, source = None):
483         """Create and return lists of target and source nodes.
484         """
485         src_suf = self.get_src_suffix(env)
486
487         target_factory = env.get_factory(self.target_factory)
488         source_factory = env.get_factory(self.source_factory)
489
490         source = self._adjustixes(source, None, src_suf)
491         slist = env.arg2nodes(source, source_factory)
492
493         pre = self.get_prefix(env, slist)
494         suf = self.get_suffix(env, slist)
495
496         if target is None:
497             try:
498                 t_from_s = slist[0].target_from_source
499             except AttributeError:
500                 raise UserError("Do not know how to create a target from source `%s'" % slist[0])
501             except IndexError:
502                 tlist = []
503             else:
504                 splitext = lambda S,self=self,env=env: self.splitext(S,env)
505                 tlist = [ t_from_s(pre, suf, splitext) ]
506         else:
507             target = self._adjustixes(target, pre, suf, self.ensure_suffix)
508             tlist = env.arg2nodes(target, target_factory, target=target, source=source)
509
510         if self.emitter:
511             # The emitter is going to do str(node), but because we're
512             # being called *from* a builder invocation, the new targets
513             # don't yet have a builder set on them and will look like
514             # source files.  Fool the emitter's str() calls by setting
515             # up a temporary builder on the new targets.
516             new_targets = []
517             for t in tlist:
518                 if not t.is_derived():
519                     t.builder_set(self)
520                     new_targets.append(t)
521
522             orig_tlist = tlist[:]
523             orig_slist = slist[:]
524
525             target, source = self.emitter(target=tlist, source=slist, env=env)
526
527             # Now delete the temporary builders that we attached to any
528             # new targets, so that _node_errors() doesn't do weird stuff
529             # to them because it thinks they already have builders.
530             for t in new_targets:
531                 if t.builder is self:
532                     # Only delete the temporary builder if the emitter
533                     # didn't change it on us.
534                     t.builder_set(None)
535
536             # Have to call arg2nodes yet again, since it is legal for
537             # emitters to spit out strings as well as Node instances.
538             tlist = env.arg2nodes(target, target_factory,
539                                   target=orig_tlist, source=orig_slist)
540             slist = env.arg2nodes(source, source_factory,
541                                   target=orig_tlist, source=orig_slist)
542
543         return tlist, slist
544
545     def _execute(self, env, target, source, overwarn={}, executor_kw={}):
546         # We now assume that target and source are lists or None.
547         if self.src_builder:
548             source = self.src_builder_sources(env, source, overwarn)
549
550         if self.single_source and len(source) > 1 and target is None:
551             result = []
552             if target is None: target = [None]*len(source)
553             for tgt, src in zip(target, source):
554                 if not tgt is None: tgt = [tgt]
555                 if not src is None: src = [src]
556                 result.extend(self._execute(env, tgt, src, overwarn))
557             return SCons.Node.NodeList(result)
558
559         overwarn.warn()
560
561         tlist, slist = self._create_nodes(env, target, source)
562
563         # Check for errors with the specified target/source lists.
564         _node_errors(self, env, tlist, slist)
565
566         # The targets are fine, so find or make the appropriate Executor to
567         # build this particular list of targets from this particular list of
568         # sources.
569         if self.multi:
570             get_executor = self.get_multi_executor
571         else:
572             get_executor = self.get_single_executor
573         executor = get_executor(env, tlist, slist, executor_kw)
574
575         # Now set up the relevant information in the target Nodes themselves.
576         for t in tlist:
577             t.cwd = env.fs.getcwd()
578             t.builder_set(self)
579             t.env_set(env)
580             t.add_source(slist)
581             t.set_executor(executor)
582             t.set_explicit(self.is_explicit)
583
584         return SCons.Node.NodeList(tlist)
585
586     def __call__(self, env, target=None, source=None, chdir=_null, **kw):
587         # We now assume that target and source are lists or None.
588         # The caller (typically Environment.BuilderWrapper) is
589         # responsible for converting any scalar values to lists.
590         if chdir is _null:
591             ekw = self.executor_kw
592         else:
593             ekw = self.executor_kw.copy()
594             ekw['chdir'] = chdir
595         if kw:
596             if kw.has_key('srcdir'):
597                 def prependDirIfRelative(f, srcdir=kw['srcdir']):
598                     import os.path
599                     if SCons.Util.is_String(f) and not os.path.isabs(f):
600                         f = os.path.join(srcdir, f)
601                     return f
602                 if not SCons.Util.is_List(source):
603                     source = [source]
604                 source = map(prependDirIfRelative, source)
605                 del kw['srcdir']
606             if self.overrides:
607                 env_kw = self.overrides.copy()
608                 env_kw.update(kw)
609             else:
610                 env_kw = kw
611         else:
612             env_kw = self.overrides
613         env = env.Override(env_kw)
614         return self._execute(env, target, source, OverrideWarner(kw), ekw)
615
616     def adjust_suffix(self, suff):
617         if suff and not suff[0] in [ '.', '_', '$' ]:
618             return '.' + suff
619         return suff
620
621     def get_prefix(self, env, sources=[]):
622         prefix = self.prefix
623         if callable(prefix):
624             prefix = prefix(env, sources)
625         return env.subst(prefix)
626
627     def set_suffix(self, suffix):
628         if not callable(suffix):
629             suffix = self.adjust_suffix(suffix)
630         self.suffix = suffix
631
632     def get_suffix(self, env, sources=[]):
633         suffix = self.suffix
634         if callable(suffix):
635             suffix = suffix(env, sources)
636         return env.subst(suffix)
637
638     def set_src_suffix(self, src_suffix):
639         if not src_suffix:
640             src_suffix = []
641         elif not SCons.Util.is_List(src_suffix):
642             src_suffix = [ src_suffix ]
643         adjust = lambda suf, s=self: \
644                         callable(suf) and suf or s.adjust_suffix(suf)
645         self.src_suffix = map(adjust, src_suffix)
646
647     def get_src_suffix(self, env):
648         """Get the first src_suffix in the list of src_suffixes."""
649         ret = self.src_suffixes(env)
650         if not ret:
651             return ''
652         return ret[0]
653
654     def add_emitter(self, suffix, emitter):
655         """Add a suffix-emitter mapping to this Builder.
656
657         This assumes that emitter has been initialized with an
658         appropriate dictionary type, and will throw a TypeError if
659         not, so the caller is responsible for knowing that this is an
660         appropriate method to call for the Builder in question.
661         """
662         self.emitter[suffix] = emitter
663
664     def add_src_builder(self, builder):
665         """
666         Add a new Builder to the list of src_builders.
667
668         This requires wiping out cached values so that the computed
669         lists of source suffixes get re-calculated.
670         """
671         self._memo = {}
672         self.src_builder.append(builder)
673
674     def _get_sdict(self, env):
675         """
676         Returns a dictionary mapping all of the source suffixes of all
677         src_builders of this Builder to the underlying Builder that
678         should be called first.
679
680         This dictionary is used for each target specified, so we save a
681         lot of extra computation by memoizing it for each construction
682         environment.
683
684         Note that this is re-computed each time, not cached, because there
685         might be changes to one of our source Builders (or one of their
686         source Builders, and so on, and so on...) that we can't "see."
687
688         The underlying methods we call cache their computed values,
689         though, so we hope repeatedly aggregating them into a dictionary
690         like this won't be too big a hit.  We may need to look for a
691         better way to do this if performance data show this has turned
692         into a significant bottleneck.
693         """
694         sdict = {}
695         for bld in self.get_src_builders(env):
696             for suf in bld.src_suffixes(env):
697                 sdict[suf] = bld
698         return sdict
699
700     def src_builder_sources(self, env, source, overwarn={}):
701         sdict = self._get_sdict(env)
702
703         src_suffixes = self.src_suffixes(env)
704
705         lengths = list(set(map(len, src_suffixes)))
706
707         def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths):
708             node_suffixes = map(lambda l, n=name: n[-l:], lengths)
709             for suf in src_suffixes:
710                 if suf in node_suffixes:
711                     return suf
712             return None
713
714         result = []
715         for s in SCons.Util.flatten(source):
716             if SCons.Util.is_String(s):
717                 match_suffix = match_src_suffix(env.subst(s))
718                 if not match_suffix and not '.' in s:
719                     src_suf = self.get_src_suffix(env)
720                     s = self._adjustixes(s, None, src_suf)[0]
721             else:
722                 match_suffix = match_src_suffix(s.name)
723             if match_suffix:
724                 try:
725                     bld = sdict[match_suffix]
726                 except KeyError:
727                     result.append(s)
728                 else:
729                     tlist = bld._execute(env, None, [s], overwarn)
730                     # If the subsidiary Builder returned more than one
731                     # target, then filter out any sources that this
732                     # Builder isn't capable of building.
733                     if len(tlist) > 1:
734                         mss = lambda t, m=match_src_suffix: m(t.name)
735                         tlist = filter(mss, tlist)
736                     result.extend(tlist)
737             else:
738                 result.append(s)
739
740         source_factory = env.get_factory(self.source_factory)
741
742         return env.arg2nodes(result, source_factory)
743
744     def _get_src_builders_key(self, env):
745         return id(env)
746
747     memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
748
749     def get_src_builders(self, env):
750         """
751         Returns the list of source Builders for this Builder.
752
753         This exists mainly to look up Builders referenced as
754         strings in the 'BUILDER' variable of the construction
755         environment and cache the result.
756         """
757         memo_key = id(env)
758         try:
759             memo_dict = self._memo['get_src_builders']
760         except KeyError:
761             memo_dict = {}
762             self._memo['get_src_builders'] = memo_dict
763         else:
764             try:
765                 return memo_dict[memo_key]
766             except KeyError:
767                 pass
768
769         builders = []
770         for bld in self.src_builder:
771             if SCons.Util.is_String(bld):
772                 try:
773                     bld = env['BUILDERS'][bld]
774                 except KeyError:
775                     continue
776             builders.append(bld)
777
778         memo_dict[memo_key] = builders
779         return builders
780
781     def _subst_src_suffixes_key(self, env):
782         return id(env)
783
784     memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
785
786     def subst_src_suffixes(self, env):
787         """
788         The suffix list may contain construction variable expansions,
789         so we have to evaluate the individual strings.  To avoid doing
790         this over and over, we memoize the results for each construction
791         environment.
792         """
793         memo_key = id(env)
794         try:
795             memo_dict = self._memo['subst_src_suffixes']
796         except KeyError:
797             memo_dict = {}
798             self._memo['subst_src_suffixes'] = memo_dict
799         else:
800             try:
801                 return memo_dict[memo_key]
802             except KeyError:
803                 pass
804         suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
805         memo_dict[memo_key] = suffixes
806         return suffixes
807
808     def src_suffixes(self, env):
809         """
810         Returns the list of source suffixes for all src_builders of this
811         Builder.
812
813         This is essentially a recursive descent of the src_builder "tree."
814         (This value isn't cached because there may be changes in a
815         src_builder many levels deep that we can't see.)
816         """
817         sdict = {}
818         suffixes = self.subst_src_suffixes(env)
819         for s in suffixes:
820             sdict[s] = 1
821         for builder in self.get_src_builders(env):
822             for s in builder.src_suffixes(env):
823                 if not sdict.has_key(s):
824                     sdict[s] = 1
825                     suffixes.append(s)
826         return suffixes
827
828 class CompositeBuilder(SCons.Util.Proxy):
829     """A Builder Proxy whose main purpose is to always have
830     a DictCmdGenerator as its action, and to provide access
831     to the DictCmdGenerator's add_action() method.
832     """
833
834     def __init__(self, builder, cmdgen):
835         if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
836         SCons.Util.Proxy.__init__(self, builder)
837
838         # cmdgen should always be an instance of DictCmdGenerator.
839         self.cmdgen = cmdgen
840         self.builder = builder
841
842     def add_action(self, suffix, action):
843         self.cmdgen.add_action(suffix, action)
844         self.set_src_suffix(self.cmdgen.src_suffixes())