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