aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Greg Estren <gregce@google.com>2016-08-09 22:36:51 +0000
committerGravatar Yue Gan <yueg@google.com>2016-08-10 08:38:45 +0000
commit373e3e28274cca5b87f48abe33884edb84016dd3 (patch)
tree61744785eadf07f70d0a9a3264c00cf992441190 /src
parenteff8b365c172b7e904ac6f7bba0c893fed7c91a8 (diff)
Implements dynamic split transitions on latebound attributes.
With this change, dynamic configs are at full feature parity for split transitions (minus some small differences in composed transitions from BuildConfigurationCollection.configurationHook). -- MOS_MIGRATED_REVID=129802414
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/BuildView.java37
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java91
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java15
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/PostConfiguredTargetFunction.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/SkyframeDependencyResolver.java31
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java21
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java17
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/LateBoundSplitUtil.java157
10 files changed, 344 insertions, 41 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 dde2d0684e..55bcf81851 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
@@ -38,7 +38,9 @@ import com.google.devtools.build.lib.analysis.ExtraActionArtifactsProvider.Extra
import com.google.devtools.build.lib.analysis.config.BinTools;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
+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.InvalidConfigurationException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -81,6 +83,7 @@ import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.WalkableGraph;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -865,7 +868,7 @@ public class BuildView {
@VisibleForTesting
public Iterable<ConfiguredTarget> getDirectPrerequisitesForTesting(
EventHandler eventHandler, ConfiguredTarget ct, BuildConfigurationCollection configurations)
- throws EvalException, InterruptedException {
+ throws EvalException, InvalidConfigurationException, InterruptedException {
return skyframeExecutor.getConfiguredTargets(
eventHandler, ct.getConfiguration(),
ImmutableSet.copyOf(
@@ -875,8 +878,9 @@ public class BuildView {
@VisibleForTesting
public ListMultimap<Attribute, Dependency> getDirectPrerequisiteDependenciesForTesting(
- final EventHandler eventHandler, ConfiguredTarget ct,
- BuildConfigurationCollection configurations) throws EvalException, InterruptedException {
+ final EventHandler eventHandler, final ConfiguredTarget ct,
+ BuildConfigurationCollection configurations)
+ throws EvalException, InvalidConfigurationException, InterruptedException {
if (!(ct.getTarget() instanceof Rule)) {
return ArrayListMultimap.create();
}
@@ -908,6 +912,24 @@ public class BuildView {
throw new IllegalStateException(e);
}
}
+
+ @Override
+ protected List<BuildConfiguration> getConfigurations(
+ Set<Class<? extends BuildConfiguration.Fragment>> fragments,
+ Iterable<BuildOptions> buildOptions) {
+ Preconditions.checkArgument(ct.getConfiguration().fragmentClasses().equals(fragments));
+ Dependency asDep = Dependency.withTransitionAndAspects(ct.getLabel(),
+ Attribute.ConfigurationTransition.NONE, ImmutableSet.<AspectDescriptor>of());
+ ImmutableList.Builder<BuildConfiguration> builder = ImmutableList.builder();
+ for (BuildOptions options : buildOptions) {
+ builder.add(Iterables.getOnlyElement(
+ skyframeExecutor
+ .getConfigurations(eventHandler, options, ImmutableList.<Dependency>of(asDep))
+ .values()
+ ));
+ }
+ return builder.build();
+ }
}
DependencyResolver dependencyResolver = new SilentDependencyResolver();
@@ -945,7 +967,8 @@ public class BuildView {
private ListMultimap<Attribute, ConfiguredTarget> getPrerequisiteMapForTesting(
final EventHandler eventHandler, ConfiguredTarget target,
- BuildConfigurationCollection configurations) throws EvalException, InterruptedException {
+ BuildConfigurationCollection configurations)
+ throws EvalException, InvalidConfigurationException, InterruptedException {
ListMultimap<Attribute, Dependency> depNodeNames = getDirectPrerequisiteDependenciesForTesting(
eventHandler, target, configurations);
@@ -978,7 +1001,7 @@ public class BuildView {
public RuleContext getRuleContextForTesting(
ConfiguredTarget target, StoredEventHandler eventHandler,
BuildConfigurationCollection configurations, BinTools binTools)
- throws EvalException, InterruptedException {
+ throws EvalException, InvalidConfigurationException, InterruptedException {
BuildConfiguration targetConfig = target.getConfiguration();
CachingAnalysisEnvironment env =
new CachingAnalysisEnvironment(getArtifactFactory(),
@@ -995,7 +1018,7 @@ public class BuildView {
@VisibleForTesting
public RuleContext getRuleContextForTesting(EventHandler eventHandler, ConfiguredTarget target,
AnalysisEnvironment env, BuildConfigurationCollection configurations)
- throws EvalException, InterruptedException {
+ throws EvalException, InvalidConfigurationException, InterruptedException {
BuildConfiguration targetConfig = target.getConfiguration();
return new RuleContext.Builder(
env,
@@ -1023,7 +1046,7 @@ public class BuildView {
public ConfiguredTarget getPrerequisiteConfiguredTargetForTesting(
EventHandler eventHandler, ConfiguredTarget dependentTarget, Label desiredTarget,
BuildConfigurationCollection configurations)
- throws EvalException, InterruptedException {
+ throws EvalException, InvalidConfigurationException, InterruptedException {
Collection<ConfiguredTarget> configuredTargets =
getPrerequisiteMapForTesting(eventHandler, dependentTarget, configurations).values();
for (ConfiguredTarget ct : configuredTargets) {
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 66360f4828..3956037516 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,7 +22,10 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
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.InvalidConfigurationException;
+import com.google.devtools.build.lib.analysis.config.PatchTransition;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.Aspect;
@@ -94,7 +97,7 @@ public abstract class DependencyResolver {
BuildConfiguration hostConfig,
@Nullable Aspect aspect,
ImmutableMap<Label, ConfigMatchingProvider> configConditions)
- throws EvalException, InterruptedException {
+ throws EvalException, InvalidConfigurationException, InterruptedException {
NestedSetBuilder<Label> rootCauses = NestedSetBuilder.<Label>stableOrder();
ListMultimap<Attribute, Dependency> outgoingEdges = dependentNodeMap(
node, hostConfig, aspect, configConditions, rootCauses);
@@ -138,7 +141,7 @@ public abstract class DependencyResolver {
@Nullable Aspect aspect,
ImmutableMap<Label, ConfigMatchingProvider> configConditions,
NestedSetBuilder<Label> rootCauses)
- throws EvalException, InterruptedException {
+ throws EvalException, InvalidConfigurationException, InterruptedException {
Target target = node.getTarget();
BuildConfiguration config = node.getConfiguration();
ListMultimap<Attribute, Dependency> outgoingEdges = ArrayListMultimap.create();
@@ -168,7 +171,7 @@ public abstract class DependencyResolver {
ImmutableMap<Label, ConfigMatchingProvider> configConditions,
NestedSetBuilder<Label> rootCauses,
ListMultimap<Attribute, Dependency> outgoingEdges)
- throws EvalException, InterruptedException {
+ throws EvalException, InvalidConfigurationException, InterruptedException {
Preconditions.checkArgument(node.getTarget() instanceof Rule);
BuildConfiguration ruleConfig = Preconditions.checkNotNull(node.getConfiguration());
Rule rule = (Rule) node.getTarget();
@@ -316,7 +319,7 @@ public abstract class DependencyResolver {
RuleResolver depResolver,
BuildConfiguration ruleConfig,
BuildConfiguration hostConfig)
- throws EvalException, InterruptedException {
+ throws EvalException, InvalidConfigurationException, InterruptedException {
ConfiguredAttributeMapper attributeMap = depResolver.attributeMap;
for (Attribute attribute : depResolver.attributes) {
if (!attribute.isLateBound() || !attribute.getCondition().apply(attributeMap)) {
@@ -327,7 +330,9 @@ public abstract class DependencyResolver {
LateBoundDefault<BuildConfiguration> lateBoundDefault =
(LateBoundDefault<BuildConfiguration>) attribute.getLateBoundDefault();
- if (attribute.hasSplitConfigurationTransition()) {
+ Collection<BuildOptions> splitOptions =
+ getSplitOptions(depResolver.rule, attribute, ruleConfig);
+ if (!splitOptions.isEmpty()) {
// Late-bound attribute with a split transition:
// Since we want to get the same results as BuildConfiguration.evaluateTransition (but
// skip it since we've already applied the split), we want to make sure this logic
@@ -336,9 +341,18 @@ public abstract class DependencyResolver {
// of those apply, in the name of keeping the fork as simple as possible.
Verify.verify(attribute.getConfigurator() == null);
Verify.verify(!lateBoundDefault.useHostConfiguration());
- for (BuildConfiguration splitConfig : ruleConfig.getSplitConfigurations(
- attribute.getSplitTransition(depResolver.rule))) {
- // TODO(gregce): support dynamic split transitions
+
+ Iterable<BuildConfiguration> splitConfigs;
+ if (!ruleConfig.useDynamicConfigurations()) {
+ splitConfigs = ruleConfig
+ .getSplitConfigurations(attribute.getSplitTransition(depResolver.rule));
+ } else {
+ splitConfigs = getConfigurations(ruleConfig.fragmentClasses(), splitOptions);
+ if (splitConfigs == null) {
+ continue; // Need Skyframe deps.
+ }
+ }
+ for (BuildConfiguration splitConfig : splitConfigs) {
for (Label dep : resolveLateBoundAttribute(
depResolver.rule, attribute, splitConfig, attributeMap)) {
// Skip the normal config transition pipeline and directly feed the split config. This
@@ -360,6 +374,23 @@ public abstract class DependencyResolver {
}
/**
+ * Returns true if the rule's attribute triggers a split in this configuration.
+ *
+ * <p>Even though the attribute may have a split, splits don't have to apply in every
+ * configuration (see {@link Attribute.SplitTransition#split}).
+ */
+ private static Collection<BuildOptions> getSplitOptions(Rule rule, Attribute attribute,
+ BuildConfiguration ruleConfig) {
+ if (!attribute.hasSplitConfigurationTransition()) {
+ return ImmutableList.<BuildOptions>of();
+ }
+ @SuppressWarnings("unchecked") // Attribute.java doesn't have the BuildOptions symbol.
+ Attribute.SplitTransition<BuildOptions> transition =
+ (Attribute.SplitTransition<BuildOptions>) attribute.getSplitTransition(rule);
+ return transition.split(ruleConfig.getOptions());
+ }
+
+ /**
* Returns the label dependencies for the given late-bound attribute in this rule.
*
* @param rule the rule being evaluated
@@ -586,8 +617,6 @@ public abstract class DependencyResolver {
* configurations to apply to it.
*/
void resolveDep(Attribute attribute, Label depLabel) {
- // Late-bound split attributes are separately handled (see LateBoundSplitResolver).
- Verify.verify(!(attribute.isLateBound() && attribute.hasSplitConfigurationTransition()));
Target toTarget = getTarget(rule, depLabel, rootCauses);
if (toTarget == null) {
return; // Skip this round: we still need to Skyframe-evaluate the dep's target.
@@ -625,10 +654,9 @@ public abstract class DependencyResolver {
ImmutableSet<AspectDescriptor> aspects = requiredAspects(aspect, attribute, toTarget, rule);
Dependency dep;
if (config.useDynamicConfigurations() && !applyNullTransition) {
- // Since we feed a pre-prepared configuration directly to the dep, it won't get trimmed to
- // the dep's fragments.
- // TODO(gregce): properly trim this configuration, too.
- dep = Dependency.withConfigurationAndAspects(depLabel, config, aspects);
+ // 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));
}
@@ -637,6 +665,27 @@ public abstract class DependencyResolver {
}
}
+ /**
+ * A patch transition that returns a fixed set of options regardless of the input.
+ */
+ private static class FixedTransition implements PatchTransition {
+ private final BuildOptions toOptions;
+
+ FixedTransition(BuildOptions toOptions) {
+ this.toOptions = toOptions;
+ }
+
+ @Override
+ public BuildOptions apply(BuildOptions options) {
+ return toOptions;
+ }
+
+ @Override
+ public boolean defaultsToSelf() {
+ return false;
+ }
+ }
+
private void visitTargetVisibility(TargetAndConfiguration node,
NestedSetBuilder<Label> rootCauses, Collection<Dependency> outgoingEdges) {
Target target = node.getTarget();
@@ -696,4 +745,18 @@ public abstract class DependencyResolver {
*/
@Nullable
protected abstract Target getTarget(Target from, Label label, NestedSetBuilder<Label> rootCauses);
+
+ /**
+ * Returns the build configurations with the given options and fragments, in the same order as
+ * the input options.
+ *
+ * <p>Returns null if any configurations aren't ready to be returned at this moment. If
+ * getConfigurations returns null once or more during a {@link #dependentNodeMap} call, the
+ * results of that call will be incomplete. For use within Skyframe, where several iterations may
+ * be needed to discover all dependencies.
+ */
+ @Nullable
+ protected abstract List<BuildConfiguration> getConfigurations(
+ Set<Class<? extends BuildConfiguration.Fragment>> fragments,
+ Iterable<BuildOptions> buildOptions) throws InvalidConfigurationException;
}
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 27b0d093d1..f97ea94f5a 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
@@ -1102,7 +1102,12 @@ public final class BuildConfiguration {
public boolean equalsOrIsSupersetOf(BuildConfiguration other) {
return this.equals(other)
|| (other != null
- && outputRoots.equals(other.outputRoots)
+ // TODO(gregce): add back in output root checking. This requires a better approach to
+ // configuration-safe output paths. If the parent config has a fragment the child config
+ // doesn't, it may inject $(FOO) into the output roots. So the child bindir might be
+ // "bazel-out/arm-linux-fastbuild/bin" while the parent bindir is
+ // "bazel-out/android-arm-linux-fastbuild/bin". That's pretty awkward to check here.
+ // && outputRoots.equals(other.outputRoots)
&& actionsEnabled == other.actionsEnabled
&& fragments.values().containsAll(other.fragments.values())
&& buildOptions.getOptions().containsAll(other.buildOptions.getOptions()));
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
index 6fc4476782..4db1a92b53 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
@@ -37,7 +37,6 @@ import com.google.devtools.build.lib.packages.Attribute.Transition;
import com.google.devtools.build.lib.rules.cpp.CppRuleClasses.LipoTransition;
import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -76,19 +75,7 @@ public class BazelConfigurationCollection implements ConfigurationCollectionFact
ListMultimap<SplitTransition<?>, BuildConfiguration> splitTransitionsTable =
ArrayListMultimap.create();
for (SplitTransition<BuildOptions> transition : buildOptions.getPotentialSplitTransitions()) {
- List<BuildOptions> splitOptionsList = transition.split(buildOptions);
-
- // While it'd be clearer to condition the below on "if (!splitOptionsList.empty())",
- // IosExtension.ExtensionSplitArchTransition defaults to a single-value split. If we failed
- // that case then no builds would work, whether or not they're iOS builds (since iOS
- // configurations are unconditionally loaded). Once we have dynamic configuraiton support
- // for split transitions, this will all go away.
- if (splitOptionsList.size() > 1 && targetConfiguration.useDynamicConfigurations()) {
- throw new InvalidConfigurationException(
- "dynamic configurations don't yet support split transitions");
- }
-
- for (BuildOptions splitOptions : splitOptionsList) {
+ for (BuildOptions splitOptions : transition.split(buildOptions)) {
BuildConfiguration splitConfig = configurationFactory.getConfiguration(
packageProvider, splitOptions, false, cache);
splitTransitionsTable.put(transition, splitConfig);
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 1e4d11466e..b09fc1dbe4 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
@@ -296,6 +296,8 @@ final class ConfiguredTargetFunction implements SkyFunction {
env.getListener().handle(Event.error(e.getLocation(), e.getMessage()));
throw new DependencyEvaluationException(
new ConfiguredValueCreationException(e.print(), ctgValue.getLabel()));
+ } catch (InvalidConfigurationException e) {
+ throw new DependencyEvaluationException(e);
}
// Trim each dep's configuration so it only includes the fragments needed by its transitive
@@ -456,7 +458,7 @@ final class ConfiguredTargetFunction implements SkyFunction {
new AttributeAndLabel(depsEntry.getKey(), dep.getLabel());
if (dep.hasStaticConfiguration()) {
- // Certain targets (like output files and late-bound splits) trivially pass their
+ // Certain targets (like output files) trivially pass their
// configurations to their deps. So no need to transform them in any way.
trimmedDeps.put(attributeAndLabel, dep);
continue;
@@ -561,7 +563,7 @@ final class ConfiguredTargetFunction implements SkyFunction {
AttributeAndLabel attr = new AttributeAndLabel(info.getKey(), originalDep.getLabel());
Dependency resolvedDep = Dependency.withConfigurationAndAspects(originalDep.getLabel(),
trimmedConfig.getConfiguration(), originalDep.getAspects());
- if (originalDep.getTransition() instanceof Attribute.SplitTransition) {
+ if (attr.attribute.hasSplitConfigurationTransition()) {
trimmedDeps.put(attr, resolvedDep);
} else {
putOnlyEntry(trimmedDeps, attr, resolvedDep);
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 d342bb66a4..92b2678d13 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
@@ -24,6 +24,7 @@ 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.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.BuildType;
@@ -116,6 +117,8 @@ public class PostConfiguredTargetFunction implements SkyFunction {
throw new PostConfiguredTargetFunctionException(e);
} catch (ConfiguredTargetFunction.DependencyEvaluationException e) {
throw new PostConfiguredTargetFunctionException(e);
+ } catch (InvalidConfigurationException e) {
+ throw new PostConfiguredTargetFunctionException(e);
}
env.getValues(Iterables.transform(deps.values(), TO_KEYS));
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 4e4c603dff..09fc0f2176 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
@@ -13,8 +13,12 @@
// limitations under the License.
package com.google.devtools.build.lib.skyframe;
+import com.google.common.collect.ImmutableList;
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.InvalidConfigurationException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.events.Event;
@@ -26,6 +30,12 @@ import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.skyframe.SkyFunction.Environment;
import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.ValueOrException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import javax.annotation.Nullable;
@@ -110,4 +120,25 @@ public final class SkyframeDependencyResolver extends DependencyResolver {
return null;
}
}
+
+ @Nullable
+ @Override
+ protected List<BuildConfiguration> getConfigurations(
+ Set<Class<? extends BuildConfiguration.Fragment>> fragments,
+ Iterable<BuildOptions> buildOptions) throws InvalidConfigurationException {
+ List<SkyKey> keys = new ArrayList<>();
+ for (BuildOptions options : buildOptions) {
+ keys.add(BuildConfigurationValue.key(fragments, options));
+ }
+ Map<SkyKey, ValueOrException<InvalidConfigurationException>> configValues =
+ env.getValuesOrThrow(keys, InvalidConfigurationException.class);
+ if (env.valuesMissing()) {
+ return null;
+ }
+ ImmutableList.Builder<BuildConfiguration> result = ImmutableList.builder();
+ for (SkyKey key : keys) {
+ result.add(((BuildConfigurationValue) configValues.get(key).get()).getConfiguration());
+ }
+ return result.build();
+ }
}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
index b1539f8766..d48756ad1b 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
@@ -37,6 +37,7 @@ import com.google.devtools.build.lib.actions.Actions;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.FailAction;
import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult;
+import com.google.devtools.build.lib.analysis.config.ConfigurationFactory;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.analysis.util.AnalysisMock;
import com.google.devtools.build.lib.analysis.util.BuildViewTestBase;
@@ -58,6 +59,7 @@ import com.google.devtools.build.skyframe.NotifyingHelper.Listener;
import com.google.devtools.build.skyframe.NotifyingHelper.Order;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.TrackingAwaiter;
+
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.concurrent.CountDownLatch;
@@ -1252,6 +1254,25 @@ public class BuildViewTest extends BuildViewTestBase {
+ "required config fragments: Jvm");
}
+ @Test
+ public void lateBoundSplitAttributeConfigs() throws Exception {
+ useRuleClassProvider(LateBoundSplitUtil.getRuleClassProvider());
+ // Register the latebound split fragment with the config creation environment.
+ useConfigurationFactory(new ConfigurationFactory(
+ ruleClassProvider.getConfigurationCollectionFactory(),
+ ruleClassProvider.getConfigurationFragments()));
+
+ scratch.file("foo/BUILD",
+ "rule_with_latebound_split(",
+ " name = 'foo')",
+ "sh_binary(",
+ " name = 'latebound_dep',",
+ " srcs = ['latebound_dep.sh'])");
+ update("//foo:foo");
+ assertNotNull(getConfiguredTarget("//foo:foo"));
+ // TODO(bazel-team): also check that the dep is created in each expected configuration.
+ }
+
/** Runs the same test with the reduced loading phase. */
@TestSpec(size = Suite.SMALL_TESTS)
@RunWith(JUnit4.class)
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java b/src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java
index 239e2f12ad..7f1538464d 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java
@@ -18,6 +18,8 @@ import static org.junit.Assert.assertNotNull;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
+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.util.AnalysisTestCase;
import com.google.devtools.build.lib.analysis.util.TestAspects;
@@ -31,14 +33,14 @@ import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.NoSuchThingException;
import com.google.devtools.build.lib.packages.Target;
-
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import javax.annotation.Nullable;
-
/**
* Tests for {@link DependencyResolver}.
*
@@ -80,6 +82,15 @@ public class DependencyResolverTest extends AnalysisTestCase {
throw new IllegalStateException(e);
}
}
+
+ @Nullable
+ @Override
+ protected List<BuildConfiguration> getConfigurations(
+ Set<Class<? extends BuildConfiguration.Fragment>> fragments,
+ Iterable<BuildOptions> buildOptions) {
+ throw new UnsupportedOperationException(
+ "this functionality is covered by analysis-phase integration tests");
+ }
};
}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/LateBoundSplitUtil.java b/src/test/java/com/google/devtools/build/lib/analysis/LateBoundSplitUtil.java
new file mode 100644
index 0000000000..a6a2480786
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/LateBoundSplitUtil.java
@@ -0,0 +1,157 @@
+// Copyright 2016 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;
+
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+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.ConfigurationEnvironment;
+import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
+import com.google.devtools.build.lib.analysis.config.FragmentOptions;
+import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.AttributeMap;
+import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+import com.google.devtools.build.lib.testutil.UnknownRuleConfiguredTarget;
+import com.google.devtools.build.lib.util.FileTypeSet;
+import com.google.devtools.common.options.Option;
+
+import java.util.List;
+
+/**
+ * Rule and configuration class definitions for testing late-bound split attributes.
+ */
+public class LateBoundSplitUtil {
+ /**
+ * A custom {@link FragmentOptions} with the option to be split.
+ */
+ public static class Options extends FragmentOptions { // public for options loader
+ @Option(
+ name = "foo",
+ defaultValue = "",
+ category = "undocumented"
+ )
+ public String fooFlag;
+
+ @Override
+ public List<Attribute.SplitTransition<BuildOptions>> getPotentialSplitTransitions() {
+ return ImmutableList.<Attribute.SplitTransition<BuildOptions>>of(SIMPLE_SPLIT);
+ }
+ }
+
+ /**
+ * The split.
+ */
+ private static final Attribute.SplitTransition<BuildOptions> SIMPLE_SPLIT =
+ new Attribute.SplitTransition<BuildOptions>() {
+ @Override
+ public List<BuildOptions> split(BuildOptions buildOptions) {
+ BuildOptions split1 = buildOptions.clone();
+ split1.get(Options.class).fooFlag = "one";
+ BuildOptions split2 = buildOptions.clone();
+ split2.get(Options.class).fooFlag = "two";
+ return ImmutableList.<BuildOptions>of(split1, split2);
+ }
+
+ @Override
+ public boolean defaultsToSelf() {
+ return false;
+ }
+ };
+
+ /**
+ * The {@link BuildConfiguration.Fragment} that contains the options.
+ */
+ private static class Fragment extends BuildConfiguration.Fragment {
+ }
+
+ /**
+ * The fragment's loader.
+ */
+ static class FragmentLoader implements ConfigurationFragmentFactory {
+ @Override
+ public BuildConfiguration.Fragment create(ConfigurationEnvironment env,
+ BuildOptions buildOptions)
+ throws InvalidConfigurationException {
+ return new Fragment();
+ }
+
+ @Override
+ public Class<? extends BuildConfiguration.Fragment> creates() {
+ return Fragment.class;
+ }
+
+ @Override
+ public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() {
+ return ImmutableSet.<Class<? extends FragmentOptions>>of(Options.class);
+ }
+ }
+
+ /**
+ * The resolver that chooses the late-bound attribute's value.
+ */
+ private static final Attribute.LateBoundLabel<BuildConfiguration> SIMPLE_LATEBOUND_RESOLVER =
+ new Attribute.LateBoundLabel<BuildConfiguration>() {
+ @Override
+ public Label resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) {
+ return Label.parseAbsoluteUnchecked("//foo:latebound_dep");
+ }
+ };
+
+ /**
+ * A custom rule that applies a late-bound split attribute.
+ */
+ static class RuleWithLateBoundSplitAttribute implements RuleDefinition {
+ @Override
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) {
+ return builder
+ .add(attr(":latebound_split_attr", BuildType.LABEL)
+ .allowedFileTypes(FileTypeSet.ANY_FILE)
+ .allowedRuleClasses(Attribute.ANY_RULE)
+ .cfg(SIMPLE_SPLIT)
+ .value(SIMPLE_LATEBOUND_RESOLVER))
+ .requiresConfigurationFragments(Fragment.class)
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name("rule_with_latebound_split")
+ .ancestors(BaseRuleClasses.RuleBase.class)
+ .factoryClass(UnknownRuleConfiguredTarget.class)
+ .build();
+ }
+ }
+
+ /**
+ * Returns a rule class provider with standard test setup plus the above rules/configs.
+ */
+ static ConfiguredRuleClassProvider getRuleClassProvider() {
+ ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
+ TestRuleClassProvider.addStandardRules(builder);
+ builder.addRuleDefinition(new RuleWithLateBoundSplitAttribute());
+ builder.addConfigurationFragment(new FragmentLoader());
+ builder.addConfigurationOptions(Options.class);
+ return builder.build();
+ }
+} \ No newline at end of file