docs: def resolve_dangling_targets(self,
                                   dag: SoS_DAG,
                                   targets: Optional[sos_targets] = None) -> int:
          '''Feed dangling targets with their dependncies from auxiliary steps,
          optionally add other targets'''
          resolved = 0
          while True:
              added_node = 0
              # first resolve missing
              dangling_targets = dag.dangling(targets)[0]
              if dangling_targets:
                  env.log_to_file(
                      'EXECUTOR',
                      f'Resolving {dangling_targets} objects from {dag.number_of_nodes()} nodes'
                  )
              # find matching steps
              # check auxiliary steps and see if any steps provides it
              remaining_targets = dangling_targets
              while remaining_targets:
                  target = remaining_targets.pop()
                  mo = self.match(target)
                  if not mo:
                      #
                      # if no step produces the target, it is possible that it is an indexed step
                      # so the execution of its previous steps would solves the dependency
                      #
                      # find all the nodes that depends on target
                      nodes = dag._all_depends_files[target]
                      for node in nodes:
                          # if this is an index step... simply let it depends on previous steps
                          if node._node_index is not None:
                              indexed = [
                                  x for x in dag.nodes()
                                  if x._node_index is not None and
                                  x._node_index < node._node_index and
                                  not x._output_targets.valid()
                              ]
                              indexed.sort(key=lambda x: x._node_index)
                              if not indexed:
                                  raise RuntimeError(
                                      f'No step to generate target {target}{dag.steps_depending_on(target, self.workflow)}'
                                  )
                              if isinstance(target, sos_step) and not any(
                                      self.workflow.section_by_id(x._step_uuid)
                                      .match(target.target_name())
                                      for x in indexed):
                                  raise RuntimeError(
                                      f'No step to generate target {target}{dag.steps_depending_on(target, self.workflow)}'
                                  )
                              # now, if it is not a sos_step, but its previous steps have already been executed and still
                              # could not satisfy the requirement..., we should generate an error
                              if not any(
                                      x._status is None or
                                      x._status.endswith('pending')
                                      for x in indexed):
                                  # all previous status has been failed or completed...
                                  raise RuntimeError(
                                      f'Previous step{" has" if len(indexed) == 1 else "s have"} not generated target {target}{dag.steps_depending_on(target, self.workflow)}'
                                  )
                              if node._input_targets.valid():
                                  node._input_targets = sos_targets([])
                              if node._depends_targets.valid():
                                  node._depends_targets = sos_targets([])
                          else:
                              raise RuntimeError(
                                  f'No step to generate target {target}{dag.steps_depending_on(target, self.workflow)}'
                              )
                      if nodes:
                          resolved += 1
                      # cannot resolve, but the step is numerically indexed so we just wait
                      continue
                  if len(mo) > 1:
                      # sos_step('a') could match to step a_1, a_2, etc, in this case we are adding a subworkflow
                      if isinstance(target, sos_step):
                          # create a new forward_workflow that is different from the master one
                          # get the step names
                          sections = sorted(mo, key=lambda x: x.step_name())
                          # this is only useful for executing auxiliary steps and
                          # might interfere with the step analysis
                          env.sos_dict.pop('__default_output__', None)
                          env.log_to_file(
                              'DAG',
                              f'Adding {len(sections)} steps to resolve target {target}'
                          )
  
                          n_added = self.add_forward_workflow(
                              dag, sections, satisfies=target)
  
                          added_node += n_added
                          resolved += 1
                          # dag.show_nodes()
                          continue
                      else:
                          raise RuntimeError(
                              f'Multiple steps {", ".join(x.step_name() for x in mo)} to generate target {target}'
                          )
                  #
                  # m0[0] can be a tuple
                  #   section, context
                  # or just a section
                  #   section
                  if isinstance(mo[0], tuple):
                      # for auxiliary, we need to set input and output, here
                      # now, if the step does not provide any alternative (e.g. no variable generated
                      # from patten), we should specify all output as output of step. Otherwise the
                      # step will be created for multiple outputs. issue #243
                      if mo[0][1]:
                          env.sos_dict['__default_output__'] = sos_targets(target)
                      else:
                          env.sos_dict['__default_output__'] = sos_targets(
                              mo[0][0].options['provides'])
                      n_added, _ = self.add_backward_step(
                          dag,
                          section=mo[0][0],
                          context=mo[0][1] if isinstance(mo[0][1], dict) else {},
                          target=target)
                  else:
                      env.sos_dict['__default_output__'] = sos_targets(target)
                      n_added, _ = self.add_backward_step(
                          dag, section=mo[0], context={}, target=target)
  
                  added_node += n_added
                  resolved += n_added
                  # this could be made more efficient by removing step output directly
                  # but it is inefficient to remove elements from list
                  remaining_targets = dag.dangling(remaining_targets)[0]
  
              # for existing targets that are not in DAG
              traced = [x for x in targets if x.traced]
              if not env.config['trace_existing'] and not traced:
                  if added_node == 0:
                      break
                  else:
                      continue
  
              existing_targets = set(dag.dangling(
                  targets)[1]) if env.config['trace_existing'] else traced
  
              remaining_targets = existing_targets
              while remaining_targets:
                  target = remaining_targets.pop()
                  # now we need to build DAG for existing...
                  mo = self.match(target)
                  if not mo:
                      # this is ok, this is just an existing target, no one is designed to
                      # generate it.
                      continue
                  if len(mo) > 1:
                      # this is not ok.
                      raise RuntimeError(
                          f'Multiple steps {", ".join(x.step_name() for x in mo)} to generate target {target}'
                      )
  
                  if isinstance(mo[0], tuple):
                      # for auxiliary, we need to set input and output, here
                      # now, if the step does not provide any alternative (e.g. no variable generated
                      # from patten), we should specify all output as output of step. Otherwise the
                      # step will be created for multiple outputs. issue #243
                      if mo[0][1]:
                          env.sos_dict['__default_output__'] = sos_targets(target)
                      else:
                          env.sos_dict['__default_output__'] = sos_targets(
                              mo[0][0].options['provides'])
                      n_added, resolved_output = self.add_backward_step(
                          dag,
                          section=mo[0][0],
                          context=mo[0][1] if isinstance(mo[0][1], dict) else {},
                          target=target)
                  else:
                      env.sos_dict['__default_output__'] = sos_targets(target)
                      n_added, resolved_output = self.add_backward_step(
                          dag, section=mo[0], context={}, target=target)
  
                  added_node += n_added
                  resolved += n_added
  
                  # adding one step can resolve many targets #1199
                  if len(resolved_output) > 1:
                      remaining_targets -= set(resolved_output.targets)
                      # set(dag.dangling(remaining_targets)[1])
  
              if added_node == 0:
                  break
          return resolved