aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/skyframe
diff options
context:
space:
mode:
authorGravatar Greg Estren <gregce@google.com>2016-08-05 21:07:02 +0000
committerGravatar Yue Gan <yueg@google.com>2016-08-08 08:08:13 +0000
commit66cadd3d1c6014799616ba22f9d1381a82006c48 (patch)
treeb12a01b53e88aeb239ad29c1c37f990e7704a322 /src/main/java/com/google/devtools/build/lib/skyframe
parent57472638ce94796deabc3d3db4ba851a1d0d600f (diff)
Implements dynamic split transitions (minus latebound attribute splits).
With the prereq work behind this, this is surprisingly straightforward. The main change is to eliminate BuildConfiguration.SplittableTransitionApplier, make both DynamicTransitionApplier and StaticTransitionApplier split-aware, and add awareness of this to ConfiguredTargetFunction.trimConfigurations. Latebound splits will follow next. -- MOS_MIGRATED_REVID=129480309
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/skyframe')
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java98
1 files changed, 68 insertions, 30 deletions
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.