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 3842 2008/12/20 22:59:52 scons" 
 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( 138 errstr=msg, 139 node=self.targets, 140 executor=self, 141 action=act) 142 return status
143 144 # use extra indirection because with new-style objects (Python 2.2 145 # and above) we can't override special methods, and nullify() needs 146 # to be able to do this. 147
148 - def __call__(self, target, **kw):
149 return self.do_execute(target, kw)
150
151 - def cleanup(self):
152 self._memo = {}
153
154 - def add_sources(self, sources):
155 """Add source files to this Executor's list. This is necessary 156 for "multi" Builders that can be called repeatedly to build up 157 a source file list for a given target.""" 158 self.sources.extend(sources) 159 self.sources_need_sorting = True
160
161 - def get_sources(self):
162 if self.sources_need_sorting: 163 self.sources = SCons.Util.uniquer_hashables(self.sources) 164 self.sources_need_sorting = False 165 return self.sources
166
167 - def prepare(self):
168 """ 169 Preparatory checks for whether this Executor can go ahead 170 and (try to) build its targets. 171 """ 172 for s in self.get_sources(): 173 if s.missing(): 174 msg = "Source `%s' not found, needed by target `%s'." 175 raise SCons.Errors.StopError, msg % (s, self.targets[0])
176
177 - def add_pre_action(self, action):
178 self.pre_actions.append(action)
179
180 - def add_post_action(self, action):
181 self.post_actions.append(action)
182 183 # another extra indirection for new-style objects and nullify... 184
185 - def my_str(self):
186 env = self.get_build_env() 187 get = lambda action, t=self.targets, s=self.get_sources(), e=env: \ 188 action.genstring(t, s, e) 189 return string.join(map(get, self.get_action_list()), "\n")
190 191
192 - def __str__(self):
193 return self.my_str()
194
195 - def nullify(self):
196 self.cleanup() 197 self.do_execute = self.do_nothing 198 self.my_str = lambda S=self: ''
199 200 memoizer_counters.append(SCons.Memoize.CountValue('get_contents')) 201
202 - def get_contents(self):
203 """Fetch the signature contents. This is the main reason this 204 class exists, so we can compute this once and cache it regardless 205 of how many target or source Nodes there are. 206 """ 207 try: 208 return self._memo['get_contents'] 209 except KeyError: 210 pass 211 env = self.get_build_env() 212 get = lambda action, t=self.targets, s=self.get_sources(), e=env: \ 213 action.get_contents(t, s, e) 214 result = string.join(map(get, self.get_action_list()), "") 215 self._memo['get_contents'] = result 216 return result
217
218 - def get_timestamp(self):
219 """Fetch a time stamp for this Executor. We don't have one, of 220 course (only files do), but this is the interface used by the 221 timestamp module. 222 """ 223 return 0
224
225 - def scan_targets(self, scanner):
226 self.scan(scanner, self.targets)
227
228 - def scan_sources(self, scanner):
229 if self.sources: 230 self.scan(scanner, self.get_sources())
231
232 - def scan(self, scanner, node_list):
233 """Scan a list of this Executor's files (targets or sources) for 234 implicit dependencies and update all of the targets with them. 235 This essentially short-circuits an N*M scan of the sources for 236 each individual target, which is a hell of a lot more efficient. 237 """ 238 env = self.get_build_env() 239 240 deps = [] 241 if scanner: 242 for node in node_list: 243 node.disambiguate() 244 s = scanner.select(node) 245 if not s: 246 continue 247 path = self.get_build_scanner_path(s) 248 deps.extend(node.get_implicit_deps(env, s, path)) 249 else: 250 kw = self.get_kw() 251 for node in node_list: 252 node.disambiguate() 253 scanner = node.get_env_scanner(env, kw) 254 if not scanner: 255 continue 256 scanner = scanner.select(node) 257 if not scanner: 258 continue 259 path = self.get_build_scanner_path(scanner) 260 deps.extend(node.get_implicit_deps(env, scanner, path)) 261 262 deps.extend(self.get_implicit_deps()) 263 264 for tgt in self.targets: 265 tgt.add_to_implicit(deps)
266
267 - def _get_unignored_sources_key(self, ignore=()):
268 return tuple(ignore)
269 270 memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key)) 271
272 - def get_unignored_sources(self, ignore=()):
273 ignore = tuple(ignore) 274 try: 275 memo_dict = self._memo['get_unignored_sources'] 276 except KeyError: 277 memo_dict = {} 278 self._memo['get_unignored_sources'] = memo_dict 279 else: 280 try: 281 return memo_dict[ignore] 282 except KeyError: 283 pass 284 285 sourcelist = self.get_sources() 286 if ignore: 287 idict = {} 288 for i in ignore: 289 idict[i] = 1 290 sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist) 291 292 memo_dict[ignore] = sourcelist 293 294 return sourcelist
295
296 - def _process_sources_key(self, func, ignore=()):
297 return (func, tuple(ignore))
298 299 memoizer_counters.append(SCons.Memoize.CountDict('process_sources', _process_sources_key)) 300
301 - def process_sources(self, func, ignore=()):
302 memo_key = (func, tuple(ignore)) 303 try: 304 memo_dict = self._memo['process_sources'] 305 except KeyError: 306 memo_dict = {} 307 self._memo['process_sources'] = memo_dict 308 else: 309 try: 310 return memo_dict[memo_key] 311 except KeyError: 312 pass 313 314 result = map(func, self.get_unignored_sources(ignore)) 315 316 memo_dict[memo_key] = result 317 318 return result
319
320 - def get_implicit_deps(self):
321 """Return the executor's implicit dependencies, i.e. the nodes of 322 the commands to be executed.""" 323 result = [] 324 build_env = self.get_build_env() 325 for act in self.get_action_list(): 326 result.extend(act.get_implicit_deps(self.targets, self.get_sources(), build_env)) 327 return result
328 329 nullenv = None 330
331 -def get_NullEnvironment():
332 """Use singleton pattern for Null Environments.""" 333 global nullenv 334 335 import SCons.Util 336 class NullEnvironment(SCons.Util.Null): 337 import SCons.CacheDir 338 _CacheDir_path = None 339 _CacheDir = SCons.CacheDir.CacheDir(None) 340 def get_CacheDir(self): 341 return self._CacheDir
342 343 if not nullenv: 344 nullenv = NullEnvironment() 345 return nullenv 346
347 -class Null:
348 """A null Executor, with a null build Environment, that does 349 nothing when the rest of the methods call it. 350 351 This might be able to disapper when we refactor things to 352 disassociate Builders from Nodes entirely, so we're not 353 going to worry about unit tests for this--at least for now. 354 """
355 - def __init__(self, *args, **kw):
356 if __debug__: logInstanceCreation(self, 'Executor.Null') 357 self.targets = kw['targets']
358 - def get_build_env(self):
359 return get_NullEnvironment()
360 - def get_build_scanner_path(self):
361 return None
362 - def cleanup(self):
363 pass
364 - def prepare(self):
365 pass
366 - def get_unignored_sources(self, *args, **kw):
367 return tuple(())
368 - def get_action_list(self):
369 return []
370 - def __call__(self, *args, **kw):
371 return 0
372 - def get_contents(self):
373 return ''
374
375 - def _morph(self):
376 """Morph this Null executor to a real Executor object.""" 377 self.__class__ = Executor 378 self.__init__([], targets=self.targets)
379 380 # The following methods require morphing this Null Executor to a 381 # real Executor object. 382
383 - def add_pre_action(self, action):
384 self._morph() 385 self.add_pre_action(action)
386 - def add_post_action(self, action):
387 self._morph() 388 self.add_post_action(action)
389 - def set_action_list(self, action):
390 self._morph() 391 self.set_action_list(action)
392