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 2928 2008/04/29 22:44:09 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 prepare(self):
164 """ 165 Preparatory checks for whether this Executor can go ahead 166 and (try to) build its targets. 167 """ 168 for s in self.get_sources(): 169 if s.missing(): 170 msg = "Source `%s' not found, needed by target `%s'." 171 raise SCons.Errors.StopError, msg % (s, self.targets[0])
172
173 - def add_pre_action(self, action):
174 self.pre_actions.append(action)
175
176 - def add_post_action(self, action):
177 self.post_actions.append(action)
178 179 # another extra indirection for new-style objects and nullify... 180
181 - def my_str(self):
182 env = self.get_build_env() 183 get = lambda action, t=self.targets, s=self.get_sources(), e=env: \ 184 action.genstring(t, s, e) 185 return string.join(map(get, self.get_action_list()), "\n")
186 187
188 - def __str__(self):
189 return self.my_str()
190
191 - def nullify(self):
192 self.cleanup() 193 self.do_execute = self.do_nothing 194 self.my_str = lambda S=self: ''
195 196 memoizer_counters.append(SCons.Memoize.CountValue('get_contents')) 197
198 - def get_contents(self):
199 """Fetch the signature contents. This is the main reason this 200 class exists, so we can compute this once and cache it regardless 201 of how many target or source Nodes there are. 202 """ 203 try: 204 return self._memo['get_contents'] 205 except KeyError: 206 pass 207 env = self.get_build_env() 208 get = lambda action, t=self.targets, s=self.get_sources(), e=env: \ 209 action.get_contents(t, s, e) 210 result = string.join(map(get, self.get_action_list()), "") 211 self._memo['get_contents'] = result 212 return result
213
214 - def get_timestamp(self):
215 """Fetch a time stamp for this Executor. We don't have one, of 216 course (only files do), but this is the interface used by the 217 timestamp module. 218 """ 219 return 0
220
221 - def scan_targets(self, scanner):
222 self.scan(scanner, self.targets)
223
224 - def scan_sources(self, scanner):
225 if self.sources: 226 self.scan(scanner, self.get_sources())
227
228 - def scan(self, scanner, node_list):
229 """Scan a list of this Executor's files (targets or sources) for 230 implicit dependencies and update all of the targets with them. 231 This essentially short-circuits an N*M scan of the sources for 232 each individual target, which is a hell of a lot more efficient. 233 """ 234 env = self.get_build_env() 235 236 deps = [] 237 if scanner: 238 for node in node_list: 239 node.disambiguate() 240 s = scanner.select(node) 241 if not s: 242 continue 243 path = self.get_build_scanner_path(s) 244 deps.extend(node.get_implicit_deps(env, s, path)) 245 else: 246 kw = self.get_kw() 247 for node in node_list: 248 node.disambiguate() 249 scanner = node.get_env_scanner(env, kw) 250 if not scanner: 251 continue 252 scanner = scanner.select(node) 253 if not scanner: 254 continue 255 path = self.get_build_scanner_path(scanner) 256 deps.extend(node.get_implicit_deps(env, scanner, path)) 257 258 deps.extend(self.get_implicit_deps()) 259 260 for tgt in self.targets: 261 tgt.add_to_implicit(deps)
262
263 - def _get_unignored_sources_key(self, ignore=()):
264 return tuple(ignore)
265 266 memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key)) 267
268 - def get_unignored_sources(self, ignore=()):
269 ignore = tuple(ignore) 270 try: 271 memo_dict = self._memo['get_unignored_sources'] 272 except KeyError: 273 memo_dict = {} 274 self._memo['get_unignored_sources'] = memo_dict 275 else: 276 try: 277 return memo_dict[ignore] 278 except KeyError: 279 pass 280 281 sourcelist = self.get_sources() 282 if ignore: 283 idict = {} 284 for i in ignore: 285 idict[i] = 1 286 sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist) 287 288 memo_dict[ignore] = sourcelist 289 290 return sourcelist
291
292 - def _process_sources_key(self, func, ignore=()):
293 return (func, tuple(ignore))
294 295 memoizer_counters.append(SCons.Memoize.CountDict('process_sources', _process_sources_key)) 296
297 - def process_sources(self, func, ignore=()):
298 memo_key = (func, tuple(ignore)) 299 try: 300 memo_dict = self._memo['process_sources'] 301 except KeyError: 302 memo_dict = {} 303 self._memo['process_sources'] = memo_dict 304 else: 305 try: 306 return memo_dict[memo_key] 307 except KeyError: 308 pass 309 310 result = map(func, self.get_unignored_sources(ignore)) 311 312 memo_dict[memo_key] = result 313 314 return result
315
316 - def get_implicit_deps(self):
317 """Return the executor's implicit dependencies, i.e. the nodes of 318 the commands to be executed.""" 319 result = [] 320 build_env = self.get_build_env() 321 for act in self.get_action_list(): 322 result.extend(act.get_implicit_deps(self.targets, self.get_sources(), build_env)) 323 return result
324 325 326 _Executor = Executor 327
328 -class Null(_Executor):
329 """A null Executor, with a null build Environment, that does 330 nothing when the rest of the methods call it. 331 332 This might be able to disapper when we refactor things to 333 disassociate Builders from Nodes entirely, so we're not 334 going to worry about unit tests for this--at least for now. 335 """
336 - def __init__(self, *args, **kw):
337 if __debug__: logInstanceCreation(self, 'Executor.Null') 338 kw['action'] = [] 339 apply(_Executor.__init__, (self,), kw)
340 - def get_build_env(self):
341 import SCons.Util 342 class NullEnvironment(SCons.Util.Null): 343 import SCons.CacheDir 344 _CacheDir_path = None 345 _CacheDir = SCons.CacheDir.CacheDir(None) 346 def get_CacheDir(self): 347 return self._CacheDir
348 return NullEnvironment()
349 - def get_build_scanner_path(self):
350 return None
351 - def cleanup(self):
352 pass
353 - def prepare(self):
354 pass
355