aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java
diff options
context:
space:
mode:
authorGravatar Greg Estren <gregce@google.com>2015-08-25 16:43:47 +0000
committerGravatar Lukacs Berki <lberki@google.com>2015-08-26 07:40:12 +0000
commit000494327d4efab63362f3a940db74ffa8b9d712 (patch)
tree9a883bd60f9297d1d9582b45cbb4d289572349b6 /src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java
parentd4ce9af5c70780a356e221da0a85a22b7a230440 (diff)
Implement the core structure for dynamic configurations.
This is a big change, so let me walk you through the key pieces: 1) This cl provides an alternative mechanism for creating configurations and doing configuration transitions that's "dynamic" in that the configurations can be created on the fly and the transitions are arbitrary mappings from BuildOptions --> BuildOptions that can also be created on the fly. It also integrates this with ConfiguredTargetFunction, so the configured target graph automatically uses this framework. 2) It does *not* replace old-style (which we'll call "static") configurations. For a number of important reasons: It's not yet at feature parity (particularly: no LIPO). It's not remotely tested across real projects enough to have confidence that it's battle-ready. It doesn't yet handle certain "special" functions like BuildConfiguration.prepareToBuild() and BuildConfiguration.getRoots(). It still relies on the old static transition logic to determine what transitions to apply (eventually we'll distribute that logic out, but that's too much for a single cl). We need the option to toggle it on and off until we have enough confidence in it. So with this cl, builds can be done in either mode. 3) The new flag --experimental_dynamic_configs toggles use of dynamic configurations. 4) Dynamic configurations are created with the Skyframe function BuildConfigurationFunction (this was already created in an earlier change). This consumes a BuildOptions and a set of configuration fragments to produce a BuildConfiguration. 5) Dynamic transitions are instances of the new class PatchTransition, which simply maps an input BuildOptions to an output BuildOptions. 6) Since static and dynamic configurations have to co-exist (for now), this cl tries hard to keep today's transition logic defined in a single place (vs. forking a dedicated copy for each configuration style). This is done via the new interface BuildConfiguration.TransitionApplier. BuildConfiguration.evaluateTransition is modified to feed its transition logic into TransitionApplier's common API. Both dynamic and static configurations have their own implementations that "do the right thing" with the results. 7) The transition applier for dynamic configurations basically stores the Transition, then allows DependencyResolver (which calls BuildConfiguration.evaluateTransition) to return Dependency instances containing that Transition (vs. a BuildConfiguration, which they traditionally contain). 7.5) An earlier variation of the dynamic transition applier retained BuildOptions (e.g. when it got a Transition it immediately applied it to get its output BuildOptions, then stored that). This had the advantage of making composing of transitions easier, especially within BuildConfiguration.evaluateTransition (which can theoretically apply multiple transitions to the input configuration). But it turns out that applying transitions has a cost, and it's simply more performant to pass them through until they're really needed. 8) In dynamic configuration mode, after ConfiguredTargetFunction gets its deps (e.g. an <Attribute, Dependency> multimap), it "trims" the configurations for its dependencies by a) only including config fragments required by the deps' subtrees and b) applying the transitions that came from 7). This all happens in the new method ConfiguredTargetFunction.trimConfigurations. 9) trimConfigurations is heavily performance-optimized based on a lot of experience running this through a large project within Google. As it turns out, the cost of host transitions can be atrocious (because there are a lot of them). Also, BuildOptions.clone() is expensive. And just creating BuildConfiguration SkyKeys also has a cost (largely because of BuildOptions cloning), so that shouldn't be done except when really necessary. My experience with this convinced me it's worth making this method complicated for the sake of making it fast. Since it basically visits every edge in the configured target graph (at least), it really needs to be slick. 10) Since host transitions in particular are problematic w.r.t. speed, I compute the host *once* in ConfigurationCollectionFunction.getHostConfiguration() and expose that reference to ConfiguredTargetFunction and other Skyframe functions. This limits recomputation to just when the fragments are trimmed. 11) Since options cloning is expensive, I'm doing something scary: exposing a BuildConfiguration.getOptions() method that returns a direct reference. Since BuildOptions is mutable, this is dangerous in the wrong hands. I can experiment with going back to cloning (with the caching of host transitions it may not matter as much), but we may ultimately have to put up with this risk for the sake of performant analysis time. What would be *really* awesome would be to make BuildOptions immutable. But that's not going to happen in this cl. So in short, the key abstractions in this cl are: - PatchTransition - BuildConfiguration.TransitionApplier - ConfiguredTargetFunction.trimConfigurations The current implementation imposes no analysis time penalty -- MOS_MIGRATED_REVID=101474620
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java34
1 files changed, 30 insertions, 4 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java
index 03920b7982..5f5d538956 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java
@@ -18,24 +18,27 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.DependencyResolver.Dependency;
import com.google.devtools.build.lib.analysis.LabelAndConfiguration;
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.ConfigMatchingProvider;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.RawAttributeMapper;
import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.skyframe.SkyframeActionExecutor.ConflictException;
+import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Label;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
-import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -56,10 +59,13 @@ public class PostConfiguredTargetFunction implements SkyFunction {
};
private final SkyframeExecutor.BuildViewProvider buildViewProvider;
+ private final RuleClassProvider ruleClassProvider;
public PostConfiguredTargetFunction(
- SkyframeExecutor.BuildViewProvider buildViewProvider) {
+ SkyframeExecutor.BuildViewProvider buildViewProvider,
+ RuleClassProvider ruleClassProvider) {
this.buildViewProvider = Preconditions.checkNotNull(buildViewProvider);
+ this.ruleClassProvider = ruleClassProvider;
}
@Nullable
@@ -90,8 +96,22 @@ public class PostConfiguredTargetFunction implements SkyFunction {
return null;
}
- Collection<Dependency> deps = resolver.dependentNodes(ctgValue, configConditions);
- env.getValues(Iterables.transform(deps, TO_KEYS));
+ ListMultimap<Attribute, Dependency> deps;
+ try {
+ BuildConfiguration hostConfiguration =
+ buildViewProvider.getSkyframeBuildView().getHostConfiguration(ct.getConfiguration());
+ deps = resolver.dependentNodeMap(ctgValue, hostConfiguration, null, configConditions);
+ if (ct.getConfiguration() != null && ct.getConfiguration().useDynamicConfigurations()) {
+ deps = ConfiguredTargetFunction.trimConfigurations(env, ctgValue, deps, hostConfiguration,
+ ruleClassProvider);
+ }
+ } catch (EvalException e) {
+ throw new PostConfiguredTargetFunctionException(e);
+ } catch (ConfiguredTargetFunction.DependencyEvaluationException e) {
+ throw new PostConfiguredTargetFunctionException(e);
+ }
+
+ env.getValues(Iterables.transform(deps.values(), TO_KEYS));
if (env.valuesMissing()) {
return null;
}
@@ -142,4 +162,10 @@ public class PostConfiguredTargetFunction implements SkyFunction {
super(e, Transience.PERSISTENT);
}
}
+
+ private static class PostConfiguredTargetFunctionException extends SkyFunctionException {
+ public PostConfiguredTargetFunctionException(Exception e) {
+ super(e, Transience.PERSISTENT);
+ }
+ }
}