http://scons.tigris.org/issues/show_bug.cgi?id=2345
[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 from __future__ import generators  ### KEEP FOR COMPATIBILITY FIXERS
101
102 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
103
104 import collections
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 def match_splitext(path, suffixes = []):
122     if suffixes:
123         matchsuf = [S for S in suffixes if path[-len(S):] == S]
124         if matchsuf:
125             suf = max(list(map(None, list(map(len, matchsuf)), matchsuf)))[1]
126             return [path[:-len(suf)], path[-len(suf):]]
127     return SCons.Util.splitext(path)
128
129 class DictCmdGenerator(SCons.Util.Selector):
130     """This is a callable class that can be used as a
131     command generator function.  It holds on to a dictionary
132     mapping file suffixes to Actions.  It uses that dictionary
133     to return the proper action based on the file suffix of
134     the source file."""
135
136     def __init__(self, dict=None, source_ext_match=1):
137         SCons.Util.Selector.__init__(self, dict)
138         self.source_ext_match = source_ext_match
139
140     def src_suffixes(self):
141         return self.keys()
142
143     def add_action(self, suffix, action):
144         """Add a suffix-action pair to the mapping.
145         """
146         self[suffix] = action
147
148     def __call__(self, target, source, env, for_signature):
149         if not source:
150             return []
151
152         if self.source_ext_match:
153             suffixes = self.src_suffixes()
154             ext = None
155             for src in map(str, source):
156                 my_ext = match_splitext(src, suffixes)[1]
157                 if ext and my_ext != ext:
158                     raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s"
159                              % (repr(list(map(str, target))), src, ext, my_ext))
160                 ext = my_ext
161         else:
162             ext = match_splitext(str(source[0]), self.src_suffixes())[1]
163
164         if not ext:
165             #return ext
166             raise UserError("While building `%s': "
167                             "Cannot deduce file extension from source files: %s"
168                  % (repr(list(map(str, target))), repr(list(map(str, source)))))
169
170         try:
171             ret = SCons.Util.Selector.__call__(self, env, source, ext)
172         except KeyError, e:
173             raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
174         if ret is None:
175             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." % \
176                             (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(self.keys())))
177         return ret
178
179 class CallableSelector(SCons.Util.Selector):
180     """A callable dictionary that will, in turn, call the value it
181     finds if it can."""
182     def __call__(self, env, source):
183         value = SCons.Util.Selector.__call__(self, env, source)
184         if callable(value):
185             value = value(env, source)
186         return value
187
188 class DictEmitter(SCons.Util.Selector):
189     """A callable dictionary that maps file suffixes to emitters.
190     When called, it finds the right emitter in its dictionary for the
191     suffix of the first source file, and calls that emitter to get the
192     right lists of targets and sources to return.  If there's no emitter
193     for the suffix in its dictionary, the original target and source are
194     returned.
195     """
196     def __call__(self, target, source, env):
197         emitter = SCons.Util.Selector.__call__(self, env, source)
198         if emitter:
199             target, source = emitter(target, source, env)
200         return (target, source)
201
202 class ListEmitter(collections.UserList):
203     """A callable list of emitters that calls each in sequence,
204     returning the result.
205     """
206     def __call__(self, target, source, env):
207         for e in self.data:
208             target, source = e(target, source, env)
209         return (target, source)
210
211 # These are a common errors when calling a Builder;
212 # they are similar to the 'target' and 'source' keyword args to builders,
213 # so we issue warnings when we see them.  The warnings can, of course,
214 # be disabled.
215 misleading_keywords = {
216     'targets'   : 'target',
217     'sources'   : 'source',
218 }
219
220 class OverrideWarner(collections.UserDict):
221     """A class for warning about keyword arguments that we use as
222     overrides in a Builder call.
223
224     This class exists to handle the fact that a single Builder call
225     can actually invoke multiple builders.  This class only emits the
226     warnings once, no matter how many Builders are invoked.
227     """
228     def __init__(self, dict):
229         collections.UserDict.__init__(self, dict)
230         if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
231         self.already_warned = None
232     def warn(self):
233         if self.already_warned:
234             return
235         for k in self.keys():
236             if k in misleading_keywords:
237                 alt = misleading_keywords[k]
238                 msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
239                 SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
240         self.already_warned = 1
241
242 def Builder(**kw):
243     """A factory for builder objects."""
244     composite = None
245     if 'generator' in kw:
246         if 'action' in kw:
247             raise UserError("You must not specify both an action and a generator.")
248         kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {})
249         del kw['generator']
250     elif 'action' in kw:
251         source_ext_match = kw.get('source_ext_match', 1)
252         if 'source_ext_match' in kw:
253             del kw['source_ext_match']
254         if SCons.Util.is_Dict(kw['action']):
255             composite = DictCmdGenerator(kw['action'], source_ext_match)
256             kw['action'] = SCons.Action.CommandGeneratorAction(composite, {})
257             kw['src_suffix'] = composite.src_suffixes()
258         else:
259             kw['action'] = SCons.Action.Action(kw['action'])
260
261     if 'emitter' in kw:
262         emitter = kw['emitter']
263         if SCons.Util.is_String(emitter):
264             # This allows users to pass in an Environment
265             # variable reference (like "$FOO") as an emitter.
266             # We will look in that Environment variable for
267             # a callable to use as the actual emitter.
268             var = SCons.Util.get_environment_var(emitter)
269             if not var:
270                 raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter)
271             kw['emitter'] = EmitterProxy(var)
272         elif SCons.Util.is_Dict(emitter):
273             kw['emitter'] = DictEmitter(emitter)
274         elif SCons.Util.is_List(emitter):
275             kw['emitter'] = ListEmitter(emitter)
276
277     result = BuilderBase(**kw)
278
279     if not composite is None:
280         result = CompositeBuilder(result, composite)
281
282     return result
283
284 def _node_errors(builder, env, tlist, slist):
285     """Validate that the lists of target and source nodes are
286     legal for this builder and environment.  Raise errors or
287     issue warnings as appropriate.
288     """
289
290     # First, figure out if there are any errors in the way the targets
291     # were specified.
292     for t in tlist:
293         if t.side_effect:
294             raise UserError("Multiple ways to build the same target were specified for: %s" % t)
295         if t.has_explicit_builder():
296             if not t.env is None and not t.env is env:
297                 action = t.builder.action
298                 t_contents = action.get_contents(tlist, slist, t.env)
299                 contents = action.get_contents(tlist, slist, env)
300
301                 if t_contents == contents:
302                     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))
303                     SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
304                 else:
305                     msg = "Two environments with different actions were specified for the same target: %s" % t
306                     raise UserError(msg)
307             if builder.multi:
308                 if t.builder != builder:
309                     msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
310                     raise UserError(msg)
311                 # TODO(batch):  list constructed each time!
312                 if t.get_executor().get_all_targets() != tlist:
313                     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)))
314                     raise UserError(msg)
315             elif t.sources != slist:
316                 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)))
317                 raise UserError(msg)
318
319     if builder.single_source:
320         if len(slist) > 1:
321             raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist))))
322
323 class EmitterProxy:
324     """This is a callable class that can act as a
325     Builder emitter.  It holds on to a string that
326     is a key into an Environment dictionary, and will
327     look there at actual build time to see if it holds
328     a callable.  If so, we will call that as the actual
329     emitter."""
330     def __init__(self, var):
331         self.var = SCons.Util.to_String(var)
332
333     def __call__(self, target, source, env):
334         emitter = self.var
335
336         # Recursively substitute the variable.
337         # We can't use env.subst() because it deals only
338         # in strings.  Maybe we should change that?
339         while SCons.Util.is_String(emitter) and emitter in env:
340             emitter = env[emitter]
341         if callable(emitter):
342             target, source = emitter(target, source, env)
343         elif SCons.Util.is_List(emitter):
344             for e in emitter:
345                 target, source = e(target, source, env)
346
347         return (target, source)
348
349
350     def __cmp__(self, other):
351         return cmp(self.var, other.var)
352
353 class BuilderBase:
354     """Base class for Builders, objects that create output
355     nodes (files) from input nodes (files).
356     """
357
358     if SCons.Memoize.use_memoizer:
359         __metaclass__ = SCons.Memoize.Memoized_Metaclass
360
361     memoizer_counters = []
362
363     def __init__(self,  action = None,
364                         prefix = '',
365                         suffix = '',
366                         src_suffix = '',
367                         target_factory = None,
368                         source_factory = None,
369                         target_scanner = None,
370                         source_scanner = None,
371                         emitter = None,
372                         multi = 0,
373                         env = None,
374                         single_source = 0,
375                         name = None,
376                         chdir = _null,
377                         is_explicit = 1,
378                         src_builder = None,
379                         ensure_suffix = False,
380                         **overrides):
381         if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
382         self._memo = {}
383         self.action = action
384         self.multi = multi
385         if SCons.Util.is_Dict(prefix):
386             prefix = CallableSelector(prefix)
387         self.prefix = prefix
388         if SCons.Util.is_Dict(suffix):
389             suffix = CallableSelector(suffix)
390         self.env = env
391         self.single_source = single_source
392         if 'overrides' in overrides:
393             SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
394                 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
395                 "\tspecify the items as keyword arguments to the Builder() call instead.")
396             overrides.update(overrides['overrides'])
397             del overrides['overrides']
398         if 'scanner' in overrides:
399             SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
400                                 "The \"scanner\" keyword to Builder() creation has been deprecated;\n"
401                                 "\tuse: source_scanner or target_scanner as appropriate.")
402             del overrides['scanner']
403         self.overrides = overrides
404
405         self.set_suffix(suffix)
406         self.set_src_suffix(src_suffix)
407         self.ensure_suffix = ensure_suffix
408
409         self.target_factory = target_factory
410         self.source_factory = source_factory
411         self.target_scanner = target_scanner
412         self.source_scanner = source_scanner
413
414         self.emitter = emitter
415
416         # Optional Builder name should only be used for Builders
417         # that don't get attached to construction environments.
418         if name:
419             self.name = name
420         self.executor_kw = {}
421         if not chdir is _null:
422             self.executor_kw['chdir'] = chdir
423         self.is_explicit = is_explicit
424
425         if src_builder is None:
426             src_builder = []
427         elif not SCons.Util.is_List(src_builder):
428             src_builder = [ src_builder ]
429         self.src_builder = src_builder
430
431     def __nonzero__(self):
432         raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead")
433
434     def get_name(self, env):
435         """Attempts to get the name of the Builder.
436
437         Look at the BUILDERS variable of env, expecting it to be a
438         dictionary containing this Builder, and return the key of the
439         dictionary.  If there's no key, then return a directly-configured
440         name (if there is one) or the name of the class (by default)."""
441
442         try:
443             index = env['BUILDERS'].values().index(self)
444             return env['BUILDERS'].keys()[index]
445         except (AttributeError, KeyError, TypeError, ValueError):
446             try:
447                 return self.name
448             except AttributeError:
449                 return str(self.__class__)
450
451     def __cmp__(self, other):
452         return cmp(self.__dict__, other.__dict__)
453
454     def splitext(self, path, env=None):
455         if not env:
456             env = self.env
457         if env:
458             suffixes = self.src_suffixes(env)
459         else:
460             suffixes = []
461         return match_splitext(path, suffixes)
462
463     def _adjustixes(self, files, pre, suf, ensure_suffix=False):
464         if not files:
465             return []
466         result = []
467         if not SCons.Util.is_List(files):
468             files = [files]
469
470         for f in files:
471             if SCons.Util.is_String(f):
472                 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
473             result.append(f)
474         return result
475
476     def _create_nodes(self, env, target = None, source = None):
477         """Create and return lists of target and source nodes.
478         """
479         src_suf = self.get_src_suffix(env)
480
481         target_factory = env.get_factory(self.target_factory)
482         source_factory = env.get_factory(self.source_factory)
483
484         source = self._adjustixes(source, None, src_suf)
485         slist = env.arg2nodes(source, source_factory)
486
487         pre = self.get_prefix(env, slist)
488         suf = self.get_suffix(env, slist)
489
490         if target is None:
491             try:
492                 t_from_s = slist[0].target_from_source
493             except AttributeError:
494                 raise UserError("Do not know how to create a target from source `%s'" % slist[0])
495             except IndexError:
496                 tlist = []
497             else:
498                 splitext = lambda S: self.splitext(S,env)
499                 tlist = [ t_from_s(pre, suf, splitext) ]
500         else:
501             target = self._adjustixes(target, pre, suf, self.ensure_suffix)
502             tlist = env.arg2nodes(target, target_factory, target=target, source=source)
503
504         if self.emitter:
505             # The emitter is going to do str(node), but because we're
506             # being called *from* a builder invocation, the new targets
507             # don't yet have a builder set on them and will look like
508             # source files.  Fool the emitter's str() calls by setting
509             # up a temporary builder on the new targets.
510             new_targets = []
511             for t in tlist:
512                 if not t.is_derived():
513                     t.builder_set(self)
514                     new_targets.append(t)
515
516             orig_tlist = tlist[:]
517             orig_slist = slist[:]
518
519             target, source = self.emitter(target=tlist, source=slist, env=env)
520
521             # Now delete the temporary builders that we attached to any
522             # new targets, so that _node_errors() doesn't do weird stuff
523             # to them because it thinks they already have builders.
524             for t in new_targets:
525                 if t.builder is self:
526                     # Only delete the temporary builder if the emitter
527                     # didn't change it on us.
528                     t.builder_set(None)
529
530             # Have to call arg2nodes yet again, since it is legal for
531             # emitters to spit out strings as well as Node instances.
532             tlist = env.arg2nodes(target, target_factory,
533                                   target=orig_tlist, source=orig_slist)
534             slist = env.arg2nodes(source, source_factory,
535                                   target=orig_tlist, source=orig_slist)
536
537         return tlist, slist
538
539     def _execute(self, env, target, source, overwarn={}, executor_kw={}):
540         # We now assume that target and source are lists or None.
541         if self.src_builder:
542             source = self.src_builder_sources(env, source, overwarn)
543
544         if self.single_source and len(source) > 1 and target is None:
545             result = []
546             if target is None: target = [None]*len(source)
547             for tgt, src in zip(target, source):
548                 if not tgt is None: tgt = [tgt]
549                 if not src is None: src = [src]
550                 result.extend(self._execute(env, tgt, src, overwarn))
551             return SCons.Node.NodeList(result)
552
553         overwarn.warn()
554
555         tlist, slist = self._create_nodes(env, target, source)
556
557         # Check for errors with the specified target/source lists.
558         _node_errors(self, env, tlist, slist)
559
560         # The targets are fine, so find or make the appropriate Executor to
561         # build this particular list of targets from this particular list of
562         # sources.
563
564         executor = None
565         key = None
566
567         if self.multi:
568             try:
569                 executor = tlist[0].get_executor(create = 0)
570             except (AttributeError, IndexError):
571                 pass
572             else:
573                 executor.add_sources(slist)
574
575         if executor is None:
576             if not self.action:
577                 fmt = "Builder %s must have an action to build %s."
578                 raise UserError(fmt % (self.get_name(env or self.env),
579                                         list(map(str,tlist))))
580             key = self.action.batch_key(env or self.env, tlist, slist)
581             if key:
582                 try:
583                     executor = SCons.Executor.GetBatchExecutor(key)
584                 except KeyError:
585                     pass
586                 else:
587                     executor.add_batch(tlist, slist)
588
589         if executor is None:
590             executor = SCons.Executor.Executor(self.action, env, [],
591                                                tlist, slist, executor_kw)
592             if key:
593                 SCons.Executor.AddBatchExecutor(key, executor)
594
595         # Now set up the relevant information in the target Nodes themselves.
596         for t in tlist:
597             t.cwd = env.fs.getcwd()
598             t.builder_set(self)
599             t.env_set(env)
600             t.add_source(slist)
601             t.set_executor(executor)
602             t.set_explicit(self.is_explicit)
603
604         return SCons.Node.NodeList(tlist)
605
606     def __call__(self, env, target=None, source=None, chdir=_null, **kw):
607         # We now assume that target and source are lists or None.
608         # The caller (typically Environment.BuilderWrapper) is
609         # responsible for converting any scalar values to lists.
610         if chdir is _null:
611             ekw = self.executor_kw
612         else:
613             ekw = self.executor_kw.copy()
614             ekw['chdir'] = chdir
615         if kw:
616             if 'srcdir' in kw:
617                 def prependDirIfRelative(f, srcdir=kw['srcdir']):
618                     import os.path
619                     if SCons.Util.is_String(f) and not os.path.isabs(f):
620                         f = os.path.join(srcdir, f)
621                     return f
622                 if not SCons.Util.is_List(source):
623                     source = [source]
624                 source = list(map(prependDirIfRelative, source))
625                 del kw['srcdir']
626             if self.overrides:
627                 env_kw = self.overrides.copy()
628                 env_kw.update(kw)
629             else:
630                 env_kw = kw
631         else:
632             env_kw = self.overrides
633         env = env.Override(env_kw)
634         return self._execute(env, target, source, OverrideWarner(kw), ekw)
635
636     def adjust_suffix(self, suff):
637         if suff and not suff[0] in [ '.', '_', '$' ]:
638             return '.' + suff
639         return suff
640
641     def get_prefix(self, env, sources=[]):
642         prefix = self.prefix
643         if callable(prefix):
644             prefix = prefix(env, sources)
645         return env.subst(prefix)
646
647     def set_suffix(self, suffix):
648         if not callable(suffix):
649             suffix = self.adjust_suffix(suffix)
650         self.suffix = suffix
651
652     def get_suffix(self, env, sources=[]):
653         suffix = self.suffix
654         if callable(suffix):
655             suffix = suffix(env, sources)
656         return env.subst(suffix)
657
658     def set_src_suffix(self, src_suffix):
659         if not src_suffix:
660             src_suffix = []
661         elif not SCons.Util.is_List(src_suffix):
662             src_suffix = [ src_suffix ]
663         self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix]
664
665     def get_src_suffix(self, env):
666         """Get the first src_suffix in the list of src_suffixes."""
667         ret = self.src_suffixes(env)
668         if not ret:
669             return ''
670         return ret[0]
671
672     def add_emitter(self, suffix, emitter):
673         """Add a suffix-emitter mapping to this Builder.
674
675         This assumes that emitter has been initialized with an
676         appropriate dictionary type, and will throw a TypeError if
677         not, so the caller is responsible for knowing that this is an
678         appropriate method to call for the Builder in question.
679         """
680         self.emitter[suffix] = emitter
681
682     def add_src_builder(self, builder):
683         """
684         Add a new Builder to the list of src_builders.
685
686         This requires wiping out cached values so that the computed
687         lists of source suffixes get re-calculated.
688         """
689         self._memo = {}
690         self.src_builder.append(builder)
691
692     def _get_sdict(self, env):
693         """
694         Returns a dictionary mapping all of the source suffixes of all
695         src_builders of this Builder to the underlying Builder that
696         should be called first.
697
698         This dictionary is used for each target specified, so we save a
699         lot of extra computation by memoizing it for each construction
700         environment.
701
702         Note that this is re-computed each time, not cached, because there
703         might be changes to one of our source Builders (or one of their
704         source Builders, and so on, and so on...) that we can't "see."
705
706         The underlying methods we call cache their computed values,
707         though, so we hope repeatedly aggregating them into a dictionary
708         like this won't be too big a hit.  We may need to look for a
709         better way to do this if performance data show this has turned
710         into a significant bottleneck.
711         """
712         sdict = {}
713         for bld in self.get_src_builders(env):
714             for suf in bld.src_suffixes(env):
715                 sdict[suf] = bld
716         return sdict
717
718     def src_builder_sources(self, env, source, overwarn={}):
719         sdict = self._get_sdict(env)
720
721         src_suffixes = self.src_suffixes(env)
722
723         lengths = list(set(map(len, src_suffixes)))
724
725         def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths):
726             node_suffixes = [name[-l:] for l in lengths]
727             for suf in src_suffixes:
728                 if suf in node_suffixes:
729                     return suf
730             return None
731
732         result = []
733         for s in SCons.Util.flatten(source):
734             if SCons.Util.is_String(s):
735                 match_suffix = match_src_suffix(env.subst(s))
736                 if not match_suffix and not '.' in s:
737                     src_suf = self.get_src_suffix(env)
738                     s = self._adjustixes(s, None, src_suf)[0]
739             else:
740                 match_suffix = match_src_suffix(s.name)
741             if match_suffix:
742                 try:
743                     bld = sdict[match_suffix]
744                 except KeyError:
745                     result.append(s)
746                 else:
747                     tlist = bld._execute(env, None, [s], overwarn)
748                     # If the subsidiary Builder returned more than one
749                     # target, then filter out any sources that this
750                     # Builder isn't capable of building.
751                     if len(tlist) > 1:
752                         tlist = [t for t in tlist if match_src_suffix(t.name)]
753                     result.extend(tlist)
754             else:
755                 result.append(s)
756
757         source_factory = env.get_factory(self.source_factory)
758
759         return env.arg2nodes(result, source_factory)
760
761     def _get_src_builders_key(self, env):
762         return id(env)
763
764     memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
765
766     def get_src_builders(self, env):
767         """
768         Returns the list of source Builders for this Builder.
769
770         This exists mainly to look up Builders referenced as
771         strings in the 'BUILDER' variable of the construction
772         environment and cache the result.
773         """
774         memo_key = id(env)
775         try:
776             memo_dict = self._memo['get_src_builders']
777         except KeyError:
778             memo_dict = {}
779             self._memo['get_src_builders'] = memo_dict
780         else:
781             try:
782                 return memo_dict[memo_key]
783             except KeyError:
784                 pass
785
786         builders = []
787         for bld in self.src_builder:
788             if SCons.Util.is_String(bld):
789                 try:
790                     bld = env['BUILDERS'][bld]
791                 except KeyError:
792                     continue
793             builders.append(bld)
794
795         memo_dict[memo_key] = builders
796         return builders
797
798     def _subst_src_suffixes_key(self, env):
799         return id(env)
800
801     memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
802
803     def subst_src_suffixes(self, env):
804         """
805         The suffix list may contain construction variable expansions,
806         so we have to evaluate the individual strings.  To avoid doing
807         this over and over, we memoize the results for each construction
808         environment.
809         """
810         memo_key = id(env)
811         try:
812             memo_dict = self._memo['subst_src_suffixes']
813         except KeyError:
814             memo_dict = {}
815             self._memo['subst_src_suffixes'] = memo_dict
816         else:
817             try:
818                 return memo_dict[memo_key]
819             except KeyError:
820                 pass
821         suffixes = [env.subst(x) for x in self.src_suffix]
822         memo_dict[memo_key] = suffixes
823         return suffixes
824
825     def src_suffixes(self, env):
826         """
827         Returns the list of source suffixes for all src_builders of this
828         Builder.
829
830         This is essentially a recursive descent of the src_builder "tree."
831         (This value isn't cached because there may be changes in a
832         src_builder many levels deep that we can't see.)
833         """
834         sdict = {}
835         suffixes = self.subst_src_suffixes(env)
836         for s in suffixes:
837             sdict[s] = 1
838         for builder in self.get_src_builders(env):
839             for s in builder.src_suffixes(env):
840                 if s not in sdict:
841                     sdict[s] = 1
842                     suffixes.append(s)
843         return suffixes
844
845 class CompositeBuilder(SCons.Util.Proxy):
846     """A Builder Proxy whose main purpose is to always have
847     a DictCmdGenerator as its action, and to provide access
848     to the DictCmdGenerator's add_action() method.
849     """
850
851     def __init__(self, builder, cmdgen):
852         if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
853         SCons.Util.Proxy.__init__(self, builder)
854
855         # cmdgen should always be an instance of DictCmdGenerator.
856         self.cmdgen = cmdgen
857         self.builder = builder
858
859     def add_action(self, suffix, action):
860         self.cmdgen.add_action(suffix, action)
861         self.set_src_suffix(self.cmdgen.src_suffixes())
862
863 def is_a_Builder(obj):
864     """"Returns True iff the specified obj is one of our Builder classes.
865
866     The test is complicated a bit by the fact that CompositeBuilder
867     is a proxy, not a subclass of BuilderBase.
868     """
869     return (isinstance(obj, BuilderBase)
870             or isinstance(obj, CompositeBuilder)
871             or callable(obj))
872
873 # Local Variables:
874 # tab-width:4
875 # indent-tabs-mode:nil
876 # End:
877 # vim: set expandtab tabstop=4 shiftwidth=4: