Merged revisions 2454-2525 via svnmerge from
[scons.git] / src / 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__
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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
102
103 import SCons.compat
104
105 import UserDict
106 import UserList
107
108 import SCons.Action
109 from SCons.Debug import logInstanceCreation
110 from SCons.Errors import InternalError, UserError
111 import SCons.Executor
112 import SCons.Memoize
113 import SCons.Node
114 import SCons.Node.FS
115 import SCons.Util
116 import SCons.Warnings
117
118 class _Null:
119     pass
120
121 _null = _Null
122
123 class DictCmdGenerator(SCons.Util.Selector):
124     """This is a callable class that can be used as a
125     command generator function.  It holds on to a dictionary
126     mapping file suffixes to Actions.  It uses that dictionary
127     to return the proper action based on the file suffix of
128     the source file."""
129
130     def __init__(self, dict=None, source_ext_match=1):
131         SCons.Util.Selector.__init__(self, dict)
132         self.source_ext_match = source_ext_match
133
134     def src_suffixes(self):
135         return self.keys()
136
137     def add_action(self, suffix, action):
138         """Add a suffix-action pair to the mapping.
139         """
140         self[suffix] = action
141
142     def __call__(self, target, source, env, for_signature):
143         if not source:
144             return []
145
146         if self.source_ext_match:
147             ext = None
148             for src in map(str, source):
149                 my_ext = SCons.Util.splitext(src)[1]
150                 if ext and my_ext != ext:
151                     raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
152                 ext = my_ext
153         else:
154             ext = SCons.Util.splitext(str(source[0]))[1]
155
156         if not ext:
157             raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
158
159         try:
160             ret = SCons.Util.Selector.__call__(self, env, source)
161         except KeyError, e:
162             raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
163         if ret is None:
164             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." % \
165                             (repr(map(str, target)), repr(map(str, source)), ext, repr(self.keys())))
166         return ret
167
168 class CallableSelector(SCons.Util.Selector):
169     """A callable dictionary that will, in turn, call the value it
170     finds if it can."""
171     def __call__(self, env, source):
172         value = SCons.Util.Selector.__call__(self, env, source)
173         if callable(value):
174             value = value(env, source)
175         return value
176
177 class DictEmitter(SCons.Util.Selector):
178     """A callable dictionary that maps file suffixes to emitters.
179     When called, it finds the right emitter in its dictionary for the
180     suffix of the first source file, and calls that emitter to get the
181     right lists of targets and sources to return.  If there's no emitter
182     for the suffix in its dictionary, the original target and source are
183     returned.
184     """
185     def __call__(self, target, source, env):
186         emitter = SCons.Util.Selector.__call__(self, env, source)
187         if emitter:
188             target, source = emitter(target, source, env)
189         return (target, source)
190
191 class ListEmitter(UserList.UserList):
192     """A callable list of emitters that calls each in sequence,
193     returning the result.
194     """
195     def __call__(self, target, source, env):
196         for e in self.data:
197             target, source = e(target, source, env)
198         return (target, source)
199
200 # These are a common errors when calling a Builder;
201 # they are similar to the 'target' and 'source' keyword args to builders,
202 # so we issue warnings when we see them.  The warnings can, of course,
203 # be disabled.
204 misleading_keywords = {
205     'targets'   : 'target',
206     'sources'   : 'source',
207 }
208
209 class OverrideWarner(UserDict.UserDict):
210     """A class for warning about keyword arguments that we use as
211     overrides in a Builder call.
212
213     This class exists to handle the fact that a single Builder call
214     can actually invoke multiple builders.  This class only emits the
215     warnings once, no matter how many Builders are invoked.
216     """
217     def __init__(self, dict):
218         UserDict.UserDict.__init__(self, dict)
219         if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
220         self.already_warned = None
221     def warn(self):
222         if self.already_warned:
223             return
224         for k in self.keys():
225             if misleading_keywords.has_key(k):
226                 alt = misleading_keywords[k]
227                 msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
228                 SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
229         self.already_warned = 1
230
231 def Builder(**kw):
232     """A factory for builder objects."""
233     composite = None
234     if kw.has_key('generator'):
235         if kw.has_key('action'):
236             raise UserError, "You must not specify both an action and a generator."
237         kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'])
238         del kw['generator']
239     elif kw.has_key('action'):
240         source_ext_match = kw.get('source_ext_match', 1)
241         if kw.has_key('source_ext_match'):
242             del kw['source_ext_match']
243         if SCons.Util.is_Dict(kw['action']):
244             composite = DictCmdGenerator(kw['action'], source_ext_match)
245             kw['action'] = SCons.Action.CommandGeneratorAction(composite)
246             kw['src_suffix'] = composite.src_suffixes()
247         else:
248             kw['action'] = SCons.Action.Action(kw['action'])
249
250     if kw.has_key('emitter'):
251         emitter = kw['emitter']
252         if SCons.Util.is_String(emitter):
253             # This allows users to pass in an Environment
254             # variable reference (like "$FOO") as an emitter.
255             # We will look in that Environment variable for
256             # a callable to use as the actual emitter.
257             var = SCons.Util.get_environment_var(emitter)
258             if not var:
259                 raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter
260             kw['emitter'] = EmitterProxy(var)
261         elif SCons.Util.is_Dict(emitter):
262             kw['emitter'] = DictEmitter(emitter)
263         elif SCons.Util.is_List(emitter):
264             kw['emitter'] = ListEmitter(emitter)
265
266     result = apply(BuilderBase, (), kw)
267
268     if not composite is None:
269         result = CompositeBuilder(result, composite)
270
271     return result
272
273 def _node_errors(builder, env, tlist, slist):
274     """Validate that the lists of target and source nodes are
275     legal for this builder and environment.  Raise errors or
276     issue warnings as appropriate.
277     """
278
279     # First, figure out if there are any errors in the way the targets
280     # were specified.
281     for t in tlist:
282         if t.side_effect:
283             raise UserError, "Multiple ways to build the same target were specified for: %s" % t
284         if t.has_explicit_builder():
285             if not t.env is None and not t.env is env:
286                 action = t.builder.action
287                 t_contents = action.get_contents(tlist, slist, t.env)
288                 contents = action.get_contents(tlist, slist, env)
289
290                 if t_contents == contents:
291                     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))
292                     SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
293                 else:
294                     msg = "Two environments with different actions were specified for the same target: %s" % t
295                     raise UserError, msg
296             if builder.multi:
297                 if t.builder != builder:
298                     msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
299                     raise UserError, msg
300                 if t.get_executor().targets != tlist:
301                     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))
302                     raise UserError, msg
303             elif t.sources != slist:
304                 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))
305                 raise UserError, msg
306
307     if builder.single_source:
308         if len(slist) > 1:
309             raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,slist))
310
311 class EmitterProxy:
312     """This is a callable class that can act as a
313     Builder emitter.  It holds on to a string that
314     is a key into an Environment dictionary, and will
315     look there at actual build time to see if it holds
316     a callable.  If so, we will call that as the actual
317     emitter."""
318     def __init__(self, var):
319         self.var = SCons.Util.to_String(var)
320
321     def __call__(self, target, source, env):
322         emitter = self.var
323
324         # Recursively substitute the variable.
325         # We can't use env.subst() because it deals only
326         # in strings.  Maybe we should change that?
327         while SCons.Util.is_String(emitter) and env.has_key(emitter):
328             emitter = env[emitter]
329         if callable(emitter):
330             target, source = emitter(target, source, env)
331         elif SCons.Util.is_List(emitter):
332             for e in emitter:
333                 target, source = e(target, source, env)
334
335         return (target, source)
336
337
338     def __cmp__(self, other):
339         return cmp(self.var, other.var)
340
341 class BuilderBase:
342     """Base class for Builders, objects that create output
343     nodes (files) from input nodes (files).
344     """
345
346     if SCons.Memoize.use_memoizer:
347         __metaclass__ = SCons.Memoize.Memoized_Metaclass
348
349     memoizer_counters = []
350
351     def __init__(self,  action = None,
352                         prefix = '',
353                         suffix = '',
354                         src_suffix = '',
355                         target_factory = None,
356                         source_factory = None,
357                         target_scanner = None,
358                         source_scanner = None,
359                         emitter = None,
360                         multi = 0,
361                         env = None,
362                         single_source = 0,
363                         name = None,
364                         chdir = _null,
365                         is_explicit = 1,
366                         src_builder = [],
367                         ensure_suffix = False,
368                         **overrides):
369         if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
370         self._memo = {}
371         self.action = action
372         self.multi = multi
373         if SCons.Util.is_Dict(prefix):
374             prefix = CallableSelector(prefix)
375         self.prefix = prefix
376         if SCons.Util.is_Dict(suffix):
377             suffix = CallableSelector(suffix)
378         self.env = env
379         self.single_source = single_source
380         if overrides.has_key('overrides'):
381             SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
382                 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
383                 "\tspecify the items as keyword arguments to the Builder() call instead.")
384             overrides.update(overrides['overrides'])
385             del overrides['overrides']
386         if overrides.has_key('scanner'):
387             SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
388                                 "The \"scanner\" keyword to Builder() creation has been deprecated;\n"
389                                 "\tuse: source_scanner or target_scanner as appropriate.")
390             del overrides['scanner']
391         self.overrides = overrides
392
393         self.set_suffix(suffix)
394         self.set_src_suffix(src_suffix)
395         self.ensure_suffix = ensure_suffix
396
397         self.target_factory = target_factory
398         self.source_factory = source_factory
399         self.target_scanner = target_scanner
400         self.source_scanner = source_scanner
401
402         self.emitter = emitter
403
404         # Optional Builder name should only be used for Builders
405         # that don't get attached to construction environments.
406         if name:
407             self.name = name
408         self.executor_kw = {}
409         if not chdir is _null:
410             self.executor_kw['chdir'] = chdir
411         self.is_explicit = is_explicit
412
413         if 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)
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
716         if SCons.Util.is_List(source):
717             source = SCons.Util.flatten(source)
718         else:
719             source = [source]
720         for s in source:
721             if SCons.Util.is_String(s):
722                 match_suffix = match_src_suffix(s)
723                 if not match_suffix and not '.' in s:
724                     src_suf = self.get_src_suffix(env)
725                     s = self._adjustixes(s, None, src_suf)[0]
726             else:
727                 match_suffix = match_src_suffix(s.name)
728             if match_suffix:
729                 try:
730                     bld = sdict[match_suffix]
731                 except KeyError:
732                     result.append(s)
733                 else:
734                     tlist = bld._execute(env, None, [s], overwarn)
735                     # If the subsidiary Builder returned more than one
736                     # target, then filter out any sources that this
737                     # Builder isn't capable of building.
738                     if len(tlist) > 1:
739                         mss = lambda t, m=match_src_suffix: m(t.name)
740                         tlist = filter(mss, tlist)
741                     result.extend(tlist)
742             else:
743                 result.append(s)
744
745         source_factory = env.get_factory(self.source_factory)
746
747         return env.arg2nodes(result, source_factory)
748
749     def _get_src_builders_key(self, env):
750         return id(env)
751
752     memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
753
754     def get_src_builders(self, env):
755         """
756         Returns the list of source Builders for this Builder.
757
758         This exists mainly to look up Builders referenced as
759         strings in the 'BUILDER' variable of the construction
760         environment and cache the result.
761         """
762         memo_key = id(env)
763         try:
764             memo_dict = self._memo['get_src_builders']
765         except KeyError:
766             memo_dict = {}
767             self._memo['get_src_builders'] = memo_dict
768         else:
769             try:
770                 return memo_dict[memo_key]
771             except KeyError:
772                 pass
773
774         builders = []
775         for bld in self.src_builder:
776             if SCons.Util.is_String(bld):
777                 try:
778                     bld = env['BUILDERS'][bld]
779                 except KeyError:
780                     continue
781             builders.append(bld)
782
783         memo_dict[memo_key] = builders
784         return builders
785
786     def _subst_src_suffixes_key(self, env):
787         return id(env)
788
789     memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
790
791     def subst_src_suffixes(self, env):
792         """
793         The suffix list may contain construction variable expansions,
794         so we have to evaluate the individual strings.  To avoid doing
795         this over and over, we memoize the results for each construction
796         environment.
797         """
798         memo_key = id(env)
799         try:
800             memo_dict = self._memo['subst_src_suffixes']
801         except KeyError:
802             memo_dict = {}
803             self._memo['subst_src_suffixes'] = memo_dict
804         else:
805             try:
806                 return memo_dict[memo_key]
807             except KeyError:
808                 pass
809         suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
810         memo_dict[memo_key] = suffixes
811         return suffixes
812
813     def src_suffixes(self, env):
814         """
815         Returns the list of source suffixes for all src_builders of this
816         Builder.
817
818         This is essentially a recursive descent of the src_builder "tree."
819         (This value isn't cached because there may be changes in a
820         src_builder many levels deep that we can't see.)
821         """
822         sdict = {}
823         suffixes = self.subst_src_suffixes(env)
824         for s in suffixes:
825             sdict[s] = 1
826         for builder in self.get_src_builders(env):
827             for s in builder.src_suffixes(env):
828                 if not sdict.has_key(s):
829                     sdict[s] = 1
830                     suffixes.append(s)
831         return suffixes
832
833 class CompositeBuilder(SCons.Util.Proxy):
834     """A Builder Proxy whose main purpose is to always have
835     a DictCmdGenerator as its action, and to provide access
836     to the DictCmdGenerator's add_action() method.
837     """
838
839     def __init__(self, builder, cmdgen):
840         if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
841         SCons.Util.Proxy.__init__(self, builder)
842
843         # cmdgen should always be an instance of DictCmdGenerator.
844         self.cmdgen = cmdgen
845         self.builder = builder
846
847     def add_action(self, suffix, action):
848         self.cmdgen.add_action(suffix, action)
849         self.set_src_suffix(self.cmdgen.src_suffixes())