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