SCons.Builder

Package SCons :: Module Builder |
[hide private]
[frames] | no frames]

# Source Code for Module SCons.Builder

      1  """SCons.Builder
      2
      3  Builder object subsystem.
      4
      5  A Builder object is a callable that encapsulates information about how
      6  to execute actions to create a target Node (file) from source Nodes
      7  (files), and how to create those dependencies for tracking.
      8
      9  The main entry point here is the Builder() factory method.  This provides
     10  a procedural interface that creates the right underlying Builder object
     11  based on the keyword arguments supplied and the types of the arguments.
     12
     13  The goal is for this external interface to be simple enough that the
     14  vast majority of users can create new Builders as necessary to support
     15  building new types of files in their configurations, without having to
     16  dive any deeper into this subsystem.
     17
     18  The base class here is BuilderBase.  This is a concrete base class which
     19  does, in fact, represent the Builder objects that we (or users) create.
     20
     21  There is also a proxy that looks like a Builder:
     22
     23      CompositeBuilder
     24
     25          This proxies for a Builder with an action that is actually a
     26          dictionary that knows how to map file suffixes to a specific
     27          action.  This is so that we can invoke different actions
     28          (compilers, compile options) for different flavors of source
     29          files.
     30
     31  Builders and their proxies have the following public interface methods
     32  used by other modules:
     33
     34      __call__()
     35          THE public interface.  Calling a Builder object (with the
     36          use of internal helper methods) sets up the target and source
     37          dependencies, appropriate mapping to a specific action, and the
     38          environment manipulation necessary for overridden construction
     39          variable.  This also takes care of warning about possible mistakes
     40          in keyword arguments.
     41
     42      add_emitter()
     43          Adds an emitter for a specific file suffix, used by some Tool
     44          modules to specify that (for example) a yacc invocation on a .y
     45          can create a .h *and* a .c file.
     46
     47      add_action()
     48          Adds an action for a specific file suffix, heavily used by
     49          Tool modules to add their specific action(s) for turning
     50          a source file into an object file to the global static
     51          and shared object file Builders.
     52
     53  There are the following methods for internal use within this module:
     54
     55      _execute()
     56          The internal method that handles the heavily lifting when a
     57          Builder is called.  This is used so that the __call__() methods
     58          can set up warning about possible mistakes in keyword-argument
     59          overrides, and *then* execute all of the steps necessary so that
     60          the warnings only occur once.
     61
     62      get_name()
     63          Returns the Builder's name within a specific Environment,
     64          primarily used to try to return helpful information in error
     65          messages.
     66
     67      adjust_suffix()
     68      get_prefix()
     69      get_suffix()
     70      get_src_suffix()
     71      set_src_suffix()
     72          Miscellaneous stuff for handling the prefix and suffix
     73          manipulation we use in turning source file names into target
     74          file names.
     75
     76  """
     77
     78  #
     79  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
     80  #
     81  # Permission is hereby granted, free of charge, to any person obtaining
     82  # a copy of this software and associated documentation files (the
     83  # "Software"), to deal in the Software without restriction, including
     84  # without limitation the rights to use, copy, modify, merge, publish,
     85  # distribute, sublicense, and/or sell copies of the Software, and to
     86  # permit persons to whom the Software is furnished to do so, subject to
     87  # the following conditions:
     88  #
     89  # The above copyright notice and this permission notice shall be included
     90  # in all copies or substantial portions of the Software.
     91  #
     92  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
     93  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
     94  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     95  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     96  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     97  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     98  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     99  #
    100
    101  __revision__ = "src/engine/SCons/Builder.py 2725 2008/03/31 12:52:02 knight"
    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          for s in SCons.Util.flatten(source):
    716              if SCons.Util.is_String(s):
    717                  match_suffix = match_src_suffix(env.subst(s))
    718                  if not match_suffix and not '.' in s:
    719                      src_suf = self.get_src_suffix(env)
    720                      s = self._adjustixes(s, None, src_suf)[0]
    721              else:
    722                  match_suffix = match_src_suffix(s.name)
    723              if match_suffix:
    724                  try:
    725                      bld = sdict[match_suffix]
    726                  except KeyError:
    727                      result.append(s)
    728                  else:
    729                      tlist = bld._execute(env, None, [s], overwarn)
    730                      # If the subsidiary Builder returned more than one
    731                      # target, then filter out any sources that this
    732                      # Builder isn't capable of building.
    733                      if len(tlist) > 1:
    734                          mss = lambda t, m=match_src_suffix: m(t.name)
    735                          tlist = filter(mss, tlist)
    736                      result.extend(tlist)
    737              else:
    738                  result.append(s)
    739
    740          source_factory = env.get_factory(self.source_factory)
    741
    742          return env.arg2nodes(result, source_factory)
    743
    744 -    def _get_src_builders_key(self, env):
    745          return id(env)
    746
    747      memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
    748
    749 -    def get_src_builders(self, env):
    750          """
    751          Returns the list of source Builders for this Builder.
    752
    753          This exists mainly to look up Builders referenced as
    754          strings in the 'BUILDER' variable of the construction
    755          environment and cache the result.
    756          """
    757          memo_key = id(env)
    758          try:
    759              memo_dict = self._memo['get_src_builders']
    760          except KeyError:
    761              memo_dict = {}
    762              self._memo['get_src_builders'] = memo_dict
    763          else:
    764              try:
    765                  return memo_dict[memo_key]
    766              except KeyError:
    767                  pass
    768
    769          builders = []
    770          for bld in self.src_builder:
    771              if SCons.Util.is_String(bld):
    772                  try:
    773                      bld = env['BUILDERS'][bld]
    774                  except KeyError:
    775                      continue
    776              builders.append(bld)
    777
    778          memo_dict[memo_key] = builders
    779          return builders
    780
    781 -    def _subst_src_suffixes_key(self, env):
    782          return id(env)
    783
    784      memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
    785
    786 -    def subst_src_suffixes(self, env):
    787          """
    788          The suffix list may contain construction variable expansions,
    789          so we have to evaluate the individual strings.  To avoid doing
    790          this over and over, we memoize the results for each construction
    791          environment.
    792          """
    793          memo_key = id(env)
    794          try:
    795              memo_dict = self._memo['subst_src_suffixes']
    796          except KeyError:
    797              memo_dict = {}
    798              self._memo['subst_src_suffixes'] = memo_dict
    799          else:
    800              try:
    801                  return memo_dict[memo_key]
    802              except KeyError:
    803                  pass
    804          suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
    805          memo_dict[memo_key] = suffixes
    806          return suffixes
    807
    808 -    def src_suffixes(self, env):
    809          """
    810          Returns the list of source suffixes for all src_builders of this
    811          Builder.
    812
    813          This is essentially a recursive descent of the src_builder "tree."
    814          (This value isn't cached because there may be changes in a
    815          src_builder many levels deep that we can't see.)
    816          """
    817          sdict = {}
    818          suffixes = self.subst_src_suffixes(env)
    819          for s in suffixes:
    820              sdict[s] = 1
    821          for builder in self.get_src_builders(env):
    822              for s in builder.src_suffixes(env):
    823                  if not sdict.has_key(s):
    824                      sdict[s] = 1
    825                      suffixes.append(s)
    826          return suffixes
    827
    828 -class CompositeBuilder(SCons.Util.Proxy):
    829      """A Builder Proxy whose main purpose is to always have
    830      a DictCmdGenerator as its action, and to provide access
    831      to the DictCmdGenerator's add_action() method.
    832      """
    833
    834 -    def __init__(self, builder, cmdgen):
    835          if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
    836          SCons.Util.Proxy.__init__(self, builder)
    837
    838          # cmdgen should always be an instance of DictCmdGenerator.
    839          self.cmdgen = cmdgen
    840          self.builder = builder
    841
    842 -    def add_action(self, suffix, action):
    843          self.cmdgen.add_action(suffix, action)
    844          self.set_src_suffix(self.cmdgen.src_suffixes())
    845