code: def _require_homogeneous_roots(self, accept_predicate, reject_predicate):
      
      if len(self.context.target_roots) == 0:
        raise self.NoActivationsError('No target specified.')
  
      def resolve(targets):
        # Recursively resolve target aliases.
        for t in targets:
          if type(t) == Target:
            for r in resolve(t.dependencies):
              yield r
          else:
            yield t
  
      expanded_roots = list(resolve(self.context.target_roots))
  
      accepted = list(filter(accept_predicate, expanded_roots))
      rejected = list(filter(reject_predicate, expanded_roots))
      if len(accepted) == 0:
        # no targets were accepted, regardless of rejects
        return None
      elif len(rejected) == 0:
        # we have at least one accepted target, and no rejected targets
        return accepted
      else:
        # both accepted and rejected targets
        # TODO: once https://github.com/pantsbuild/pants/issues/425 lands, we should add
        # language-specific flags that would resolve the ambiguity here
        def render_target(target):
          return '{} (a {})'.format(target.address.reference(), target.type_alias)
        raise self.IncompatibleActivationsError('Mutually incompatible targets specified: {} vs {} '
                                                '(and {} others)'
                                                .format(render_target(accepted[0]),
                                                        render_target(rejected[0]),
                                                        len(accepted) + len(rejected) - 2))
docs: Ensures that there is no ambiguity in the context according to the given predicates.
  
      If any targets in the context satisfy the accept_predicate, and no targets satisfy the
      reject_predicate, returns the accepted targets.
  
      If no targets satisfy the accept_predicate, returns None.
  
      Otherwise throws TaskError.