Taskmaster.py
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:29k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. #
  2. # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining
  5. # a copy of this software and associated documentation files (the
  6. # "Software"), to deal in the Software without restriction, including
  7. # without limitation the rights to use, copy, modify, merge, publish,
  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
  10. # the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included
  13. # in all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  16. # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  17. # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. #
  23. __doc__ = """
  24. Generic Taskmaster module for the SCons build engine.
  25. This module contains the primary interface(s) between a wrapping user
  26. interface and the SCons build engine.  There are two key classes here:
  27.     Taskmaster
  28.         This is the main engine for walking the dependency graph and
  29.         calling things to decide what does or doesn't need to be built.
  30.     Task
  31.         This is the base class for allowing a wrapping interface to
  32.         decide what does or doesn't actually need to be done.  The
  33.         intention is for a wrapping interface to subclass this as
  34.         appropriate for different types of behavior it may need.
  35.         The canonical example is the SCons native Python interface,
  36.         which has Task subclasses that handle its specific behavior,
  37.         like printing "`foo' is up to date" when a top-level target
  38.         doesn't need to be built, and handling the -c option by removing
  39.         targets as its "build" action.  There is also a separate subclass
  40.         for suppressing this output when the -q option is used.
  41.         The Taskmaster instantiates a Task object for each (set of)
  42.         target(s) that it decides need to be evaluated and/or built.
  43. """
  44. __revision__ = "src/engine/SCons/Taskmaster.py 3057 2008/06/09 22:21:00 knight"
  45. import SCons.compat
  46. from itertools import chain
  47. import operator
  48. import string
  49. import sys
  50. import traceback
  51. import SCons.Errors
  52. import SCons.Node
  53. StateString = SCons.Node.StateString
  54. NODE_NO_STATE = SCons.Node.no_state
  55. NODE_PENDING = SCons.Node.pending
  56. NODE_EXECUTING = SCons.Node.executing
  57. NODE_UP_TO_DATE = SCons.Node.up_to_date
  58. NODE_EXECUTED = SCons.Node.executed
  59. NODE_FAILED = SCons.Node.failed
  60. # A subsystem for recording stats about how different Nodes are handled by
  61. # the main Taskmaster loop.  There's no external control here (no need for
  62. # a --debug= option); enable it by changing the value of CollectStats.
  63. CollectStats = None
  64. class Stats:
  65.     """
  66.     A simple class for holding statistics about the disposition of a
  67.     Node by the Taskmaster.  If we're collecting statistics, each Node
  68.     processed by the Taskmaster gets one of these attached, in which case
  69.     the Taskmaster records its decision each time it processes the Node.
  70.     (Ideally, that's just once per Node.)
  71.     """
  72.     def __init__(self):
  73.         """
  74.         Instantiates a Taskmaster.Stats object, initializing all
  75.         appropriate counters to zero.
  76.         """
  77.         self.considered  = 0
  78.         self.already_handled  = 0
  79.         self.problem  = 0
  80.         self.child_failed  = 0
  81.         self.not_built  = 0
  82.         self.side_effects  = 0
  83.         self.build  = 0
  84. StatsNodes = []
  85. fmt = "%(considered)3d "
  86.       "%(already_handled)3d " 
  87.       "%(problem)3d " 
  88.       "%(child_failed)3d " 
  89.       "%(not_built)3d " 
  90.       "%(side_effects)3d " 
  91.       "%(build)3d "
  92. def dump_stats():
  93.     StatsNodes.sort(lambda a, b: cmp(str(a), str(b)))
  94.     for n in StatsNodes:
  95.         print (fmt % n.stats.__dict__) + str(n)
  96. class Task:
  97.     """
  98.     Default SCons build engine task.
  99.     This controls the interaction of the actual building of node
  100.     and the rest of the engine.
  101.     This is expected to handle all of the normally-customizable
  102.     aspects of controlling a build, so any given application
  103.     *should* be able to do what it wants by sub-classing this
  104.     class and overriding methods as appropriate.  If an application
  105.     needs to customze something by sub-classing Taskmaster (or
  106.     some other build engine class), we should first try to migrate
  107.     that functionality into this class.
  108.     Note that it's generally a good idea for sub-classes to call
  109.     these methods explicitly to update state, etc., rather than
  110.     roll their own interaction with Taskmaster from scratch.
  111.     """
  112.     def __init__(self, tm, targets, top, node):
  113.         self.tm = tm
  114.         self.targets = targets
  115.         self.top = top
  116.         self.node = node
  117.         self.exc_clear()
  118.     def display(self, message):
  119.         """
  120.         Hook to allow the calling interface to display a message.
  121.         This hook gets called as part of preparing a task for execution
  122.         (that is, a Node to be built).  As part of figuring out what Node
  123.         should be built next, the actually target list may be altered,
  124.         along with a message describing the alteration.  The calling
  125.         interface can subclass Task and provide a concrete implementation
  126.         of this method to see those messages.
  127.         """
  128.         pass
  129.     def prepare(self):
  130.         """
  131.         Called just before the task is executed.
  132.         This is mainly intended to give the target Nodes a chance to
  133.         unlink underlying files and make all necessary directories before
  134.         the Action is actually called to build the targets.
  135.         """
  136.         # Now that it's the appropriate time, give the TaskMaster a
  137.         # chance to raise any exceptions it encountered while preparing
  138.         # this task.
  139.         self.exception_raise()
  140.         if self.tm.message:
  141.             self.display(self.tm.message)
  142.             self.tm.message = None
  143.         # Let the targets take care of any necessary preparations.
  144.         # This includes verifying that all of the necessary sources
  145.         # and dependencies exist, removing the target file(s), etc.
  146.         #
  147.         # As of April 2008, the get_executor().prepare() method makes
  148.         # sure that all of the aggregate sources necessary to build this
  149.         # Task's target(s) exist in one up-front check.  The individual
  150.         # target t.prepare() methods check that each target's explicit
  151.         # or implicit dependencies exists, and also initialize the
  152.         # .sconsign info.
  153.         self.targets[0].get_executor().prepare()
  154.         for t in self.targets:
  155.             t.prepare()
  156.             for s in t.side_effects:
  157.                 s.prepare()
  158.     def get_target(self):
  159.         """Fetch the target being built or updated by this task.
  160.         """
  161.         return self.node
  162.     def needs_execute(self):
  163.         """
  164.         Called to determine whether the task's execute() method should
  165.         be run.
  166.         This method allows one to skip the somethat costly execution
  167.         of the execute() method in a seperate thread. For example,
  168.         that would be unnecessary for up-to-date targets.
  169.         """
  170.         return True
  171.     def execute(self):
  172.         """
  173.         Called to execute the task.
  174.         This method is called from multiple threads in a parallel build,
  175.         so only do thread safe stuff here.  Do thread unsafe stuff in
  176.         prepare(), executed() or failed().
  177.         """
  178.         try:
  179.             everything_was_cached = 1
  180.             for t in self.targets:
  181.                 if not t.retrieve_from_cache():
  182.                     everything_was_cached = 0
  183.                     break
  184.             if not everything_was_cached:
  185.                 self.targets[0].build()
  186.         except SystemExit:
  187.             exc_value = sys.exc_info()[1]
  188.             raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code)
  189.         except SCons.Errors.UserError:
  190.             raise
  191.         except SCons.Errors.BuildError:
  192.             raise
  193.         except:
  194.             raise SCons.Errors.TaskmasterException(self.targets[0],
  195.                                                    sys.exc_info())
  196.     def executed_without_callbacks(self):
  197.         """
  198.         Called when the task has been successfully executed
  199.         and the Taskmaster instance doesn't want to call
  200.         the Node's callback methods.
  201.         """
  202.         for t in self.targets:
  203.             if t.get_state() == NODE_EXECUTING:
  204.                 for side_effect in t.side_effects:
  205.                     side_effect.set_state(NODE_NO_STATE)
  206.                 t.set_state(NODE_EXECUTED)
  207.     def executed_with_callbacks(self):
  208.         """
  209.         Called when the task has been successfully executed and
  210.         the Taskmaster instance wants to call the Node's callback
  211.         methods.
  212.         This may have been a do-nothing operation (to preserve build
  213.         order), so we must check the node's state before deciding whether
  214.         it was "built", in which case we call the appropriate Node method.
  215.         In any event, we always call "visited()", which will handle any
  216.         post-visit actions that must take place regardless of whether
  217.         or not the target was an actual built target or a source Node.
  218.         """
  219.         for t in self.targets:
  220.             if t.get_state() == NODE_EXECUTING:
  221.                 for side_effect in t.side_effects:
  222.                     side_effect.set_state(NODE_NO_STATE)
  223.                 t.set_state(NODE_EXECUTED)
  224.                 t.built()
  225.             t.visited()
  226.     executed = executed_with_callbacks
  227.     def failed(self):
  228.         """
  229.         Default action when a task fails:  stop the build.
  230.         """
  231.         self.fail_stop()
  232.     def fail_stop(self):
  233.         """
  234.         Explicit stop-the-build failure.
  235.         """
  236.         
  237.         # Invoke will_not_build() to clean-up the pending children
  238.         # list.
  239.         self.tm.will_not_build(self.targets)
  240.         # Tell the taskmaster to not start any new tasks
  241.         self.tm.stop()
  242.         # We're stopping because of a build failure, but give the
  243.         # calling Task class a chance to postprocess() the top-level
  244.         # target under which the build failure occurred.
  245.         self.targets = [self.tm.current_top]
  246.         self.top = 1
  247.     def fail_continue(self):
  248.         """
  249.         Explicit continue-the-build failure.
  250.         This sets failure status on the target nodes and all of
  251.         their dependent parent nodes.
  252.         """
  253.         self.tm.will_not_build(self.targets)
  254.         
  255.     def make_ready_all(self):
  256.         """
  257.         Marks all targets in a task ready for execution.
  258.         This is used when the interface needs every target Node to be
  259.         visited--the canonical example being the "scons -c" option.
  260.         """
  261.         self.out_of_date = self.targets[:]
  262.         for t in self.targets:
  263.             t.disambiguate().set_state(NODE_EXECUTING)
  264.             for s in t.side_effects:
  265.                 s.set_state(NODE_EXECUTING)
  266.     def make_ready_current(self):
  267.         """
  268.         Marks all targets in a task ready for execution if any target
  269.         is not current.
  270.         This is the default behavior for building only what's necessary.
  271.         """
  272.         self.out_of_date = []
  273.         needs_executing = False
  274.         for t in self.targets:
  275.             try:
  276.                 t.disambiguate().make_ready()
  277.                 is_up_to_date = not t.has_builder() or 
  278.                                 (not t.always_build and t.is_up_to_date())
  279.             except EnvironmentError, e:
  280.                 raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename)
  281.             if not is_up_to_date:
  282.                 self.out_of_date.append(t)
  283.                 needs_executing = True
  284.         if needs_executing:
  285.             for t in self.targets:
  286.                 t.set_state(NODE_EXECUTING)
  287.                 for s in t.side_effects:
  288.                     s.set_state(NODE_EXECUTING)
  289.         else:                
  290.             for t in self.targets:
  291.                 # We must invoke visited() to ensure that the node
  292.                 # information has been computed before allowing the
  293.                 # parent nodes to execute. (That could occur in a
  294.                 # parallel build...)
  295.                 t.visited()
  296.                 t.set_state(NODE_UP_TO_DATE)
  297.     make_ready = make_ready_current
  298.     def postprocess(self):
  299.         """
  300.         Post-processes a task after it's been executed.
  301.         This examines all the targets just built (or not, we don't care
  302.         if the build was successful, or even if there was no build
  303.         because everything was up-to-date) to see if they have any
  304.         waiting parent Nodes, or Nodes waiting on a common side effect,
  305.         that can be put back on the candidates list.
  306.         """
  307.         # We may have built multiple targets, some of which may have
  308.         # common parents waiting for this build.  Count up how many
  309.         # targets each parent was waiting for so we can subtract the
  310.         # values later, and so we *don't* put waiting side-effect Nodes
  311.         # back on the candidates list if the Node is also a waiting
  312.         # parent.
  313.         targets = set(self.targets)
  314.         parents = {}
  315.         for t in targets:
  316.             for p in t.waiting_parents:
  317.                 parents[p] = parents.get(p, 0) + 1
  318.         for t in targets:
  319.             for s in t.side_effects:
  320.                 if s.get_state() == NODE_EXECUTING:
  321.                     s.set_state(NODE_NO_STATE)
  322.                     for p in s.waiting_parents:
  323.                         parents[p] = parents.get(p, 0) + 1
  324.                 for p in s.waiting_s_e:
  325.                     if p.ref_count == 0:
  326.                         self.tm.candidates.append(p)
  327.                         self.tm.pending_children.discard(p)
  328.         for p, subtract in parents.items():
  329.             p.ref_count = p.ref_count - subtract
  330.             if p.ref_count == 0:
  331.                 self.tm.candidates.append(p)
  332.                 self.tm.pending_children.discard(p)
  333.         for t in targets:
  334.             t.postprocess()
  335.     # Exception handling subsystem.
  336.     #
  337.     # Exceptions that occur while walking the DAG or examining Nodes
  338.     # must be raised, but must be raised at an appropriate time and in
  339.     # a controlled manner so we can, if necessary, recover gracefully,
  340.     # possibly write out signature information for Nodes we've updated,
  341.     # etc.  This is done by having the Taskmaster tell us about the
  342.     # exception, and letting
  343.     def exc_info(self):
  344.         """
  345.         Returns info about a recorded exception.
  346.         """
  347.         return self.exception
  348.     def exc_clear(self):
  349.         """
  350.         Clears any recorded exception.
  351.         This also changes the "exception_raise" attribute to point
  352.         to the appropriate do-nothing method.
  353.         """
  354.         self.exception = (None, None, None)
  355.         self.exception_raise = self._no_exception_to_raise
  356.     def exception_set(self, exception=None):
  357.         """
  358.         Records an exception to be raised at the appropriate time.
  359.         This also changes the "exception_raise" attribute to point
  360.         to the method that will, in fact
  361.         """
  362.         if not exception:
  363.             exception = sys.exc_info()
  364.         self.exception = exception
  365.         self.exception_raise = self._exception_raise
  366.     def _no_exception_to_raise(self):
  367.         pass
  368.     def _exception_raise(self):
  369.         """
  370.         Raises a pending exception that was recorded while getting a
  371.         Task ready for execution.
  372.         """
  373.         exc = self.exc_info()[:]
  374.         try:
  375.             exc_type, exc_value, exc_traceback = exc
  376.         except ValueError:
  377.             exc_type, exc_value = exc
  378.             exc_traceback = None
  379.         raise exc_type, exc_value, exc_traceback
  380. def find_cycle(stack, visited):
  381.     if stack[-1] in visited:
  382.         return None
  383.     visited.add(stack[-1])
  384.     for n in stack[-1].waiting_parents:
  385.         stack.append(n)
  386.         if stack[0] == stack[-1]:
  387.             return stack
  388.         if find_cycle(stack, visited):
  389.             return stack
  390.         stack.pop()
  391.     return None
  392. class Taskmaster:
  393.     """
  394.     The Taskmaster for walking the dependency DAG.
  395.     """
  396.     def __init__(self, targets=[], tasker=Task, order=None, trace=None):
  397.         self.original_top = targets
  398.         self.top_targets_left = targets[:]
  399.         self.top_targets_left.reverse()
  400.         self.candidates = []
  401.         self.tasker = tasker
  402.         if not order:
  403.             order = lambda l: l
  404.         self.order = order
  405.         self.message = None
  406.         self.trace = trace
  407.         self.next_candidate = self.find_next_candidate
  408.         self.pending_children = set()
  409.     def find_next_candidate(self):
  410.         """
  411.         Returns the next candidate Node for (potential) evaluation.
  412.         The candidate list (really a stack) initially consists of all of
  413.         the top-level (command line) targets provided when the Taskmaster
  414.         was initialized.  While we walk the DAG, visiting Nodes, all the
  415.         children that haven't finished processing get pushed on to the
  416.         candidate list.  Each child can then be popped and examined in
  417.         turn for whether *their* children are all up-to-date, in which
  418.         case a Task will be created for their actual evaluation and
  419.         potential building.
  420.         Here is where we also allow candidate Nodes to alter the list of
  421.         Nodes that should be examined.  This is used, for example, when
  422.         invoking SCons in a source directory.  A source directory Node can
  423.         return its corresponding build directory Node, essentially saying,
  424.         "Hey, you really need to build this thing over here instead."
  425.         """
  426.         try:
  427.             return self.candidates.pop()
  428.         except IndexError:
  429.             pass
  430.         try:
  431.             node = self.top_targets_left.pop()
  432.         except IndexError:
  433.             return None
  434.         self.current_top = node
  435.         alt, message = node.alter_targets()
  436.         if alt:
  437.             self.message = message
  438.             self.candidates.append(node)
  439.             self.candidates.extend(self.order(alt))
  440.             node = self.candidates.pop()
  441.         return node
  442.     def no_next_candidate(self):
  443.         """
  444.         Stops Taskmaster processing by not returning a next candidate.
  445.         
  446.         Note that we have to clean-up the Taskmaster candidate list
  447.         because the cycle detection depends on the fact all nodes have
  448.         been processed somehow.
  449.         """
  450.         while self.candidates:
  451.             candidates = self.candidates
  452.             self.candidates = []
  453.             self.will_not_build(candidates, lambda n: n.state < NODE_UP_TO_DATE)
  454.         return None
  455.     def _find_next_ready_node(self):
  456.         """
  457.         Finds the next node that is ready to be built.
  458.         This is *the* main guts of the DAG walk.  We loop through the
  459.         list of candidates, looking for something that has no un-built
  460.         children (i.e., that is a leaf Node or has dependencies that are
  461.         all leaf Nodes or up-to-date).  Candidate Nodes are re-scanned
  462.         (both the target Node itself and its sources, which are always
  463.         scanned in the context of a given target) to discover implicit
  464.         dependencies.  A Node that must wait for some children to be
  465.         built will be put back on the candidates list after the children
  466.         have finished building.  A Node that has been put back on the
  467.         candidates list in this way may have itself (or its sources)
  468.         re-scanned, in order to handle generated header files (e.g.) and
  469.         the implicit dependencies therein.
  470.         Note that this method does not do any signature calculation or
  471.         up-to-date check itself.  All of that is handled by the Task
  472.         class.  This is purely concerned with the dependency graph walk.
  473.         """
  474.         self.ready_exc = None
  475.         T = self.trace
  476.         if T: T.write('nTaskmaster: Looking for a node to evaluaten')
  477.         while 1:
  478.             node = self.next_candidate()
  479.             if node is None:
  480.                 if T: T.write('Taskmaster: No candidate anymore.nn')
  481.                 return None
  482.             node = node.disambiguate()
  483.             state = node.get_state()
  484.             if CollectStats:
  485.                 if not hasattr(node, 'stats'):
  486.                     node.stats = Stats()
  487.                     StatsNodes.append(node)
  488.                 S = node.stats
  489.                 S.considered = S.considered + 1
  490.             else:
  491.                 S = None
  492.             if T: T.write('Taskmaster:     Considering node <%-10s %-3s %s> and its children:n' % 
  493.                           (StateString[node.get_state()], node.ref_count, repr(str(node))))
  494.             if state == NODE_NO_STATE:
  495.                 # Mark this node as being on the execution stack:
  496.                 node.set_state(NODE_PENDING)
  497.             elif state > NODE_PENDING:
  498.                 # Skip this node if it has already been evaluated:
  499.                 if S: S.already_handled = S.already_handled + 1
  500.                 if T: T.write('Taskmaster:        already handled (executed)n')
  501.                 continue
  502.             try:
  503.                 children = node.children()
  504.             except SystemExit:
  505.                 exc_value = sys.exc_info()[1]
  506.                 e = SCons.Errors.ExplicitExit(node, exc_value.code)
  507.                 self.ready_exc = (SCons.Errors.ExplicitExit, e)
  508.                 if T: T.write('Taskmaster:        SystemExitn')
  509.                 return node
  510.             except:
  511.                 # We had a problem just trying to figure out the
  512.                 # children (like a child couldn't be linked in to a
  513.                 # VariantDir, or a Scanner threw something).  Arrange to
  514.                 # raise the exception when the Task is "executed."
  515.                 self.ready_exc = sys.exc_info()
  516.                 if S: S.problem = S.problem + 1
  517.                 if T: T.write('Taskmaster:        exception while scanning children.n')
  518.                 return node
  519.             children_not_visited = []
  520.             children_pending = set()
  521.             children_not_ready = []
  522.             children_failed = False
  523.             for child in chain(children,node.prerequisites):
  524.                 childstate = child.get_state()
  525.                 if T: T.write('Taskmaster:        <%-10s %-3s %s>n' % 
  526.                               (StateString[childstate], child.ref_count, repr(str(child))))
  527.                 if childstate == NODE_NO_STATE:
  528.                     children_not_visited.append(child)
  529.                 elif childstate == NODE_PENDING:
  530.                     children_pending.add(child)
  531.                 elif childstate == NODE_FAILED:
  532.                     children_failed = True
  533.                 if childstate <= NODE_EXECUTING:
  534.                     children_not_ready.append(child)
  535.             # These nodes have not even been visited yet.  Add
  536.             # them to the list so that on some next pass we can
  537.             # take a stab at evaluating them (or their children).
  538.             children_not_visited.reverse()
  539.             self.candidates.extend(self.order(children_not_visited))
  540.             #if T and children_not_visited:
  541.             #    T.write('Taskmaster:      adding to candidates: %sn' % map(str, children_not_visited))
  542.             #    T.write('Taskmaster:      candidates now: %sn' % map(str, self.candidates))
  543.             # Skip this node if any of its children have failed.
  544.             #
  545.             # This catches the case where we're descending a top-level
  546.             # target and one of our children failed while trying to be
  547.             # built by a *previous* descent of an earlier top-level
  548.             # target.
  549.             #
  550.             # It can also occur if a node is reused in multiple
  551.             # targets. One first descends though the one of the
  552.             # target, the next time occurs through the other target.
  553.             #
  554.             # Note that we can only have failed_children if the
  555.             # --keep-going flag was used, because without it the build
  556.             # will stop before diving in the other branch.
  557.             #
  558.             # Note that even if one of the children fails, we still
  559.             # added the other children to the list of candidate nodes
  560.             # to keep on building (--keep-going).
  561.             if children_failed:
  562.                 node.set_state(NODE_FAILED)
  563.                 if S: S.child_failed = S.child_failed + 1
  564.                 if T: T.write('Taskmaster:****** <%-10s %-3s %s>n' % 
  565.                               (StateString[node.get_state()], node.ref_count, repr(str(node))))
  566.                 continue
  567.             if children_not_ready:
  568.                 for child in children_not_ready:
  569.                     # We're waiting on one or more derived targets
  570.                     # that have not yet finished building.
  571.                     if S: S.not_built = S.not_built + 1
  572.                     # Add this node to the waiting parents lists of
  573.                     # anything we're waiting on, with a reference
  574.                     # count so we can be put back on the list for
  575.                     # re-evaluation when they've all finished.
  576.                     node.ref_count =  node.ref_count + child.add_to_waiting_parents(node)
  577.                     if T: T.write('Taskmaster:      adjusting ref count: <%-10s %-3s %s>n' %
  578.                                   (StateString[node.get_state()], node.ref_count, repr(str(node))))
  579.                 self.pending_children = self.pending_children | children_pending
  580.                 
  581.                 continue
  582.             # Skip this node if it has side-effects that are
  583.             # currently being built:
  584.             wait_side_effects = False
  585.             for se in node.side_effects:
  586.                 if se.get_state() == NODE_EXECUTING:
  587.                     se.add_to_waiting_s_e(node)
  588.                     wait_side_effects = True
  589.             if wait_side_effects:
  590.                 if S: S.side_effects = S.side_effects + 1
  591.                 continue
  592.             # The default when we've gotten through all of the checks above:
  593.             # this node is ready to be built.
  594.             if S: S.build = S.build + 1
  595.             if T: T.write('Taskmaster: Evaluating <%-10s %-3s %s>n' % 
  596.                           (StateString[node.get_state()], node.ref_count, repr(str(node))))
  597.             return node
  598.         return None
  599.     def next_task(self):
  600.         """
  601.         Returns the next task to be executed.
  602.         This simply asks for the next Node to be evaluated, and then wraps
  603.         it in the specific Task subclass with which we were initialized.
  604.         """
  605.         node = self._find_next_ready_node()
  606.         if node is None:
  607.             return None
  608.         tlist = node.get_executor().targets
  609.         task = self.tasker(self, tlist, node in self.original_top, node)
  610.         try:
  611.             task.make_ready()
  612.         except:
  613.             # We had a problem just trying to get this task ready (like
  614.             # a child couldn't be linked in to a VariantDir when deciding
  615.             # whether this node is current).  Arrange to raise the
  616.             # exception when the Task is "executed."
  617.             self.ready_exc = sys.exc_info()
  618.         if self.ready_exc:
  619.             task.exception_set(self.ready_exc)
  620.         self.ready_exc = None
  621.         return task
  622.     def will_not_build(self, nodes, mark_fail=lambda n: n.state != NODE_FAILED):
  623.         """
  624.         Perform clean-up about nodes that will never be built.
  625.         """
  626.         pending_children = self.pending_children
  627.         to_visit = set()
  628.         for node in nodes:
  629.             # Set failure state on all of the parents that were dependent
  630.             # on this failed build.
  631.             if mark_fail(node):
  632.                 node.set_state(NODE_FAILED)
  633.                 parents = node.waiting_parents
  634.                 to_visit = to_visit | parents
  635.                 pending_children = pending_children - parents
  636.         try:
  637.             while 1:
  638.                 try:
  639.                     node = to_visit.pop()
  640.                 except AttributeError:
  641.                     # Python 1.5.2
  642.                     if len(to_visit):
  643.                         node = to_visit[0]
  644.                         to_visit.remove(node)
  645.                     else:
  646.                         break
  647.                 if mark_fail(node):
  648.                     node.set_state(NODE_FAILED)
  649.                     parents = node.waiting_parents
  650.                     to_visit = to_visit | parents
  651.                     pending_children = pending_children - parents
  652.         except KeyError:
  653.             # The container to_visit has been emptied.
  654.             pass
  655.         # We have the stick back the pending_children list into the
  656.         # task master because the python 1.5.2 compatibility does not
  657.         # allow us to use in-place updates
  658.         self.pending_children = pending_children
  659.     def stop(self):
  660.         """
  661.         Stops the current build completely.
  662.         """
  663.         self.next_candidate = self.no_next_candidate
  664.     def cleanup(self):
  665.         """
  666.         Check for dependency cycles.
  667.         """
  668.         if self.pending_children:
  669.             desc = 'Found dependency cycle(s):n'
  670.             for node in self.pending_children:
  671.                 cycle = find_cycle([node], set())
  672.                 if cycle:
  673.                     desc = desc + "  " + string.join(map(str, cycle), " -> ") + "n"
  674.                 else:
  675.                     desc = desc + 
  676.                         "  Internal Error: no cycle found for node %s (%s) in state %sn" %  
  677.                         (node, repr(node), StateString[node.get_state()]) 
  678.             raise SCons.Errors.UserError, desc