/** * 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; }
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); } } } }
/** * 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; }