OLD | NEW |
1 # | 1 # |
2 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundat
ion | 2 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons F
oundation |
3 # | 3 # |
4 # Permission is hereby granted, free of charge, to any person obtaining | 4 # Permission is hereby granted, free of charge, to any person obtaining |
5 # a copy of this software and associated documentation files (the | 5 # a copy of this software and associated documentation files (the |
6 # "Software"), to deal in the Software without restriction, including | 6 # "Software"), to deal in the Software without restriction, including |
7 # without limitation the rights to use, copy, modify, merge, publish, | 7 # without limitation the rights to use, copy, modify, merge, publish, |
8 # distribute, sublicense, and/or sell copies of the Software, and to | 8 # distribute, sublicense, and/or sell copies of the Software, and to |
9 # permit persons to whom the Software is furnished to do so, subject to | 9 # permit persons to whom the Software is furnished to do so, subject to |
10 # the following conditions: | 10 # the following conditions: |
11 # | 11 # |
12 # The above copyright notice and this permission notice shall be included | 12 # The above copyright notice and this permission notice shall be included |
(...skipping 28 matching lines...) Expand all Loading... |
41 which has Task subclasses that handle its specific behavior, | 41 which has Task subclasses that handle its specific behavior, |
42 like printing "`foo' is up to date" when a top-level target | 42 like printing "`foo' is up to date" when a top-level target |
43 doesn't need to be built, and handling the -c option by removing | 43 doesn't need to be built, and handling the -c option by removing |
44 targets as its "build" action. There is also a separate subclass | 44 targets as its "build" action. There is also a separate subclass |
45 for suppressing this output when the -q option is used. | 45 for suppressing this output when the -q option is used. |
46 | 46 |
47 The Taskmaster instantiates a Task object for each (set of) | 47 The Taskmaster instantiates a Task object for each (set of) |
48 target(s) that it decides need to be evaluated and/or built. | 48 target(s) that it decides need to be evaluated and/or built. |
49 """ | 49 """ |
50 | 50 |
51 __revision__ = "src/engine/SCons/Taskmaster.py 3842 2008/12/20 22:59:52 scons" | 51 __revision__ = "src/engine/SCons/Taskmaster.py 3897 2009/01/13 06:45:54 scons" |
52 | 52 |
53 from itertools import chain | 53 from itertools import chain |
54 import operator | 54 import operator |
55 import string | 55 import string |
56 import sys | 56 import sys |
57 import traceback | 57 import traceback |
58 | 58 |
59 import SCons.Errors | 59 import SCons.Errors |
60 import SCons.Node | 60 import SCons.Node |
| 61 import SCons.Warnings |
61 | 62 |
62 StateString = SCons.Node.StateString | 63 StateString = SCons.Node.StateString |
63 NODE_NO_STATE = SCons.Node.no_state | 64 NODE_NO_STATE = SCons.Node.no_state |
64 NODE_PENDING = SCons.Node.pending | 65 NODE_PENDING = SCons.Node.pending |
65 NODE_EXECUTING = SCons.Node.executing | 66 NODE_EXECUTING = SCons.Node.executing |
66 NODE_UP_TO_DATE = SCons.Node.up_to_date | 67 NODE_UP_TO_DATE = SCons.Node.up_to_date |
67 NODE_EXECUTED = SCons.Node.executed | 68 NODE_EXECUTED = SCons.Node.executed |
68 NODE_FAILED = SCons.Node.failed | 69 NODE_FAILED = SCons.Node.failed |
69 | 70 |
70 | 71 |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 # Let the targets take care of any necessary preparations. | 179 # Let the targets take care of any necessary preparations. |
179 # This includes verifying that all of the necessary sources | 180 # This includes verifying that all of the necessary sources |
180 # and dependencies exist, removing the target file(s), etc. | 181 # and dependencies exist, removing the target file(s), etc. |
181 # | 182 # |
182 # As of April 2008, the get_executor().prepare() method makes | 183 # As of April 2008, the get_executor().prepare() method makes |
183 # sure that all of the aggregate sources necessary to build this | 184 # sure that all of the aggregate sources necessary to build this |
184 # Task's target(s) exist in one up-front check. The individual | 185 # Task's target(s) exist in one up-front check. The individual |
185 # target t.prepare() methods check that each target's explicit | 186 # target t.prepare() methods check that each target's explicit |
186 # or implicit dependencies exists, and also initialize the | 187 # or implicit dependencies exists, and also initialize the |
187 # .sconsign info. | 188 # .sconsign info. |
188 self.targets[0].get_executor().prepare() | 189 executor = self.targets[0].get_executor() |
189 for t in self.targets: | 190 executor.prepare() |
| 191 for t in executor.get_action_targets(): |
190 t.prepare() | 192 t.prepare() |
191 for s in t.side_effects: | 193 for s in t.side_effects: |
192 s.prepare() | 194 s.prepare() |
193 | 195 |
194 def get_target(self): | 196 def get_target(self): |
195 """Fetch the target being built or updated by this task. | 197 """Fetch the target being built or updated by this task. |
196 """ | 198 """ |
197 return self.node | 199 return self.node |
198 | 200 |
199 def needs_execute(self): | 201 def needs_execute(self): |
200 """ | 202 # TODO(deprecate): "return True" is the old default behavior; |
201 Called to determine whether the task's execute() method should | 203 # change it to NotImplementedError (after running through the |
202 be run. | 204 # Deprecation Cycle) so the desired behavior is explicitly |
203 | 205 # determined by which concrete subclass is used. |
204 This method allows one to skip the somethat costly execution | 206 #raise NotImplementedError |
205 of the execute() method in a seperate thread. For example, | 207 msg = ('Direct use of the Taskmaster.Task class will be deprecated\n' |
206 that would be unnecessary for up-to-date targets. | 208 + '\tin a future release.') |
207 """ | 209 SCons.Warnings.warn(SCons.Warnings.TaskmasterNeedsExecuteWarning, msg) |
208 return True | 210 return True |
209 | 211 |
210 def execute(self): | 212 def execute(self): |
211 """ | 213 """ |
212 Called to execute the task. | 214 Called to execute the task. |
213 | 215 |
214 This method is called from multiple threads in a parallel build, | 216 This method is called from multiple threads in a parallel build, |
215 so only do thread safe stuff here. Do thread unsafe stuff in | 217 so only do thread safe stuff here. Do thread unsafe stuff in |
216 prepare(), executed() or failed(). | 218 prepare(), executed() or failed(). |
217 """ | 219 """ |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
494 Task ready for execution. | 496 Task ready for execution. |
495 """ | 497 """ |
496 exc = self.exc_info()[:] | 498 exc = self.exc_info()[:] |
497 try: | 499 try: |
498 exc_type, exc_value, exc_traceback = exc | 500 exc_type, exc_value, exc_traceback = exc |
499 except ValueError: | 501 except ValueError: |
500 exc_type, exc_value = exc | 502 exc_type, exc_value = exc |
501 exc_traceback = None | 503 exc_traceback = None |
502 raise exc_type, exc_value, exc_traceback | 504 raise exc_type, exc_value, exc_traceback |
503 | 505 |
| 506 class AlwaysTask(Task): |
| 507 def needs_execute(self): |
| 508 """ |
| 509 Always returns True (indicating this Task should always |
| 510 be executed). |
| 511 |
| 512 Subclasses that need this behavior (as opposed to the default |
| 513 of only executing Nodes that are out of date w.r.t. their |
| 514 dependencies) can use this as follows: |
| 515 |
| 516 class MyTaskSubclass(SCons.Taskmaster.Task): |
| 517 needs_execute = SCons.Taskmaster.Task.execute_always |
| 518 """ |
| 519 return True |
| 520 |
| 521 class OutOfDateTask(Task): |
| 522 def needs_execute(self): |
| 523 """ |
| 524 Returns True (indicating this Task should be executed) if this |
| 525 Task's target state indicates it needs executing, which has |
| 526 already been determined by an earlier up-to-date check. |
| 527 """ |
| 528 return self.targets[0].get_state() == SCons.Node.executing |
| 529 |
504 | 530 |
505 def find_cycle(stack, visited): | 531 def find_cycle(stack, visited): |
506 if stack[-1] in visited: | 532 if stack[-1] in visited: |
507 return None | 533 return None |
508 visited.add(stack[-1]) | 534 visited.add(stack[-1]) |
509 for n in stack[-1].waiting_parents: | 535 for n in stack[-1].waiting_parents: |
510 stack.append(n) | 536 stack.append(n) |
511 if stack[0] == stack[-1]: | 537 if stack[0] == stack[-1]: |
512 return stack | 538 return stack |
513 if find_cycle(stack, visited): | 539 if find_cycle(stack, visited): |
514 return stack | 540 return stack |
515 stack.pop() | 541 stack.pop() |
516 return None | 542 return None |
517 | 543 |
518 | 544 |
519 class Taskmaster: | 545 class Taskmaster: |
520 """ | 546 """ |
521 The Taskmaster for walking the dependency DAG. | 547 The Taskmaster for walking the dependency DAG. |
522 """ | 548 """ |
523 | 549 |
524 def __init__(self, targets=[], tasker=Task, order=None, trace=None): | 550 def __init__(self, targets=[], tasker=None, order=None, trace=None): |
525 self.original_top = targets | 551 self.original_top = targets |
526 self.top_targets_left = targets[:] | 552 self.top_targets_left = targets[:] |
527 self.top_targets_left.reverse() | 553 self.top_targets_left.reverse() |
528 self.candidates = [] | 554 self.candidates = [] |
| 555 if tasker is None: |
| 556 tasker = OutOfDateTask |
529 self.tasker = tasker | 557 self.tasker = tasker |
530 if not order: | 558 if not order: |
531 order = lambda l: l | 559 order = lambda l: l |
532 self.order = order | 560 self.order = order |
533 self.message = None | 561 self.message = None |
534 self.trace = trace | 562 self.trace = trace |
535 self.next_candidate = self.find_next_candidate | 563 self.next_candidate = self.find_next_candidate |
536 self.pending_children = set() | 564 self.pending_children = set() |
537 | 565 |
538 def find_next_candidate(self): | 566 def find_next_candidate(self): |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
729 | 757 |
730 if state == NODE_NO_STATE: | 758 if state == NODE_NO_STATE: |
731 # Mark this node as being on the execution stack: | 759 # Mark this node as being on the execution stack: |
732 node.set_state(NODE_PENDING) | 760 node.set_state(NODE_PENDING) |
733 elif state > NODE_PENDING: | 761 elif state > NODE_PENDING: |
734 # Skip this node if it has already been evaluated: | 762 # Skip this node if it has already been evaluated: |
735 if S: S.already_handled = S.already_handled + 1 | 763 if S: S.already_handled = S.already_handled + 1 |
736 if T: T.write(self.trace_message(' already handled (execut
ed)')) | 764 if T: T.write(self.trace_message(' already handled (execut
ed)')) |
737 continue | 765 continue |
738 | 766 |
| 767 executor = node.get_executor() |
| 768 |
739 try: | 769 try: |
740 children = node.children() | 770 children = executor.get_all_children() |
741 except SystemExit: | 771 except SystemExit: |
742 exc_value = sys.exc_info()[1] | 772 exc_value = sys.exc_info()[1] |
743 e = SCons.Errors.ExplicitExit(node, exc_value.code) | 773 e = SCons.Errors.ExplicitExit(node, exc_value.code) |
744 self.ready_exc = (SCons.Errors.ExplicitExit, e) | 774 self.ready_exc = (SCons.Errors.ExplicitExit, e) |
745 if T: T.write(self.trace_message(' SystemExit')) | 775 if T: T.write(self.trace_message(' SystemExit')) |
746 return node | 776 return node |
747 except Exception, e: | 777 except Exception, e: |
748 # We had a problem just trying to figure out the | 778 # We had a problem just trying to figure out the |
749 # children (like a child couldn't be linked in to a | 779 # children (like a child couldn't be linked in to a |
750 # VariantDir, or a Scanner threw something). Arrange to | 780 # VariantDir, or a Scanner threw something). Arrange to |
751 # raise the exception when the Task is "executed." | 781 # raise the exception when the Task is "executed." |
752 self.ready_exc = sys.exc_info() | 782 self.ready_exc = sys.exc_info() |
753 if S: S.problem = S.problem + 1 | 783 if S: S.problem = S.problem + 1 |
754 if T: T.write(self.trace_message(' exception %s while scan
ning children.\n' % e)) | 784 if T: T.write(self.trace_message(' exception %s while scan
ning children.\n' % e)) |
755 return node | 785 return node |
756 | 786 |
757 children_not_visited = [] | 787 children_not_visited = [] |
758 children_pending = set() | 788 children_pending = set() |
759 children_not_ready = [] | 789 children_not_ready = [] |
760 children_failed = False | 790 children_failed = False |
761 | 791 |
762 for child in chain(children,node.prerequisites): | 792 for child in chain(children, executor.get_all_prerequisites()): |
763 childstate = child.get_state() | 793 childstate = child.get_state() |
764 | 794 |
765 if T: T.write(self.trace_message(' ' + self.trace_node(chi
ld))) | 795 if T: T.write(self.trace_message(' ' + self.trace_node(chi
ld))) |
766 | 796 |
767 if childstate == NODE_NO_STATE: | 797 if childstate == NODE_NO_STATE: |
768 children_not_visited.append(child) | 798 children_not_visited.append(child) |
769 elif childstate == NODE_PENDING: | 799 elif childstate == NODE_PENDING: |
770 children_pending.add(child) | 800 children_pending.add(child) |
771 elif childstate == NODE_FAILED: | 801 elif childstate == NODE_FAILED: |
772 children_failed = True | 802 children_failed = True |
(...skipping 23 matching lines...) Expand all Loading... |
796 # target, the next time occurs through the other target. | 826 # target, the next time occurs through the other target. |
797 # | 827 # |
798 # Note that we can only have failed_children if the | 828 # Note that we can only have failed_children if the |
799 # --keep-going flag was used, because without it the build | 829 # --keep-going flag was used, because without it the build |
800 # will stop before diving in the other branch. | 830 # will stop before diving in the other branch. |
801 # | 831 # |
802 # Note that even if one of the children fails, we still | 832 # Note that even if one of the children fails, we still |
803 # added the other children to the list of candidate nodes | 833 # added the other children to the list of candidate nodes |
804 # to keep on building (--keep-going). | 834 # to keep on building (--keep-going). |
805 if children_failed: | 835 if children_failed: |
806 node.set_state(NODE_FAILED) | 836 for n in executor.get_action_targets(): |
| 837 n.set_state(NODE_FAILED) |
807 | 838 |
808 if S: S.child_failed = S.child_failed + 1 | 839 if S: S.child_failed = S.child_failed + 1 |
809 if T: T.write(self.trace_message('****** %s\n' % self.trace_node
(node))) | 840 if T: T.write(self.trace_message('****** %s\n' % self.trace_node
(node))) |
810 continue | 841 continue |
811 | 842 |
812 if children_not_ready: | 843 if children_not_ready: |
813 for child in children_not_ready: | 844 for child in children_not_ready: |
814 # We're waiting on one or more derived targets | 845 # We're waiting on one or more derived targets |
815 # that have not yet finished building. | 846 # that have not yet finished building. |
816 if S: S.not_built = S.not_built + 1 | 847 if S: S.not_built = S.not_built + 1 |
(...skipping 10 matching lines...) Expand all Loading... |
827 for pc in children_pending: | 858 for pc in children_pending: |
828 T.write(self.trace_message(' adding %s to the pend
ing children set\n' % | 859 T.write(self.trace_message(' adding %s to the pend
ing children set\n' % |
829 self.trace_node(pc))) | 860 self.trace_node(pc))) |
830 self.pending_children = self.pending_children | children_pending | 861 self.pending_children = self.pending_children | children_pending |
831 | 862 |
832 continue | 863 continue |
833 | 864 |
834 # Skip this node if it has side-effects that are | 865 # Skip this node if it has side-effects that are |
835 # currently being built: | 866 # currently being built: |
836 wait_side_effects = False | 867 wait_side_effects = False |
837 for se in node.side_effects: | 868 for se in executor.get_action_side_effects(): |
838 if se.get_state() == NODE_EXECUTING: | 869 if se.get_state() == NODE_EXECUTING: |
839 se.add_to_waiting_s_e(node) | 870 se.add_to_waiting_s_e(node) |
840 wait_side_effects = True | 871 wait_side_effects = True |
841 | 872 |
842 if wait_side_effects: | 873 if wait_side_effects: |
843 if S: S.side_effects = S.side_effects + 1 | 874 if S: S.side_effects = S.side_effects + 1 |
844 continue | 875 continue |
845 | 876 |
846 # The default when we've gotten through all of the checks above: | 877 # The default when we've gotten through all of the checks above: |
847 # this node is ready to be built. | 878 # this node is ready to be built. |
(...skipping 18 matching lines...) Expand all Loading... |
866 Returns the next task to be executed. | 897 Returns the next task to be executed. |
867 | 898 |
868 This simply asks for the next Node to be evaluated, and then wraps | 899 This simply asks for the next Node to be evaluated, and then wraps |
869 it in the specific Task subclass with which we were initialized. | 900 it in the specific Task subclass with which we were initialized. |
870 """ | 901 """ |
871 node = self._find_next_ready_node() | 902 node = self._find_next_ready_node() |
872 | 903 |
873 if node is None: | 904 if node is None: |
874 return None | 905 return None |
875 | 906 |
876 tlist = node.get_executor().targets | 907 tlist = node.get_executor().get_all_targets() |
877 | 908 |
878 task = self.tasker(self, tlist, node in self.original_top, node) | 909 task = self.tasker(self, tlist, node in self.original_top, node) |
879 try: | 910 try: |
880 task.make_ready() | 911 task.make_ready() |
881 except: | 912 except: |
882 # We had a problem just trying to get this task ready (like | 913 # We had a problem just trying to get this task ready (like |
883 # a child couldn't be linked in to a VariantDir when deciding | 914 # a child couldn't be linked in to a VariantDir when deciding |
884 # whether this node is current). Arrange to raise the | 915 # whether this node is current). Arrange to raise the |
885 # exception when the Task is "executed." | 916 # exception when the Task is "executed." |
886 self.ready_exc = sys.exc_info() | 917 self.ready_exc = sys.exc_info() |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
976 desc = 'Found dependency cycle(s):\n' | 1007 desc = 'Found dependency cycle(s):\n' |
977 for node, cycle in nclist: | 1008 for node, cycle in nclist: |
978 if cycle: | 1009 if cycle: |
979 desc = desc + " " + string.join(map(str, cycle), " -> ") + "\n" | 1010 desc = desc + " " + string.join(map(str, cycle), " -> ") + "\n" |
980 else: | 1011 else: |
981 desc = desc + \ | 1012 desc = desc + \ |
982 " Internal Error: no cycle found for node %s (%s) in state
%s\n" % \ | 1013 " Internal Error: no cycle found for node %s (%s) in state
%s\n" % \ |
983 (node, repr(node), StateString[node.get_state()]) | 1014 (node, repr(node), StateString[node.get_state()]) |
984 | 1015 |
985 raise SCons.Errors.UserError, desc | 1016 raise SCons.Errors.UserError, desc |
OLD | NEW |