aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java22
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java220
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/Attribute.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java98
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java19
5 files changed, 188 insertions, 175 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index b429606b48..1bd77b5719 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -19,6 +19,7 @@ import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
+import com.google.common.base.Verify;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
@@ -43,6 +44,7 @@ import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildInfoKey;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
+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.FragmentCollection;
import com.google.devtools.build.lib.cmdline.Label;
@@ -704,26 +706,28 @@ public final class RuleContext extends TargetContext
checkAttribute(attributeName, Mode.SPLIT);
Attribute attributeDefinition = getAttribute(attributeName);
- SplitTransition<?> transition = attributeDefinition.getSplitTransition(rule);
- List<BuildConfiguration> configurations =
- getConfiguration().getTransitions().getSplitConfigurationsNoSelf(transition);
- if (configurations.isEmpty()) {
+ @SuppressWarnings("unchecked") // Attribute.java doesn't have the BuildOptions symbol.
+ SplitTransition<BuildOptions> transition =
+ (SplitTransition<BuildOptions>) attributeDefinition.getSplitTransition(rule);
+ List<ConfiguredTarget> deps = targetMap.get(attributeName);
+
+ List<BuildOptions> splitOptions = transition.split(getConfiguration().getOptions());
+ if (splitOptions.isEmpty()) {
// The split transition is not active. Defer the decision on which CPU to use.
- return ImmutableMap.of(Optional.<String>absent(), targetMap.get(attributeName));
+ return ImmutableMap.of(Optional.<String>absent(), deps);
}
Set<String> cpus = new HashSet<>();
- for (BuildConfiguration config : configurations) {
+ for (BuildOptions options : splitOptions) {
// This method should only be called when the split config is enabled on the command line, in
// which case this cpu can't be null.
- Preconditions.checkNotNull(config.getCpu());
- cpus.add(config.getCpu());
+ cpus.add(Verify.verifyNotNull(options.get(BuildConfiguration.Options.class).getCpu()));
}
// Use an ImmutableListMultimap.Builder here to preserve ordering.
ImmutableListMultimap.Builder<Optional<String>, TransitiveInfoCollection> result =
ImmutableListMultimap.builder();
- for (TransitiveInfoCollection t : targetMap.get(attributeName)) {
+ for (TransitiveInfoCollection t : deps) {
if (t.getConfiguration() != null) {
result.put(Optional.of(t.getConfiguration().getCpu()), t);
} else {
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 d5836c6d42..27b0d093d1 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
@@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.MutableClassToInstanceMap;
@@ -1499,12 +1500,6 @@ public final class BuildConfiguration {
void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget);
/**
- * Returns the underlying {@Transitions} object for this instance's current configuration.
- * Does not work for split configurations.
- */
- Transitions getCurrentTransitions();
-
- /**
* 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.
@@ -1517,14 +1512,16 @@ public final class BuildConfiguration {
* {@link com.google.devtools.build.lib.analysis.Dependency} objects with
* actual configurations.
*
- * <p>Does not support split transitions (see {@link SplittableTransitionApplier}).
* TODO(bazel-team): remove this when dynamic configurations are fully production-ready.
*/
private static class StaticTransitionApplier implements TransitionApplier {
- BuildConfiguration currentConfiguration;
+ // 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.currentConfiguration = originalConfiguration;
+ this.toConfigurations = ImmutableList.<BuildConfiguration>of(originalConfiguration);
}
@Override
@@ -1535,72 +1532,95 @@ public final class BuildConfiguration {
@Override
public void applyTransition(Transition transition) {
if (transition == Attribute.ConfigurationTransition.NULL) {
- currentConfiguration = null;
+ toConfigurations = Lists.<BuildConfiguration>asList(null, new BuildConfiguration[0]);
} else {
- currentConfiguration =
- currentConfiguration.getTransitions().getStaticConfiguration(transition);
+ ImmutableList.Builder<BuildConfiguration> newConfigs = ImmutableList.builder();
+ for (BuildConfiguration currentConfig : toConfigurations) {
+ newConfigs.add(currentConfig.getTransitions().getStaticConfiguration(transition));
+ }
+ toConfigurations = newConfigs.build();
}
}
@Override
public void split(SplitTransition<?> splitTransition) {
- throw new UnsupportedOperationException("This only works with SplittableTransitionApplier");
+ // Split transitions can't be nested, so if we're splitting we must be doing it over
+ // a single config.
+ toConfigurations =
+ Iterables.getOnlyElement(toConfigurations).getSplitConfigurations(splitTransition);
}
@Override
public boolean isNull() {
- return currentConfiguration == null;
+ return toConfigurations.size() == 1
+ ? Iterables.getOnlyElement(toConfigurations) == null
+ : false;
}
@Override
public void applyAttributeConfigurator(Attribute attribute, Rule fromRule, Target toTarget) {
+ // Checks that evaluateTransition never applies an attribute configurator and split
+ // transition in the same call.
+ Verify.verify(toConfigurations.size() == 1);
@SuppressWarnings("unchecked")
Configurator<BuildConfiguration, Rule> configurator =
(Configurator<BuildConfiguration, Rule>) attribute.getConfigurator();
Verify.verifyNotNull(configurator);
- currentConfiguration =
- configurator.apply(fromRule, currentConfiguration, attribute, toTarget);
+ toConfigurations = ImmutableList.<BuildConfiguration>of(
+ configurator.apply(fromRule, Iterables.getOnlyElement(toConfigurations), attribute,
+ toTarget));
}
@Override
public void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget) {
- currentConfiguration.getTransitions().configurationHook(fromRule, attribute, toTarget, this);
+ ImmutableList.Builder<BuildConfiguration> toConfigs = ImmutableList.builder();
+ for (BuildConfiguration currentConfig : toConfigurations) {
+ // BuildConfigurationCollection.configurationHook can apply further transitions. We want
+ // those transitions to only affect currentConfig (not everything in toConfigurations), so
+ // we use a delegate bound to only that config.
+ StaticTransitionApplier delegate = new StaticTransitionApplier(currentConfig);
+ currentConfig.getTransitions().configurationHook(fromRule, attribute, toTarget, delegate);
+ currentConfig = Iterables.getOnlyElement(delegate.toConfigurations);
+
+ // Allow rule classes to override their own configurations.
+ Rule associatedRule = toTarget.getAssociatedRule();
+ if (associatedRule != null) {
+ @SuppressWarnings("unchecked")
+ RuleClass.Configurator<BuildConfiguration, Rule> func =
+ associatedRule.getRuleClassObject().<BuildConfiguration, Rule>getConfigurator();
+ currentConfig = func.apply(associatedRule, currentConfig);
+ }
- // Allow rule classes to override their own configurations.
- Rule associatedRule = toTarget.getAssociatedRule();
- if (associatedRule != null) {
- @SuppressWarnings("unchecked")
- RuleClass.Configurator<BuildConfiguration, Rule> func =
- associatedRule.getRuleClassObject().<BuildConfiguration, Rule>getConfigurator();
- currentConfiguration = func.apply(associatedRule, currentConfiguration);
+ toConfigs.add(currentConfig);
}
- }
-
- @Override
- public Transitions getCurrentTransitions() {
- return currentConfiguration.getTransitions();
+ toConfigurations = toConfigs.build();
}
@Override
public Iterable<Dependency> getDependencies(
Label label, ImmutableSet<AspectDescriptor> aspects) {
- return ImmutableList.of(
- currentConfiguration != null
- ? Dependency.withConfigurationAndAspects(label, currentConfiguration, aspects)
- : Dependency.withNullConfiguration(label));
+ 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
- * transition definitions that the caller subsequently creates configurations out of.
- *
- * <p>Does not support split transitions (see {@link SplittableTransitionApplier}).
+ * transitions that the caller subsequently creates configurations from.
*/
private static class DynamicTransitionApplier implements TransitionApplier {
private final BuildConfiguration originalConfiguration;
- private Transition transition = Attribute.ConfigurationTransition.NONE;
+ // The transition this applier applies to dep rules. May change multiple times. However,
+ // composed transitions (e.g. fromConfig -> FooTransition -> BarTransition) are not currently
+ // supported, in the name of keeping the model simple. We can always revisit that assumption
+ // if needed.
+ private Transition currentTransition = Attribute.ConfigurationTransition.NONE;
private DynamicTransitionApplier(BuildConfiguration originalConfiguration) {
this.originalConfiguration = originalConfiguration;
@@ -1612,37 +1632,42 @@ public final class BuildConfiguration {
}
@Override
- public void applyTransition(Transition transition) {
- if (transition == Attribute.ConfigurationTransition.NONE) {
+ public void applyTransition(Transition transitionToApply) {
+ if (transitionToApply == Attribute.ConfigurationTransition.NONE
+ // Outside of LIPO, data transitions are a no-op. Since dynamic configs don't yet support
+ // LIPO, just return fast as a no-op. This isn't just convenient: evaluateTransition
+ // calls configurationHook after standard attribute transitions. If configurationHook
+ // triggers a data transition, that undoes the earlier transitions (because of lack of
+ // composed transition support). That's dangerous and especially pointless for non-LIPO
+ // builds. Hence this check.
+ // TODO(gregce): add LIPO support and/or make this special case unnecessary.
+ || transitionToApply == Attribute.ConfigurationTransition.DATA
+ // This means it's not possible to transition back out of a host transition. We may
+ // need to revise this when we properly support multiple host configurations.
+ || currentTransition == HostTransition.INSTANCE) {
return;
- } else if (this.transition != HostTransition.INSTANCE) {
- // We don't currently support composed transitions (e.g. applyTransitions shouldn't be
- // called multiple times). We can add support for this if needed by simply storing a list of
- // transitions instead of a single transition. But we only want to do that if really
- // necessary - if we can simplify BuildConfiguration's transition logic to not require
- // scenarios like that, it's better to keep this simpler interface.
- //
- // The HostTransition exemption is because of limited cases where composition can
- // occur. See relevant comments beginning with "BuildConfiguration.applyTransition NOTE"
- // in the transition logic code if available.
-
- // Ensure we don't already have any mutating transitions registered.
- // Note that for dynamic configurations, LipoDataTransition is equivalent to NONE. That's
- // because dynamic transitions don't work with LIPO, so there's no LIPO context to change.
- Verify.verify(this.transition == Attribute.ConfigurationTransition.NONE
- || this.transition.toString().contains("LipoDataTransition"));
- this.transition = getCurrentTransitions().getDynamicTransition(transition);
}
+
+ // Since we don't support composed transitions, we need to be careful applying a transition
+ // when another transition has already been applied (the latter will simply overwrite the
+ // former). All allowed cases should be explicitly asserted here.
+ Verify.verify(currentTransition == Attribute.ConfigurationTransition.NONE
+ // LIPO transitions are okay because they're no-ops outside LIPO builds. And dynamic
+ // configs don't yet support LIPO builds.
+ || currentTransition.toString().contains("LipoDataTransition"));
+ currentTransition = getCurrentTransitions().getDynamicTransition(transitionToApply);
}
@Override
public void split(SplitTransition<?> splitTransition) {
- throw new UnsupportedOperationException("This only works with SplittableTransitionApplier");
+ Verify.verify(currentTransition == Attribute.ConfigurationTransition.NONE,
+ "split transitions aren't expected to mix with other transitions");
+ currentTransition = splitTransition;
}
@Override
public boolean isNull() {
- return transition == Attribute.ConfigurationTransition.NULL;
+ return currentTransition == Attribute.ConfigurationTransition.NULL;
}
@Override
@@ -1679,8 +1704,7 @@ public final class BuildConfiguration {
}
}
- @Override
- public Transitions getCurrentTransitions() {
+ private Transitions getCurrentTransitions() {
return originalConfiguration.getTransitions();
}
@@ -1688,78 +1712,7 @@ public final class BuildConfiguration {
public Iterable<Dependency> getDependencies(
Label label, ImmutableSet<AspectDescriptor> aspects) {
return ImmutableList.of(
- Dependency.withTransitionAndAspects(label, transition, aspects));
- }
- }
-
- /**
- * Transition applier that wraps an underlying implementation with added support for
- * split transitions. All external calls into BuildConfiguration should use this applier.
- */
- private static class SplittableTransitionApplier implements TransitionApplier {
- private List<TransitionApplier> appliers;
-
- private SplittableTransitionApplier(TransitionApplier original) {
- appliers = ImmutableList.of(original);
- }
-
- @Override
- public TransitionApplier create(BuildConfiguration configuration) {
- throw new UnsupportedOperationException("Not intended to be wrapped under another applier");
- }
-
- @Override
- public void applyTransition(Transition transition) {
- for (TransitionApplier applier : appliers) {
- applier.applyTransition(transition);
- }
- }
-
- @Override
- public void split(SplitTransition<?> splitTransition) {
- TransitionApplier originalApplier = Iterables.getOnlyElement(appliers);
- ImmutableList.Builder<TransitionApplier> splitAppliers = ImmutableList.builder();
- for (BuildConfiguration splitConfig :
- originalApplier.getCurrentTransitions().getSplitConfigurations(splitTransition)) {
- splitAppliers.add(originalApplier.create(splitConfig));
- }
- appliers = splitAppliers.build();
- }
-
- @Override
- public boolean isNull() {
- throw new UnsupportedOperationException("Only for use from a Transitions instance");
- }
-
-
- @Override
- public void applyAttributeConfigurator(Attribute attribute, Rule fromRule, Target toTarget) {
- for (TransitionApplier applier : appliers) {
- applier.applyAttributeConfigurator(attribute, fromRule, toTarget);
- }
- }
-
- @Override
- public void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget) {
- for (TransitionApplier applier : appliers) {
- applier.applyConfigurationHook(fromRule, attribute, toTarget);
- }
- }
-
- @Override
- public Transitions getCurrentTransitions() {
- throw new UnsupportedOperationException("Only for use from a Transitions instance");
- }
-
-
- @Override
- public Iterable<Dependency> getDependencies(
- Label label, ImmutableSet<AspectDescriptor> aspects) {
- ImmutableList.Builder<Dependency> builder = ImmutableList.builder();
- for (TransitionApplier applier : appliers) {
- builder.addAll(applier.getDependencies(label, aspects));
- }
- return builder.build();
+ Dependency.withTransitionAndAspects(label, currentTransition, aspects));
}
}
@@ -1767,10 +1720,9 @@ public final class BuildConfiguration {
* Returns the {@link TransitionApplier} that should be passed to {#evaluateTransition} calls.
*/
public TransitionApplier getTransitionApplier() {
- TransitionApplier applier = useDynamicConfigurations()
+ return useDynamicConfigurations()
? new DynamicTransitionApplier(this)
: new StaticTransitionApplier(this);
- return new SplittableTransitionApplier(applier);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
index 9088840bd5..f2fb3355e9 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
@@ -149,6 +149,9 @@ public final class Attribute implements Comparable<Attribute> {
* A configuration split transition; this should be used to transition to multiple configurations
* simultaneously. Note that the corresponding rule implementations must have special support to
* handle this.
+ *
+ * <p>{@code T} must always be {@code BuildOptions}, but it can't be defined that way because
+ * the symbol isn't available here.
*/
// TODO(bazel-team): Serializability constraints?
public interface SplitTransition<T> extends Transition {
@@ -1476,6 +1479,7 @@ public final class Attribute implements Comparable<Attribute> {
* Returns the split configuration transition for this attribute.
*
* @param rule the originating {@link Rule} which owns this attribute
+ * @return a SplitTransition<BuildOptions> object
* @throws IllegalStateException if {@link #hasSplitConfigurationTransition} is not true
*/
public SplitTransition<?> getSplitTransition(Rule rule) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
index b9f839c977..1e4d11466e 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -77,9 +77,9 @@ import com.google.devtools.build.skyframe.ValueOrException;
import com.google.devtools.build.skyframe.ValueOrException2;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
@@ -396,11 +396,12 @@ final class ConfiguredTargetFunction implements SkyFunction {
}
/**
- * Variation of {@link Map#put} that triggers an exception if another value already exists.
+ * Variation of {@link Multimap#put} that triggers an exception if a value already exists.
*/
- private static <K, V> void putOnlyEntry(Map<K, V> map, K key, V value) {
- Verify.verify(map.put(key, value) == null,
+ private static <K, V> void putOnlyEntry(Multimap<K, V> map, K key, V value) {
+ Verify.verify(!map.containsKey(key),
"couldn't insert %s: map already has key %s", value.toString(), key.toString());
+ map.put(key, value);
}
/**
@@ -432,7 +433,9 @@ final class ConfiguredTargetFunction implements SkyFunction {
// particular subset of fragments. By caching this, we save from redundantly computing the
// same transition for every dependency edge that requests that transition. This can have
// real effect on analysis time for commonly triggered transitions.
- Map<FragmentsAndTransition, BuildOptions> transitionsMap = new HashMap<>();
+ //
+ // Split transitions may map to multiple values. All other transitions map to one.
+ Map<FragmentsAndTransition, List<BuildOptions>> transitionsMap = new LinkedHashMap<>();
// The fragments used by the current target's configuration.
Set<Class<? extends BuildConfiguration.Fragment>> ctgFragments =
@@ -445,7 +448,7 @@ final class ConfiguredTargetFunction implements SkyFunction {
// the results in order (some results need Skyframe-evaluated configurations while others can
// be computed trivially), we dump them all into this map, then as a final step iterate through
// the original list and pluck out values from here for the final value.
- Map<AttributeAndLabel, Dependency> trimmedDeps = new HashMap<>();
+ Multimap<AttributeAndLabel, Dependency> trimmedDeps = ArrayListMultimap.create();
for (Map.Entry<Attribute, Dependency> depsEntry : originalDeps.entries()) {
Dependency dep = depsEntry.getValue();
@@ -455,7 +458,7 @@ final class ConfiguredTargetFunction implements SkyFunction {
if (dep.hasStaticConfiguration()) {
// Certain targets (like output files and late-bound splits) trivially pass their
// configurations to their deps. So no need to transform them in any way.
- putOnlyEntry(trimmedDeps, attributeAndLabel, dep);
+ trimmedDeps.put(attributeAndLabel, dep);
continue;
} else if (dep.getTransition() == Attribute.ConfigurationTransition.NULL) {
putOnlyEntry(
@@ -508,26 +511,24 @@ final class ConfiguredTargetFunction implements SkyFunction {
// Apply the transition or use the cached result if it was already applied.
FragmentsAndTransition transitionKey = new FragmentsAndTransition(depFragments, transition);
- BuildOptions toOptions = transitionsMap.get(transitionKey);
+ List<BuildOptions> toOptions = transitionsMap.get(transitionKey);
if (toOptions == null) {
- Verify.verify(transition == Attribute.ConfigurationTransition.NONE
- || transition instanceof PatchTransition);
- BuildOptions fromOptions = ctgOptions;
- // TODO(bazel-team): safety-check that the below call never mutates fromOptions.
- toOptions = transition == Attribute.ConfigurationTransition.NONE
- ? fromOptions
- : ((PatchTransition) transition).apply(fromOptions);
- if (!sameFragments) {
- // TODO(bazel-team): pre-compute getOptionsClasses in the constructor.
- toOptions = toOptions.trim(BuildConfiguration.getOptionsClasses(
- transitiveDepInfo.getTransitiveConfigFragments(), ruleClassProvider));
+ ImmutableList.Builder<BuildOptions> toOptionsBuilder = ImmutableList.builder();
+ for (BuildOptions options : getDynamicTransitionOptions(ctgOptions, transition)) {
+ if (!sameFragments) {
+ options = options.trim(BuildConfiguration.getOptionsClasses(
+ transitiveDepInfo.getTransitiveConfigFragments(), ruleClassProvider));
+ }
+ toOptionsBuilder.add(options);
}
+ toOptions = toOptionsBuilder.build();
transitionsMap.put(transitionKey, toOptions);
}
// If the transition doesn't change the configuration, trivially re-use the original
// configuration.
- if (sameFragments && toOptions.equals(ctgOptions)) {
+ if (sameFragments && toOptions.size() == 1
+ && Iterables.getOnlyElement(toOptions).equals(ctgOptions)) {
putOnlyEntry(
trimmedDeps,
attributeAndLabel,
@@ -537,7 +538,9 @@ final class ConfiguredTargetFunction implements SkyFunction {
}
// If we get here, we have to get the configuration from Skyframe.
- keysToEntries.put(BuildConfigurationValue.key(depFragments, toOptions), depsEntry);
+ for (BuildOptions options : toOptions) {
+ keysToEntries.put(BuildConfigurationValue.key(depFragments, options), depsEntry);
+ }
}
// Get all BuildConfigurations we need to get from Skyframe.
@@ -555,11 +558,14 @@ final class ConfiguredTargetFunction implements SkyFunction {
BuildConfigurationValue trimmedConfig = (BuildConfigurationValue) entry.getValue().get();
for (Map.Entry<Attribute, Dependency> info : keysToEntries.get(key)) {
Dependency originalDep = info.getValue();
- putOnlyEntry(trimmedDeps, new AttributeAndLabel(info.getKey(), originalDep.getLabel()),
- Dependency.withConfigurationAndAspects(
- originalDep.getLabel(),
- trimmedConfig.getConfiguration(),
- originalDep.getAspects()));
+ AttributeAndLabel attr = new AttributeAndLabel(info.getKey(), originalDep.getLabel());
+ Dependency resolvedDep = Dependency.withConfigurationAndAspects(originalDep.getLabel(),
+ trimmedConfig.getConfiguration(), originalDep.getAspects());
+ if (originalDep.getTransition() instanceof Attribute.SplitTransition) {
+ trimmedDeps.put(attr, resolvedDep);
+ } else {
+ putOnlyEntry(trimmedDeps, attr, resolvedDep);
+ }
}
}
} catch (InvalidConfigurationException e) {
@@ -570,15 +576,47 @@ final class ConfiguredTargetFunction implements SkyFunction {
// appear in the same order) as the input.
ListMultimap<Attribute, Dependency> result = ArrayListMultimap.create();
for (Map.Entry<Attribute, Dependency> depsEntry : originalDeps.entries()) {
- Dependency trimmedDep = Verify.verifyNotNull(
- trimmedDeps.get(
- new AttributeAndLabel(depsEntry.getKey(), depsEntry.getValue().getLabel())));
- result.put(depsEntry.getKey(), trimmedDep);
+ Collection<Dependency> trimmedAttrDeps = trimmedDeps.get(
+ new AttributeAndLabel(depsEntry.getKey(), depsEntry.getValue().getLabel()));
+ Verify.verify(!trimmedAttrDeps.isEmpty());
+ result.putAll(depsEntry.getKey(), trimmedAttrDeps);
}
return result;
}
/**
+ * Applies a dynamic configuration transition over a set of build options.
+ *
+ * @return the build options for the transitioned configuration. Contains the same fragment
+ * options as the input.
+ */
+ private static Collection<BuildOptions> getDynamicTransitionOptions(BuildOptions fromOptions,
+ Attribute.Transition transition) {
+ if (transition == Attribute.ConfigurationTransition.NONE) {
+ return ImmutableList.<BuildOptions>of(fromOptions);
+ } else if (transition instanceof PatchTransition) {
+ // TODO(bazel-team): safety-check that this never mutates fromOptions.
+ return ImmutableList.<BuildOptions>of(((PatchTransition) transition).apply(fromOptions));
+ } else if (transition instanceof Attribute.SplitTransition) {
+ @SuppressWarnings("unchecked") // Attribute.java doesn't have the BuildOptions symbol.
+ List<BuildOptions> toOptions =
+ ((Attribute.SplitTransition<BuildOptions>) transition).split(fromOptions);
+ if (toOptions.isEmpty()) {
+ // When the split returns an empty list, it's signaling it doesn't apply to this instance.
+ // Check that it's safe to skip the transition and return the original options.
+ Verify.verify(transition.defaultsToSelf());
+ return ImmutableList.<BuildOptions>of(fromOptions);
+ } else {
+ return toOptions;
+ }
+ } else {
+ throw new IllegalStateException(String.format(
+ "unsupported dynamic transition type: %s", transition.getClass().getName()));
+ }
+ }
+
+
+ /**
* Diagnostic helper method for dynamic configurations: checks the config fragments required by
* a dep against the fragments in its actual configuration. If any are missing, triggers a
* descriptive "missing fragments" error.
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index bb630a3c55..4fdde18aab 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -173,6 +173,7 @@ public abstract class BuildViewTestCase extends FoundationTestCase {
protected BuildConfigurationCollection masterConfig;
protected BuildConfiguration targetConfig; // "target" or "build" config
private List<String> configurationArgs;
+ private boolean useDynamicConfigs;
protected OptionsParser optionsParser;
private PackageCacheOptions packageCacheOptions;
@@ -381,13 +382,27 @@ public abstract class BuildViewTestCase extends FoundationTestCase {
* @throws IllegalArgumentException
*/
protected final void useConfiguration(String... args) throws Exception {
- masterConfig = createConfigurations(args);
+ String[] actualArgs;
+ if (useDynamicConfigs) {
+ actualArgs = Arrays.copyOf(args, args.length + 1);
+ actualArgs[args.length] = "--experimental_dynamic_configs";
+ } else {
+ actualArgs = args;
+ }
+ masterConfig = createConfigurations(actualArgs);
targetConfig = getTargetConfiguration();
- configurationArgs = Arrays.asList(args);
+ configurationArgs = Arrays.asList(actualArgs);
createBuildView();
}
/**
+ * Makes subsequent {@link #useConfiguration} calls automatically enable dynamic configurations.
+ */
+ protected final void useDynamicConfigurations() {
+ useDynamicConfigs = true;
+ }
+
+ /**
* Creates BuildView using current hostConfig/targetConfig values.
* Ensures that hostConfig is either identical to the targetConfig or has
* 'host' short name.