aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools
diff options
context:
space:
mode:
authorGravatar gregce <gregce@google.com>2017-08-18 21:20:29 +0200
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2017-08-21 14:15:59 +0200
commit8529746358ff5d88dc7ddf584a85ba0aa1a269a8 (patch)
tree5aa2c9f9e6f22b37d4d1c320d21fa4436545051b /src/main/java/com/google/devtools
parent8158a286a8e72ec3f5259d155f6856fea9d07fea (diff)
Replace TransitionApplier interface with a dedicated class.
This accomplishes a few goals: 1. Removes the outdated BuildConfiguration.StaticConfigurationApplier code. 2. Removes the TransitionApplier abstraction completely. This was an awkward bridge meant to support both static and dynamic implementations. 3. Moves transition logic to its own dedicated class: ConfigurationResolver. This no longer belongs in BuildConfiguration, which we ultimately want to become a simple <key, value> map. Part of the static config cleanup effort. PiperOrigin-RevId: 165736955
Diffstat (limited to 'src/main/java/com/google/devtools')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/BuildView.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java104
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java377
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/config/ConfigurationResolver.java255
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/config/DynamicTransitionMapper.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java1
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/SkyframeDependencyResolver.java4
9 files changed, 316 insertions, 441 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
index 26447804a1..cb4f086fcd 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
@@ -1061,6 +1061,10 @@ public class BuildView {
}
class SilentDependencyResolver extends DependencyResolver {
+ private SilentDependencyResolver() {
+ super(ruleClassProvider.getDynamicTransitionMapper());
+ }
+
@Override
protected void invalidVisibilityReferenceHook(TargetAndConfiguration node, Label label) {
throw new RuntimeException("bad visibility on " + label + " during testing unexpected");
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
index ee6e2569b5..e2380261a8 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
@@ -22,6 +22,8 @@ import com.google.devtools.build.lib.analysis.AspectCollection.AspectCycleOnPath
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
+import com.google.devtools.build.lib.analysis.config.ConfigurationResolver;
+import com.google.devtools.build.lib.analysis.config.DynamicTransitionMapper;
import com.google.devtools.build.lib.analysis.config.HostTransition;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.analysis.config.PatchTransition;
@@ -60,7 +62,10 @@ import javax.annotation.Nullable;
* <p>Includes logic to derive the right configurations depending on transition type.
*/
public abstract class DependencyResolver {
- protected DependencyResolver() {
+ private final ConfigurationResolver configResolver;
+
+ protected DependencyResolver(DynamicTransitionMapper transitionMapper) {
+ this.configResolver = new ConfigurationResolver(transitionMapper);
}
/**
@@ -562,38 +567,6 @@ public abstract class DependencyResolver {
}
}
-
- private AspectCollection requiredAspects(
- Iterable<Aspect> aspectPath,
- AttributeAndOwner attributeAndOwner,
- final Target target,
- Rule originalRule) throws InconsistentAspectOrderException {
- if (!(target instanceof Rule)) {
- return AspectCollection.EMPTY;
- }
-
- if (attributeAndOwner.ownerAspect != null) {
- // Do not propagate aspects along aspect attributes.
- return AspectCollection.EMPTY;
- }
-
- ImmutableList.Builder<Aspect> filteredAspectPath = ImmutableList.builder();
- ImmutableSet.Builder<AspectDescriptor> visibleAspects = ImmutableSet.builder();
-
- Attribute attribute = attributeAndOwner.attribute;
- collectOriginatingAspects(originalRule, attribute, (Rule) target,
- filteredAspectPath, visibleAspects);
-
- collectPropagatingAspects(aspectPath,
- attribute,
- (Rule) target, filteredAspectPath, visibleAspects);
- try {
- return AspectCollection.create(filteredAspectPath.build(), visibleAspects.build());
- } catch (AspectCycleOnPathException e) {
- throw new InconsistentAspectOrderException(originalRule, attribute, target, e);
- }
- }
-
/**
* Collects into {@code filteredAspectPath}
* aspects from {@code aspectPath} that propagate along {@code attribute}
@@ -734,22 +707,22 @@ public abstract class DependencyResolver {
if (toTarget == null) {
return; // Skip this round: we still need to Skyframe-evaluate the dep's target.
}
- BuildConfiguration.TransitionApplier resolver = ruleConfig.getTransitionApplier();
- ruleConfig.evaluateTransition(rule, attributeAndOwner.attribute, toTarget, resolver);
- // An <Attribute, Label> pair can resolve to multiple deps because of split transitions.
- for (Dependency dependency :
- resolver.getDependencies(depLabel,
- requiredAspects(aspects, attributeAndOwner, toTarget, rule))) {
- outgoingEdges.put(attributeAndOwner.attribute, dependency);
- }
+ Attribute.Transition transition = configResolver.evaluateTransition(
+ ruleConfig, rule, attributeAndOwner.attribute, toTarget);
+ outgoingEdges.put(
+ attributeAndOwner.attribute,
+ transition == Attribute.ConfigurationTransition.NULL
+ ? Dependency.withNullConfiguration(depLabel)
+ : Dependency.withTransitionAndAspects(depLabel, transition,
+ requiredAspects(attributeAndOwner, toTarget)));
}
/**
* Resolves the given dep for the given attribute using a pre-prepared configuration.
*
* <p>Use this method with care: it skips Bazel's standard config transition semantics ({@link
- * BuildConfiguration#evaluateTransition}). That means attributes passed through here won't obey
- * standard rules on which configurations apply to their deps. This should only be done for
+ * ConfigurationResolver#evaluateTransition}). That means attributes passed through here won't
+ * obey standard rules on which configurations apply to their deps. This should only be done for
* special circumstances that really justify the difference. When in doubt, use {@link
* #resolveDep(AttributeAndOwner, Label)}.
*/
@@ -759,25 +732,40 @@ public abstract class DependencyResolver {
if (toTarget == null) {
return; // Skip this round: this is either a loading error or unevaluated Skyframe dep.
}
- BuildConfiguration.TransitionApplier transitionApplier = config.getTransitionApplier();
- boolean applyNullTransition = false;
- if (BuildConfiguration.usesNullConfiguration(toTarget)) {
- transitionApplier.applyTransition(Attribute.ConfigurationTransition.NULL);
- applyNullTransition = true;
+ outgoingEdges.put(
+ attributeAndOwner.attribute,
+ configResolver.usesNullConfiguration(toTarget)
+ ? Dependency.withNullConfiguration(depLabel)
+ : Dependency.withTransitionAndAspects(depLabel, new FixedTransition(
+ config.getOptions()), requiredAspects(attributeAndOwner, toTarget)));
+ }
+
+ private AspectCollection requiredAspects(AttributeAndOwner attributeAndOwner,
+ final Target target) throws InconsistentAspectOrderException {
+ if (!(target instanceof Rule)) {
+ return AspectCollection.EMPTY;
}
- AspectCollection aspects =
- requiredAspects(this.aspects, attributeAndOwner, toTarget, rule);
- Dependency dep;
- if (!applyNullTransition) {
- // Pass a transition rather than directly feeding the configuration so deps get trimmed.
- dep = Dependency.withTransitionAndAspects(
- depLabel, new FixedTransition(config.getOptions()), aspects);
- } else {
- dep = Iterables.getOnlyElement(transitionApplier.getDependencies(depLabel, aspects));
+ if (attributeAndOwner.ownerAspect != null) {
+ // Do not propagate aspects along aspect attributes.
+ return AspectCollection.EMPTY;
}
- outgoingEdges.put(attributeAndOwner.attribute, dep);
+ ImmutableList.Builder<Aspect> filteredAspectPath = ImmutableList.builder();
+ ImmutableSet.Builder<AspectDescriptor> visibleAspects = ImmutableSet.builder();
+
+ Attribute attribute = attributeAndOwner.attribute;
+ collectOriginatingAspects(rule, attribute, (Rule) target,
+ filteredAspectPath, visibleAspects);
+
+ collectPropagatingAspects(aspects,
+ attribute,
+ (Rule) target, filteredAspectPath, visibleAspects);
+ try {
+ return AspectCollection.create(filteredAspectPath.build(), visibleAspects.build());
+ } catch (AspectCycleOnPathException e) {
+ throw new InconsistentAspectOrderException(rule, attribute, target, e);
+ }
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
index 5165403667..ccb3ea6a42 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
@@ -34,10 +34,8 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.MutableClassToInstanceMap;
import com.google.devtools.build.lib.actions.ActionEnvironment;
import com.google.devtools.build.lib.actions.Root;
-import com.google.devtools.build.lib.analysis.AspectCollection;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
-import com.google.devtools.build.lib.analysis.Dependency;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
import com.google.devtools.build.lib.buildeventstream.BuildEvent;
@@ -50,23 +48,13 @@ import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
-import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
-import com.google.devtools.build.lib.packages.Attribute.Configurator;
-import com.google.devtools.build.lib.packages.Attribute.SplitTransition;
-import com.google.devtools.build.lib.packages.Attribute.Transition;
-import com.google.devtools.build.lib.packages.InputFile;
-import com.google.devtools.build.lib.packages.PackageGroup;
-import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClassProvider;
-import com.google.devtools.build.lib.packages.RuleTransitionFactory;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.OS;
-import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.RegexFilter;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -1094,7 +1082,6 @@ public final class BuildConfiguration implements BuildEvent {
private final ImmutableMap<Class<? extends Fragment>, Fragment> fragments;
private final ImmutableMap<String, Class<? extends Fragment>> skylarkVisibleFragments;
private final RepositoryName mainRepositoryName;
- private final DynamicTransitionMapper dynamicTransitionMapper;
private final ImmutableSet<String> reservedActionMnemonics;
/**
@@ -1386,8 +1373,7 @@ public final class BuildConfiguration implements BuildEvent {
public BuildConfiguration(BlazeDirectories directories,
Map<Class<? extends Fragment>, Fragment> fragmentsMap,
BuildOptions buildOptions,
- String repositoryName,
- @Nullable DynamicTransitionMapper dynamicTransitionMapper) {
+ String repositoryName) {
this.directories = directories;
this.fragments = ImmutableSortedMap.copyOf(fragmentsMap, lexicalFragmentSorter);
@@ -1398,7 +1384,6 @@ public final class BuildConfiguration implements BuildEvent {
this.options = buildOptions.get(Options.class);
this.separateGenfilesDirectory = options.separateGenfilesDirectory;
this.mainRepositoryName = RepositoryName.createFromValidStrippedName(repositoryName);
- this.dynamicTransitionMapper = dynamicTransitionMapper;
// We can't use an ImmutableMap.Builder here; we need the ability to add entries with keys that
// are already in the map so that the same define can be specified on the command line twice,
@@ -1492,8 +1477,7 @@ public final class BuildConfiguration implements BuildEvent {
directories,
fragmentsMap,
options,
- mainRepositoryName.strippedName(),
- dynamicTransitionMapper);
+ mainRepositoryName.strippedName());
return newConfig;
}
@@ -1574,363 +1558,6 @@ public final class BuildConfiguration implements BuildEvent {
}
/**
- * A common interface for static vs. dynamic configuration implementations that allows
- * common configuration and transition-selection logic to seamlessly work with either.
- *
- * <p>The basic role of this interface is to "accept" a desired transition and produce
- * an actual configuration change from it in an implementation-appropriate way.
- */
- public interface TransitionApplier {
- /**
- * Accepts the given configuration transition. The implementation decides how to turn
- * this into an actual configuration. This may be called multiple times (representing a
- * request for a sequence of transitions).
- */
- void applyTransition(Transition transition);
-
- /**
- * Accepts the given split transition. The implementation decides how to turn this into
- * actual configurations.
- */
- void split(SplitTransition<BuildOptions> splitTransition);
-
- /**
- * Returns whether or not all configuration(s) represented by the current state of this
- * instance are null.
- */
- boolean isNull();
-
- /**
- * Applies the given attribute configurator to the current configuration(s).
- */
- void applyAttributeConfigurator(Configurator<BuildOptions> configurator);
-
- /**
- * Applies a custom configuration hook for the given rule.
- */
- void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget);
-
- /**
- * Populates a {@link com.google.devtools.build.lib.analysis.Dependency}
- * for each configuration represented by this instance.
- * TODO(bazel-team): this is a really ugly reverse dependency: factor this away.
- */
- Iterable<Dependency> getDependencies(Label label, AspectCollection aspects);
- }
-
- /**
- * Transition applier for static configurations. This implementation populates
- * {@link com.google.devtools.build.lib.analysis.Dependency} objects with
- * actual configurations.
- *
- * TODO(bazel-team): remove this when dynamic configurations are fully production-ready.
- */
- private static class StaticTransitionApplier implements TransitionApplier {
- // The configuration(s) this applier applies to dep rules. Plural because of split transitions.
- // May change multiple times: the ultimate transition might be a sequence of intermediate
- // transitions.
- List<BuildConfiguration> toConfigurations;
-
- private StaticTransitionApplier(BuildConfiguration originalConfiguration) {
- this.toConfigurations = ImmutableList.<BuildConfiguration>of(originalConfiguration);
- }
-
- @Override
- public void applyTransition(Transition transition) {
- throw new UnsupportedOperationException("dead static config code being removed");
- }
-
- @Override
- public void split(SplitTransition<BuildOptions> splitTransition) {
- throw new UnsupportedOperationException("dead static config code being removed");
- }
-
- @Override
- public boolean isNull() {
- return toConfigurations.size() == 1
- ? Iterables.getOnlyElement(toConfigurations) == null
- : false;
- }
-
- @Override
- public void applyAttributeConfigurator(Configurator<BuildOptions> configurator) {
- // There should only be one output configuration at this point: splits don't occur down
- // attributes with attribute configurators. We can lift this restriction later if desired.
- BuildOptions toOptions = Iterables.getOnlyElement(toConfigurations).getOptions();
- applyTransition(configurator.apply(toOptions));
- }
-
- @Override
- public void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget) {
- throw new UnsupportedOperationException("dead static config code being removed");
- }
-
- @Override
- public Iterable<Dependency> getDependencies(
- Label label, AspectCollection aspects) {
- ImmutableList.Builder<Dependency> deps = ImmutableList.builder();
- for (BuildConfiguration config : toConfigurations) {
- deps.add(config != null
- ? Dependency.withConfigurationAndAspects(label, config, aspects)
- : Dependency.withNullConfiguration(label));
- }
- return deps.build();
- }
- }
-
- /**
- * Transition applier for dynamic configurations. This implementation populates
- * {@link com.google.devtools.build.lib.analysis.Dependency} objects with
- * transitions that the caller subsequently creates configurations from.
- */
- private static class DynamicTransitionApplier implements TransitionApplier {
- private final DynamicTransitionMapper dynamicTransitionMapper;
- private boolean splitApplied = false;
-
- // The transition this applier applies to dep rules. When multiple transitions are requested,
- // this is a ComposingSplitTransition, which encapsulates the sequence into a single instance
- // so calling code doesn't need special logic to support combinations.
- private Transition currentTransition = Attribute.ConfigurationTransition.NONE;
-
- private DynamicTransitionApplier(DynamicTransitionMapper dynamicTransitionMapper) {
- this.dynamicTransitionMapper = dynamicTransitionMapper;
- }
-
- /**
- * Returns true if the given transition should not be modifiable by subsequent ones, i.e.
- * once this transition is applied it's the final word on the output configuration.
- */
- private static boolean isFinal(Transition transition) {
- return (transition == Attribute.ConfigurationTransition.NULL
- || transition == HostTransition.INSTANCE);
- }
-
- @Override
- public void applyTransition(Transition transitionToApply) {
- currentTransition = composeTransitions(currentTransition, transitionToApply);
- }
-
- /**
- * Composes two transitions together efficiently.
- */
- private Transition composeTransitions(Transition transition1, Transition transition2) {
- if (isFinal(transition1)) {
- return transition1;
- } else if (transition2 == Attribute.ConfigurationTransition.NONE) {
- return transition1;
- } else if (transition2 == Attribute.ConfigurationTransition.NULL) {
- // A NULL transition can just replace earlier transitions: no need to cfpose them.
- return Attribute.ConfigurationTransition.NULL;
- } else if (transition2 == Attribute.ConfigurationTransition.HOST) {
- // A HOST transition can just replace earlier transitions: no need to compose them.
- // But it also improves performance: host transitions are common, and
- // ConfiguredTargetFunction has special optimized logic to handle them. If they were buried
- // in the last segment of a ComposingSplitTransition, those optimizations wouldn't trigger.
- return HostTransition.INSTANCE;
- }
-
- // TODO(gregce): remove this dynamic transition mapping when static configs are removed.
- Transition dynamicTransition = dynamicTransitionMapper.map(transition2);
- return transition1 == Attribute.ConfigurationTransition.NONE
- ? dynamicTransition
- : new ComposingSplitTransition(transition1, dynamicTransition);
- }
-
- @Override
- // TODO(gregce): fold this into applyTransition during the static config code removal cleanup
- public void split(SplitTransition<BuildOptions> splitTransition) {
- // This "single split" check doesn't come from any design restriction. Its purpose is to
- // protect against runaway graph explosion, e.g. applying split[1,2,3] -> split[4,5,6] -> ...
- // and getting 3^n versions of a dep. So it's fine to loosen or lift this restriction
- // for a principled use case.
- Preconditions.checkState(!splitApplied,
- "dependency edges may apply at most one split transition");
- Preconditions.checkState(currentTransition != Attribute.ConfigurationTransition.NULL,
- "cannot apply splits after null transitions (null transitions are expected to be final)");
- Preconditions.checkState(currentTransition != HostTransition.INSTANCE,
- "cannot apply splits after host transitions (host transitions are expected to be final)");
- currentTransition = currentTransition == Attribute.ConfigurationTransition.NONE
- ? splitTransition
- : new ComposingSplitTransition(currentTransition, splitTransition);
- splitApplied = true;
- }
-
- @Override
- public boolean isNull() {
- return currentTransition == Attribute.ConfigurationTransition.NULL;
- }
-
- /**
- * A {@link PatchTransition} that applies an attribute configurator over some input options
- * to determine which transition to use, then applies that transition over those options
- * for the final output.
- */
- private static final class AttributeConfiguratorTransition implements PatchTransition {
- private final Configurator<BuildOptions> configurator;
-
- AttributeConfiguratorTransition(Configurator<BuildOptions> configurator) {
- this.configurator = configurator;
- }
-
- @Override
- public BuildOptions apply(BuildOptions options) {
- return Iterables.getOnlyElement(
- ComposingSplitTransition.apply(options, configurator.apply(options)));
- }
-
- @Override
- public boolean defaultsToSelf() {
- return false;
- }
- }
- /**
- * Unlike the static config version, this one can be composed with arbitrary transitions
- * (including splits).
- */
- @Override
- public void applyAttributeConfigurator(Configurator<BuildOptions> configurator) {
- if (isFinal(currentTransition)) {
- return;
- }
- currentTransition =
- composeTransitions(currentTransition, new AttributeConfiguratorTransition(configurator));
- }
-
- @Override
- public void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget) {
- if (isFinal(currentTransition)) {
- return;
- }
- Rule associatedRule = toTarget.getAssociatedRule();
- RuleTransitionFactory transitionFactory =
- associatedRule.getRuleClassObject().getTransitionFactory();
- if (transitionFactory != null) {
- // dynamicTransitionMapper is only needed because of Attribute.ConfigurationTransition.DATA:
- // this is C++-specific but non-C++ rules declare it. So they can't directly provide the
- // C++-specific patch transition that implements it.
- PatchTransition ruleClassTransition = (PatchTransition)
- dynamicTransitionMapper.map(transitionFactory.buildTransitionFor(associatedRule));
- if (ruleClassTransition != null) {
- if (currentTransition == ConfigurationTransition.NONE) {
- currentTransition = ruleClassTransition;
- } else {
- currentTransition = new ComposingSplitTransition(currentTransition,
- ruleClassTransition);
- }
- }
- }
- }
-
- @Override
- public Iterable<Dependency> getDependencies(
- Label label, AspectCollection aspects) {
- return ImmutableList.of(
- isNull()
- // We can trivially set the final value for null-configured targets now. This saves
- // us from having to recreate a new Dependency object for the final value later. Since
- // there are lots of null-configured targets (e.g. all source files), this can add up
- // over the course of a build.
- ? Dependency.withNullConfiguration(label)
- : Dependency.withTransitionAndAspects(label, currentTransition, aspects));
- }
- }
-
- /**
- * Returns the {@link TransitionApplier} that should be passed to {#evaluateTransition} calls.
- */
- public TransitionApplier getTransitionApplier() {
- return new DynamicTransitionApplier(dynamicTransitionMapper);
- }
-
- /**
- * Returns true if the given target uses a null configuration, false otherwise. Consider
- * this method the "source of truth" for determining this.
- */
- public static boolean usesNullConfiguration(Target target) {
- return target instanceof InputFile || target instanceof PackageGroup;
- }
-
- /**
- * Calculates the configurations of a direct dependency. If a rule in some BUILD file refers
- * to a target (like another rule or a source file) using a label attribute, that target needs
- * to have a configuration, too. This method figures out the proper configuration for the
- * dependency.
- *
- * @param fromRule the rule that's depending on some target
- * @param attribute the attribute using which the rule depends on that target (eg. "srcs")
- * @param toTarget the target that's dependeded on
- * @param transitionApplier the transition applier to accept transitions requests
- */
- public void evaluateTransition(final Rule fromRule, final Attribute attribute,
- final Target toTarget, TransitionApplier transitionApplier) {
- // Fantastic configurations and where to find them:
-
- // I. Input files and package groups have no configurations. We don't want to duplicate them.
- if (usesNullConfiguration(toTarget)) {
- transitionApplier.applyTransition(Attribute.ConfigurationTransition.NULL);
- return;
- }
-
- // II. Host configurations never switch to another. All prerequisites of host targets have the
- // same host configuration.
- if (isHostConfiguration()) {
- transitionApplier.applyTransition(Attribute.ConfigurationTransition.NONE);
- return;
- }
-
- // Make sure config_setting dependencies are resolved in the referencing rule's configuration,
- // unconditionally. For example, given:
- //
- // genrule(
- // name = 'myrule',
- // tools = select({ '//a:condition': [':sometool'] })
- //
- // all labels in "tools" get resolved in the host configuration (since the "tools" attribute
- // declares a host configuration transition). We want to explicitly exclude configuration labels
- // from these transitions, since their *purpose* is to do computation on the owning
- // rule's configuration.
- // TODO(bazel-team): don't require special casing here. This is far too hackish.
- if (toTarget instanceof Rule && ((Rule) toTarget).getRuleClassObject().isConfigMatcher()) {
- transitionApplier.applyTransition(Attribute.ConfigurationTransition.NONE); // Unnecessary.
- return;
- }
-
- // Apply the parent rule's outgoing transition if it has one.
- RuleTransitionFactory transitionFactory =
- fromRule.getRuleClassObject().getOutgoingTransitionFactory();
- if (transitionFactory != null) {
- Transition transition = transitionFactory.buildTransitionFor(toTarget.getAssociatedRule());
- if (transition != null) {
- transitionApplier.applyTransition(transition);
- }
- }
-
- // TODO(gregce): make the below transitions composable (i.e. take away the "else" clauses) once
- // the static config code path is removed. They can be mixed freely with dynamic configurations.
- if (attribute.hasSplitConfigurationTransition()) {
- Preconditions.checkState(attribute.getConfigurator() == null);
- transitionApplier.split(
- (SplitTransition<BuildOptions>) attribute.getSplitTransition(fromRule));
- } else {
- // III. Attributes determine configurations. The configuration of a prerequisite is determined
- // by the attribute.
- @SuppressWarnings("unchecked")
- Configurator<BuildOptions> configurator =
- (Configurator<BuildOptions>) attribute.getConfigurator();
- if (configurator != null) {
- // TODO(gregce): remove this branch when static config logic is removed. Attribute
- // configurators can just be implemented as standard attribute transitions, via
- // applyTransition.
- transitionApplier.applyAttributeConfigurator(configurator);
- } else {
- transitionApplier.applyTransition(attribute.getConfigurationTransition());
- }
- }
-
- transitionApplier.applyConfigurationHook(fromRule, attribute, toTarget);
- }
-
- /**
* The platform string, suitable for use as a key into a MakeEnvironment.
*/
public String getPlatformName() {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigurationResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigurationResolver.java
new file mode 100644
index 0000000000..5432b7cbab
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigurationResolver.java
@@ -0,0 +1,255 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.analysis.config;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
+import com.google.devtools.build.lib.packages.Attribute.Configurator;
+import com.google.devtools.build.lib.packages.Attribute.SplitTransition;
+import com.google.devtools.build.lib.packages.Attribute.Transition;
+import com.google.devtools.build.lib.packages.InputFile;
+import com.google.devtools.build.lib.packages.PackageGroup;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.RuleTransitionFactory;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.util.Preconditions;
+
+/**
+ * Determines which configurations targets should take.
+ *
+ * <p>This is the "generic engine" for configuration selection. It doesn't know anything about
+ * specific rules or their requirements. Rule writers decide those with appropriately placed
+ * {@link PatchTransition} declarations. This class then processes those declarations to determine
+ * final configurations.
+ */
+public final class ConfigurationResolver {
+ private final DynamicTransitionMapper transitionMapper;
+
+ /**
+ * Instantiates this resolver with a helper class that maps non-{@link PatchTransition}s to
+ * {@link PatchTransition}s.
+ */
+ public ConfigurationResolver(DynamicTransitionMapper transitionMapper) {
+ this.transitionMapper = transitionMapper;
+ }
+
+ /**
+ * Given a parent rule and configuration depending on a child through an attribute, determines
+ * the configuration the child should take.
+ *
+ * @param fromConfig the parent rule's configuration
+ * @param fromRule the parent rule
+ * @param attribute the attribute creating the dependency (e.g. "srcs")
+ * @param toTarget the child target (which may or may not be a rule)
+ *
+ * @return the child's configuration, expressed as a diff from the parent's configuration. This
+ * is usually a {@PatchTransition} but exceptions apply (e.g.
+ * {@link Attribute.ConfigurationTransition}).
+ */
+ public Transition evaluateTransition(BuildConfiguration fromConfig, final Rule fromRule,
+ final Attribute attribute, final Target toTarget) {
+
+ // I. Input files and package groups have no configurations. We don't want to duplicate them.
+ if (usesNullConfiguration(toTarget)) {
+ return Attribute.ConfigurationTransition.NULL;
+ }
+
+ // II. Host configurations never switch to another. All prerequisites of host targets have the
+ // same host configuration.
+ if (fromConfig.isHostConfiguration()) {
+ return Attribute.ConfigurationTransition.NONE;
+ }
+
+ // Make sure config_setting dependencies are resolved in the referencing rule's configuration,
+ // unconditionally. For example, given:
+ //
+ // genrule(
+ // name = 'myrule',
+ // tools = select({ '//a:condition': [':sometool'] })
+ //
+ // all labels in "tools" get resolved in the host configuration (since the "tools" attribute
+ // declares a host configuration transition). We want to explicitly exclude configuration labels
+ // from these transitions, since their *purpose* is to do computation on the owning
+ // rule's configuration.
+ // TODO(bazel-team): don't require special casing here. This is far too hackish.
+ if (toTarget instanceof Rule && ((Rule) toTarget).getRuleClassObject().isConfigMatcher()) {
+ // TODO(gregce): see if this actually gets called
+ return Attribute.ConfigurationTransition.NONE;
+ }
+
+ // The current transition to apply. When multiple transitions are requested, this is a
+ // ComposingSplitTransition, which encapsulates them into a single object so calling code
+ // doesn't need special logic for combinations.
+ Transition currentTransition = Attribute.ConfigurationTransition.NONE;
+
+ // Apply the parent rule's outgoing transition if it has one.
+ RuleTransitionFactory transitionFactory =
+ fromRule.getRuleClassObject().getOutgoingTransitionFactory();
+ if (transitionFactory != null) {
+ Transition transition = transitionFactory.buildTransitionFor(toTarget.getAssociatedRule());
+ if (transition != null) {
+ currentTransition = composeTransitions(currentTransition, transition);
+ }
+ }
+
+ // TODO(gregce): make the below transitions composable (i.e. take away the "else" clauses).
+ // The "else" is a legacy restriction from static configurations.
+ if (attribute.hasSplitConfigurationTransition()) {
+ Preconditions.checkState(attribute.getConfigurator() == null);
+ currentTransition = split(currentTransition,
+ (SplitTransition<BuildOptions>) attribute.getSplitTransition(fromRule));
+ } else {
+ // III. Attributes determine configurations. The configuration of a prerequisite is determined
+ // by the attribute.
+ @SuppressWarnings("unchecked")
+ Configurator<BuildOptions> configurator =
+ (Configurator<BuildOptions>) attribute.getConfigurator();
+ if (configurator != null) {
+ // TODO(gregce): attribute configurators are a holdover from static configurations. Remove
+ // them and remove this branch.
+ currentTransition = applyAttributeConfigurator(currentTransition, configurator);
+ } else {
+ currentTransition = composeTransitions(currentTransition,
+ attribute.getConfigurationTransition());
+ }
+ }
+
+ return applyConfigurationHook(currentTransition, toTarget);
+ }
+
+ /**
+ * Returns true if the given target should have a null configuration. This method is the
+ * "source of truth" for this determination.
+ */
+ public static boolean usesNullConfiguration(Target target) {
+ return target instanceof InputFile || target instanceof PackageGroup;
+ }
+
+ /**
+ * Composes two transitions together efficiently.
+ */
+ @VisibleForTesting
+ public Transition composeTransitions(Transition transition1, Transition transition2) {
+ if (isFinal(transition1)) {
+ return transition1;
+ } else if (transition2 == Attribute.ConfigurationTransition.NONE) {
+ return transition1;
+ } else if (transition2 == Attribute.ConfigurationTransition.NULL) {
+ // A NULL transition can just replace earlier transitions: no need to cfpose them.
+ return Attribute.ConfigurationTransition.NULL;
+ } else if (transition2 == Attribute.ConfigurationTransition.HOST) {
+ // A HOST transition can just replace earlier transitions: no need to compose them.
+ // But it also improves performance: host transitions are common, and
+ // ConfiguredTargetFunction has special optimized logic to handle them. If they were buried
+ // in the last segment of a ComposingSplitTransition, those optimizations wouldn't trigger.
+ return HostTransition.INSTANCE;
+ }
+
+ // TODO(gregce): remove the below conversion when all transitions are patch transitions.
+ Transition dynamicTransition = transitionMapper.map(transition2);
+ return transition1 == Attribute.ConfigurationTransition.NONE
+ ? dynamicTransition
+ : new ComposingSplitTransition(transition1, dynamicTransition);
+ }
+
+ /**
+ * Returns true if once the given transition is applied to a dep no followup transitions should
+ * be composed after it.
+ */
+ private static boolean isFinal(Transition transition) {
+ return (transition == Attribute.ConfigurationTransition.NULL
+ || transition == HostTransition.INSTANCE);
+ }
+
+ /**
+ * Applies the given split and composes it after an existing transition.
+ */
+ private static Transition split(Transition currentTransition,
+ SplitTransition<BuildOptions> split) {
+ Preconditions.checkState(currentTransition != Attribute.ConfigurationTransition.NULL,
+ "cannot apply splits after null transitions (null transitions are expected to be final)");
+ Preconditions.checkState(currentTransition != HostTransition.INSTANCE,
+ "cannot apply splits after host transitions (host transitions are expected to be final)");
+ return currentTransition == Attribute.ConfigurationTransition.NONE
+ ? split
+ : new ComposingSplitTransition(currentTransition, split);
+ }
+
+ /**
+ * Applies the given attribute configurator and composes it after an existing transition.
+ */
+ @VisibleForTesting
+ public Transition applyAttributeConfigurator(Transition currentTransition,
+ Configurator<BuildOptions> configurator) {
+ if (isFinal(currentTransition)) {
+ return currentTransition;
+ }
+ return composeTransitions(currentTransition, new AttributeConfiguratorTransition(configurator));
+ }
+
+ /**
+ * A {@link PatchTransition} that applies an attribute configurator over some input options
+ * to determine which transition to use, then applies that transition over those options
+ * for the final output.
+ */
+ private static final class AttributeConfiguratorTransition implements PatchTransition {
+ private final Configurator<BuildOptions> configurator;
+
+ AttributeConfiguratorTransition(Configurator<BuildOptions> configurator) {
+ this.configurator = configurator;
+ }
+
+ @Override
+ public BuildOptions apply(BuildOptions options) {
+ return Iterables.getOnlyElement(
+ ComposingSplitTransition.apply(options, configurator.apply(options)));
+ }
+
+ @Override
+ public boolean defaultsToSelf() {
+ return false;
+ }
+ }
+
+ /**
+ * Applies any configuration hooks associated with the dep target, composes their transitions
+ * after an existing transition, and returns the composed result.
+ */
+ private Transition applyConfigurationHook(Transition currentTransition, Target toTarget) {
+ if (isFinal(currentTransition)) {
+ return currentTransition;
+ }
+ Rule associatedRule = toTarget.getAssociatedRule();
+ RuleTransitionFactory transitionFactory =
+ associatedRule.getRuleClassObject().getTransitionFactory();
+ if (transitionFactory != null) {
+ // transitionMapper is only needed because of Attribute.ConfigurationTransition.DATA: this is
+ // C++-specific but non-C++ rules declare it. So they can't directly provide the C++-specific
+ // patch transition that implements it.
+ PatchTransition ruleClassTransition = (PatchTransition)
+ transitionMapper.map(transitionFactory.buildTransitionFor(associatedRule));
+ if (ruleClassTransition != null) {
+ if (currentTransition == ConfigurationTransition.NONE) {
+ return ruleClassTransition;
+ } else {
+ return new ComposingSplitTransition(currentTransition, ruleClassTransition);
+ }
+ }
+ }
+ return currentTransition;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/DynamicTransitionMapper.java b/src/main/java/com/google/devtools/build/lib/analysis/config/DynamicTransitionMapper.java
index 9cd4077100..66d0cf90af 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/DynamicTransitionMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/DynamicTransitionMapper.java
@@ -74,7 +74,9 @@ public final class DynamicTransitionMapper {
* {@link IllegalArgumentException}.
*/
public Transition map(Transition fromTransition) {
- if (fromTransition instanceof PatchTransition || fromTransition == null) {
+ if (fromTransition instanceof PatchTransition
+ || fromTransition instanceof Attribute.SplitTransition<?>
+ || fromTransition == null) {
return fromTransition;
}
Transition toTransition = map.get(fromTransition);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
index 5f109739b4..06e4c9b687 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
@@ -101,7 +101,6 @@ public class CppRuleClasses {
ImmutableMap.of(
Attribute.ConfigurationTransition.DATA, DisableLipoTransition.INSTANCE,
LipoTransition.LIPO_COLLECTOR, LipoContextCollectorTransition.INSTANCE
- // TARGET_CONFIG_FOR_LIPO has no entry because only static configurations use it.
);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java
index 266b320fe7..e335ceaa71 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java
@@ -76,8 +76,7 @@ public class BuildConfigurationFunction implements SkyFunction {
directories,
fragmentsMap,
key.getBuildOptions(),
- workspaceNameValue.getName(),
- ruleClassProvider.getDynamicTransitionMapper());
+ workspaceNameValue.getName());
return new BuildConfigurationValue(config);
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
index 90773dc07d..f05c5a424b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
@@ -54,7 +54,6 @@ import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.Package;
-import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.pkgcache.LoadingFailureEvent;
import com.google.devtools.build.lib.pkgcache.LoadingPhaseRunner;
@@ -108,7 +107,7 @@ public final class SkyframeBuildView {
private Set<SkyKey> dirtiedConfiguredTargetKeys = Sets.newConcurrentHashSet();
private volatile boolean anyConfiguredTargetDeleted = false;
- private final RuleClassProvider ruleClassProvider;
+ private final ConfiguredRuleClassProvider ruleClassProvider;
// The host configuration containing all fragments used by this build's transitive closure.
private BuildConfiguration topLevelHostConfiguration;
@@ -554,7 +553,7 @@ public final class SkyframeBuildView {
}
SkyframeDependencyResolver createDependencyResolver(Environment env) {
- return new SkyframeDependencyResolver(env);
+ return new SkyframeDependencyResolver(env, ruleClassProvider.getDynamicTransitionMapper());
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeDependencyResolver.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeDependencyResolver.java
index 965c3fb4d0..0a6e9a702d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeDependencyResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeDependencyResolver.java
@@ -18,6 +18,7 @@ import com.google.devtools.build.lib.analysis.DependencyResolver;
import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.analysis.config.DynamicTransitionMapper;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
@@ -44,7 +45,8 @@ public final class SkyframeDependencyResolver extends DependencyResolver {
private final Environment env;
- public SkyframeDependencyResolver(Environment env) {
+ public SkyframeDependencyResolver(Environment env, DynamicTransitionMapper transitionMapper) {
+ super(transitionMapper);
this.env = env;
}