A prototype implementation of top-level aspects.
--
MOS_MIGRATED_REVID=101033236
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Aspect.java b/src/main/java/com/google/devtools/build/lib/analysis/Aspect.java
index 3f4a06e..95f0683 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Aspect.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Aspect.java
@@ -17,10 +17,16 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.UnmodifiableIterator;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.TreeMap;
+
+import javax.annotation.Nullable;
/**
* Extra information about a configured target computed on request of a dependent.
@@ -34,15 +40,25 @@
*/
@Immutable
public final class Aspect implements Iterable<TransitiveInfoProvider> {
- private final
- ImmutableMap<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider> providers;
+ private final String name;
+ private final ImmutableMap<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider>
+ providers;
private Aspect(
+ String name,
ImmutableMap<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider> providers) {
+ this.name = name;
this.providers = providers;
}
/**
+ * Returns the aspect name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
* Returns the providers created by the aspect.
*/
public ImmutableMap<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider>
@@ -50,6 +66,14 @@
return providers;
}
+
+ @Nullable
+ <P extends TransitiveInfoProvider> P getProvider(Class<P> providerClass) {
+ AnalysisUtils.checkProvider(providerClass);
+
+ return providerClass.cast(providers.get(providerClass));
+ }
+
@Override
public UnmodifiableIterator<TransitiveInfoProvider> iterator() {
return providers.values().iterator();
@@ -61,6 +85,12 @@
public static class Builder {
private final Map<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider>
providers = new LinkedHashMap<>();
+ private final Map<String, NestedSetBuilder<Artifact>> outputGroupBuilders = new TreeMap<>();
+ private final String name;
+
+ public Builder(String name) {
+ this.name = name;
+ }
/**
* Adds a provider to the aspect.
@@ -75,8 +105,35 @@
return this;
}
+ /**
+ * Adds a set of files to an output group.
+ */
+ public Builder addOutputGroup(String name, NestedSet<Artifact> artifacts) {
+ NestedSetBuilder<Artifact> nestedSetBuilder = outputGroupBuilders.get(name);
+ if (nestedSetBuilder == null) {
+ nestedSetBuilder = NestedSetBuilder.<Artifact>stableOrder();
+ outputGroupBuilders.put(name, nestedSetBuilder);
+ }
+ nestedSetBuilder.addTransitive(artifacts);
+ return this;
+ }
+
+
public Aspect build() {
- return new Aspect(ImmutableMap.copyOf(providers));
+ if (!outputGroupBuilders.isEmpty()) {
+ ImmutableMap.Builder<String, NestedSet<Artifact>> outputGroups = ImmutableMap.builder();
+ for (Map.Entry<String, NestedSetBuilder<Artifact>> entry : outputGroupBuilders.entrySet()) {
+ outputGroups.put(entry.getKey(), entry.getValue().build());
+ }
+
+ if (providers.containsKey(OutputGroupProvider.class)) {
+ throw new IllegalStateException(
+ "OutputGroupProvider was provided explicitly; do not use addOutputGroup");
+ }
+ addProvider(OutputGroupProvider.class, new OutputGroupProvider(outputGroups.build()));
+ }
+
+ return new Aspect(name, ImmutableMap.copyOf(providers));
}
}
}
\ No newline at end of file
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java
new file mode 100644
index 0000000..faefb3c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java
@@ -0,0 +1,73 @@
+// Copyright 2015 Google Inc. 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 com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.skyframe.AspectValue;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.skyframe.SkyValue;
+
+/**
+ * This event is fired as soon as a top-level aspect is either built or fails.
+ */
+public class AspectCompleteEvent implements SkyValue {
+ private final AspectValue aspectValue;
+ private final NestedSet<Label> rootCauses;
+
+ private AspectCompleteEvent(AspectValue aspectValue, NestedSet<Label> rootCauses) {
+ this.aspectValue = aspectValue;
+ this.rootCauses =
+ (rootCauses == null) ? NestedSetBuilder.<Label>emptySet(Order.STABLE_ORDER) : rootCauses;
+ }
+
+ /**
+ * Construct a successful target completion event.
+ */
+ public static AspectCompleteEvent createSuccessful(AspectValue value) {
+ return new AspectCompleteEvent(value, null);
+ }
+
+ /**
+ * Construct a target completion event for a failed target, with the given non-empty root causes.
+ */
+ public static AspectCompleteEvent createFailed(AspectValue value, NestedSet<Label> rootCauses) {
+ Preconditions.checkArgument(!Iterables.isEmpty(rootCauses));
+ return new AspectCompleteEvent(value, rootCauses);
+ }
+
+ /**
+ * Returns the target associated with the event.
+ */
+ public AspectValue getAspectValue() {
+ return aspectValue;
+ }
+
+ /**
+ * Determines whether the target has failed or succeeded.
+ */
+ public boolean failed() {
+ return !rootCauses.isEmpty();
+ }
+
+ /**
+ * Get the root causes of the target. May be empty.
+ */
+ public Iterable<Label> getRootCauses() {
+ return rootCauses;
+ }
+}
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 3baae74..7ce8c04 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
@@ -65,13 +65,15 @@
import com.google.devtools.build.lib.rules.test.CoverageReportActionFactory;
import com.google.devtools.build.lib.rules.test.CoverageReportActionFactory.CoverageReportActionsWrapper;
import com.google.devtools.build.lib.skyframe.ActionLookupValue;
+import com.google.devtools.build.lib.skyframe.AspectValue;
+import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.skyframe.CoverageReportValue;
+import com.google.devtools.build.lib.skyframe.SkyframeAnalysisResult;
import com.google.devtools.build.lib.skyframe.SkyframeBuildView;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Label;
-import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.RegexFilter;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.skyframe.SkyKey;
@@ -79,6 +81,7 @@
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;
import java.util.HashSet;
@@ -434,12 +437,17 @@
*/
public static final class AnalysisResult {
- public static final AnalysisResult EMPTY = new AnalysisResult(
- ImmutableList.<ConfiguredTarget>of(), null, null, null,
- ImmutableList.<Artifact>of(),
- ImmutableList.<ConfiguredTarget>of(),
- ImmutableList.<ConfiguredTarget>of(),
- null);
+ public static final AnalysisResult EMPTY =
+ new AnalysisResult(
+ ImmutableList.<ConfiguredTarget>of(),
+ ImmutableList.<AspectValue>of(),
+ null,
+ null,
+ null,
+ ImmutableList.<Artifact>of(),
+ ImmutableList.<ConfiguredTarget>of(),
+ ImmutableList.<ConfiguredTarget>of(),
+ null);
private final ImmutableList<ConfiguredTarget> targetsToBuild;
@Nullable private final ImmutableList<ConfiguredTarget> targetsToTest;
@@ -449,13 +457,20 @@
private final ImmutableSet<ConfiguredTarget> parallelTests;
private final ImmutableSet<ConfiguredTarget> exclusiveTests;
@Nullable private final TopLevelArtifactContext topLevelContext;
+ private final ImmutableList<AspectValue> aspects;
private AnalysisResult(
- Collection<ConfiguredTarget> targetsToBuild, Collection<ConfiguredTarget> targetsToTest,
- @Nullable String error, ActionGraph actionGraph,
- Collection<Artifact> artifactsToBuild, Collection<ConfiguredTarget> parallelTests,
- Collection<ConfiguredTarget> exclusiveTests, TopLevelArtifactContext topLevelContext) {
+ Collection<ConfiguredTarget> targetsToBuild,
+ Collection<AspectValue> aspects,
+ Collection<ConfiguredTarget> targetsToTest,
+ @Nullable String error,
+ ActionGraph actionGraph,
+ Collection<Artifact> artifactsToBuild,
+ Collection<ConfiguredTarget> parallelTests,
+ Collection<ConfiguredTarget> exclusiveTests,
+ TopLevelArtifactContext topLevelContext) {
this.targetsToBuild = ImmutableList.copyOf(targetsToBuild);
+ this.aspects = ImmutableList.copyOf(aspects);
this.targetsToTest = targetsToTest == null ? null : ImmutableList.copyOf(targetsToTest);
this.error = error;
this.actionGraph = actionGraph;
@@ -473,6 +488,16 @@
}
/**
+ * Returns aspects of configured targets to build.
+ *
+ * <p>If this list is empty, build the targets returned by {@code getTargetsToBuild()}.
+ * Otherwise, only build these aspects of the targets returned by {@code getTargetsToBuild()}.
+ */
+ public Collection<AspectValue> getAspects() {
+ return aspects;
+ }
+
+ /**
* Returns the configured targets to run as tests, or {@code null} if testing was not
* requested (e.g. "build" command rather than "test" command).
*/
@@ -520,10 +545,11 @@
static Iterable<? extends ConfiguredTarget> filterTestsByTargets(
Collection<? extends ConfiguredTarget> targets,
final Set<? extends Target> allowedTargets) {
- return Iterables.filter(targets,
+ return Iterables.filter(
+ targets,
new Predicate<ConfiguredTarget>() {
@Override
- public boolean apply(ConfiguredTarget rule) {
+ public boolean apply(ConfiguredTarget rule) {
return allowedTargets.contains(rule.getTarget());
}
});
@@ -536,10 +562,15 @@
}
@ThreadCompatible
- public AnalysisResult update(LoadingResult loadingResult,
- BuildConfigurationCollection configurations, Options viewOptions,
- TopLevelArtifactContext topLevelOptions, EventHandler eventHandler, EventBus eventBus)
- throws ViewCreationFailedException, InterruptedException {
+ public AnalysisResult update(
+ LoadingResult loadingResult,
+ BuildConfigurationCollection configurations,
+ List<String> aspects,
+ Options viewOptions,
+ TopLevelArtifactContext topLevelOptions,
+ EventHandler eventHandler,
+ EventBus eventBus)
+ throws ViewCreationFailedException, InterruptedException {
LOG.info("Starting analysis");
pollInterruptedStatus();
@@ -580,21 +611,36 @@
}
});
+ List<AspectKey> aspectKeys = new ArrayList<>();
+ for (String aspect : aspects) {
+ @SuppressWarnings("unchecked")
+ final Class<? extends ConfiguredAspectFactory> aspectFactoryClass =
+ (Class<? extends ConfiguredAspectFactory>)
+ ruleClassProvider.getAspectFactoryMap().get(aspect);
+ if (aspectFactoryClass != null) {
+ for (ConfiguredTargetKey targetSpec : targetSpecs) {
+ aspectKeys.add(
+ AspectValue.createAspectKey(
+ targetSpec.getLabel(), targetSpec.getConfiguration(), aspectFactoryClass));
+ }
+ } else {
+ throw new ViewCreationFailedException("Aspect '" + aspect + "' is unknown");
+ }
+ }
+
prepareToBuild(new SkyframePackageRootResolver(skyframeExecutor));
skyframeExecutor.injectWorkspaceStatusData();
- Collection<ConfiguredTarget> configuredTargets;
- WalkableGraph graph;
+ SkyframeAnalysisResult skyframeAnalysisResult;
try {
- Pair<Collection<ConfiguredTarget>, WalkableGraph> configuredTargetsResult =
- skyframeBuildView.configureTargets(targetSpecs, eventBus, viewOptions.keepGoing);
- configuredTargets = configuredTargetsResult.getFirst();
- graph = configuredTargetsResult.getSecond();
+ skyframeAnalysisResult =
+ skyframeBuildView.configureTargets(
+ targetSpecs, aspectKeys, eventBus, viewOptions.keepGoing);
} finally {
skyframeBuildView.clearInvalidatedConfiguredTargets();
}
int numTargetsToAnalyze = nodes.size();
- int numSuccessful = configuredTargets.size();
+ int numSuccessful = skyframeAnalysisResult.getConfiguredTargets().size();
boolean analysisSuccessful = (numSuccessful == numTargetsToAnalyze);
if (0 < numSuccessful && numSuccessful < numTargetsToAnalyze) {
String msg = String.format("Analysis succeeded for only %d of %d top-level targets",
@@ -603,17 +649,28 @@
LOG.info(msg);
}
- AnalysisResult result = createResult(loadingResult, topLevelOptions,
- viewOptions, configuredTargets, analysisSuccessful, graph);
+ AnalysisResult result =
+ createResult(
+ loadingResult,
+ topLevelOptions,
+ viewOptions,
+ skyframeAnalysisResult.getConfiguredTargets(),
+ skyframeAnalysisResult.getAspects(),
+ skyframeAnalysisResult.getWalkableGraph(),
+ analysisSuccessful);
LOG.info("Finished analysis");
return result;
}
- private AnalysisResult createResult(LoadingResult loadingResult,
- TopLevelArtifactContext topLevelOptions, BuildView.Options viewOptions,
- Collection<ConfiguredTarget> configuredTargets, boolean analysisSuccessful,
- final WalkableGraph graph)
- throws InterruptedException {
+ private AnalysisResult createResult(
+ LoadingResult loadingResult,
+ TopLevelArtifactContext topLevelOptions,
+ BuildView.Options viewOptions,
+ Collection<ConfiguredTarget> configuredTargets,
+ Collection<AspectValue> aspects,
+ final WalkableGraph graph,
+ boolean analysisSuccessful)
+ throws InterruptedException {
Collection<Target> testsToRun = loadingResult.getTestsToRun();
Collection<ConfiguredTarget> allTargetsToTest = null;
if (testsToRun != null) {
@@ -667,8 +724,16 @@
return null;
}
};
- return new AnalysisResult(configuredTargets, allTargetsToTest, error, actionGraph,
- artifactsToBuild, parallelTests, exclusiveTests, topLevelOptions);
+ return new AnalysisResult(
+ configuredTargets,
+ aspects,
+ allTargetsToTest,
+ error,
+ actionGraph,
+ artifactsToBuild,
+ parallelTests,
+ exclusiveTests,
+ topLevelOptions);
}
private static NestedSet<Artifact> getBaselineCoverageArtifacts(
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
index 3b40300..543c71e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -31,6 +31,7 @@
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.graph.Digraph;
import com.google.devtools.build.lib.graph.Node;
+import com.google.devtools.build.lib.packages.AspectFactory;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClassProvider;
@@ -83,6 +84,8 @@
private final Map<String, RuleClass> ruleClassMap = new HashMap<>();
private final Map<String, Class<? extends RuleDefinition>> ruleDefinitionMap =
new HashMap<>();
+ private final Map<String, Class<? extends AspectFactory<?, ?, ?>>> aspectFactoryMap =
+ new HashMap<>();
private final Map<Class<? extends RuleDefinition>, RuleClass> ruleMap = new HashMap<>();
private final Map<Class<? extends RuleDefinition>, RuleDefinition> ruleDefinitionInstanceCache =
new HashMap<>();
@@ -117,6 +120,13 @@
return this;
}
+ public Builder addAspectFactory(
+ String name, Class<? extends AspectFactory<?, ?, ?>> configuredAspectFactoryClass) {
+ aspectFactoryMap.put(name, configuredAspectFactoryClass);
+
+ return this;
+ }
+
public Builder addConfigurationOptions(Class<? extends FragmentOptions> configurationOptions) {
this.configurationOptions.add(configurationOptions);
return this;
@@ -199,6 +209,7 @@
return new ConfiguredRuleClassProvider(
ImmutableMap.copyOf(ruleClassMap),
ImmutableMap.copyOf(ruleDefinitionMap),
+ ImmutableMap.copyOf(aspectFactoryMap),
defaultWorkspaceFile.toString(),
ImmutableList.copyOf(buildInfoFactories),
ImmutableList.copyOf(configurationOptions),
@@ -247,6 +258,11 @@
private final ImmutableMap<String, Class<? extends RuleDefinition>> ruleDefinitionMap;
/**
+ * Maps aspect name to the aspect factory meta class.
+ */
+ private final ImmutableMap<String, Class<? extends AspectFactory<?, ?, ?>>> aspectFactoryMap;
+
+ /**
* The configuration options that affect the behavior of the rules.
*/
private final ImmutableList<Class<? extends FragmentOptions>> configurationOptions;
@@ -272,6 +288,7 @@
public ConfiguredRuleClassProvider(
ImmutableMap<String, RuleClass> ruleClassMap,
ImmutableMap<String, Class<? extends RuleDefinition>> ruleDefinitionMap,
+ ImmutableMap<String, Class<? extends AspectFactory<?, ?, ?>>> aspectFactoryMap,
String defaultWorkspaceFile,
ImmutableList<BuildInfoFactory> buildInfoFactories,
ImmutableList<Class<? extends FragmentOptions>> configurationOptions,
@@ -282,6 +299,7 @@
this.ruleClassMap = ruleClassMap;
this.ruleDefinitionMap = ruleDefinitionMap;
+ this.aspectFactoryMap = aspectFactoryMap;
this.defaultWorkspaceFile = defaultWorkspaceFile;
this.buildInfoFactories = buildInfoFactories;
this.configurationOptions = configurationOptions;
@@ -302,6 +320,11 @@
return ruleClassMap;
}
+ @Override
+ public Map<String, Class<? extends AspectFactory<?, ?, ?>>> getAspectFactoryMap() {
+ return aspectFactoryMap;
+ }
+
/**
* Returns a list of build info factories that are needed for the supported languages.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/OutputGroupProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/OutputGroupProvider.java
index 90b373f..5411f0f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/OutputGroupProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/OutputGroupProvider.java
@@ -22,6 +22,12 @@
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
/**
* {@code ConfiguredTarget}s implementing this interface can provide artifacts that <b>can</b> be
* built when the target is mentioned on the command line (as opposed to being always built, like
@@ -93,7 +99,7 @@
private final ImmutableMap<String, NestedSet<Artifact>> outputGroups;
- OutputGroupProvider(ImmutableMap<String, NestedSet<Artifact>> outputGroups) {
+ public OutputGroupProvider(ImmutableMap<String, NestedSet<Artifact>> outputGroups) {
this.outputGroups = outputGroups;
}
@@ -107,4 +113,32 @@
? outputGroups.get(outputGroupName)
: NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER);
}
+
+ /**
+ * Merges output groups from two output providers. The set of output groups must be disjoint.
+ *
+ * @param providers providers to merge {@code this} with.
+ */
+ @Nullable
+ public static OutputGroupProvider merge(List<OutputGroupProvider> providers) {
+ if (providers.size() == 0) {
+ return null;
+ }
+ if (providers.size() == 1) {
+ return providers.get(0);
+ }
+
+ ImmutableMap.Builder<String, NestedSet<Artifact>> resultBuilder = new ImmutableMap.Builder<>();
+ Set<String> seenGroups = new HashSet<>();
+ for (OutputGroupProvider provider : providers) {
+ for (String outputGroup : provider.outputGroups.keySet()) {
+ if (!seenGroups.add(outputGroup)) {
+ throw new IllegalStateException("Output group " + outputGroup + " provided twice");
+ }
+
+ resultBuilder.put(outputGroup, provider.getOutputGroup(outputGroup));
+ }
+ }
+ return new OutputGroupProvider(resultBuilder.build());
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
index 6ba6f1e..fb99467 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
@@ -27,8 +27,10 @@
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.rules.SkylarkApiProvider;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -127,15 +129,50 @@
Set<Class<? extends TransitiveInfoProvider>> providers = new HashSet<>();
providers.addAll(base.providers.keySet());
+
+ // Merge output group providers.
+ OutputGroupProvider baseOutputGroupProvider = base.getProvider(OutputGroupProvider.class);
+ List<OutputGroupProvider> outputGroupProviders = new ArrayList<>();
+ if (baseOutputGroupProvider != null) {
+ outputGroupProviders.add(baseOutputGroupProvider);
+ }
+
+ for (Aspect aspect : aspects) {
+ final OutputGroupProvider aspectProvider = aspect.getProvider(OutputGroupProvider.class);
+ if (aspectProvider == null) {
+ continue;
+ }
+ outputGroupProviders.add(aspectProvider);
+ }
+ OutputGroupProvider outputGroupProvider = OutputGroupProvider.merge(outputGroupProviders);
+
+ // Validate that all other providers are only provided once.
for (Aspect aspect : aspects) {
for (TransitiveInfoProvider aspectProvider : aspect) {
- if (!providers.add(aspectProvider.getClass())) {
- throw new IllegalStateException(
- "Provider " + aspectProvider.getClass() + " provided twice");
+ Class<? extends TransitiveInfoProvider> aClass = aspectProvider.getClass();
+ if (OutputGroupProvider.class.equals(aClass)) {
+ continue;
+ }
+ if (!providers.add(aClass)) {
+ throw new IllegalStateException("Provider " + aClass + " provided twice");
}
}
}
- this.providers = base.providers;
+
+ if (baseOutputGroupProvider == outputGroupProvider) {
+ this.providers = base.providers;
+ } else {
+ ImmutableMap.Builder<Class<? extends TransitiveInfoProvider>, Object> builder =
+ new ImmutableMap.Builder<>();
+ for (Class<? extends TransitiveInfoProvider> aClass : base.providers.keySet()) {
+ if (OutputGroupProvider.class.equals(aClass)) {
+ continue;
+ }
+ builder.put(aClass, base.providers.get(aClass));
+ }
+ builder.put(OutputGroupProvider.class, outputGroupProvider);
+ this.providers = builder.build();
+ }
this.mandatoryStampFiles = base.mandatoryStampFiles;
this.configConditions = base.configConditions;
this.aspects = ImmutableList.copyOf(aspects);
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TopLevelArtifactHelper.java b/src/main/java/com/google/devtools/build/lib/analysis/TopLevelArtifactHelper.java
index ddf3646..043f4e3 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TopLevelArtifactHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TopLevelArtifactHelper.java
@@ -21,6 +21,7 @@
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.rules.test.TestProvider;
+import com.google.devtools.build.lib.skyframe.AspectValue;
/**
* A small static class containing utility methods for handling the inclusion of
@@ -96,6 +97,22 @@
}
/**
+ * Utility function to form a NestedSet of all top-level Artifacts of the given targets.
+ */
+ public static ArtifactsToBuild getAllArtifactsToBuildFromAspects(
+ Iterable<AspectValue> aspects, TopLevelArtifactContext context) {
+ NestedSetBuilder<Artifact> allArtifacts = NestedSetBuilder.stableOrder();
+ NestedSetBuilder<Artifact> importantArtifacts = NestedSetBuilder.stableOrder();
+ for (AspectValue aspect : aspects) {
+ ArtifactsToBuild aspectArtifacts = getAllArtifactsToBuild(aspect, context);
+ allArtifacts.addTransitive(aspectArtifacts.getAllArtifacts());
+ importantArtifacts.addTransitive(aspectArtifacts.getImportantArtifacts());
+ }
+ return new ArtifactsToBuild(importantArtifacts.build(), allArtifacts.build());
+ }
+
+
+ /**
* Returns all artifacts to build if this target is requested as a top-level target. The resulting
* set includes the temps and either the files to compile, if
* {@code context.compileOnly() == true}, or the files to run.
@@ -138,4 +155,39 @@
allBuilder.addTransitive(importantArtifacts);
return new ArtifactsToBuild(importantArtifacts, allBuilder.build());
}
+
+ public static ArtifactsToBuild getAllArtifactsToBuild(
+ AspectValue aspectValue, TopLevelArtifactContext context) {
+ NestedSetBuilder<Artifact> importantBuilder = NestedSetBuilder.stableOrder();
+ NestedSetBuilder<Artifact> allBuilder = NestedSetBuilder.stableOrder();
+
+ OutputGroupProvider outputGroupProvider =
+ aspectValue.getAspect().getProvider(OutputGroupProvider.class);
+
+ for (String outputGroup : context.outputGroups()) {
+ NestedSetBuilder<Artifact> results = NestedSetBuilder.stableOrder();
+
+ if (outputGroup.equals(OutputGroupProvider.DEFAULT)) {
+ // For the default group, we also throw in filesToBuild
+ FileProvider fileProvider = aspectValue.getAspect().getProvider(FileProvider.class);
+ if (fileProvider != null) {
+ results.addTransitive(fileProvider.getFilesToBuild());
+ }
+ }
+
+ if (outputGroupProvider != null) {
+ results.addTransitive(outputGroupProvider.getOutputGroup(outputGroup));
+ }
+
+ if (outputGroup.startsWith(OutputGroupProvider.HIDDEN_OUTPUT_GROUP_PREFIX)) {
+ allBuilder.addTransitive(results.build());
+ } else {
+ importantBuilder.addTransitive(results.build());
+ }
+ }
+
+ NestedSet<Artifact> importantArtifacts = importantBuilder.build();
+ allBuilder.addTransitive(importantArtifacts);
+ return new ArtifactsToBuild(importantArtifacts, allBuilder.build());
+ }
}