private void collectTransitiveClosure(
      PackageProviderForConfigurations packageProvider, Set<Label> reachableLabels, Label from)
      throws NoSuchThingException {
    if (!reachableLabels.add(from)) {
      return;
    }
    Target fromTarget = packageProvider.getTarget(from);
    if (fromTarget instanceof Rule) {
      Rule rule = (Rule) fromTarget;
      if (rule.getRuleClassObject().hasAttr("srcs", BuildType.LABEL_LIST)) {
        // TODO(bazel-team): refine this. This visits "srcs" reachable under *any* configuration,
        // not necessarily the configuration actually applied to the rule. We should correlate the
        // two. However, doing so requires faithfully reflecting the configuration transitions that
        // might happen as we traverse the dependency chain.
        // TODO(bazel-team): Why don't we use AbstractAttributeMapper#visitLabels() here?
        for (List<Label> labelsForConfiguration :
            AggregatingAttributeMapper.of(rule).visitAttribute("srcs", BuildType.LABEL_LIST)) {
          for (Label label : labelsForConfiguration) {
            collectTransitiveClosure(
                packageProvider, reachableLabels, from.resolveRepositoryRelative(label));
          }
        }
      }

      if (rule.getRuleClass().equals("bind")) {
        Label actual = AggregatingAttributeMapper.of(rule).get("actual", BuildType.LABEL);
        if (actual != null) {
          collectTransitiveClosure(packageProvider, reachableLabels, actual);
        }
      }
    }
  }
  @Override
  @Nullable
  public BuildConfiguration createConfigurations(
      ConfigurationFactory configurationFactory,
      Cache<String, BuildConfiguration> cache,
      PackageProviderForConfigurations packageProvider,
      BuildOptions buildOptions,
      EventHandler eventHandler,
      boolean performSanityCheck)
      throws InvalidConfigurationException {
    // Target configuration
    BuildConfiguration targetConfiguration =
        configurationFactory.getConfiguration(packageProvider, buildOptions, false, cache);
    if (targetConfiguration == null) {
      return null;
    }

    BuildConfiguration dataConfiguration = targetConfiguration;

    // Host configuration
    // Note that this passes in the dataConfiguration, not the target
    // configuration. This is intentional.
    BuildConfiguration hostConfiguration =
        getHostConfigurationFromRequest(
            configurationFactory, packageProvider, dataConfiguration, buildOptions, cache);
    if (hostConfiguration == null) {
      return null;
    }

    ListMultimap<SplitTransition<?>, BuildConfiguration> splitTransitionsTable =
        ArrayListMultimap.create();
    for (SplitTransition<BuildOptions> transition : buildOptions.getPotentialSplitTransitions()) {
      List<BuildOptions> splitOptionsList = transition.split(buildOptions);

      // While it'd be clearer to condition the below on "if (!splitOptionsList.empty())",
      // IosExtension.ExtensionSplitArchTransition defaults to a single-value split. If we failed
      // that case then no builds would work, whether or not they're iOS builds (since iOS
      // configurations are unconditionally loaded). Once we have dynamic configuraiton support
      // for split transitions, this will all go away.
      if (splitOptionsList.size() > 1 && targetConfiguration.useDynamicConfigurations()) {
        throw new InvalidConfigurationException(
            "dynamic configurations don't yet support split transitions");
      }

      for (BuildOptions splitOptions : splitOptionsList) {
        BuildConfiguration splitConfig =
            configurationFactory.getConfiguration(packageProvider, splitOptions, false, cache);
        splitTransitionsTable.put(transition, splitConfig);
      }
    }
    if (packageProvider.valuesMissing()) {
      return null;
    }

    // Sanity check that the implicit labels are all in the transitive closure of explicit ones.
    // This also registers all targets in the cache entry and validates them on subsequent requests.
    Set<Label> reachableLabels = new HashSet<>();
    if (performSanityCheck) {
      // We allow the package provider to be null for testing.
      for (Map.Entry<String, Label> entry : buildOptions.getAllLabels().entries()) {
        Label label = entry.getValue();
        try {
          collectTransitiveClosure(packageProvider, reachableLabels, label);
        } catch (NoSuchThingException e) {
          eventHandler.handle(Event.error(e.getMessage()));
          throw new InvalidConfigurationException(
              String.format("Failed to load required %s target: '%s'", entry.getKey(), label));
        }
      }
      if (packageProvider.valuesMissing()) {
        return null;
      }
      sanityCheckImplicitLabels(reachableLabels, targetConfiguration);
      sanityCheckImplicitLabels(reachableLabels, hostConfiguration);
    }

    BuildConfiguration result =
        setupTransitions(
            targetConfiguration, dataConfiguration, hostConfiguration, splitTransitionsTable);
    result.reportInvalidOptions(eventHandler);
    return result;
  }