SCons.Executor

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

# Source Code for Module SCons.Executor

      1  """SCons.Executor
      2
      3  A module for executing actions with specific lists of target and source
      4  Nodes.
      5
      6  """
      7
      8  #
      9  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
     10  #
     11  # Permission is hereby granted, free of charge, to any person obtaining
     12  # a copy of this software and associated documentation files (the
     13  # "Software"), to deal in the Software without restriction, including
     14  # without limitation the rights to use, copy, modify, merge, publish,
     15  # distribute, sublicense, and/or sell copies of the Software, and to
     16  # permit persons to whom the Software is furnished to do so, subject to
     17  # the following conditions:
     18  #
     19  # The above copyright notice and this permission notice shall be included
     20  # in all copies or substantial portions of the Software.
     21  #
     22  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
     23  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
     24  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     25  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     26  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     27  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     28  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     29  #
     30
     31  __revision__ = "src/engine/SCons/Executor.py 2725 2008/03/31 12:52:02 knight"
     32
     33  import string
     34
     35  from SCons.Debug import logInstanceCreation
     36  import SCons.Errors
     37  import SCons.Memoize
     38
     39
     40 -class Executor:
     41      """A class for controlling instances of executing an action.
     42
     43      This largely exists to hold a single association of an action,
     44      environment, list of environment override dictionaries, targets
     45      and sources for later processing as needed.
     46      """
     47
     48      if SCons.Memoize.use_memoizer:
     49          __metaclass__ = SCons.Memoize.Memoized_Metaclass
     50
     51      memoizer_counters = []
     52
     53 -    def __init__(self, action, env=None, overridelist=[{}],
     54                   targets=[], sources=[], builder_kw={}):
     55          if __debug__: logInstanceCreation(self, 'Executor.Executor')
     56          self.set_action_list(action)
     57          self.pre_actions = []
     58          self.post_actions = []
     59          self.env = env
     60          self.overridelist = overridelist
     61          self.targets = targets
     62          self.sources = sources[:]
     63          self.sources_need_sorting = False
     64          self.builder_kw = builder_kw
     65          self._memo = {}
     66
     67 -    def set_action_list(self, action):
     68          import SCons.Util
     69          if not SCons.Util.is_List(action):
     70              if not action:
     71                  import SCons.Errors
     72                  raise SCons.Errors.UserError, "Executor must have an action."
     73              action = [action]
     74          self.action_list = action
     75
     76 -    def get_action_list(self):
     77          return self.pre_actions + self.action_list + self.post_actions
     78
     79      memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
     80
     81 -    def get_build_env(self):
     82          """Fetch or create the appropriate build Environment
     83          for this Executor.
     84          """
     85          try:
     86              return self._memo['get_build_env']
     87          except KeyError:
     88              pass
     89
     90          # Create the build environment instance with appropriate
     91          # overrides.  These get evaluated against the current
     92          # environment's construction variables so that users can
     93          # add to existing values by referencing the variable in
     94          # the expansion.
     95          overrides = {}
     96          for odict in self.overridelist:
     97              overrides.update(odict)
     98
     99          import SCons.Defaults
    100          env = self.env or SCons.Defaults.DefaultEnvironment()
    101          build_env = env.Override(overrides)
    102
    103          self._memo['get_build_env'] = build_env
    104
    105          return build_env
    106
    107 -    def get_build_scanner_path(self, scanner):
    108          """Fetch the scanner path for this executor's targets and sources.
    109          """
    110          env = self.get_build_env()
    111          try:
    112              cwd = self.targets[0].cwd
    113          except (IndexError, AttributeError):
    114              cwd = None
    115          return scanner.path(env, cwd, self.targets, self.get_sources())
    116
    117 -    def get_kw(self, kw={}):
    118          result = self.builder_kw.copy()
    119          result.update(kw)
    120          return result
    121
    122 -    def do_nothing(self, target, kw):
    123          return 0
    124
    125 -    def do_execute(self, target, kw):
    126          """Actually execute the action list."""
    127          env = self.get_build_env()
    128          kw = self.get_kw(kw)
    129          status = 0
    130          for act in self.get_action_list():
    131              status = apply(act, (self.targets, self.get_sources(), env), kw)
    132              if isinstance(status, SCons.Errors.BuildError):
    133                  status.executor = self
    134                  raise status
    135              elif status:
    136                  msg = "Error %s" % status
    137                  raise SCons.Errors.BuildError(errstr=msg, executor=self, action=act)
    138          return status
    139
    140      # use extra indirection because with new-style objects (Python 2.2
    141      # and above) we can't override special methods, and nullify() needs
    142      # to be able to do this.
    143
    144 -    def __call__(self, target, **kw):
    145          return self.do_execute(target, kw)
    146
    147 -    def cleanup(self):
    148          self._memo = {}
    149
    150 -    def add_sources(self, sources):
    151          """Add source files to this Executor's list.  This is necessary
    152          for "multi" Builders that can be called repeatedly to build up
    153          a source file list for a given target."""
    154          self.sources.extend(sources)
    155          self.sources_need_sorting = True
    156
    157 -    def get_sources(self):
    158          if self.sources_need_sorting:
    159              self.sources = SCons.Util.uniquer_hashables(self.sources)
    160              self.sources_need_sorting = False
    161          return self.sources
    162
    163 -    def add_pre_action(self, action):
    164          self.pre_actions.append(action)
    165
    166 -    def add_post_action(self, action):
    167          self.post_actions.append(action)
    168
    169      # another extra indirection for new-style objects and nullify...
    170
    171 -    def my_str(self):
    172          env = self.get_build_env()
    173          get = lambda action, t=self.targets, s=self.get_sources(), e=env: \
    174                       action.genstring(t, s, e)
    175          return string.join(map(get, self.get_action_list()), "\n")
    176
    177
    178 -    def __str__(self):
    179          return self.my_str()
    180
    181 -    def nullify(self):
    182          self.cleanup()
    183          self.do_execute = self.do_nothing
    184          self.my_str     = lambda S=self: ''
    185
    186      memoizer_counters.append(SCons.Memoize.CountValue('get_contents'))
    187
    188 -    def get_contents(self):
    189          """Fetch the signature contents.  This is the main reason this
    190          class exists, so we can compute this once and cache it regardless
    191          of how many target or source Nodes there are.
    192          """
    193          try:
    194              return self._memo['get_contents']
    195          except KeyError:
    196              pass
    197          env = self.get_build_env()
    198          get = lambda action, t=self.targets, s=self.get_sources(), e=env: \
    199                       action.get_contents(t, s, e)
    200          result = string.join(map(get, self.get_action_list()), "")
    201          self._memo['get_contents'] = result
    202          return result
    203
    204 -    def get_timestamp(self):
    205          """Fetch a time stamp for this Executor.  We don't have one, of
    206          course (only files do), but this is the interface used by the
    207          timestamp module.
    208          """
    209          return 0
    210
    211 -    def scan_targets(self, scanner):
    212          self.scan(scanner, self.targets)
    213
    214 -    def scan_sources(self, scanner):
    215          if self.sources:
    216              self.scan(scanner, self.get_sources())
    217
    218 -    def scan(self, scanner, node_list):
    219          """Scan a list of this Executor's files (targets or sources) for
    220          implicit dependencies and update all of the targets with them.
    221          This essentially short-circuits an N*M scan of the sources for
    222          each individual target, which is a hell of a lot more efficient.
    223          """
    224          map(lambda N: N.disambiguate(), node_list)
    225
    226          env = self.get_build_env()
    227          select_specific_scanner = lambda t: (t[0], t[1].select(t[0]))
    228          remove_null_scanners = lambda t: not t[1] is None
    229          add_scanner_path = lambda t, s=self: \
    230                                    (t[0], t[1], s.get_build_scanner_path(t[1]))
    231          if scanner:
    232              scanner_list = map(lambda n, s=scanner: (n, s), node_list)
    233          else:
    234              kw = self.get_kw()
    235              get_initial_scanners = lambda n, e=env, kw=kw: \
    236                                            (n, n.get_env_scanner(e, kw))
    237              scanner_list = map(get_initial_scanners, node_list)
    238              scanner_list = filter(remove_null_scanners, scanner_list)
    239
    240          scanner_list = map(select_specific_scanner, scanner_list)
    241          scanner_list = filter(remove_null_scanners, scanner_list)
    242          scanner_path_list = map(add_scanner_path, scanner_list)
    243
    244          deps = []
    245          for node, scanner, path in scanner_path_list:
    246              deps.extend(node.get_implicit_deps(env, scanner, path))
    247
    248          deps.extend(self.get_implicit_deps())
    249
    250          for tgt in self.targets:
    251              tgt.add_to_implicit(deps)
    252
    253 -    def get_missing_sources(self):
    254          """
    255          """
    256          return filter(lambda s: s.missing(), self.get_sources())
    257
    258 -    def _get_unignored_sources_key(self, ignore=()):
    259          return tuple(ignore)
    260
    261      memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key))
    262
    263 -    def get_unignored_sources(self, ignore=()):
    264          ignore = tuple(ignore)
    265          try:
    266              memo_dict = self._memo['get_unignored_sources']
    267          except KeyError:
    268              memo_dict = {}
    269              self._memo['get_unignored_sources'] = memo_dict
    270          else:
    271              try:
    272                  return memo_dict[ignore]
    273              except KeyError:
    274                  pass
    275
    276          sourcelist = self.get_sources()
    277          if ignore:
    278              idict = {}
    279              for i in ignore:
    280                  idict[i] = 1
    281              sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist)
    282
    283          memo_dict[ignore] = sourcelist
    284
    285          return sourcelist
    286
    287 -    def _process_sources_key(self, func, ignore=()):
    288          return (func, tuple(ignore))
    289
    290      memoizer_counters.append(SCons.Memoize.CountDict('process_sources', _process_sources_key))
    291
    292 -    def process_sources(self, func, ignore=()):
    293          memo_key = (func, tuple(ignore))
    294          try:
    295              memo_dict = self._memo['process_sources']
    296          except KeyError:
    297              memo_dict = {}
    298              self._memo['process_sources'] = memo_dict
    299          else:
    300              try:
    301                  return memo_dict[memo_key]
    302              except KeyError:
    303                  pass
    304
    305          result = map(func, self.get_unignored_sources(ignore))
    306
    307          memo_dict[memo_key] = result
    308
    309          return result
    310
    311 -    def get_implicit_deps(self):
    312          """Return the executor's implicit dependencies, i.e. the nodes of
    313          the commands to be executed."""
    314          result = []
    315          build_env = self.get_build_env()
    316          for act in self.get_action_list():
    317              result.extend(act.get_implicit_deps(self.targets, self.get_sources(), build_env))
    318          return result
    319
    320
    321  _Executor = Executor
    322
    323 -class Null(_Executor):
    324      """A null Executor, with a null build Environment, that does
    325      nothing when the rest of the methods call it.
    326
    327      This might be able to disapper when we refactor things to
    328      disassociate Builders from Nodes entirely, so we're not
    329      going to worry about unit tests for this--at least for now.
    330      """
    331 -    def __init__(self, *args, **kw):
    332          if __debug__: logInstanceCreation(self, 'Executor.Null')
    333          kw['action'] = []
    334          apply(_Executor.__init__, (self,), kw)
    335 -    def get_build_env(self):
    336          import SCons.Util
    337          class NullEnvironment(SCons.Util.Null):
    338              import SCons.CacheDir
    339              _CacheDir_path = None
    340              _CacheDir = SCons.CacheDir.CacheDir(None)
    341              def get_CacheDir(self):
    342                  return self._CacheDir
    343          return NullEnvironment()
    344 -    def get_build_scanner_path(self):
    345          return None
    346 -    def cleanup(self):
    347          pass
    348