1 """SCons.Executor
2
3 A module for executing actions with specific lists of target and source
4 Nodes.
5
6 """
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 __revision__ = "src/engine/SCons/Executor.py 5110 2010/07/25 16:14:38 bdeegan"
32
33 import string
34 import UserList
35
36 from SCons.Debug import logInstanceCreation
37 import SCons.Errors
38 import SCons.Memoize
39
40
42 """Remembers exact association between targets
43 and sources of executor."""
44 - def __init__(self, targets=[], sources=[]):
45 self.targets = targets
46 self.sources = sources
47
48
49
50 -class TSList(UserList.UserList):
51 """A class that implements $TARGETS or $SOURCES expansions by wrapping
52 an executor Method. This class is used in the Executor.lvars()
53 to delay creation of NodeList objects until they're needed.
54
55 Note that we subclass UserList.UserList purely so that the
56 is_Sequence() function will identify an object of this class as
57 a list during variable expansion. We're not really using any
58 UserList.UserList methods in practice.
59 """
63 nl = self.func()
64 return getattr(nl, attr)
66 nl = self.func()
67 return nl[i]
69 nl = self.func()
70 i = max(i, 0); j = max(j, 0)
71 return nl[i:j]
73 nl = self.func()
74 return str(nl)
76 nl = self.func()
77 return repr(nl)
78
80 """A class that implements $TARGET or $SOURCE expansions by wrapping
81 an Executor method.
82 """
86 n = self.func()
87 return getattr(n, attr)
89 n = self.func()
90 if n:
91 return str(n)
92 return ''
94 n = self.func()
95 if n:
96 return repr(n)
97 return ''
98
100 """
101 A function to return the results of a Node's rfile() method,
102 if it exists, and the Node itself otherwise (if it's a Value
103 Node, e.g.).
104 """
105 try:
106 rfile = node.rfile
107 except AttributeError:
108 return node
109 else:
110 return rfile()
111
112
114 """A class for controlling instances of executing an action.
115
116 This largely exists to hold a single association of an action,
117 environment, list of environment override dictionaries, targets
118 and sources for later processing as needed.
119 """
120
121 if SCons.Memoize.use_memoizer:
122 __metaclass__ = SCons.Memoize.Memoized_Metaclass
123
124 memoizer_counters = []
125
126 - def __init__(self, action, env=None, overridelist=[{}],
127 targets=[], sources=[], builder_kw={}):
128 if __debug__: logInstanceCreation(self, 'Executor.Executor')
129 self.set_action_list(action)
130 self.pre_actions = []
131 self.post_actions = []
132 self.env = env
133 self.overridelist = overridelist
134 if targets or sources:
135 self.batches = [Batch(targets[:], sources[:])]
136 else:
137 self.batches = []
138 self.builder_kw = builder_kw
139 self._memo = {}
140
156
173
175 try:
176 return self._changed_sources_list
177 except AttributeError:
178 self._get_changes()
179 return self._changed_sources_list
180
182 try:
183 return self._changed_targets_list
184 except AttributeError:
185 self._get_changes()
186 return self._changed_targets_list
187
191
194
198
201
203 try:
204 return self._unchanged_sources_list
205 except AttributeError:
206 self._get_changes()
207 return self._unchanged_sources_list
208
210 try:
211 return self._unchanged_targets_list
212 except AttributeError:
213 self._get_changes()
214 return self._unchanged_targets_list
215
217 if not self.action_list:
218 return []
219 targets_string = self.action_list[0].get_targets(self.env, self)
220 if targets_string[0] == '$':
221 targets_string = targets_string[1:]
222 return self.get_lvars()[targets_string]
223
232
234 return self.pre_actions + self.action_list + self.post_actions
235
237 """Returns all targets for all batches of this Executor."""
238 result = []
239 for batch in self.batches:
240
241 result.extend(list(batch.targets))
242 return result
243
245 """Returns all sources for all batches of this Executor."""
246 result = []
247 for batch in self.batches:
248
249 result.extend(list(batch.sources))
250 return result
251
253 """Returns all unique children (dependencies) for all batches
254 of this Executor.
255
256 The Taskmaster can recognize when it's already evaluated a
257 Node, so we don't have to make this list unique for its intended
258 canonical use case, but we expect there to be a lot of redundancy
259 (long lists of batched .cc files #including the same .h files
260 over and over), so removing the duplicates once up front should
261 save the Taskmaster a lot of work.
262 """
263 result = SCons.Util.UniqueList([])
264 for target in self.get_all_targets():
265 result.extend(target.children())
266 return result
267
277
287
288 memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
289
291 """Fetch or create the appropriate build Environment
292 for this Executor.
293 """
294 try:
295 return self._memo['get_build_env']
296 except KeyError:
297 pass
298
299
300
301
302
303
304 overrides = {}
305 for odict in self.overridelist:
306 overrides.update(odict)
307
308 import SCons.Defaults
309 env = self.env or SCons.Defaults.DefaultEnvironment()
310 build_env = env.Override(overrides)
311
312 self._memo['get_build_env'] = build_env
313
314 return build_env
315
317 """Fetch the scanner path for this executor's targets and sources.
318 """
319 env = self.get_build_env()
320 try:
321 cwd = self.batches[0].targets[0].cwd
322 except (IndexError, AttributeError):
323 cwd = None
324 return scanner.path(env, cwd,
325 self.get_all_targets(),
326 self.get_all_sources())
327
333
336
338 """Actually execute the action list."""
339 env = self.get_build_env()
340 kw = self.get_kw(kw)
341 status = 0
342 for act in self.get_action_list():
343
344 args = ([], [], env)
345 status = apply(act, args, kw)
346 if isinstance(status, SCons.Errors.BuildError):
347 status.executor = self
348 raise status
349 elif status:
350 msg = "Error %s" % status
351 raise SCons.Errors.BuildError(
352 errstr=msg,
353 node=self.batches[0].targets,
354 executor=self,
355 action=act)
356 return status
357
358
359
360
361
364
367
369 """Add source files to this Executor's list. This is necessary
370 for "multi" Builders that can be called repeatedly to build up
371 a source file list for a given target."""
372
373 assert (len(self.batches) == 1)
374
375 sources = filter(lambda x, s=self.batches[0].sources: x not in s, sources)
376 self.batches[0].sources.extend(sources)
377
379 return self.batches[0].sources
380
382 """Add pair of associated target and source to this Executor's list.
383 This is necessary for "batch" Builders that can be called repeatedly
384 to build up a list of matching target and source files that will be
385 used in order to update multiple target files at once from multiple
386 corresponding source files, for tools like MSVC that support it."""
387 self.batches.append(Batch(targets, sources))
388
390 """
391 Preparatory checks for whether this Executor can go ahead
392 and (try to) build its targets.
393 """
394 for s in self.get_all_sources():
395 if s.missing():
396 msg = "Source `%s' not found, needed by target `%s'."
397 raise SCons.Errors.StopError, msg % (s, self.batches[0].targets[0])
398
400 self.pre_actions.append(action)
401
402 - def add_post_action(self, action):
403 self.post_actions.append(action)
404
405
406
412
413
416
421
422 memoizer_counters.append(SCons.Memoize.CountValue('get_contents'))
423
424 - def get_contents(self):
425 """Fetch the signature contents. This is the main reason this
426 class exists, so we can compute this once and cache it regardless
427 of how many target or source Nodes there are.
428 """
429 try:
430 return self._memo['get_contents']
431 except KeyError:
432 pass
433 env = self.get_build_env()
434 get = lambda action, t=self.get_all_targets(), s=self.get_all_sources(), e=env: \
435 action.get_contents(t, s, e)
436 result = string.join(map(get, self.get_action_list()), "")
437 self._memo['get_contents'] = result
438 return result
439
441 """Fetch a time stamp for this Executor. We don't have one, of
442 course (only files do), but this is the interface used by the
443 timestamp module.
444 """
445 return 0
446
450
455
456 - def scan(self, scanner, node_list):
491
493 return (node,) + tuple(ignore)
494
495 memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key))
496
498 key = (node,) + tuple(ignore)
499 try:
500 memo_dict = self._memo['get_unignored_sources']
501 except KeyError:
502 memo_dict = {}
503 self._memo['get_unignored_sources'] = memo_dict
504 else:
505 try:
506 return memo_dict[key]
507 except KeyError:
508 pass
509
510 if node:
511
512
513 sourcelist = []
514 for b in self.batches:
515 if node in b.targets:
516 sourcelist = b.sources
517 break
518 else:
519 sourcelist = self.get_all_sources()
520 if ignore:
521 idict = {}
522 for i in ignore:
523 idict[i] = 1
524 sourcelist = filter(lambda s, i=idict: not i.has_key(s), sourcelist)
525
526 memo_dict[key] = sourcelist
527
528 return sourcelist
529
541
542
543
544 _batch_executors = {}
545
548
552
553 nullenv = None
554
555
557 """Use singleton pattern for Null Environments."""
558 global nullenv
559
560 import SCons.Util
561 class NullEnvironment(SCons.Util.Null):
562 import SCons.CacheDir
563 _CacheDir_path = None
564 _CacheDir = SCons.CacheDir.CacheDir(None)
565 def get_CacheDir(self):
566 return self._CacheDir
567
568 if not nullenv:
569 nullenv = NullEnvironment()
570 return nullenv
571
573 """A null Executor, with a null build Environment, that does
574 nothing when the rest of the methods call it.
575
576 This might be able to disapper when we refactor things to
577 disassociate Builders from Nodes entirely, so we're not
578 going to worry about unit tests for this--at least for now.
579 """
598 return self.batches[0].targets
600 return self.batches[0].targets[0].sources
609 - def get_contents(self):
612 """Morph this Null executor to a real Executor object."""
613 batches = self.batches
614 self.__class__ = Executor
615 self.__init__([])
616 self.batches = batches
617
618
619
620
624 - def add_post_action(self, action):
625 self._morph()
626 self.add_post_action(action)
630
631
632
633
634
635
636
637