def _visit_goal(self, goal, context, goal_info_by_goal, target_roots_replacement):
    if goal in goal_info_by_goal:
      return

    tasktypes_by_name = OrderedDict()
    goal_dependencies = set()
    visited_task_types = set()
    for task_name in reversed(goal.ordered_task_names()):
      task_type = goal.task_type_by_name(task_name)
      tasktypes_by_name[task_name] = task_type
      visited_task_types.add(task_type)

      alternate_target_roots = task_type._alternate_target_roots(context.options,
                                                                 context.address_mapper,
                                                                 context.build_graph)
      target_roots_replacement.propose_alternates(task_type, alternate_target_roots)

      round_manager = RoundManager(context)
      task_type._prepare(context.options, round_manager)
      try:
        dependencies = round_manager.get_dependencies()
        for producer_info in dependencies:
          producer_goal = producer_info.goal
          if producer_goal == goal:
            if producer_info.task_type == task_type:
              # We allow a task to produce products it itself needs.  We trust the Task writer
              # to arrange for proper sequencing.
              pass
            elif producer_info.task_type in visited_task_types:
              ordering = '\n\t'.join("[{0}] '{1}' {2}".format(i, tn,
                                                              goal.task_type_by_name(tn).__name__)
                                     for i, tn in enumerate(goal.ordered_task_names()))
              raise self.TaskOrderError(
                  "TaskRegistrar '{name}' with action {consumer_task} depends on {data} from task "
                  "{producer_task} which is ordered after it in the '{goal}' goal:\n\t{ordering}"
                  .format(name=task_name,
                          consumer_task=task_type.__name__,
                          data=producer_info.product_type,
                          producer_task=producer_info.task_type.__name__,
                          goal=goal.name,
                          ordering=ordering))
            else:
              # We don't express dependencies on downstream tasks in this same goal.
              pass
          else:
            goal_dependencies.add(producer_goal)
      except round_manager.MissingProductError as e:
        raise self.MissingProductError(
            "Could not satisfy data dependencies for goal '{name}' with action {action}: {error}"
            .format(name=task_name, action=task_type.__name__, error=e))

    goal_info = self.GoalInfo(goal, tasktypes_by_name, goal_dependencies)
    goal_info_by_goal[goal] = goal_info

    for goal_dependency in goal_dependencies:
      self._visit_goal(goal_dependency, context, goal_info_by_goal, target_roots_replacement)
dataset/ETHPy150Open pantsbuild/pants/src/python/pants/engine/round_engine.py/RoundEngine._visit_goal/original