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 __revision__ = "src/engine/SCons/Executor.py 5023 2010/06/14 22:05:46 scons"
31
32 import collections
33
34 from SCons.Debug import logInstanceCreation
35 import SCons.Errors
36 import SCons.Memoize
37
38
40 """Remembers exact association between targets
41 and sources of executor."""
42 - def __init__(self, targets=[], sources=[]):
43 self.targets = targets
44 self.sources = sources
45
46
47
48 -class TSList(collections.UserList):
49 """A class that implements $TARGETS or $SOURCES expansions by wrapping
50 an executor Method. This class is used in the Executor.lvars()
51 to delay creation of NodeList objects until they're needed.
52
53 Note that we subclass collections.UserList purely so that the
54 is_Sequence() function will identify an object of this class as
55 a list during variable expansion. We're not really using any
56 collections.UserList methods in practice.
57 """
61 nl = self.func()
62 return getattr(nl, attr)
64 nl = self.func()
65 return nl[i]
67 nl = self.func()
68 i = max(i, 0); j = max(j, 0)
69 return nl[i:j]
71 nl = self.func()
72 return str(nl)
74 nl = self.func()
75 return repr(nl)
76
78 """A class that implements $TARGET or $SOURCE expansions by wrapping
79 an Executor method.
80 """
84 n = self.func()
85 return getattr(n, attr)
87 n = self.func()
88 if n:
89 return str(n)
90 return ''
92 n = self.func()
93 if n:
94 return repr(n)
95 return ''
96
98 """
99 A function to return the results of a Node's rfile() method,
100 if it exists, and the Node itself otherwise (if it's a Value
101 Node, e.g.).
102 """
103 try:
104 rfile = node.rfile
105 except AttributeError:
106 return node
107 else:
108 return rfile()
109
110
112 """A class for controlling instances of executing an action.
113
114 This largely exists to hold a single association of an action,
115 environment, list of environment override dictionaries, targets
116 and sources for later processing as needed.
117 """
118
119 if SCons.Memoize.use_memoizer:
120 __metaclass__ = SCons.Memoize.Memoized_Metaclass
121
122 memoizer_counters = []
123
124 - def __init__(self, action, env=None, overridelist=[{}],
125 targets=[], sources=[], builder_kw={}):
126 if __debug__: logInstanceCreation(self, 'Executor.Executor')
127 self.set_action_list(action)
128 self.pre_actions = []
129 self.post_actions = []
130 self.env = env
131 self.overridelist = overridelist
132 if targets or sources:
133 self.batches = [Batch(targets[:], sources[:])]
134 else:
135 self.batches = []
136 self.builder_kw = builder_kw
137 self._memo = {}
138
154
171
173 try:
174 return self._changed_sources_list
175 except AttributeError:
176 self._get_changes()
177 return self._changed_sources_list
178
180 try:
181 return self._changed_targets_list
182 except AttributeError:
183 self._get_changes()
184 return self._changed_targets_list
185
189
192
196
199
201 try:
202 return self._unchanged_sources_list
203 except AttributeError:
204 self._get_changes()
205 return self._unchanged_sources_list
206
208 try:
209 return self._unchanged_targets_list
210 except AttributeError:
211 self._get_changes()
212 return self._unchanged_targets_list
213
215 if not self.action_list:
216 return []
217 targets_string = self.action_list[0].get_targets(self.env, self)
218 if targets_string[0] == '$':
219 targets_string = targets_string[1:]
220 return self.get_lvars()[targets_string]
221
230
232 return self.pre_actions + self.action_list + self.post_actions
233
235 """Returns all targets for all batches of this Executor."""
236 result = []
237 for batch in self.batches:
238 result.extend(batch.targets)
239 return result
240
242 """Returns all sources for all batches of this Executor."""
243 result = []
244 for batch in self.batches:
245 result.extend(batch.sources)
246 return result
247
249 """Returns all unique children (dependencies) for all batches
250 of this Executor.
251
252 The Taskmaster can recognize when it's already evaluated a
253 Node, so we don't have to make this list unique for its intended
254 canonical use case, but we expect there to be a lot of redundancy
255 (long lists of batched .cc files #including the same .h files
256 over and over), so removing the duplicates once up front should
257 save the Taskmaster a lot of work.
258 """
259 result = SCons.Util.UniqueList([])
260 for target in self.get_all_targets():
261 result.extend(target.children())
262 return result
263
272
282
283 memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
284
286 """Fetch or create the appropriate build Environment
287 for this Executor.
288 """
289 try:
290 return self._memo['get_build_env']
291 except KeyError:
292 pass
293
294
295
296
297
298
299 overrides = {}
300 for odict in self.overridelist:
301 overrides.update(odict)
302
303 import SCons.Defaults
304 env = self.env or SCons.Defaults.DefaultEnvironment()
305 build_env = env.Override(overrides)
306
307 self._memo['get_build_env'] = build_env
308
309 return build_env
310
312 """Fetch the scanner path for this executor's targets and sources.
313 """
314 env = self.get_build_env()
315 try:
316 cwd = self.batches[0].targets[0].cwd
317 except (IndexError, AttributeError):
318 cwd = None
319 return scanner.path(env, cwd,
320 self.get_all_targets(),
321 self.get_all_sources())
322
328
331
333 """Actually execute the action list."""
334 env = self.get_build_env()
335 kw = self.get_kw(kw)
336 status = 0
337 for act in self.get_action_list():
338
339 args = ([], [], env)
340 status = act(*args, **kw)
341 if isinstance(status, SCons.Errors.BuildError):
342 status.executor = self
343 raise status
344 elif status:
345 msg = "Error %s" % status
346 raise SCons.Errors.BuildError(
347 errstr=msg,
348 node=self.batches[0].targets,
349 executor=self,
350 action=act)
351 return status
352
353
354
355
356
359
362
364 """Add source files to this Executor's list. This is necessary
365 for "multi" Builders that can be called repeatedly to build up
366 a source file list for a given target."""
367
368 assert (len(self.batches) == 1)
369
370 sources = [x for x in sources if x not in self.batches[0].sources]
371 self.batches[0].sources.extend(sources)
372
374 return self.batches[0].sources
375
377 """Add pair of associated target and source to this Executor's list.
378 This is necessary for "batch" Builders that can be called repeatedly
379 to build up a list of matching target and source files that will be
380 used in order to update multiple target files at once from multiple
381 corresponding source files, for tools like MSVC that support it."""
382 self.batches.append(Batch(targets, sources))
383
385 """
386 Preparatory checks for whether this Executor can go ahead
387 and (try to) build its targets.
388 """
389 for s in self.get_all_sources():
390 if s.missing():
391 msg = "Source `%s' not found, needed by target `%s'."
392 raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0]))
393
395 self.pre_actions.append(action)
396
397 - def add_post_action(self, action):
398 self.post_actions.append(action)
399
400
401
408
409
412
417
418 memoizer_counters.append(SCons.Memoize.CountValue('get_contents'))
419
420 - def get_contents(self):
421 """Fetch the signature contents. This is the main reason this
422 class exists, so we can compute this once and cache it regardless
423 of how many target or source Nodes there are.
424 """
425 try:
426 return self._memo['get_contents']
427 except KeyError:
428 pass
429 env = self.get_build_env()
430 result = "".join([action.get_contents(self.get_all_targets(),
431 self.get_all_sources(),
432 env)
433 for action in self.get_action_list()])
434 self._memo['get_contents'] = result
435 return result
436
438 """Fetch a time stamp for this Executor. We don't have one, of
439 course (only files do), but this is the interface used by the
440 timestamp module.
441 """
442 return 0
443
447
452
453 - def scan(self, scanner, node_list):
488
490 return (node,) + tuple(ignore)
491
492 memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key))
493
495 key = (node,) + tuple(ignore)
496 try:
497 memo_dict = self._memo['get_unignored_sources']
498 except KeyError:
499 memo_dict = {}
500 self._memo['get_unignored_sources'] = memo_dict
501 else:
502 try:
503 return memo_dict[key]
504 except KeyError:
505 pass
506
507 if node:
508
509
510 sourcelist = []
511 for b in self.batches:
512 if node in b.targets:
513 sourcelist = b.sources
514 break
515 else:
516 sourcelist = self.get_all_sources()
517 if ignore:
518 idict = {}
519 for i in ignore:
520 idict[i] = 1
521 sourcelist = [s for s in sourcelist if s not in idict]
522
523 memo_dict[key] = sourcelist
524
525 return sourcelist
526
538
539
540
541 _batch_executors = {}
542
545
549
550 nullenv = None
551
552
554 """Use singleton pattern for Null Environments."""
555 global nullenv
556
557 import SCons.Util
558 class NullEnvironment(SCons.Util.Null):
559 import SCons.CacheDir
560 _CacheDir_path = None
561 _CacheDir = SCons.CacheDir.CacheDir(None)
562 def get_CacheDir(self):
563 return self._CacheDir
564
565 if not nullenv:
566 nullenv = NullEnvironment()
567 return nullenv
568
570 """A null Executor, with a null build Environment, that does
571 nothing when the rest of the methods call it.
572
573 This might be able to disapper when we refactor things to
574 disassociate Builders from Nodes entirely, so we're not
575 going to worry about unit tests for this--at least for now.
576 """
595 return self.batches[0].targets
597 return self.batches[0].targets[0].sources
606 - def get_contents(self):
609 """Morph this Null executor to a real Executor object."""
610 batches = self.batches
611 self.__class__ = Executor
612 self.__init__([])
613 self.batches = batches
614
615
616
617
621 - def add_post_action(self, action):
622 self._morph()
623 self.add_post_action(action)
627
628
629
630
631
632
633
634