// 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.TransitionResolver; 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.causes.Cause; 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.EventHandler; 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.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.BuildConfigurationValue; import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData; 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.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", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.NO_OP}, help = "Treat visible analysis warnings as errors." ) public boolean analysisWarningsAsErrors; @Option( name = "discard_analysis_cache", defaultValue = "false", 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 = "", 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", 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", 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( EventHandler eventHandler, BuildConfigurationCollection configurations) { skyframeBuildView.setConfigurations(eventHandler, configurations); } public ArtifactFactory getArtifactFactory() { return skyframeBuildView.getArtifactFactory(); } @VisibleForTesting WorkspaceStatusAction getLastWorkspaceBuildInfoActionForTesting() throws InterruptedException { return skyframeExecutor.getLastWorkspaceStatusAction(); } @Override public int hashCode() { throw new UnsupportedOperationException(); // avoid nondeterminism } /** 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(eventHandler, 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 label. // We convert it for compatibility reasons (this will be removed in the future). String bzlFileLoadLikeString = aspect.substring(0, delimiterPosition); if (!bzlFileLoadLikeString.startsWith("//") && !bzlFileLoadLikeString.startsWith("@")) { // "Legacy" behavior of '--aspects' parameter. if (bzlFileLoadLikeString.startsWith("/")) { bzlFileLoadLikeString = bzlFileLoadLikeString.substring(1); } int lastSlashPosition = bzlFileLoadLikeString.lastIndexOf('/'); if (lastSlashPosition >= 0) { bzlFileLoadLikeString = "//" + bzlFileLoadLikeString.substring(0, lastSlashPosition) + ":" + bzlFileLoadLikeString.substring(lastSlashPosition + 1); } else { bzlFileLoadLikeString = "//:" + bzlFileLoadLikeString; } if (!bzlFileLoadLikeString.endsWith(".bzl")) { bzlFileLoadLikeString = bzlFileLoadLikeString + ".bzl"; } } SkylarkImport skylarkImport; try { skylarkImport = SkylarkImports.create( bzlFileLoadLikeString, /* repositoryMapping= */ ImmutableMap.of()); } 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))); } 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(), input -> skyframeExecutor.getConfiguration(eventHandler, input), 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()); ImmutableSet aspects = ImmutableSet.copyOf(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 Collection getDirectPrerequisitesForTesting( ExtendedEventHandler eventHandler, ConfiguredTarget ct, BuildConfigurationCollection configurations) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException { return Collections2.transform( getConfiguredTargetAndDataDirectPrerequisitesForTesting(eventHandler, ct, configurations), ConfiguredTargetAndData::getConfiguredTarget); } // TODO(janakr): pass the configuration in as a parameter here and above. @VisibleForTesting public Collection getConfiguredTargetAndDataDirectPrerequisitesForTesting( ExtendedEventHandler eventHandler, ConfiguredTarget ct, BuildConfigurationCollection configurations) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException { return getConfiguredTargetAndDataDirectPrerequisitesForTesting( eventHandler, ct, ct.getConfigurationKey(), configurations); } @VisibleForTesting public Collection getConfiguredTargetAndDataDirectPrerequisitesForTesting( ExtendedEventHandler eventHandler, ConfiguredTargetAndData ct, BuildConfigurationCollection configurations) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException { return getConfiguredTargetAndDataDirectPrerequisitesForTesting( eventHandler, ct.getConfiguredTarget(), ct.getConfiguredTarget().getConfigurationKey(), configurations); } private Collection getConfiguredTargetAndDataDirectPrerequisitesForTesting( ExtendedEventHandler eventHandler, ConfiguredTarget ct, BuildConfigurationValue.Key configuration, BuildConfigurationCollection configurations) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException { return skyframeExecutor.getConfiguredTargetsForTesting( eventHandler, configuration, ImmutableSet.copyOf( getDirectPrerequisiteDependenciesForTesting( eventHandler, ct, configurations, /*toolchainContext=*/ null) .values())); } @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 rootCauses) throws InterruptedException { try { return skyframeExecutor.getPackageManager().getTarget(eventHandler, label); } catch (NoSuchThingException e) { throw new IllegalStateException(e); } } @Override protected List getConfigurations( FragmentClassSet fragments, Iterable buildOptions, BuildOptions defaultBuildOptions) { Preconditions.checkArgument( fragments.fragmentClasses().equals(ct.getConfigurationKey().getFragments()), "Mismatch: %s %s", ct, fragments); Dependency asDep = Dependency.withTransitionAndAspects(ct.getLabel(), NoTransition.INSTANCE, AspectCollection.EMPTY); ImmutableList.Builder builder = ImmutableList.builder(); for (BuildOptions options : buildOptions) { builder.add(Iterables.getOnlyElement( skyframeExecutor .getConfigurations(eventHandler, options, ImmutableList.of(asDep)) .values() )); } return builder.build(); } } DependencyResolver dependencyResolver = new SilentDependencyResolver(); TargetAndConfiguration ctgNode = new TargetAndConfiguration( target, skyframeExecutor.getConfiguration(eventHandler, ct.getConfigurationKey())); return dependencyResolver.dependentNodeMap( ctgNode, configurations.getHostConfiguration(), /*aspect=*/ null, getConfigurableAttributeKeysForTesting(eventHandler, ctgNode), toolchainContext == null ? ImmutableSet.of() : toolchainContext.getResolvedToolchainLabels(), skyframeExecutor.getDefaultBuildOptions(), ruleClassProvider.getTrimmingTransitionFactory()); } /** * Returns ConfigMatchingProvider instances corresponding to the configurable attribute keys * present in this rule's attributes. */ private ImmutableMap getConfigurableAttributeKeysForTesting( ExtendedEventHandler eventHandler, TargetAndConfiguration ctg) { if (!(ctg.getTarget() instanceof Rule)) { return ImmutableMap.of(); } Rule rule = (Rule) ctg.getTarget(); Map keys = new LinkedHashMap<>(); RawAttributeMapper mapper = RawAttributeMapper.of(rule); for (Attribute attribute : rule.getAttributes()) { for (Label label : mapper.getConfigurabilityKeys(attribute.getName(), attribute.getType())) { if (BuildType.Selector.isReservedLabel(label)) { continue; } ConfiguredTarget ct = getConfiguredTargetForTesting( eventHandler, label, ctg.getConfiguration()); keys.put(label, Preconditions.checkNotNull(ct.getProvider(ConfigMatchingProvider.class))); } } return ImmutableMap.copyOf(keys); } private OrderedSetMultimap getPrerequisiteMapForTesting( final ExtendedEventHandler eventHandler, ConfiguredTarget target, BuildConfigurationCollection configurations, ToolchainContext toolchainContext) throws EvalException, InvalidConfigurationException, InterruptedException, InconsistentAspectOrderException { OrderedSetMultimap depNodeNames = getDirectPrerequisiteDependenciesForTesting( eventHandler, target, configurations, toolchainContext); ImmutableMultimap cts = skyframeExecutor.getConfiguredTargetMapForTesting( eventHandler, target.getConfigurationKey(), ImmutableSet.copyOf(depNodeNames.values())); OrderedSetMultimap result = OrderedSetMultimap.create(); for (Map.Entry entry : depNodeNames.entries()) { result.putAll(entry.getKey(), cts.get(entry.getValue())); } return result; } private ConfigurationTransition getTopLevelTransitionForTarget( Label label, BuildConfiguration config, ExtendedEventHandler handler) { Target target; try { target = skyframeExecutor.getPackageManager().getTarget(handler, label); } catch (NoSuchPackageException | NoSuchTargetException e) { return NoTransition.INSTANCE; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new AssertionError("Configuration of " + label + " interrupted"); } return TransitionResolver.evaluateTopLevelTransition( new TargetAndConfiguration(target, config), ruleClassProvider.getTrimmingTransitionFactory()); } /** * Returns a configured target for the specified target and configuration. If the target in * question has a top-level rule class transition, that transition is applied in the returned * ConfiguredTarget. * *

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, config, eventHandler)); } @VisibleForTesting public ConfiguredTargetAndData getConfiguredTargetAndDataForTesting( ExtendedEventHandler eventHandler, Label label, BuildConfiguration config) { return skyframeExecutor.getConfiguredTargetAndDataForTesting( eventHandler, label, config, getTopLevelTransitionForTarget(label, config, eventHandler)); } /** * 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 = skyframeExecutor.getConfiguration(eventHandler, target.getConfigurationKey()); 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 = skyframeExecutor.getConfiguration(eventHandler, configuredTarget.getConfigurationKey()); 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