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 from __future__ import print_function
30
31 __revision__ = "src/engine/SCons/Executor.py 74b2c53bc42290e911b334a6b44f187da698a668 2017/11/14 13:16:53 bdbaddog"
32
33 import collections
34
35 import SCons.Debug
36 from SCons.Debug import logInstanceCreation
37 import SCons.Errors
38 import SCons.Memoize
39 from SCons.compat import with_metaclass, NoSlotsPyPy
42 """Remembers exact association between targets
43 and sources of executor."""
44
45 __slots__ = ('targets',
46 'sources')
47
48 - def __init__(self, targets=[], sources=[]):
51
52
53
54 -class TSList(collections.UserList):
55 """A class that implements $TARGETS or $SOURCES expansions by wrapping
56 an executor Method. This class is used in the Executor.lvars()
57 to delay creation of NodeList objects until they're needed.
58
59 Note that we subclass collections.UserList purely so that the
60 is_Sequence() function will identify an object of this class as
61 a list during variable expansion. We're not really using any
62 collections.UserList methods in practice.
63 """
67 nl = self.func()
68 return getattr(nl, attr)
70 nl = self.func()
71 return nl[i]
73 nl = self.func()
74 i = max(i, 0); j = max(j, 0)
75 return nl[i:j]
77 nl = self.func()
78 return str(nl)
80 nl = self.func()
81 return repr(nl)
82
84 """A class that implements $TARGET or $SOURCE expansions by wrapping
85 an Executor method.
86 """
90 n = self.func()
91 return getattr(n, attr)
93 n = self.func()
94 if n:
95 return str(n)
96 return ''
98 n = self.func()
99 if n:
100 return repr(n)
101 return ''
102
104 """
105 A function to return the results of a Node's rfile() method,
106 if it exists, and the Node itself otherwise (if it's a Value
107 Node, e.g.).
108 """
109 try:
110 rfile = node.rfile
111 except AttributeError:
112 return node
113 else:
114 return rfile()
115
119
139
140 _do_execute_map = {0 : execute_nothing,
141 1 : execute_action_list}
150
153
154 _execute_str_map = {0 : execute_null_str,
155 1 : execute_actions_str}
156
157
158 -class Executor(object, with_metaclass(NoSlotsPyPy)):
159 """A class for controlling instances of executing an action.
160
161 This largely exists to hold a single association of an action,
162 environment, list of environment override dictionaries, targets
163 and sources for later processing as needed.
164 """
165
166 __slots__ = ('pre_actions',
167 'post_actions',
168 'env',
169 'overridelist',
170 'batches',
171 'builder_kw',
172 '_memo',
173 'lvars',
174 '_changed_sources_list',
175 '_changed_targets_list',
176 '_unchanged_sources_list',
177 '_unchanged_targets_list',
178 'action_list',
179 '_do_execute',
180 '_execute_str')
181
182 - def __init__(self, action, env=None, overridelist=[{}],
183 targets=[], sources=[], builder_kw={}):
198
214
233
240
247
250
253
256
259
266
273
275 if not self.action_list:
276 return []
277 targets_string = self.action_list[0].get_targets(self.env, self)
278 if targets_string[0] == '$':
279 targets_string = targets_string[1:]
280 return self.get_lvars()[targets_string]
281
290
295
302
309
311 """Returns all unique children (dependencies) for all batches
312 of this Executor.
313
314 The Taskmaster can recognize when it's already evaluated a
315 Node, so we don't have to make this list unique for its intended
316 canonical use case, but we expect there to be a lot of redundancy
317 (long lists of batched .cc files #including the same .h files
318 over and over), so removing the duplicates once up front should
319 save the Taskmaster a lot of work.
320 """
321 result = SCons.Util.UniqueList([])
322 for target in self.get_all_targets():
323 result.extend(target.children())
324 return result
325
335
345
346 @SCons.Memoize.CountMethodCall
348 """Fetch or create the appropriate build Environment
349 for this Executor.
350 """
351 try:
352 return self._memo['get_build_env']
353 except KeyError:
354 pass
355
356
357
358
359
360
361 overrides = {}
362 for odict in self.overridelist:
363 overrides.update(odict)
364
365 import SCons.Defaults
366 env = self.env or SCons.Defaults.DefaultEnvironment()
367 build_env = env.Override(overrides)
368
369 self._memo['get_build_env'] = build_env
370
371 return build_env
372
384
390
391
392
393
394
397
400
402 """Add source files to this Executor's list. This is necessary
403 for "multi" Builders that can be called repeatedly to build up
404 a source file list for a given target."""
405
406 assert (len(self.batches) == 1)
407
408 sources = [x for x in sources if x not in self.batches[0].sources]
409 self.batches[0].sources.extend(sources)
410
413
415 """Add pair of associated target and source to this Executor's list.
416 This is necessary for "batch" Builders that can be called repeatedly
417 to build up a list of matching target and source files that will be
418 used in order to update multiple target files at once from multiple
419 corresponding source files, for tools like MSVC that support it."""
420 self.batches.append(Batch(targets, sources))
421
423 """
424 Preparatory checks for whether this Executor can go ahead
425 and (try to) build its targets.
426 """
427 for s in self.get_all_sources():
428 if s.missing():
429 msg = "Source `%s' not found, needed by target `%s'."
430 raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0]))
431
434
435 - def add_post_action(self, action):
436 self.post_actions.append(action)
437
438
439
442
447
448 @SCons.Memoize.CountMethodCall
449 - def get_contents(self):
450 """Fetch the signature contents. This is the main reason this
451 class exists, so we can compute this once and cache it regardless
452 of how many target or source Nodes there are.
453 """
454 try:
455 return self._memo['get_contents']
456 except KeyError:
457 pass
458 env = self.get_build_env()
459
460 action_list = self.get_action_list()
461 all_targets = self.get_all_targets()
462 all_sources = self.get_all_sources()
463
464 result = bytearray("",'utf-8').join([action.get_contents(all_targets,
465 all_sources,
466 env)
467 for action in action_list])
468
469 self._memo['get_contents'] = result
470 return result
471
473 """Fetch a time stamp for this Executor. We don't have one, of
474 course (only files do), but this is the interface used by the
475 timestamp module.
476 """
477 return 0
478
482
487
488 - def scan(self, scanner, node_list):
509
511 return (node,) + tuple(ignore)
512
513 @SCons.Memoize.CountDictCall(_get_unignored_sources_key)
515 key = (node,) + tuple(ignore)
516 try:
517 memo_dict = self._memo['get_unignored_sources']
518 except KeyError:
519 memo_dict = {}
520 self._memo['get_unignored_sources'] = memo_dict
521 else:
522 try:
523 return memo_dict[key]
524 except KeyError:
525 pass
526
527 if node:
528
529
530 sourcelist = []
531 for b in self.batches:
532 if node in b.targets:
533 sourcelist = b.sources
534 break
535 else:
536 sourcelist = self.get_all_sources()
537 if ignore:
538 idict = {}
539 for i in ignore:
540 idict[i] = 1
541 sourcelist = [s for s in sourcelist if s not in idict]
542
543 memo_dict[key] = sourcelist
544
545 return sourcelist
546
558
559
560
561 _batch_executors = {}
565
569
570 nullenv = None
571
572
573 import SCons.Util
580
589
590 -class Null(object, with_metaclass(NoSlotsPyPy)):
591 """A null Executor, with a null build Environment, that does
592 nothing when the rest of the methods call it.
593
594 This might be able to disappear when we refactor things to
595 disassociate Builders from Nodes entirely, so we're not
596 going to worry about unit tests for this--at least for now.
597 """
598
599 __slots__ = ('pre_actions',
600 'post_actions',
601 'env',
602 'overridelist',
603 'batches',
604 'builder_kw',
605 '_memo',
606 'lvars',
607 '_changed_sources_list',
608 '_changed_targets_list',
609 '_unchanged_sources_list',
610 '_unchanged_targets_list',
611 'action_list',
612 '_do_execute',
613 '_execute_str')
614
644 - def get_contents(self):
652
653
654
655
659 - def add_post_action(self, action):
660 self._morph()
661 self.add_post_action(action)
665
666
667
668
669
670
671