diff options
author | 2015-08-19 16:57:49 +0000 | |
---|---|---|
committer | 2015-08-20 14:49:12 +0000 | |
commit | e2033b1d28d3f17c7e307f62b2b13c3eed72a7f6 (patch) | |
tree | 25d06fad6196071dd07756c5e1a661bd689198fc /src/main/java/com/google/devtools/build | |
parent | 25f4494c484512898671aab57790f4917c0f9319 (diff) |
A prototype implementation of top-level aspects.
--
MOS_MIGRATED_REVID=101033236
Diffstat (limited to 'src/main/java/com/google/devtools/build')
26 files changed, 975 insertions, 245 deletions
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 3f4a06e274..95f0683363 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 @@ package com.google.devtools.build.lib.analysis; 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 @@ import java.util.Map; */ @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 @@ public final class Aspect implements Iterable<TransitiveInfoProvider> { 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 final class Aspect implements Iterable<TransitiveInfoProvider> { 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 @@ public final class Aspect implements Iterable<TransitiveInfoProvider> { 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 0000000000..faefb3c066 --- /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 3baae742e9..7ce8c04325 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.pkgcache.PackageManager; 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.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; import java.util.HashSet; @@ -434,12 +437,17 @@ public class BuildView { */ 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 @@ public class BuildView { 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 @@ public class BuildView { } /** + * 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 @@ public class BuildView { 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 @@ public class BuildView { } @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 @@ public class BuildView { } }); + 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 @@ public class BuildView { 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 @@ public class BuildView { 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 3b4030013f..543c71e28e 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.analysis.config.FragmentOptions; 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 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { 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 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { 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 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { return new ConfiguredRuleClassProvider( ImmutableMap.copyOf(ruleClassMap), ImmutableMap.copyOf(ruleDefinitionMap), + ImmutableMap.copyOf(aspectFactoryMap), defaultWorkspaceFile.toString(), ImmutableList.copyOf(buildInfoFactories), ImmutableList.copyOf(configurationOptions), @@ -247,6 +258,11 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { 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 class ConfiguredRuleClassProvider implements RuleClassProvider { 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 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { this.ruleClassMap = ruleClassMap; this.ruleDefinitionMap = ruleDefinitionMap; + this.aspectFactoryMap = aspectFactoryMap; this.defaultWorkspaceFile = defaultWorkspaceFile; this.buildInfoFactories = buildInfoFactories; this.configurationOptions = configurationOptions; @@ -302,6 +320,11 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { 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 90b373f0d0..5411f0f5b3 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.NestedSetBuilder; 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 @@ public final class OutputGroupProvider implements TransitiveInfoProvider { 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 @@ public final class OutputGroupProvider implements TransitiveInfoProvider { ? 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 6ba6f1e320..fb994670cc 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.OutputFile; 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 @@ public final class RuleConfiguredTarget extends AbstractConfiguredTarget { 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"); + } + } + } + + 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.providers = base.providers; 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 ddf3646d9a..043f4e327d 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.NestedSet; 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 @@ public final class TopLevelArtifactHelper { } /** + * 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 @@ public final class TopLevelArtifactHelper { 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()); + } } diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java index f9277a9b3e..a506c442eb 100644 --- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java +++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequest.java @@ -81,7 +81,7 @@ public class BuildRequest implements OptionsClassProvider { /** * Options interface--can be used to parse command-line arguments. * - * See also ExecutionOptions; from the user's point of view, there's no + * <p>See also ExecutionOptions; from the user's point of view, there's no * qualitative difference between these two sets of options. */ public static class BuildRequestOptions extends OptionsBase { @@ -261,6 +261,15 @@ public class BuildRequest implements OptionsClassProvider { help = "Check for modifications made to the output files of a build. Consider setting " + "this flag to false to see the effect on incremental build times.") public boolean checkOutputFiles; + + @Option( + name = "aspects", + converter = Converters.CommaSeparatedOptionListConverter.class, + defaultValue = "", + category = "undocumented", // for now + help = "List of top-level aspects" + ) + public List<String> aspects; } /** @@ -522,6 +531,10 @@ public class BuildRequest implements OptionsClassProvider { return ImmutableSortedSet.copyOf(getBuildOptions().multiCpus); } + public ImmutableList<String> getAspects() { + return ImmutableList.copyOf(getBuildOptions().aspects); + } + public static BuildRequest create(String commandName, OptionsProvider options, OptionsProvider startupOptions, List<String> targets, OutErr outErr, UUID commandId, long commandStartTime) { diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java index 0fe2562fe0..15fdd4b1e4 100644 --- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java +++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java @@ -460,9 +460,16 @@ public class BuildTool { getReporter().handle(Event.progress("Loading complete. Analyzing...")); Profiler.instance().markPhase(ProfilePhase.ANALYZE); - AnalysisResult analysisResult = getView().update(loadingResult, configurations, - request.getViewOptions(), request.getTopLevelArtifactContext(), getReporter(), - getEventBus()); + AnalysisResult analysisResult = + getView() + .update( + loadingResult, + configurations, + request.getAspects(), + request.getViewOptions(), + request.getTopLevelArtifactContext(), + getReporter(), + getEventBus()); // TODO(bazel-team): Merge these into one event. getEventBus().post(new AnalysisPhaseCompleteEvent(analysisResult.getTargetsToBuild(), diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java index fbe41409e5..43ec616a8b 100644 --- a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java +++ b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java @@ -83,6 +83,7 @@ import com.google.devtools.build.lib.rules.fileset.FilesetActionContextImpl; import com.google.devtools.build.lib.rules.test.TestActionContext; import com.google.devtools.build.lib.runtime.BlazeModule; import com.google.devtools.build.lib.runtime.BlazeRuntime; +import com.google.devtools.build.lib.skyframe.AspectValue; import com.google.devtools.build.lib.skyframe.Builder; import com.google.devtools.build.lib.skyframe.SkyframeExecutor; import com.google.devtools.build.lib.syntax.Label; @@ -391,12 +392,20 @@ public class ExecutionTool { Set<ConfiguredTarget> builtTargets = new HashSet<>(); boolean interrupted = false; try { - Iterable<Artifact> allArtifactsForProviders = Iterables.concat( - additionalArtifacts, - TopLevelArtifactHelper.getAllArtifactsToBuild( - analysisResult.getTargetsToBuild(), analysisResult.getTopLevelContext()) - .getAllArtifacts(), - TopLevelArtifactHelper.getAllArtifactsToTest(analysisResult.getTargetsToTest())); + Collection<AspectValue> aspects = analysisResult.getAspects(); + + Iterable<Artifact> allArtifactsForProviders = + Iterables.concat( + additionalArtifacts, + TopLevelArtifactHelper.getAllArtifactsToBuild( + analysisResult.getTargetsToBuild(), analysisResult.getTopLevelContext()) + .getAllArtifacts(), + TopLevelArtifactHelper.getAllArtifactsToBuildFromAspects( + aspects, analysisResult.getTopLevelContext()) + .getAllArtifacts(), + //TODO(dslomov): Artifacts to test from aspects? + TopLevelArtifactHelper.getAllArtifactsToTest(analysisResult.getTargetsToTest())); + if (request.isRunningInEmacs()) { // The syntax of this message is tightly constrained by lisp/progmodes/compile.el in emacs request.getOutErr().printErrLn("blaze: Entering directory `" + getExecRoot() + "/'"); @@ -422,11 +431,14 @@ public class ExecutionTool { Profiler.instance().markPhase(ProfilePhase.EXECUTE); - builder.buildArtifacts(additionalArtifacts, + builder.buildArtifacts( + additionalArtifacts, analysisResult.getParallelTests(), analysisResult.getExclusiveTests(), analysisResult.getTargetsToBuild(), - executor, builtTargets, + analysisResult.getAspects(), + executor, + builtTargets, request.getBuildOptions().explanationPath != null, runtime.getLastExecutionTimeRange()); @@ -711,8 +723,8 @@ public class ExecutionTool { } } if (headerFlag) { - outErr.printErr( - "Target " + label + " up-to-date (nothing to build)\n"); + outErr.printErr("Here we are\n"); + outErr.printErr("Target " + label + " up-to-date (nothing to build)\n"); } } diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java index d01609d3d5..94c19ef61b 100644 --- a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java @@ -32,6 +32,7 @@ import com.google.devtools.build.lib.actions.Executor; import com.google.devtools.build.lib.actions.MissingInputFileException; import com.google.devtools.build.lib.actions.ResourceManager; import com.google.devtools.build.lib.actions.TestExecException; +import com.google.devtools.build.lib.analysis.AspectCompleteEvent; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.TargetCompleteEvent; import com.google.devtools.build.lib.packages.BuildFileNotFoundException; @@ -39,6 +40,8 @@ import com.google.devtools.build.lib.rules.test.TestProvider; import com.google.devtools.build.lib.runtime.BugReport; import com.google.devtools.build.lib.skyframe.ActionExecutionInactivityWatchdog; import com.google.devtools.build.lib.skyframe.ActionExecutionValue; +import com.google.devtools.build.lib.skyframe.AspectCompletionValue; +import com.google.devtools.build.lib.skyframe.AspectValue; import com.google.devtools.build.lib.skyframe.Builder; import com.google.devtools.build.lib.skyframe.SkyFunctions; import com.google.devtools.build.lib.skyframe.SkyframeActionExecutor; @@ -90,10 +93,12 @@ public class SkyframeBuilder implements Builder { } @Override - public void buildArtifacts(Set<Artifact> artifacts, + public void buildArtifacts( + Set<Artifact> artifacts, Set<ConfiguredTarget> parallelTests, Set<ConfiguredTarget> exclusiveTests, Collection<ConfiguredTarget> targetsToBuild, + Collection<AspectValue> aspects, Executor executor, Set<ConfiguredTarget> builtTargets, boolean explain, @@ -125,17 +130,32 @@ public class SkyframeBuilder implements Builder { watchdog.start(); try { - result = skyframeExecutor.buildArtifacts(executor, artifacts, targetsToBuild, parallelTests, - /*exclusiveTesting=*/false, keepGoing, explain, numJobs, actionCacheChecker, - executionProgressReceiver); + result = + skyframeExecutor.buildArtifacts( + executor, + artifacts, + targetsToBuild, + aspects, + parallelTests, + /*exclusiveTesting=*/ false, + keepGoing, + explain, + numJobs, + actionCacheChecker, + executionProgressReceiver); // progressReceiver is finished, so unsynchronized access to builtTargets is now safe. success = processResult(result, keepGoing, skyframeExecutor); Preconditions.checkState( - !success || result.keyNames().size() - == (artifacts.size() + targetsToBuild.size() + parallelTests.size()), + !success + || result.keyNames().size() + == (artifacts.size() + + targetsToBuild.size() + + aspects.size() + + parallelTests.size()), "Build reported as successful but not all artifacts and targets built: %s, %s", - result, artifacts); + result, + artifacts); // Run exclusive tests: either tagged as "exclusive" or is run in an invocation with // --test_output=streamed. @@ -143,9 +163,19 @@ public class SkyframeBuilder implements Builder { for (ConfiguredTarget exclusiveTest : exclusiveTests) { // Since only one artifact is being built at a time, we don't worry about an artifact being // built and then the build being interrupted. - result = skyframeExecutor.buildArtifacts(executor, ImmutableSet.<Artifact>of(), - targetsToBuild, ImmutableSet.of(exclusiveTest), /*exclusiveTesting=*/true, keepGoing, - explain, numJobs, actionCacheChecker, null); + result = + skyframeExecutor.buildArtifacts( + executor, + ImmutableSet.<Artifact>of(), + targetsToBuild, + aspects, + ImmutableSet.of(exclusiveTest), /*exclusiveTesting=*/ + true, + keepGoing, + explain, + numJobs, + actionCacheChecker, + null); boolean exclusiveSuccess = processResult(result, keepGoing, skyframeExecutor); Preconditions.checkState(!exclusiveSuccess || !result.keyNames().isEmpty(), "Build reported as successful but test %s not executed: %s", @@ -177,7 +207,7 @@ public class SkyframeBuilder implements Builder { /** * Process the Skyframe update, taking into account the keepGoing setting. * - * Returns false if the update() failed, but we should continue. Returns true on success. + * <p>Returns false if the update() failed, but we should continue. Returns true on success. * Throws on fail-fast failures. */ private static boolean processResult(EvaluationResult<?> result, boolean keepGoing, @@ -305,6 +335,9 @@ public class SkyframeBuilder implements Builder { ConfiguredTarget target = val.getConfiguredTarget(); builtTargets.add(target); eventBus.post(TargetCompleteEvent.createSuccessful(target)); + } else if (type == SkyFunctions.ASPECT_COMPLETION && node != null) { + AspectCompletionValue val = (AspectCompletionValue) node; + eventBus.post(AspectCompleteEvent.createSuccessful(val.getAspectValue())); } else if (type == SkyFunctions.ACTION_EXECUTION) { // Remember all completed actions, even those in error, regardless of having been cached or // really executed. diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java index 6781d56e54..3f24efb208 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java @@ -30,6 +30,11 @@ public interface RuleClassProvider { Map<String, RuleClass> getRuleClassMap(); /** + * Returns a map from aspect names to aspect factory objects. + */ + Map<String, Class<? extends AspectFactory<?, ?, ?>>> getAspectFactoryMap(); + + /** * Returns a new Skylark Environment instance for rule creation. Implementations need to be * thread safe. */ diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidNeverlinkAspect.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidNeverlinkAspect.java index 189b6fb736..92207e8734 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidNeverlinkAspect.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidNeverlinkAspect.java @@ -38,14 +38,16 @@ import java.util.List; * ijars, */ public class AndroidNeverlinkAspect implements ConfiguredAspectFactory { - private static final ImmutableList<String> ATTRIBUTES = ImmutableList.of( - "deps", "exports", "runtime_deps", "binary_under_test", "$instrumentation_test_runner"); + public static final String NAME = "AndroidNeverlinkAspect"; + private static final ImmutableList<String> ATTRIBUTES = + ImmutableList.of( + "deps", "exports", "runtime_deps", "binary_under_test", "$instrumentation_test_runner"); @Override public Aspect create(ConfiguredTarget base, RuleContext ruleContext) { if (!JavaCommon.getConstraints(ruleContext).contains("android") && !ruleContext.getRule().getRuleClass().startsWith("android_")) { - return new Aspect.Builder().build(); + return new Aspect.Builder(NAME).build(); } List<TransitiveInfoCollection> deps = new ArrayList<>(); @@ -61,12 +63,14 @@ public class AndroidNeverlinkAspect implements ConfiguredAspectFactory { deps.addAll(ruleContext.getPrerequisites(attribute, Mode.TARGET)); } - return new Aspect.Builder() - .addProvider(AndroidNeverLinkLibrariesProvider.class, - new AndroidNeverLinkLibrariesProvider(AndroidCommon.collectTransitiveNeverlinkLibraries( - ruleContext, - deps, - base.getProvider(JavaRuntimeJarProvider.class).getRuntimeJars()))) + return new Aspect.Builder(NAME) + .addProvider( + AndroidNeverLinkLibrariesProvider.class, + new AndroidNeverLinkLibrariesProvider( + AndroidCommon.collectTransitiveNeverlinkLibraries( + ruleContext, + deps, + base.getProvider(JavaRuntimeJarProvider.class).getRuntimeJars()))) .build(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java index 99bdfb4e22..669dc4eb4a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/J2ObjcAspect.java @@ -50,6 +50,7 @@ import java.util.List; * J2ObjC transpilation aspect for Java rules. */ public class J2ObjcAspect implements ConfiguredAspectFactory { + public static final String NAME = "J2ObjcAspect"; /** * Adds the attribute aspect args to the given AspectDefinition.Builder. */ @@ -85,7 +86,7 @@ public class J2ObjcAspect implements ConfiguredAspectFactory { @Override public Aspect create(ConfiguredTarget base, RuleContext ruleContext) { - Aspect.Builder builder = new Aspect.Builder(); + Aspect.Builder builder = new Aspect.Builder(NAME); JavaCompilationArgsProvider compilationArgsProvider = base.getProvider(JavaCompilationArgsProvider.class); diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectCompletionValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectCompletionValue.java new file mode 100644 index 0000000000..695bc392e1 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectCompletionValue.java @@ -0,0 +1,47 @@ +// Copyright 2014 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.skyframe; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; + +import java.util.Collection; + +/** + * The value of an AspectCompletion. Currently this just stores an Aspect. + */ +public class AspectCompletionValue implements SkyValue { + private final AspectValue aspectValue; + + AspectCompletionValue(AspectValue aspectValue) { + this.aspectValue = aspectValue; + } + + public AspectValue getAspectValue() { + return aspectValue; + } + + public static Iterable<SkyKey> keys(Collection<AspectValue> targets) { + return Iterables.transform( + targets, + new Function<AspectValue, SkyKey>() { + @Override + public SkyKey apply(AspectValue aspectValue) { + return new SkyKey(SkyFunctions.ASPECT_COMPLETION, aspectValue.getKey()); + } + }); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java index 30358aca5b..7db3e463fe 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java @@ -80,9 +80,14 @@ public final class AspectFunction implements SkyFunction { "aspects must be attached to rules")); } - RuleConfiguredTarget associatedTarget = (RuleConfiguredTarget) - ((ConfiguredTargetValue) env.getValue(ConfiguredTargetValue.key( - key.getLabel(), key.getConfiguration()))).getConfiguredTarget(); + final ConfiguredTargetValue configuredTargetValue = + (ConfiguredTargetValue) + env.getValue(ConfiguredTargetValue.key(key.getLabel(), key.getConfiguration())); + if (configuredTargetValue == null) { + return null; + } + RuleConfiguredTarget associatedTarget = + (RuleConfiguredTarget) configuredTargetValue.getConfiguredTarget(); if (associatedTarget == null) { return null; @@ -152,7 +157,11 @@ public final class AspectFunction implements SkyFunction { Preconditions.checkNotNull(aspect); return new AspectValue( - aspect, ImmutableList.copyOf(analysisEnvironment.getRegisteredActions())); + key, + associatedTarget.getLabel(), + associatedTarget.getTarget().getLocation(), + aspect, + ImmutableList.copyOf(analysisEnvironment.getRegisteredActions())); } @Nullable diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectValue.java index b8a60273f6..2657caa438 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectValue.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectValue.java @@ -19,6 +19,7 @@ import com.google.devtools.build.lib.actions.Action; import com.google.devtools.build.lib.analysis.Aspect; import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.syntax.Label; import com.google.devtools.build.skyframe.SkyFunctionName; import com.google.devtools.build.skyframe.SkyKey; @@ -91,19 +92,49 @@ public final class AspectValue extends ActionLookupValue { } } + private final Label label; + private final Location location; + private final AspectKey key; private final Aspect aspect; - public AspectValue(Aspect aspect, Iterable<Action> actions) { + public AspectValue( + AspectKey key, Label label, Location location, Aspect aspect, Iterable<Action> actions) { super(actions); + this.location = location; + this.label = label; + this.key = key; this.aspect = aspect; } - public Aspect get() { + public Aspect getAspect() { return aspect; } + public Label getLabel() { + return label; + } + + public Location getLocation() { + return location; + } + + public AspectKey getKey() { + return key; + } + public static SkyKey key(Label label, BuildConfiguration configuration, Class<? extends ConfiguredAspectFactory> aspectFactory) { return new SkyKey(SkyFunctions.ASPECT, new AspectKey(label, configuration, aspectFactory)); } + + public static SkyKey key(AspectKey aspectKey) { + return new SkyKey(SkyFunctions.ASPECT, aspectKey); + } + + public static AspectKey createAspectKey( + Label label, + BuildConfiguration configuration, + Class<? extends ConfiguredAspectFactory> aspectFactory) { + return new AspectKey(label, configuration, aspectFactory); + } } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/Builder.java b/src/main/java/com/google/devtools/build/lib/skyframe/Builder.java index f23b09f28b..67e32b143e 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/Builder.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/Builder.java @@ -54,6 +54,7 @@ public interface Builder { * artifacts. * @param exclusiveTests are executed one at a time, only after all other tasks have completed * @param targetsToBuild Set of targets which will be built + * @param aspects Set of aspects that will be built * @param executor an opaque application-specific value that will be * passed down to the execute() method of any Action executed during * this call @@ -69,10 +70,12 @@ public interface Builder { * @throws TestExecException if any test fails */ @ThreadCompatible - void buildArtifacts(Set<Artifact> artifacts, + void buildArtifacts( + Set<Artifact> artifacts, Set<ConfiguredTarget> parallelTests, Set<ConfiguredTarget> exclusiveTests, Collection<ConfiguredTarget> targetsToBuild, + Collection<AspectValue> aspects, Executor executor, Set<ConfiguredTarget> builtTargets, boolean explain, diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java new file mode 100644 index 0000000000..80eb624f4a --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java @@ -0,0 +1,275 @@ +// Copyright 2014 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.skyframe; + +import com.google.common.eventbus.EventBus; +import com.google.devtools.build.lib.actions.ActionExecutionException; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.MissingInputFileException; +import com.google.devtools.build.lib.analysis.AspectCompleteEvent; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.LabelAndConfiguration; +import com.google.devtools.build.lib.analysis.TargetCompleteEvent; +import com.google.devtools.build.lib.analysis.TopLevelArtifactContext; +import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper; +import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper.ArtifactsToBuild; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey; +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 com.google.devtools.build.skyframe.ValueOrException2; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.Nullable; + +/** + * CompletionFunction builds the artifactsToBuild collection of a {@link ConfiguredTarget}. + */ +public final class CompletionFunction<TValue extends SkyValue, TResult extends SkyValue> + implements SkyFunction { + + /** + * A strategy for completing the build. + */ + public interface Completor<TValue, TResult extends SkyValue> { + + /** + * Obtains an analysis result value from environment. + */ + TValue getValueFromSkyKey(SkyKey skyKey, Environment env); + + /** + * Returns all artefacts that need to be built to complete the {@code value} + */ + ArtifactsToBuild getAllArtifactsToBuild(TValue value, TopLevelArtifactContext context); + + /** + * Creates an event reporting an absent input artifact. + */ + Event getRootCauseError(TValue value, Label rootCause); + + /** + * Creates an error message reporting {@code missingCount} missing input files. + */ + MissingInputFileException getMissingFilesException(TValue value, int missingCount); + + /** + * Creates a successful completion value. + */ + TResult createResult(TValue value); + + /** + * Creates a failed completion value. + */ + SkyValue createFailed(TValue value, NestedSet<Label> rootCauses); + } + + private static class TargetCompletor + implements Completor<ConfiguredTargetValue, TargetCompletionValue> { + @Override + public ConfiguredTargetValue getValueFromSkyKey(SkyKey skyKey, Environment env) { + LabelAndConfiguration lac = (LabelAndConfiguration) skyKey.argument(); + return (ConfiguredTargetValue) + env.getValue(ConfiguredTargetValue.key(lac.getLabel(), lac.getConfiguration())); + } + + @Override + public ArtifactsToBuild getAllArtifactsToBuild( + ConfiguredTargetValue value, TopLevelArtifactContext topLevelContext) { + return TopLevelArtifactHelper.getAllArtifactsToBuild( + value.getConfiguredTarget(), topLevelContext); + } + + @Override + public Event getRootCauseError(ConfiguredTargetValue ctValue, Label rootCause) { + return Event.error( + ctValue.getConfiguredTarget().getTarget().getLocation(), + String.format( + "%s: missing input file '%s'", ctValue.getConfiguredTarget().getLabel(), rootCause)); + } + + @Override + public MissingInputFileException getMissingFilesException( + ConfiguredTargetValue value, int missingCount) { + return new MissingInputFileException( + value.getConfiguredTarget().getTarget().getLocation() + + " " + + missingCount + + " input file(s) do not exist", + value.getConfiguredTarget().getTarget().getLocation()); + } + + @Override + public TargetCompletionValue createResult(ConfiguredTargetValue value) { + return new TargetCompletionValue(value.getConfiguredTarget()); + } + + @Override + public SkyValue createFailed(ConfiguredTargetValue value, NestedSet<Label> rootCauses) { + return TargetCompleteEvent.createFailed(value.getConfiguredTarget(), rootCauses); + } + } + + private static class AspectCompletor implements Completor<AspectValue, AspectCompletionValue> { + @Override + public AspectValue getValueFromSkyKey(SkyKey skyKey, Environment env) { + AspectKey aspectKey = (AspectKey) skyKey.argument(); + return (AspectValue) env.getValue(AspectValue.key(aspectKey)); + } + + @Override + public ArtifactsToBuild getAllArtifactsToBuild( + AspectValue value, TopLevelArtifactContext topLevelArtifactContext) { + return TopLevelArtifactHelper.getAllArtifactsToBuild(value, topLevelArtifactContext); + } + + @Override + public Event getRootCauseError(AspectValue value, Label rootCause) { + return Event.error( + value.getLocation(), + String.format( + "%s, aspect %s: missing input file '%s'", + value.getLabel(), + value.getAspect().getName(), + rootCause)); + } + + @Override + public MissingInputFileException getMissingFilesException(AspectValue value, int missingCount) { + return new MissingInputFileException( + value.getLabel() + + ", aspect " + + value.getAspect().getName() + + missingCount + + " input file(s) do not exist", + value.getLocation()); + } + + @Override + public AspectCompletionValue createResult(AspectValue value) { + return new AspectCompletionValue(value); + } + + @Override + public SkyValue createFailed(AspectValue value, NestedSet<Label> rootCauses) { + return AspectCompleteEvent.createFailed(value, rootCauses); + } + } + + public static SkyFunction targetCompletionFunction(AtomicReference<EventBus> eventBusRef) { + return new CompletionFunction<>(eventBusRef, new TargetCompletor()); + } + + public static SkyFunction aspectCompletionFunction(AtomicReference<EventBus> eventBusRef) { + return new CompletionFunction<>(eventBusRef, new AspectCompletor()); + } + + private final AtomicReference<EventBus> eventBusRef; + private final Completor<TValue, TResult> completor; + + private CompletionFunction( + AtomicReference<EventBus> eventBusRef, Completor<TValue, TResult> completor) { + this.eventBusRef = eventBusRef; + this.completor = completor; + } + + @Nullable + @Override + public SkyValue compute(SkyKey skyKey, Environment env) throws CompletionFunctionException { + TValue value = completor.getValueFromSkyKey(skyKey, env); + TopLevelArtifactContext topLevelContext = PrecomputedValue.TOP_LEVEL_CONTEXT.get(env); + if (env.valuesMissing()) { + return null; + } + + Map<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>> inputDeps = + env.getValuesOrThrow( + ArtifactValue.mandatoryKeys( + completor.getAllArtifactsToBuild(value, topLevelContext).getAllArtifacts()), + MissingInputFileException.class, + ActionExecutionException.class); + + int missingCount = 0; + ActionExecutionException firstActionExecutionException = null; + MissingInputFileException missingInputException = null; + NestedSetBuilder<Label> rootCausesBuilder = NestedSetBuilder.stableOrder(); + for (Map.Entry<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>> + depsEntry : inputDeps.entrySet()) { + Artifact input = ArtifactValue.artifact(depsEntry.getKey()); + try { + depsEntry.getValue().get(); + } catch (MissingInputFileException e) { + missingCount++; + final Label inputOwner = input.getOwner(); + if (inputOwner != null) { + rootCausesBuilder.add(inputOwner); + env.getListener().handle(completor.getRootCauseError(value, inputOwner)); + } + } catch (ActionExecutionException e) { + rootCausesBuilder.addTransitive(e.getRootCauses()); + if (firstActionExecutionException == null) { + firstActionExecutionException = e; + } + } + } + + if (missingCount > 0) { + missingInputException = completor.getMissingFilesException(value, missingCount); + } + + NestedSet<Label> rootCauses = rootCausesBuilder.build(); + if (!rootCauses.isEmpty()) { + eventBusRef.get().post(completor.createFailed(value, rootCauses)); + if (firstActionExecutionException != null) { + throw new CompletionFunctionException(firstActionExecutionException); + } else { + throw new CompletionFunctionException(missingInputException); + } + } + + return env.valuesMissing() ? null : completor.createResult(value); + } + + @Override + public String extractTag(SkyKey skyKey) { + return Label.print(((LabelAndConfiguration) skyKey.argument()).getLabel()); + } + + private static final class CompletionFunctionException extends SkyFunctionException { + + private final ActionExecutionException actionException; + + public CompletionFunctionException(ActionExecutionException e) { + super(e, Transience.PERSISTENT); + this.actionException = e; + } + + public CompletionFunctionException(MissingInputFileException e) { + super(e, Transience.TRANSIENT); + this.actionException = null; + } + + @Override + public boolean isCatastrophic() { + return actionException != null && actionException.isCatastrophe(); + } + } +} 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 dff91d6976..1132a7b02b 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 @@ -309,7 +309,7 @@ final class ConfiguredTargetFunction implements SkyFunction { // Dependent aspect has either not been computed yet or is in error. return null; } - result.put(depKey, aspectValue.get()); + result.put(depKey, aspectValue.getAspect()); } } @@ -320,7 +320,7 @@ final class ConfiguredTargetFunction implements SkyFunction { Class<? extends ConfiguredAspectFactory> aspectFactory) { AspectDefinition aspectDefinition = AspectFactory.Util.create(aspectFactory).getDefinition(); for (Class<?> provider : aspectDefinition.getRequiredProviders()) { - if (dep.getProvider((Class<? extends TransitiveInfoProvider>) provider) == null) { + if (dep.getProvider(provider.asSubclass(TransitiveInfoProvider.class)) == null) { return false; } } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java index caec042df4..aba4e49062 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java @@ -60,8 +60,9 @@ public final class SkyFunctions { SkyFunctionName.create("POST_CONFIGURED_TARGET"); public static final SkyFunctionName TARGET_COMPLETION = SkyFunctionName.create("TARGET_COMPLETION"); - public static final SkyFunctionName TEST_COMPLETION = - SkyFunctionName.create("TEST_COMPLETION"); + public static final SkyFunctionName ASPECT_COMPLETION = + SkyFunctionName.create("ASPECT_COMPLETION"); + public static final SkyFunctionName TEST_COMPLETION = SkyFunctionName.create("TEST_COMPLETION"); public static final SkyFunctionName BUILD_CONFIGURATION = SkyFunctionName.create("BUILD_CONFIGURATION"); public static final SkyFunctionName CONFIGURATION_FRAGMENT = diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAnalysisResult.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAnalysisResult.java new file mode 100644 index 0000000000..323dc3d88a --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAnalysisResult.java @@ -0,0 +1,50 @@ +// Copyright 2014 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.skyframe; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.skyframe.WalkableGraph; + +import java.util.Collection; + +/** + * Encapsulates the raw analysis result of top level targets and aspects coming from Skyframe. + */ +public class SkyframeAnalysisResult { + private final ImmutableList<ConfiguredTarget> configuredTargets; + private final WalkableGraph walkableGraph; + private final ImmutableList<AspectValue> aspects; + + public SkyframeAnalysisResult( + ImmutableList<ConfiguredTarget> configuredTargets, + WalkableGraph walkableGraph, + ImmutableList<AspectValue> aspects) { + this.configuredTargets = configuredTargets; + this.walkableGraph = walkableGraph; + this.aspects = aspects; + } + + public Collection<ConfiguredTarget> getConfiguredTargets() { + return configuredTargets; + } + + public WalkableGraph getWalkableGraph() { + return walkableGraph; + } + + public Collection<AspectValue> getAspects() { + return aspects; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java index ed85621d49..eed73aef66 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.skyframe; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -48,11 +49,11 @@ import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.skyframe.ActionLookupValue.ActionLookupKey; +import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey; import com.google.devtools.build.lib.skyframe.BuildInfoCollectionValue.BuildInfoKeyAndConfig; import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.ConfiguredValueCreationException; import com.google.devtools.build.lib.skyframe.SkyframeActionExecutor.ConflictException; import com.google.devtools.build.lib.syntax.Label; -import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.CycleInfo; import com.google.devtools.build.skyframe.ErrorInfo; @@ -61,7 +62,6 @@ import com.google.devtools.build.skyframe.EvaluationResult; import com.google.devtools.build.skyframe.SkyFunction.Environment; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; -import com.google.devtools.build.skyframe.WalkableGraph; import java.util.Collection; import java.util.HashSet; @@ -160,33 +160,51 @@ public final class SkyframeBuildView { * * @return the configured targets that should be built along with a WalkableGraph of the analysis. */ - public Pair<Collection<ConfiguredTarget>, WalkableGraph> configureTargets( - List<ConfiguredTargetKey> values, EventBus eventBus, boolean keepGoing) - throws InterruptedException, ViewCreationFailedException { + public SkyframeAnalysisResult configureTargets( + List<ConfiguredTargetKey> values, + List<AspectKey> aspectKeys, + EventBus eventBus, + boolean keepGoing) + throws InterruptedException, ViewCreationFailedException { enableAnalysis(true); - EvaluationResult<ConfiguredTargetValue> result; + EvaluationResult<ActionLookupValue> result; try { - result = skyframeExecutor.configureTargets(values, keepGoing); + result = skyframeExecutor.configureTargets(values, aspectKeys, keepGoing); } finally { enableAnalysis(false); } ImmutableMap<Action, ConflictException> badActions = skyframeExecutor.findArtifactConflicts(); + Collection<AspectValue> goodAspects = Lists.newArrayListWithCapacity(values.size()); + for (AspectKey aspectKey : aspectKeys) { + AspectValue value = (AspectValue) result.get(AspectValue.key(aspectKey)); + if (value == null) { + // Skip aspects that couldn't be applied to targets. + continue; + } + goodAspects.add(value); + } + // Filter out all CTs that have a bad action and convert to a list of configured targets. This // code ensures that the resulting list of configured targets has the same order as the incoming // list of values, i.e., that the order is deterministic. Collection<ConfiguredTarget> goodCts = Lists.newArrayListWithCapacity(values.size()); for (ConfiguredTargetKey value : values) { - ConfiguredTargetValue ctValue = result.get(ConfiguredTargetValue.key(value)); + ConfiguredTargetValue ctValue = + (ConfiguredTargetValue) result.get(ConfiguredTargetValue.key(value)); if (ctValue == null) { continue; } goodCts.add(ctValue.getConfiguredTarget()); } + if (!result.hasError() && badActions.isEmpty()) { setDeserializedArtifactOwners(); - return Pair.of(goodCts, result.getWalkableGraph()); + return new SkyframeAnalysisResult( + ImmutableList.copyOf(goodCts), + result.getWalkableGraph(), + ImmutableList.copyOf(goodAspects)); } // --nokeep_going so we fail with an exception for the first error. @@ -291,7 +309,10 @@ public final class SkyframeBuildView { } } setDeserializedArtifactOwners(); - return Pair.of(goodCts, result.getWalkableGraph()); + return new SkyframeAnalysisResult( + ImmutableList.copyOf(goodCts), + result.getWalkableGraph(), + ImmutableList.copyOf(goodAspects)); } @Nullable diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java index 8b482e5aa2..cbcc676aa4 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java @@ -83,6 +83,7 @@ import com.google.devtools.build.lib.pkgcache.PackageManager; import com.google.devtools.build.lib.pkgcache.PathPackageLocator; import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader; import com.google.devtools.build.lib.profiler.Profiler; +import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey; import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.FileDirtinessChecker; import com.google.devtools.build.lib.skyframe.SkyframeActionExecutor.ActionCompletedReceiver; import com.google.devtools.build.lib.skyframe.SkyframeActionExecutor.ProgressSupplier; @@ -338,7 +339,8 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { map.put( SkyFunctions.WORKSPACE_FILE, new WorkspaceFileFunction(ruleClassProvider, pkgFactory, directories)); - map.put(SkyFunctions.TARGET_COMPLETION, new TargetCompletionFunction(eventBus)); + map.put(SkyFunctions.TARGET_COMPLETION, CompletionFunction.targetCompletionFunction(eventBus)); + map.put(SkyFunctions.ASPECT_COMPLETION, CompletionFunction.aspectCompletionFunction(eventBus)); map.put(SkyFunctions.TEST_COMPLETION, new TestCompletionFunction()); map.put(SkyFunctions.ARTIFACT, new ArtifactFunction(allowedMissingInputs)); map.put(SkyFunctions.BUILD_INFO_COLLECTION, new BuildInfoCollectionFunction(artifactFactory, @@ -1003,13 +1005,15 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { Executor executor, Set<Artifact> artifactsToBuild, Collection<ConfiguredTarget> targetsToBuild, + Collection<AspectValue> aspects, Collection<ConfiguredTarget> targetsToTest, boolean exclusiveTesting, boolean keepGoing, boolean explain, int numJobs, ActionCacheChecker actionCacheChecker, - @Nullable EvaluationProgressReceiver executionProgressReceiver) throws InterruptedException { + @Nullable EvaluationProgressReceiver executionProgressReceiver) + throws InterruptedException { checkActive(); Preconditions.checkState(actionLogBufferPathGenerator != null); @@ -1020,9 +1024,13 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { progressReceiver.executionProgressReceiver = executionProgressReceiver; Iterable<SkyKey> artifactKeys = ArtifactValue.mandatoryKeys(artifactsToBuild); Iterable<SkyKey> targetKeys = TargetCompletionValue.keys(targetsToBuild); + Iterable<SkyKey> aspectKeys = AspectCompletionValue.keys(aspects); Iterable<SkyKey> testKeys = TestCompletionValue.keys(targetsToTest, exclusiveTesting); - return buildDriver.evaluate(Iterables.concat(artifactKeys, targetKeys, testKeys), keepGoing, - numJobs, errorEventListener); + return buildDriver.evaluate( + Iterables.concat(artifactKeys, targetKeys, aspectKeys, testKeys), + keepGoing, + numJobs, + errorEventListener); } finally { progressReceiver.executionProgressReceiver = null; // Also releases thread locks. @@ -1108,7 +1116,7 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { continue DependentNodeLoop; } - aspects.add(((AspectValue) result.get(aspectKey)).get()); + aspects.add(((AspectValue) result.get(aspectKey)).getAspect()); } cts.add(RuleConfiguredTarget.mergeAspects(configuredTarget, aspects)); @@ -1157,13 +1165,20 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { /** * Configures a given set of configured targets. */ - public EvaluationResult<ConfiguredTargetValue> configureTargets( - List<ConfiguredTargetKey> values, boolean keepGoing) throws InterruptedException { + public EvaluationResult<ActionLookupValue> configureTargets( + List<ConfiguredTargetKey> values, List<AspectKey> aspectKeys, boolean keepGoing) + throws InterruptedException { checkActive(); + Set<SkyKey> keys = new HashSet<>(); + keys.addAll(ConfiguredTargetValue.keys(values)); + for (AspectKey aspectKey : aspectKeys) { + keys.add(AspectValue.key(aspectKey)); + } + // Make sure to not run too many analysis threads. This can cause memory thrashing. - return buildDriver.evaluate(ConfiguredTargetValue.keys(values), keepGoing, - ResourceUsage.getAvailableProcessors(), errorEventListener); + return buildDriver.evaluate( + keys, keepGoing, ResourceUsage.getAvailableProcessors(), errorEventListener); } /** diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TargetCompletionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/TargetCompletionFunction.java deleted file mode 100644 index e89c0fc56d..0000000000 --- a/src/main/java/com/google/devtools/build/lib/skyframe/TargetCompletionFunction.java +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2014 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.skyframe; - -import com.google.common.eventbus.EventBus; -import com.google.devtools.build.lib.actions.ActionExecutionException; -import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.actions.MissingInputFileException; -import com.google.devtools.build.lib.analysis.ConfiguredTarget; -import com.google.devtools.build.lib.analysis.LabelAndConfiguration; -import com.google.devtools.build.lib.analysis.TargetCompleteEvent; -import com.google.devtools.build.lib.analysis.TopLevelArtifactContext; -import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper; -import com.google.devtools.build.lib.collect.nestedset.NestedSet; -import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; -import com.google.devtools.build.lib.events.Event; -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 com.google.devtools.build.skyframe.ValueOrException2; - -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -import javax.annotation.Nullable; - -/** - * TargetCompletionFunction builds the artifactsToBuild collection of a {@link ConfiguredTarget}. - */ -public final class TargetCompletionFunction implements SkyFunction { - - private final AtomicReference<EventBus> eventBusRef; - - public TargetCompletionFunction(AtomicReference<EventBus> eventBusRef) { - this.eventBusRef = eventBusRef; - } - - @Nullable - @Override - public SkyValue compute(SkyKey skyKey, Environment env) throws TargetCompletionFunctionException { - LabelAndConfiguration lac = (LabelAndConfiguration) skyKey.argument(); - ConfiguredTargetValue ctValue = (ConfiguredTargetValue) - env.getValue(ConfiguredTargetValue.key(lac.getLabel(), lac.getConfiguration())); - TopLevelArtifactContext topLevelContext = PrecomputedValue.TOP_LEVEL_CONTEXT.get(env); - if (env.valuesMissing()) { - return null; - } - - Map<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>> inputDeps = - env.getValuesOrThrow(ArtifactValue.mandatoryKeys( - TopLevelArtifactHelper.getAllArtifactsToBuild( - ctValue.getConfiguredTarget(), topLevelContext).getAllArtifacts()), - MissingInputFileException.class, ActionExecutionException.class); - - int missingCount = 0; - ActionExecutionException firstActionExecutionException = null; - MissingInputFileException missingInputException = null; - NestedSetBuilder<Label> rootCausesBuilder = NestedSetBuilder.stableOrder(); - for (Map.Entry<SkyKey, ValueOrException2<MissingInputFileException, - ActionExecutionException>> depsEntry : inputDeps.entrySet()) { - Artifact input = ArtifactValue.artifact(depsEntry.getKey()); - try { - depsEntry.getValue().get(); - } catch (MissingInputFileException e) { - missingCount++; - if (input.getOwner() != null) { - rootCausesBuilder.add(input.getOwner()); - env.getListener().handle(Event.error( - ctValue.getConfiguredTarget().getTarget().getLocation(), - String.format("%s: missing input file '%s'", - lac.getLabel(), input.getOwner()))); - } - } catch (ActionExecutionException e) { - rootCausesBuilder.addTransitive(e.getRootCauses()); - if (firstActionExecutionException == null) { - firstActionExecutionException = e; - } - } - } - - if (missingCount > 0) { - missingInputException = new MissingInputFileException( - ctValue.getConfiguredTarget().getTarget().getLocation() + " " + missingCount - + " input file(s) do not exist", ctValue.getConfiguredTarget().getTarget().getLocation()); - } - - NestedSet<Label> rootCauses = rootCausesBuilder.build(); - if (!rootCauses.isEmpty()) { - eventBusRef.get().post( - TargetCompleteEvent.createFailed(ctValue.getConfiguredTarget(), rootCauses)); - if (firstActionExecutionException != null) { - throw new TargetCompletionFunctionException(firstActionExecutionException); - } else { - throw new TargetCompletionFunctionException(missingInputException); - } - } - - return env.valuesMissing() ? null : new TargetCompletionValue(ctValue.getConfiguredTarget()); - } - - @Override - public String extractTag(SkyKey skyKey) { - return Label.print(((LabelAndConfiguration) skyKey.argument()).getLabel()); - } - - private static final class TargetCompletionFunctionException extends SkyFunctionException { - - private final ActionExecutionException actionException; - - public TargetCompletionFunctionException(ActionExecutionException e) { - super(e, Transience.PERSISTENT); - this.actionException = e; - } - - public TargetCompletionFunctionException(MissingInputFileException e) { - super(e, Transience.TRANSIENT); - this.actionException = null; - } - - @Override - public boolean isCatastrophic() { - return actionException != null && actionException.isCatastrophe(); - } - } -} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TestCompletionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/TestCompletionFunction.java index b6f96061ef..35abad2fa2 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/TestCompletionFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/TestCompletionFunction.java @@ -23,7 +23,7 @@ import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; /** - * TargetCompletionFunction builds all relevant test artifacts of a {@link + * TestCompletionFunction builds all relevant test artifacts of a {@link * com.google.devtools.build.lib.analysis.ConfiguredTarget}. This includes test shards and repeated * runs. */ |