// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.analysis; import static com.google.common.collect.Iterables.concat; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.common.eventbus.EventBus; import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; import com.google.devtools.build.lib.actions.ActionGraph; import com.google.devtools.build.lib.actions.ActionLookupValue; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.ArtifactFactory; import com.google.devtools.build.lib.actions.ArtifactOwner; import com.google.devtools.build.lib.actions.PackageRoots; import com.google.devtools.build.lib.analysis.DependencyResolver.InconsistentAspectOrderException; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection; import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; import com.google.devtools.build.lib.analysis.config.ConfigurationResolver; import com.google.devtools.build.lib.analysis.config.FragmentClassSet; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition; import com.google.devtools.build.lib.analysis.config.transitions.NoTransition; import com.google.devtools.build.lib.analysis.constraints.TopLevelConstraintSemantics; import com.google.devtools.build.lib.analysis.test.CoverageReportActionFactory; import com.google.devtools.build.lib.analysis.test.CoverageReportActionFactory.CoverageReportActionsWrapper; import com.google.devtools.build.lib.analysis.test.InstrumentedFilesProvider; import com.google.devtools.build.lib.cmdline.Label; 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.concurrent.ThreadSafety.ThreadCompatible; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.ExtendedEventHandler; import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.AspectClass; import com.google.devtools.build.lib.packages.AspectDescriptor; import com.google.devtools.build.lib.packages.AspectParameters; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.NativeAspectClass; import com.google.devtools.build.lib.packages.NoSuchPackageException; import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.packages.NoSuchThingException; import com.google.devtools.build.lib.packages.PackageSpecification; import com.google.devtools.build.lib.packages.PackageSpecification.PackageGroupContents; import com.google.devtools.build.lib.packages.RawAttributeMapper; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleTransitionFactory; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.pkgcache.LoadingResult; import com.google.devtools.build.lib.pkgcache.PackageManager; import com.google.devtools.build.lib.pkgcache.PackageManager.PackageManagerStatistics; import com.google.devtools.build.lib.skyframe.AspectValue; import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey; import com.google.devtools.build.lib.skyframe.AspectValue.AspectValueKey; import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndTarget; 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.skyframe.ToolchainUtil.ToolchainContextException; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.SkylarkImport; import com.google.devtools.build.lib.syntax.SkylarkImports; import com.google.devtools.build.lib.syntax.SkylarkImports.SkylarkImportSyntaxException; import com.google.devtools.build.lib.util.OrderedSetMultimap; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.RegexFilter; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.WalkableGraph; import com.google.devtools.common.options.Converter; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionDocumentationCategory; import com.google.devtools.common.options.OptionEffectTag; import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParsingException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.annotation.Nullable; /** *

The BuildView presents a semantically-consistent and transitively-closed * dependency graph for some set of packages. * *

Package design

* *

This package contains the Blaze dependency analysis framework (aka * "analysis phase"). The goal of this code is to perform semantic analysis of * all of the build targets required for a given build, to report * errors/warnings for any problems in the input, and to construct an "action * graph" (see {@code lib.actions} package) correctly representing the work to * be done during the execution phase of the build. * *

Configurations the inputs to a build come from two sources: the * intrinsic inputs, specified in the BUILD file, are called targets. * The environmental inputs, coming from the build tool, the command-line, or * configuration files, are called the configuration. Only when a * target and a configuration are combined is there sufficient information to * perform a build.

* *

Targets are implemented by the {@link Target} hierarchy in the {@code * lib.packages} code. Configurations are implemented by {@link * BuildConfiguration}. The pair of these together is represented by an * instance of class {@link ConfiguredTarget}; this is the root of a hierarchy * with different implementations for each kind of target: source file, derived * file, rules, etc. * *

The framework code in this package (as opposed to its subpackages) is * responsible for constructing the {@code ConfiguredTarget} graph for a given * target and configuration, taking care of such issues as: *

* *

See also {@link ConfiguredTarget} which documents some important * invariants. */ public class BuildView { /** * Options that affect the mechanism of analysis. These are distinct from {@link * com.google.devtools.build.lib.analysis.config.BuildOptions}, which affect the value of a * BuildConfiguration. */ public static class Options extends OptionsBase { @Option( name = "analysis_warnings_as_errors", deprecationWarning = "analysis_warnings_as_errors is now a no-op and will be removed in" + " an upcoming Blaze release", defaultValue = "false", category = "strategy", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.NO_OP}, help = "Treat visible analysis warnings as errors." ) public boolean analysisWarningsAsErrors; @Option( name = "discard_analysis_cache", defaultValue = "false", category = "strategy", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Discard the analysis cache immediately after the analysis phase completes." + " Reduces memory usage by ~10%, but makes further incremental builds slower." ) public boolean discardAnalysisCache; @Option( name = "experimental_extra_action_filter", defaultValue = "", category = "experimental", converter = RegexFilter.RegexFilterConverter.class, documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Filters set of targets to schedule extra_actions for." ) public RegexFilter extraActionFilter; @Option( name = "experimental_extra_action_top_level_only", defaultValue = "false", category = "experimental", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Only schedules extra_actions for top level targets." ) public boolean extraActionTopLevelOnly; @Option( name = "version_window_for_dirty_node_gc", defaultValue = "0", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Nodes that have been dirty for more than this many versions will be deleted" + " from the graph upon the next update. Values must be non-negative long integers," + " or -1 indicating the maximum possible window." ) public long versionWindowForDirtyNodeGc; @Deprecated @Option( name = "experimental_interleave_loading_and_analysis", defaultValue = "true", category = "experimental", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, help = "No-op." ) public boolean interleaveLoadingAndAnalysis; } private static final Logger logger = Logger.getLogger(BuildView.class.getName()); private final BlazeDirectories directories; private final SkyframeExecutor skyframeExecutor; private final SkyframeBuildView skyframeBuildView; private final ConfiguredRuleClassProvider ruleClassProvider; /** * A factory class to create the coverage report action. May be null. */ @Nullable private final CoverageReportActionFactory coverageReportActionFactory; @VisibleForTesting public Set getSkyframeEvaluatedTargetKeysForTesting() { return skyframeBuildView.getEvaluatedTargetKeys(); } /** The number of targets freshly evaluated in the last analysis run. */ public int getTargetsVisited() { return skyframeBuildView.getEvaluatedTargetKeys().size(); } public PackageManagerStatistics getAndClearPkgManagerStatistics() { return skyframeExecutor.getPackageManager().getAndClearStatistics(); } public BuildView(BlazeDirectories directories, ConfiguredRuleClassProvider ruleClassProvider, SkyframeExecutor skyframeExecutor, CoverageReportActionFactory coverageReportActionFactory) { this.directories = directories; this.coverageReportActionFactory = coverageReportActionFactory; this.ruleClassProvider = ruleClassProvider; this.skyframeExecutor = Preconditions.checkNotNull(skyframeExecutor); this.skyframeBuildView = skyframeExecutor.getSkyframeBuildView(); } /** * Returns whether the given configured target has errors. */ @VisibleForTesting public boolean hasErrors(ConfiguredTarget configuredTarget) { return configuredTarget == null; } /** * Sets the configurations. Not thread-safe. DO NOT CALL except from tests! */ @VisibleForTesting public void setConfigurationsForTesting(BuildConfigurationCollection configurations) { skyframeBuildView.setConfigurations(configurations); } public ArtifactFactory getArtifactFactory() { return skyframeBuildView.getArtifactFactory(); } @VisibleForTesting WorkspaceStatusAction getLastWorkspaceBuildInfoActionForTesting() throws InterruptedException { return skyframeExecutor.getLastWorkspaceStatusAction(); } @Override public int hashCode() { throw new UnsupportedOperationException(); // avoid nondeterminism } /** * Return value for {@link BuildView#update} and {@code BuildTool.prepareToBuild}. */ public static final class AnalysisResult { private final ImmutableSet targetsToBuild; @Nullable private final ImmutableList targetsToTest; private final ImmutableSet targetsToSkip; @Nullable private final String error; private final ActionGraph actionGraph; private final ImmutableSet artifactsToBuild; private final ImmutableSet parallelTests; private final ImmutableSet exclusiveTests; @Nullable private final TopLevelArtifactContext topLevelContext; private final ImmutableList aspects; private final PackageRoots packageRoots; private final String workspaceName; List topLevelTargetsWithConfigs; private AnalysisResult( Collection targetsToBuild, Collection aspects, Collection targetsToTest, Collection targetsToSkip, @Nullable String error, ActionGraph actionGraph, Collection artifactsToBuild, Collection parallelTests, Collection exclusiveTests, TopLevelArtifactContext topLevelContext, PackageRoots packageRoots, String workspaceName, List topLevelTargetsWithConfigs) { this.targetsToBuild = ImmutableSet.copyOf(targetsToBuild); this.aspects = ImmutableList.copyOf(aspects); this.targetsToTest = targetsToTest == null ? null : ImmutableList.copyOf(targetsToTest); this.targetsToSkip = ImmutableSet.copyOf(targetsToSkip); this.error = error; this.actionGraph = actionGraph; this.artifactsToBuild = ImmutableSet.copyOf(artifactsToBuild); this.parallelTests = ImmutableSet.copyOf(parallelTests); this.exclusiveTests = ImmutableSet.copyOf(exclusiveTests); this.topLevelContext = topLevelContext; this.packageRoots = packageRoots; this.workspaceName = workspaceName; this.topLevelTargetsWithConfigs = topLevelTargetsWithConfigs; } /** * Returns configured targets to build. */ public ImmutableSet getTargetsToBuild() { return targetsToBuild; } /** @see PackageRoots */ public PackageRoots getPackageRoots() { return packageRoots; } /** * Returns aspects of configured targets to build. * *

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 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). */ @Nullable public Collection getTargetsToTest() { return targetsToTest; } /** * Returns the configured targets that should not be executed because they're not * platform-compatible with the current build. * *

For example: tests that aren't intended for the designated CPU. */ public ImmutableSet getTargetsToSkip() { return targetsToSkip; } public ImmutableSet getAdditionalArtifactsToBuild() { return artifactsToBuild; } public ImmutableSet getExclusiveTests() { return exclusiveTests; } public ImmutableSet getParallelTests() { return parallelTests; } /** * Returns an error description (if any). */ @Nullable public String getError() { return error; } public boolean hasError() { return error != null; } /** * Returns the action graph. */ public ActionGraph getActionGraph() { return actionGraph; } public TopLevelArtifactContext getTopLevelContext() { return topLevelContext; } public String getWorkspaceName() { return workspaceName; } public List getTopLevelTargetsWithConfigs() { return topLevelTargetsWithConfigs; } } /** Returns the collection of configured targets corresponding to any of the provided targets. */ @VisibleForTesting static Iterable filterTestsByTargets( Collection targets, final Set allowedTargets, ExtendedEventHandler eventHandler, PackageManager packageManager) { return targets .stream() .filter( ct -> { Target target = null; try { target = packageManager.getTarget(eventHandler, ct.getLabel()); } catch (NoSuchTargetException | NoSuchPackageException | InterruptedException e) { eventHandler.handle( Event.error("Failed to get target from package when filtering.")); return false; } return allowedTargets.contains(target); }) .collect(Collectors.toSet()); } @ThreadCompatible public AnalysisResult update( LoadingResult loadingResult, BuildConfigurationCollection configurations, List aspects, Options viewOptions, boolean keepGoing, int loadingPhaseThreads, TopLevelArtifactContext topLevelOptions, ExtendedEventHandler eventHandler, EventBus eventBus) throws ViewCreationFailedException, InterruptedException { logger.info("Starting analysis"); pollInterruptedStatus(); skyframeBuildView.resetEvaluatedConfiguredTargetKeysSet(); Collection targets = loadingResult.getTargets(); eventBus.post(new AnalysisPhaseStartedEvent(targets)); skyframeBuildView.setConfigurations(configurations); // Determine the configurations. List topLevelTargetsWithConfigs = AnalysisUtils.getTargetsWithConfigs( configurations, targets, eventHandler, ruleClassProvider, skyframeExecutor); // Report the generated association of targets to configurations Multimap byLabel = ArrayListMultimap.create(); for (TargetAndConfiguration pair : topLevelTargetsWithConfigs) { byLabel.put(pair.getLabel(), pair.getConfiguration()); } for (Target target : targets) { eventBus.post(new TargetConfiguredEvent(target, byLabel.get(target.getLabel()))); } List topLevelCtKeys = Lists.transform( topLevelTargetsWithConfigs, new Function() { @Override public ConfiguredTargetKey apply(TargetAndConfiguration node) { return ConfiguredTargetKey.of(node.getLabel(), node.getConfiguration()); } }); Multimap, BuildConfiguration> aspectConfigurations = ArrayListMultimap.create(); List aspectKeys = new ArrayList<>(); for (String aspect : aspects) { // Syntax: label%aspect int delimiterPosition = aspect.indexOf('%'); if (delimiterPosition >= 0) { // TODO(jfield): For consistency with Skylark loads, the aspect should be specified // as an absolute path. Also, we probably need to do at least basic validation of // path well-formedness here. String bzlFileLoadLikeString = aspect.substring(0, delimiterPosition); if (!bzlFileLoadLikeString.startsWith("//") && !bzlFileLoadLikeString.startsWith("@")) { // "Legacy" behavior of '--aspects' parameter. bzlFileLoadLikeString = PathFragment.create("/" + bzlFileLoadLikeString).toString(); if (bzlFileLoadLikeString.endsWith(".bzl")) { bzlFileLoadLikeString = bzlFileLoadLikeString.substring(0, bzlFileLoadLikeString.length() - ".bzl".length()); } } SkylarkImport skylarkImport; try { skylarkImport = SkylarkImports.create(bzlFileLoadLikeString); } catch (SkylarkImportSyntaxException e) { throw new ViewCreationFailedException( String.format("Invalid aspect '%s': %s", aspect, e.getMessage()), e); } String skylarkFunctionName = aspect.substring(delimiterPosition + 1); for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) { aspectConfigurations.put( Pair.of(targetSpec.getLabel(), aspect), targetSpec.getConfiguration()); aspectKeys.add( AspectValue.createSkylarkAspectKey( targetSpec.getLabel(), // For invoking top-level aspects, use the top-level configuration for both the // aspect and the base target while the top-level configuration is untrimmed. targetSpec.getConfiguration(), targetSpec.getConfiguration(), skylarkImport, skylarkFunctionName)); } } else { final NativeAspectClass aspectFactoryClass = ruleClassProvider.getNativeAspectClassMap().get(aspect); if (aspectFactoryClass != null) { for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) { // For invoking top-level aspects, use the top-level configuration for both the // aspect and the base target while the top-level configuration is untrimmed. BuildConfiguration configuration = targetSpec.getConfiguration(); aspectConfigurations.put(Pair.of(targetSpec.getLabel(), aspect), configuration); aspectKeys.add( AspectValue.createAspectKey( targetSpec.getLabel(), configuration, new AspectDescriptor(aspectFactoryClass, AspectParameters.EMPTY), configuration )); } } else { throw new ViewCreationFailedException("Aspect '" + aspect + "' is unknown"); } } } for (Pair target : aspectConfigurations.keys()) { eventBus.post( new AspectConfiguredEvent( target.getFirst(), target.getSecond(), aspectConfigurations.get(target))); } skyframeExecutor.maybeInvalidateWorkspaceStatusValue(loadingResult.getWorkspaceName()); SkyframeAnalysisResult skyframeAnalysisResult; try { skyframeAnalysisResult = skyframeBuildView.configureTargets( eventHandler, topLevelCtKeys, aspectKeys, eventBus, keepGoing, loadingPhaseThreads); setArtifactRoots(skyframeAnalysisResult.getPackageRoots()); } finally { skyframeBuildView.clearInvalidatedConfiguredTargets(); } int numTargetsToAnalyze = topLevelTargetsWithConfigs.size(); int numSuccessful = skyframeAnalysisResult.getConfiguredTargets().size(); if (0 < numSuccessful && numSuccessful < numTargetsToAnalyze) { String msg = String.format("Analysis succeeded for only %d of %d top-level targets", numSuccessful, numTargetsToAnalyze); eventHandler.handle(Event.info(msg)); logger.info(msg); } Set targetsToSkip = new TopLevelConstraintSemantics(skyframeExecutor.getPackageManager(), eventHandler) .checkTargetEnvironmentRestrictions(skyframeAnalysisResult.getConfiguredTargets()); AnalysisResult result = createResult( eventHandler, loadingResult, topLevelOptions, viewOptions, skyframeAnalysisResult, targetsToSkip, topLevelTargetsWithConfigs); logger.info("Finished analysis"); return result; } private AnalysisResult createResult( ExtendedEventHandler eventHandler, LoadingResult loadingResult, TopLevelArtifactContext topLevelOptions, BuildView.Options viewOptions, SkyframeAnalysisResult skyframeAnalysisResult, Set targetsToSkip, List topLevelTargetsWithConfigs) throws InterruptedException { Collection testsToRun = loadingResult.getTestsToRun(); Set configuredTargets = Sets.newLinkedHashSet(skyframeAnalysisResult.getConfiguredTargets()); Collection aspects = skyframeAnalysisResult.getAspects(); Set allTargetsToTest = null; if (testsToRun != null) { // Determine the subset of configured targets that are meant to be run as tests. allTargetsToTest = Sets.newLinkedHashSet( filterTestsByTargets( configuredTargets, Sets.newHashSet(testsToRun), eventHandler, skyframeExecutor.getPackageManager())); } Set artifactsToBuild = new HashSet<>(); Set parallelTests = new HashSet<>(); Set exclusiveTests = new HashSet<>(); // build-info and build-changelist. Collection buildInfoArtifacts = skyframeExecutor.getWorkspaceStatusArtifacts(eventHandler); Preconditions.checkState(buildInfoArtifacts.size() == 2, buildInfoArtifacts); artifactsToBuild.addAll(buildInfoArtifacts); // Extra actions addExtraActionsIfRequested( viewOptions, configuredTargets, aspects, artifactsToBuild, eventHandler); // Coverage NestedSet baselineCoverageArtifacts = getBaselineCoverageArtifacts(configuredTargets); Iterables.addAll(artifactsToBuild, baselineCoverageArtifacts); if (coverageReportActionFactory != null) { CoverageReportActionsWrapper actionsWrapper; actionsWrapper = coverageReportActionFactory.createCoverageReportActionsWrapper( eventHandler, directories, allTargetsToTest, baselineCoverageArtifacts, getArtifactFactory(), CoverageReportValue.COVERAGE_REPORT_KEY); if (actionsWrapper != null) { ImmutableList actions = actionsWrapper.getActions(); skyframeExecutor.injectCoverageReportData(actions); artifactsToBuild.addAll(actionsWrapper.getCoverageOutputs()); } } // Tests. This must come last, so that the exclusive tests are scheduled after everything else. scheduleTestsIfRequested( parallelTests, exclusiveTests, topLevelOptions, allTargetsToTest, skyframeExecutor, eventHandler); String error = createErrorMessage(loadingResult, skyframeAnalysisResult); final WalkableGraph graph = skyframeAnalysisResult.getWalkableGraph(); final ActionGraph actionGraph = new ActionGraph() { @Nullable @Override public ActionAnalysisMetadata getGeneratingAction(Artifact artifact) { ArtifactOwner artifactOwner = artifact.getArtifactOwner(); if (artifactOwner instanceof ActionLookupValue.ActionLookupKey) { SkyKey key = (ActionLookupValue.ActionLookupKey) artifactOwner; ActionLookupValue val; try { val = (ActionLookupValue) graph.getValue(key); } catch (InterruptedException e) { throw new IllegalStateException( "Interruption not expected from this graph: " + key, e); } return val == null ? null : val.getGeneratingActionDangerousReadJavadoc(artifact); } return null; } }; return new AnalysisResult( configuredTargets, aspects, allTargetsToTest, targetsToSkip, error, actionGraph, artifactsToBuild, parallelTests, exclusiveTests, topLevelOptions, skyframeAnalysisResult.getPackageRoots(), loadingResult.getWorkspaceName(), topLevelTargetsWithConfigs); } @Nullable public static String createErrorMessage( LoadingResult loadingResult, @Nullable SkyframeAnalysisResult skyframeAnalysisResult) { return loadingResult.hasTargetPatternError() ? "command succeeded, but there were errors parsing the target pattern" : loadingResult.hasLoadingError() || (skyframeAnalysisResult != null && skyframeAnalysisResult.hasLoadingError()) ? "command succeeded, but there were loading phase errors" : (skyframeAnalysisResult != null && skyframeAnalysisResult.hasAnalysisError()) ? "command succeeded, but not all targets were analyzed" : null; } private static NestedSet getBaselineCoverageArtifacts( Collection configuredTargets) { NestedSetBuilder baselineCoverageArtifacts = NestedSetBuilder.stableOrder(); for (ConfiguredTarget target : configuredTargets) { InstrumentedFilesProvider provider = target.getProvider(InstrumentedFilesProvider.class); if (provider != null) { baselineCoverageArtifacts.addTransitive(provider.getBaselineCoverageArtifacts()); } } return baselineCoverageArtifacts.build(); } private void addExtraActionsIfRequested( Options viewOptions, Collection configuredTargets, Collection aspects, Set artifactsToBuild, ExtendedEventHandler eventHandler) { Iterable extraActionArtifacts = concat( addExtraActionsFromTargets(viewOptions, configuredTargets, eventHandler), addExtraActionsFromAspects(viewOptions, aspects)); RegexFilter filter = viewOptions.extraActionFilter; for (Artifact artifact : extraActionArtifacts) { boolean filterMatches = filter == null || filter.isIncluded(artifact.getOwnerLabel().toString()); if (filterMatches) { artifactsToBuild.add(artifact); } } } private NestedSet addExtraActionsFromTargets( BuildView.Options viewOptions, Collection configuredTargets, ExtendedEventHandler eventHandler) { NestedSetBuilder builder = NestedSetBuilder.stableOrder(); for (ConfiguredTarget target : configuredTargets) { ExtraActionArtifactsProvider provider = target.getProvider(ExtraActionArtifactsProvider.class); if (provider != null) { if (viewOptions.extraActionTopLevelOnly) { // Collect all aspect-classes that topLevel might inject. Set aspectClasses = new HashSet<>(); Target actualTarget = null; try { actualTarget = skyframeExecutor.getPackageManager().getTarget(eventHandler, target.getLabel()); } catch (NoSuchPackageException | NoSuchTargetException | InterruptedException e) { eventHandler.handle(Event.error("")); } for (Attribute attr : actualTarget.getAssociatedRule().getAttributes()) { aspectClasses.addAll(attr.getAspectClasses()); } builder.addTransitive(provider.getExtraActionArtifacts()); if (!aspectClasses.isEmpty()) { builder.addAll(filterTransitiveExtraActions(provider, aspectClasses)); } } else { builder.addTransitive(provider.getTransitiveExtraActionArtifacts()); } } } return builder.build(); } /** * Returns a list of actions from 'provider' that were registered by an aspect from * 'aspectClasses'. All actions in 'provider' are considered - both direct and transitive. */ private ImmutableList filterTransitiveExtraActions( ExtraActionArtifactsProvider provider, Set aspectClasses) { ImmutableList.Builder artifacts = ImmutableList.builder(); // Add to 'artifacts' all extra-actions which were registered by aspects which 'topLevel' // might have injected. for (Artifact artifact : provider.getTransitiveExtraActionArtifacts()) { ArtifactOwner owner = artifact.getArtifactOwner(); if (owner instanceof AspectKey) { if (aspectClasses.contains(((AspectKey) owner).getAspectClass())) { artifacts.add(artifact); } } } return artifacts.build(); } private NestedSet addExtraActionsFromAspects( BuildView.Options viewOptions, Collection aspects) { NestedSetBuilder builder = NestedSetBuilder.stableOrder(); for (AspectValue aspect : aspects) { ExtraActionArtifactsProvider provider = aspect.getConfiguredAspect().getProvider(ExtraActionArtifactsProvider.class); if (provider != null) { if (viewOptions.extraActionTopLevelOnly) { builder.addTransitive(provider.getExtraActionArtifacts()); } else { builder.addTransitive(provider.getTransitiveExtraActionArtifacts()); } } } return builder.build(); } private static void scheduleTestsIfRequested( Collection targetsToTest, Collection targetsToTestExclusive, TopLevelArtifactContext topLevelOptions, Collection allTestTargets, SkyframeExecutor skyframeExecutor, ExtendedEventHandler eventHandler) throws InterruptedException { Set outputGroups = topLevelOptions.outputGroups(); if (!outputGroups.contains(OutputGroupInfo.FILES_TO_COMPILE) && !outputGroups.contains(OutputGroupInfo.COMPILATION_PREREQUISITES) && allTestTargets != null) { scheduleTests( targetsToTest, targetsToTestExclusive, allTestTargets, topLevelOptions.runTestsExclusively(), skyframeExecutor, eventHandler); } } /** * Returns set of artifacts representing test results, writing into targetsToTest and * targetsToTestExclusive. */ private static void scheduleTests( Collection targetsToTest, Collection targetsToTestExclusive, Collection allTestTargets, boolean isExclusive, SkyframeExecutor skyframeExecutor, ExtendedEventHandler eventHandler) throws InterruptedException { for (ConfiguredTarget configuredTarget : allTestTargets) { Target target = null; try { target = skyframeExecutor .getPackageManager() .getTarget(eventHandler, configuredTarget.getLabel()); } catch (NoSuchTargetException | NoSuchPackageException e) { eventHandler.handle(Event.error("Failed to get target when scheduling tests")); continue; } if (target instanceof Rule) { boolean exclusive = isExclusive || TargetUtils.isExclusiveTestRule((Rule) target); Collection testCollection = exclusive ? targetsToTestExclusive : targetsToTest; testCollection.add(configuredTarget); } } } /** * Gets a configuration for the given target. * *

If {@link BuildConfiguration.Options#trimConfigurations()} is true, the configuration only * includes the fragments needed by the fragment and its transitive closure. Else unconditionally * includes all fragments. */ @VisibleForTesting public BuildConfiguration getConfigurationForTesting( Target target, BuildConfiguration config, ExtendedEventHandler eventHandler) throws InterruptedException { List node = ImmutableList.of(new TargetAndConfiguration(target, config)); LinkedHashSet configs = ConfigurationResolver.getConfigurationsFromExecutor( node, AnalysisUtils.targetsToDeps( new LinkedHashSet(node), ruleClassProvider), eventHandler, skyframeExecutor); return configs.iterator().next().getConfiguration(); } /** * Sets the possible artifact roots in the artifact factory. This allows the factory to resolve * paths with unknown roots to artifacts. */ @VisibleForTesting // for BuildViewTestCase public void setArtifactRoots(PackageRoots packageRoots) { getArtifactFactory().setPackageRoots(packageRoots.getPackageRootLookup()); } /** * Tests and clears the current thread's pending "interrupted" status, and * throws InterruptedException iff it was set. */ protected final void pollInterruptedStatus() throws InterruptedException { if (Thread.interrupted()) { throw new InterruptedException(); } } // For testing @VisibleForTesting public Iterable getDirectPrerequisitesForTesting( ExtendedEventHandler eventHandler, ConfiguredTarget ct, BuildConfigurationCollection configurations) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException { return Collections2.transform( skyframeExecutor.getConfiguredTargetsForTesting( eventHandler, ct.getConfiguration(), ImmutableSet.copyOf( getDirectPrerequisiteDependenciesForTesting( eventHandler, ct, configurations, /*toolchainContext=*/ null) .values())), ConfiguredTargetAndTarget::getConfiguredTarget); } @VisibleForTesting public OrderedSetMultimap getDirectPrerequisiteDependenciesForTesting( final ExtendedEventHandler eventHandler, final ConfiguredTarget ct, BuildConfigurationCollection configurations, ToolchainContext toolchainContext) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException { Target target = null; try { target = skyframeExecutor.getPackageManager().getTarget(eventHandler, ct.getLabel()); } catch (NoSuchPackageException | NoSuchTargetException | InterruptedException e) { eventHandler.handle( Event.error("Failed to get target from package during prerequisite analysis." + e)); return OrderedSetMultimap.create(); } if (!(target instanceof Rule)) { return OrderedSetMultimap.create(); } class SilentDependencyResolver extends DependencyResolver { private SilentDependencyResolver() { } @Override protected void invalidVisibilityReferenceHook(TargetAndConfiguration node, Label label) { throw new RuntimeException("bad visibility on " + label + " during testing unexpected"); } @Override protected void invalidPackageGroupReferenceHook(TargetAndConfiguration node, Label label) { throw new RuntimeException("bad package group on " + label + " during testing unexpected"); } @Override protected void missingEdgeHook(Target from, Label to, NoSuchThingException e) { throw new RuntimeException( "missing dependency from " + from.getLabel() + " to " + to + ": " + e.getMessage(), e); } @Override protected Target getTarget(Target from, Label label, NestedSetBuilder

Returns {@code null} if something goes wrong. */ @VisibleForTesting public ConfiguredTarget getConfiguredTargetForTesting( ExtendedEventHandler eventHandler, Label label, BuildConfiguration config) { return skyframeExecutor.getConfiguredTargetForTesting(eventHandler, label, config, getTopLevelTransitionForTarget(label, eventHandler)); } @VisibleForTesting public ConfiguredTargetAndTarget getConfiguredTargetAndTargetForTesting( ExtendedEventHandler eventHandler, Label label, BuildConfiguration config) { return skyframeExecutor.getConfiguredTargetAndTargetForTesting(eventHandler, label, config); } /** * Returns a RuleContext which is the same as the original RuleContext of the target parameter. */ @VisibleForTesting public RuleContext getRuleContextForTesting( ConfiguredTarget target, StoredEventHandler eventHandler, BuildConfigurationCollection configurations) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException, ToolchainContextException { BuildConfiguration targetConfig = target.getConfiguration(); CachingAnalysisEnvironment env = new CachingAnalysisEnvironment( getArtifactFactory(), skyframeExecutor.getActionKeyContext(), ConfiguredTargetKey.of(target.getLabel(), targetConfig), /*isSystemEnv=*/ false, targetConfig.extendedSanityChecks(), eventHandler, /*env=*/ null, targetConfig.isActionsEnabled()); return getRuleContextForTesting(eventHandler, target, env, configurations); } /** * Creates and returns a rule context that is equivalent to the one that was used to create the * given configured target. */ @VisibleForTesting public RuleContext getRuleContextForTesting( ExtendedEventHandler eventHandler, ConfiguredTarget configuredTarget, AnalysisEnvironment env, BuildConfigurationCollection configurations) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException, ToolchainContextException { BuildConfiguration targetConfig = configuredTarget.getConfiguration(); Target target = null; try { target = skyframeExecutor.getPackageManager().getTarget(eventHandler, configuredTarget.getLabel()); } catch (NoSuchPackageException | NoSuchTargetException e) { eventHandler.handle( Event.error("Failed to get target when trying to get rule context for testing")); throw new IllegalStateException(e); } Set