def _find_next_ready_node(self):
        """
        Finds the next node that is ready to be built.

        This is *the* main guts of the DAG walk.  We loop through the
        list of candidates, looking for something that has no un-built
        children (i.e., that is a leaf Node or has dependencies that are
        all leaf Nodes or up-to-date).  Candidate Nodes are re-scanned
        (both the target Node itself and its sources, which are always
        scanned in the context of a given target) to discover implicit
        dependencies.  A Node that must wait for some children to be
        built will be put back on the candidates list after the children
        have finished building.  A Node that has been put back on the
        candidates list in this way may have itself (or its sources)
        re-scanned, in order to handle generated header files (e.g.) and
        the implicit dependencies therein.

        Note that this method does not do any signature calculation or
        up-to-date check itself.  All of that is handled by the Task
        class.  This is purely concerned with the dependency graph walk.
        """

        self.ready_exc = None

        T = self.trace
        if T: T.write(u'\n' + self.trace_message('Looking for a node to evaluate'))

        while True:
            node = self.next_candidate()
            if node is None:
                if T: T.write(self.trace_message('No candidate anymore.') + u'\n')
                return None

            node = node.disambiguate()
            state = node.get_state()

            # For debugging only:
            #
            # try:
            #     self._validate_pending_children()
            # except:
            #     self.ready_exc = sys.exc_info()
            #     return node

            if CollectStats:
                if not hasattr(node, 'stats'):
                    node.stats = Stats()
                    StatsNodes.append(node)
                S = node.stats
                S.considered = S.considered + 1
            else:
                S = None

            if T: T.write(self.trace_message(u'    Considering node %s and its children:' % self.trace_node(node)))

            if state == NODE_NO_STATE:
                # Mark this node as being on the execution stack:
                node.set_state(NODE_PENDING)
            elif state > NODE_PENDING:
                # Skip this node if it has already been evaluated:
                if S: S.already_handled = S.already_handled + 1
                if T: T.write(self.trace_message(u'       already handled (executed)'))
                continue

            executor = node.get_executor()

            try:
                children = executor.get_all_children()
            except __HOLE__:
                exc_value = sys.exc_info()[1]
                e = SCons.Errors.ExplicitExit(node, exc_value.code)
                self.ready_exc = (SCons.Errors.ExplicitExit, e)
                if T: T.write(self.trace_message('       SystemExit'))
                return node
            except Exception, e:
                # We had a problem just trying to figure out the
                # children (like a child couldn't be linked in to a
                # VariantDir, or a Scanner threw something).  Arrange to
                # raise the exception when the Task is "executed."
                self.ready_exc = sys.exc_info()
                if S: S.problem = S.problem + 1
                if T: T.write(self.trace_message('       exception %s while scanning children.\n' % e))
                return node

            children_not_visited = []
            children_pending = set()
            children_not_ready = []
            children_failed = False

            for child in chain(executor.get_all_prerequisites(), children):
                childstate = child.get_state()

                if T: T.write(self.trace_message(u'       ' + self.trace_node(child)))

                if childstate == NODE_NO_STATE:
                    children_not_visited.append(child)
                elif childstate == NODE_PENDING:
                    children_pending.add(child)
                elif childstate == NODE_FAILED:
                    children_failed = True

                if childstate <= NODE_EXECUTING:
                    children_not_ready.append(child)


            # These nodes have not even been visited yet.  Add
            # them to the list so that on some next pass we can
            # take a stab at evaluating them (or their children).
            children_not_visited.reverse()
            self.candidates.extend(self.order(children_not_visited))
            #if T and children_not_visited:
            #    T.write(self.trace_message('     adding to candidates: %s' % map(str, children_not_visited)))
            #    T.write(self.trace_message('     candidates now: %s\n' % map(str, self.candidates)))

            # Skip this node if any of its children have failed.
            #
            # This catches the case where we're descending a top-level
            # target and one of our children failed while trying to be
            # built by a *previous* descent of an earlier top-level
            # target.
            #
            # It can also occur if a node is reused in multiple
            # targets. One first descends though the one of the
            # target, the next time occurs through the other target.
            #
            # Note that we can only have failed_children if the
            # --keep-going flag was used, because without it the build
            # will stop before diving in the other branch.
            #
            # Note that even if one of the children fails, we still
            # added the other children to the list of candidate nodes
            # to keep on building (--keep-going).
            if children_failed:
                for n in executor.get_action_targets():
                    n.set_state(NODE_FAILED)

                if S: S.child_failed = S.child_failed + 1
                if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node)))
                continue

            if children_not_ready:
                for child in children_not_ready:
                    # We're waiting on one or more derived targets
                    # that have not yet finished building.
                    if S: S.not_built = S.not_built + 1

                    # Add this node to the waiting parents lists of
                    # anything we're waiting on, with a reference
                    # count so we can be put back on the list for
                    # re-evaluation when they've all finished.
                    node.ref_count =  node.ref_count + child.add_to_waiting_parents(node)
                    if T: T.write(self.trace_message(u'     adjusted ref count: %s, child %s' %
                                  (self.trace_node(node), repr(str(child)))))

                if T:
                    for pc in children_pending:
                        T.write(self.trace_message('       adding %s to the pending children set\n' %
                                self.trace_node(pc)))
                self.pending_children = self.pending_children | children_pending

                continue

            # Skip this node if it has side-effects that are
            # currently being built:
            wait_side_effects = False
            for se in executor.get_action_side_effects():
                if se.get_state() == NODE_EXECUTING:
                    se.add_to_waiting_s_e(node)
                    wait_side_effects = True

            if wait_side_effects:
                if S: S.side_effects = S.side_effects + 1
                continue

            # The default when we've gotten through all of the checks above:
            # this node is ready to be built.
            if S: S.build = S.build + 1
            if T: T.write(self.trace_message(u'Evaluating %s\n' %
                                             self.trace_node(node)))

            # For debugging only:
            #
            # try:
            #     self._validate_pending_children()
            # except:
            #     self.ready_exc = sys.exc_info()
            #     return node

            return node

        return None
dataset/ETHPy150Open kayhayen/Nuitka/nuitka/build/inline_copy/lib/scons-2.3.2/SCons/Taskmaster.py/Taskmaster._find_next_ready_node