Esempio n. 1
0
 /**
  * Returns the host configuration trimmed to the same fragments as the input configuration. If the
  * input is null, returns the top-level host configuration.
  *
  * <p>For static configurations, this unconditionally returns the (sole) top-level configuration.
  *
  * <p>This may only be called after {@link #setTopLevelHostConfiguration} has set the correct host
  * configuration at the top-level.
  */
 public BuildConfiguration getHostConfiguration(BuildConfiguration config) {
   if (config == null || !config.useDynamicConfigurations()) {
     return topLevelHostConfiguration;
   }
   // TODO(bazel-team): have the fragment classes be those required by the consuming target's
   // transitive closure. This isn't the same as the input configuration's fragment classes -
   // the latter may be a proper subset of the former.
   //
   // ConfigurationFactory.getConfiguration provides the reason why: if a declared required
   // fragment is evaluated and returns null, it never gets added to the configuration. So if we
   // use the configuration's fragments as the source of truth, that excludes required fragments
   // that never made it in.
   //
   // If we're just trimming an existing configuration, this is no big deal (if the original
   // configuration doesn't need the fragment, the trimmed one doesn't either). But this method
   // trims a host configuration to the same scope as a target configuration. Since their options
   // are different, the host instance may actually be able to produce the fragment. So it's
   // wrong and potentially dangerous to unilaterally exclude it.
   Set<Class<? extends BuildConfiguration.Fragment>> fragmentClasses = config.fragmentClasses();
   BuildConfiguration hostConfig = hostConfigurationCache.get(fragmentClasses);
   if (hostConfig != null) {
     return hostConfig;
   }
   BuildConfiguration trimmedConfig =
       topLevelHostConfiguration.clone(fragmentClasses, ruleClassProvider);
   hostConfigurationCache.put(fragmentClasses, trimmedConfig);
   return trimmedConfig;
 }
Esempio n. 2
0
  private void visitRule(
      Rule rule,
      AspectDefinition aspect,
      AspectParameters aspectParameters,
      ListMultimap<Attribute, LabelAndConfiguration> labelMap,
      ListMultimap<Attribute, Dependency> outgoingEdges) {
    Preconditions.checkNotNull(labelMap);
    for (Map.Entry<Attribute, Collection<LabelAndConfiguration>> entry :
        labelMap.asMap().entrySet()) {
      Attribute attribute = entry.getKey();
      for (LabelAndConfiguration dep : entry.getValue()) {
        Label label = dep.getLabel();
        BuildConfiguration config = dep.getConfiguration();

        Target toTarget;
        try {
          toTarget = getTarget(label);
        } catch (NoSuchThingException e) {
          throw new IllegalStateException(
              "not found: " + label + " from " + rule + " in " + attribute.getName());
        }
        if (toTarget == null) {
          continue;
        }
        BuildConfiguration.TransitionApplier transitionApplier = config.getTransitionApplier();
        if (config.useDynamicConfigurations()
            && config.isHostConfiguration()
            && !BuildConfiguration.usesNullConfiguration(toTarget)) {
          // This condition is needed because resolveLateBoundAttributes may switch config to
          // the host configuration, which is the only case DependencyResolver applies a
          // configuration transition outside of this method. We need to reflect that
          // transition in the results of this method, but config.evaluateTransition is hard-set
          // to return a NONE transition when the input is a host config. Since the outside
          // caller originally passed the *original* value of config (before the possible
          // switch), it can mistakenly interpret the result as a NONE transition from the
          // original value of config. This condition fixes that. Another fix would be to have
          // config.evaluateTransition return a HOST transition when the input config is a host,
          // but since this blemish is specific to DependencyResolver it seems best to keep the
          // fix here.
          // TODO(bazel-team): eliminate this special case by passing transitionApplier to
          // resolveLateBoundAttributes, so that method uses the same interface for transitions.
          transitionApplier.applyTransition(Attribute.ConfigurationTransition.HOST);
        } else {
          config.evaluateTransition(rule, attribute, toTarget, transitionApplier);
        }
        for (Dependency dependency :
            transitionApplier.getDependencies(
                label, requiredAspects(aspect, aspectParameters, attribute, toTarget, rule))) {
          outgoingEdges.put(entry.getKey(), dependency);
        }
      }
    }
  }
Esempio n. 3
0
 /**
  * Returns the host configuration trimmed to the same fragments as the input configuration. If the
  * input is null, returns the top-level host configuration.
  *
  * <p>For static configurations, this unconditionally returns the (sole) top-level configuration.
  *
  * <p>This may only be called after {@link #setTopLevelHostConfiguration} has set the correct host
  * configuration at the top-level.
  */
 public BuildConfiguration getHostConfiguration(BuildConfiguration config) {
   if (config == null || !config.useDynamicConfigurations()) {
     return topLevelHostConfiguration;
   }
   Set<Class<? extends BuildConfiguration.Fragment>> fragmentClasses = config.fragmentClasses();
   BuildConfiguration hostConfig = hostConfigurationCache.get(fragmentClasses);
   if (hostConfig != null) {
     return hostConfig;
   }
   BuildConfiguration trimmedConfig =
       topLevelHostConfiguration.clone(fragmentClasses, ruleClassProvider);
   hostConfigurationCache.put(fragmentClasses, trimmedConfig);
   return trimmedConfig;
 }
  @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;
  }