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 2014/09/27 12:51:43 garyo"
31
32 import collections
33
34 import SCons.Debug
35 from SCons.Debug import logInstanceCreation
36 import SCons.Errors
37 import SCons.Memoize
38
39
41 """Remembers exact association between targets
42 and sources of executor."""
43 - def __init__(self, targets=[], sources=[]):
44 self.targets = targets
45 self.sources = sources
46
47
48
49 -class TSList(collections.UserList):
50 """A class that implements $TARGETS or $SOURCES expansions by wrapping
51 an executor Method. This class is used in the Executor.lvars()
52 to delay creation of NodeList objects until they're needed.
53
54 Note that we subclass collections.UserList purely so that the
55 is_Sequence() function will identify an object of this class as
56 a list during variable expansion. We're not really using any
57 collections.UserList methods in practice.
58 """
62 nl = self.func()
63 return getattr(nl, attr)
65 nl = self.func()
66 return nl[i]
68 nl = self.func()
69 i = max(i, 0); j = max(j, 0)
70 return nl[i:j]
72 nl = self.func()
73 return str(nl)
75 nl = self.func()
76 return repr(nl)
77
79 """A class that implements $TARGET or $SOURCE expansions by wrapping
80 an Executor method.
81 """
85 n = self.func()
86 return getattr(n, attr)
88 n = self.func()
89 if n:
90 return str(n)
91 return ''
93 n = self.func()
94 if n:
95 return repr(n)
96 return ''
97
99 """
100 A function to return the results of a Node's rfile() method,
101 if it exists, and the Node itself otherwise (if it's a Value
102 Node, e.g.).
103 """
104 try:
105 rfile = node.rfile
106 except AttributeError:
107 return node
108 else:
109 return rfile()
110
111
113 """A class for controlling instances of executing an action.
114
115 This largely exists to hold a single association of an action,
116 environment, list of environment override dictionaries, targets
117 and sources for later processing as needed.
118 """
119
120 if SCons.Memoize.use_memoizer:
121 __metaclass__ = SCons.Memoize.Memoized_Metaclass
122
123 memoizer_counters = []
124
125 - def __init__(self, action, env=None, overridelist=[{}],
126 targets=[], sources=[], builder_kw={}):
127 if SCons.Debug.track_instances: logInstanceCreation(self, 'Executor.Executor')
128 self.set_action_list(action)
129 self.pre_actions = []
130 self.post_actions = []
131 self.env = env
132 self.overridelist = overridelist
133 if targets or sources:
134 self.batches = [Batch(targets[:], sources[:])]
135 else:
136 self.batches = []
137 self.builder_kw = builder_kw
138 self._memo = {}
139
155
172
174 try:
175 return self._changed_sources_list
176 except AttributeError:
177 self._get_changes()
178 return self._changed_sources_list
179
181 try:
182 return self._changed_targets_list
183 except AttributeError:
184 self._get_changes()
185 return self._changed_targets_list
186
190
193
197
200
202 try:
203 return self._unchanged_sources_list
204 except AttributeError:
205 self._get_changes()
206 return self._unchanged_sources_list
207
209 try:
210 return self._unchanged_targets_list
211 except AttributeError:
212 self._get_changes()
213 return self._unchanged_targets_list
214
216 if not self.action_list:
217 return []
218 targets_string = self.action_list[0].get_targets(self.env, self)
219 if targets_string[0] == '$':
220 targets_string = targets_string[1:]
221 return self.get_lvars()[targets_string]
222
231
233 if self.action_list is None:
234 return []
235 return self.pre_actions + self.action_list + self.post_actions
236
238 """Returns all targets for all batches of this Executor."""
239 result = []
240 for batch in self.batches:
241 result.extend(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 result.extend(batch.sources)
249 return result
250
252 """Returns all unique children (dependencies) for all batches
253 of this Executor.
254
255 The Taskmaster can recognize when it's already evaluated a
256 Node, so we don't have to make this list unique for its intended
257 canonical use case, but we expect there to be a lot of redundancy
258 (long lists of batched .cc files #including the same .h files
259 over and over), so removing the duplicates once up front should
260 save the Taskmaster a lot of work.
261 """
262 result = SCons.Util.UniqueList([])
263 for target in self.get_all_targets():
264 result.extend(target.children())
265 return result
266
276
286
287 memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
288
290 """Fetch or create the appropriate build Environment
291 for this Executor.
292 """
293 try:
294 return self._memo['get_build_env']
295 except KeyError:
296 pass
297
298
299
300
301
302
303 overrides = {}
304 for odict in self.overridelist:
305 overrides.update(odict)
306
307 import SCons.Defaults
308 env = self.env or SCons.Defaults.DefaultEnvironment()
309 build_env = env.Override(overrides)
310
311 self._memo['get_build_env'] = build_env
312
313 return build_env
314
316 """Fetch the scanner path for this executor's targets and sources.
317 """
318 env = self.get_build_env()
319 try:
320 cwd = self.batches[0].targets[0].cwd
321 except (IndexError, AttributeError):
322 cwd = None
323 return scanner.path(env, cwd,
324 self.get_all_targets(),
325 self.get_all_sources())
326
332
335
337 """Actually execute the action list."""
338 env = self.get_build_env()
339 kw = self.get_kw(kw)
340 status = 0
341 for act in self.get_action_list():
342
343 args = ([], [], env)
344 status = act(*args, **kw)
345 if isinstance(status, SCons.Errors.BuildError):
346 status.executor = self
347 raise status
348 elif status:
349 msg = "Error %s" % status
350 raise SCons.Errors.BuildError(
351 errstr=msg,
352 node=self.batches[0].targets,
353 executor=self,
354 action=act)
355 return status
356
357
358
359
360
363
366
368 """Add source files to this Executor's list. This is necessary
369 for "multi" Builders that can be called repeatedly to build up
370 a source file list for a given target."""
371
372 assert (len(self.batches) == 1)
373
374 sources = [x for x in sources if x not in self.batches[0].sources]
375 self.batches[0].sources.extend(sources)
376
378 return self.batches[0].sources
379
381 """Add pair of associated target and source to this Executor's list.
382 This is necessary for "batch" Builders that can be called repeatedly
383 to build up a list of matching target and source files that will be
384 used in order to update multiple target files at once from multiple
385 corresponding source files, for tools like MSVC that support it."""
386 self.batches.append(Batch(targets, sources))
387
389 """
390 Preparatory checks for whether this Executor can go ahead
391 and (try to) build its targets.
392 """
393 for s in self.get_all_sources():
394 if s.missing():
395 msg = "Source `%s' not found, needed by target `%s'."
396 raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0]))
397
399 self.pre_actions.append(action)
400
401 - def add_post_action(self, action):
402 self.post_actions.append(action)
403
404
405
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 result = "".join([action.get_contents(self.get_all_targets(),
435 self.get_all_sources(),
436 env)
437 for action in self.get_action_list()])
438 self._memo['get_contents'] = result
439 return result
440
442 """Fetch a time stamp for this Executor. We don't have one, of
443 course (only files do), but this is the interface used by the
444 timestamp module.
445 """
446 return 0
447
451
456
457 - def scan(self, scanner, node_list):
492
494 return (node,) + tuple(ignore)
495
496 memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key))
497
499 key = (node,) + tuple(ignore)
500 try:
501 memo_dict = self._memo['get_unignored_sources']
502 except KeyError:
503 memo_dict = {}
504 self._memo['get_unignored_sources'] = memo_dict
505 else:
506 try:
507 return memo_dict[key]
508 except KeyError:
509 pass
510
511 if node:
512
513
514 sourcelist = []
515 for b in self.batches:
516 if node in b.targets:
517 sourcelist = b.sources
518 break
519 else:
520 sourcelist = self.get_all_sources()
521 if ignore:
522 idict = {}
523 for i in ignore:
524 idict[i] = 1
525 sourcelist = [s for s in sourcelist if s not in idict]
526
527 memo_dict[key] = sourcelist
528
529 return sourcelist
530
542
543
544
545 _batch_executors = {}
546
549
553
554 nullenv = None
555
556
558 """Use singleton pattern for Null Environments."""
559 global nullenv
560
561 import SCons.Util
562 class NullEnvironment(SCons.Util.Null):
563 import SCons.CacheDir
564 _CacheDir_path = None
565 _CacheDir = SCons.CacheDir.CacheDir(None)
566 def get_CacheDir(self):
567 return self._CacheDir
568
569 if not nullenv:
570 nullenv = NullEnvironment()
571 return nullenv
572
574 """A null Executor, with a null build Environment, that does
575 nothing when the rest of the methods call it.
576
577 This might be able to disappear when we refactor things to
578 disassociate Builders from Nodes entirely, so we're not
579 going to worry about unit tests for this--at least for now.
580 """
599 return self.batches[0].targets
601 return self.batches[0].targets[0].sources
603 return self.batches[0].targets[0].children()
610 - def get_contents(self):
613 """Morph this Null executor to a real Executor object."""
614 batches = self.batches
615 self.__class__ = Executor
616 self.__init__([])
617 self.batches = batches
618
619
620
621
625 - def add_post_action(self, action):
626 self._morph()
627 self.add_post_action(action)
631
632
633
634
635
636
637