|  | // 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.skyframe; | 
|  |  | 
|  | import static com.google.common.collect.ImmutableList.toImmutableList; | 
|  | import static com.google.common.collect.ImmutableMap.toImmutableMap; | 
|  | import static com.google.common.collect.ImmutableSet.toImmutableSet; | 
|  | import static com.google.devtools.build.lib.buildeventstream.BuildEventIdUtil.configurationIdMessage; | 
|  | import static com.google.devtools.build.lib.skyframe.ConflictCheckingMode.NONE; | 
|  | import static com.google.devtools.build.lib.skyframe.ConflictCheckingMode.UPON_CONFIGURED_OBJECT_CREATION; | 
|  | import static com.google.devtools.build.lib.skyframe.ConflictCheckingMode.WITH_TRAVERSAL; | 
|  |  | 
|  | import com.google.common.annotations.VisibleForTesting; | 
|  | import com.google.common.base.Joiner; | 
|  | import com.google.common.base.Preconditions; | 
|  | import com.google.common.base.Stopwatch; | 
|  | import com.google.common.collect.ImmutableList; | 
|  | import com.google.common.collect.ImmutableMap; | 
|  | import com.google.common.collect.ImmutableSet; | 
|  | import com.google.common.collect.Iterables; | 
|  | import com.google.common.collect.Sets; | 
|  | import com.google.common.collect.Streams; | 
|  | import com.google.common.eventbus.EventBus; | 
|  | import com.google.common.flogger.GoogleLogger; | 
|  | import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; | 
|  | import com.google.devtools.build.lib.actions.ActionConflictException; | 
|  | import com.google.devtools.build.lib.actions.ActionKeyContext; | 
|  | import com.google.devtools.build.lib.actions.ActionLookupKey; | 
|  | import com.google.devtools.build.lib.actions.ActionLookupValue; | 
|  | import com.google.devtools.build.lib.actions.AnalysisGraphStatsEvent; | 
|  | import com.google.devtools.build.lib.actions.Artifact; | 
|  | import com.google.devtools.build.lib.actions.ArtifactFactory; | 
|  | import com.google.devtools.build.lib.actions.BuildFailedException; | 
|  | import com.google.devtools.build.lib.actions.PackageRoots; | 
|  | import com.google.devtools.build.lib.actions.ResourceManager; | 
|  | import com.google.devtools.build.lib.actions.TestExecException; | 
|  | import com.google.devtools.build.lib.actions.TotalAndConfiguredTargetOnlyMetric; | 
|  | import com.google.devtools.build.lib.analysis.AnalysisFailureEvent; | 
|  | import com.google.devtools.build.lib.analysis.AnalysisOperationWatcher; | 
|  | import com.google.devtools.build.lib.analysis.AnalysisPhaseCompleteEvent; | 
|  | import com.google.devtools.build.lib.analysis.AspectValue; | 
|  | import com.google.devtools.build.lib.analysis.CachingAnalysisEnvironment; | 
|  | import com.google.devtools.build.lib.analysis.ConfiguredAspect; | 
|  | import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; | 
|  | import com.google.devtools.build.lib.analysis.ConfiguredTarget; | 
|  | import com.google.devtools.build.lib.analysis.ConfiguredTargetFactory; | 
|  | import com.google.devtools.build.lib.analysis.ConfiguredTargetValue; | 
|  | import com.google.devtools.build.lib.analysis.DependencyKind; | 
|  | import com.google.devtools.build.lib.analysis.ExecGroupCollection; | 
|  | import com.google.devtools.build.lib.analysis.ExecGroupCollection.InvalidExecGroupException; | 
|  | import com.google.devtools.build.lib.analysis.ResolvedToolchainContext; | 
|  | import com.google.devtools.build.lib.analysis.TargetAndConfiguration; | 
|  | import com.google.devtools.build.lib.analysis.ToolchainCollection; | 
|  | import com.google.devtools.build.lib.analysis.TopLevelArtifactContext; | 
|  | import com.google.devtools.build.lib.analysis.ViewCreationFailedException; | 
|  | import com.google.devtools.build.lib.analysis.config.AdditionalConfigurationChangeEvent; | 
|  | import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; | 
|  | import com.google.devtools.build.lib.analysis.config.BuildOptions; | 
|  | import com.google.devtools.build.lib.analysis.config.ConfigConditions; | 
|  | import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; | 
|  | import com.google.devtools.build.lib.analysis.config.OptionsDiff; | 
|  | import com.google.devtools.build.lib.analysis.config.StarlarkExecTransitionLoader; | 
|  | import com.google.devtools.build.lib.analysis.config.StarlarkExecTransitionLoader.StarlarkExecTransitionLoadingException; | 
|  | import com.google.devtools.build.lib.analysis.config.StarlarkTransitionCache; | 
|  | import com.google.devtools.build.lib.analysis.starlark.StarlarkAttributeTransitionProvider; | 
|  | import com.google.devtools.build.lib.analysis.test.AnalysisFailurePropagationException; | 
|  | import com.google.devtools.build.lib.analysis.test.CoverageArtifactsKnownEvent; | 
|  | import com.google.devtools.build.lib.bugreport.BugReport; | 
|  | import com.google.devtools.build.lib.bugreport.BugReporter; | 
|  | import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildMetrics.BuildGraphMetrics; | 
|  | import com.google.devtools.build.lib.causes.AnalysisFailedCause; | 
|  | 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.MultiThreadPoolsQuiescingExecutor; | 
|  | import com.google.devtools.build.lib.concurrent.QuiescingExecutors; | 
|  | 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.packages.Package; | 
|  | 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.profiler.Profiler; | 
|  | import com.google.devtools.build.lib.profiler.SilentCloseable; | 
|  | import com.google.devtools.build.lib.server.FailureDetails; | 
|  | import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey; | 
|  | import com.google.devtools.build.lib.skyframe.AspectKeyCreator.TopLevelAspectsKey; | 
|  | import com.google.devtools.build.lib.skyframe.BuildDriverKey.TestType; | 
|  | import com.google.devtools.build.lib.skyframe.SkyframeErrorProcessor.ErrorProcessingResult; | 
|  | import com.google.devtools.build.lib.skyframe.SkyframeExecutor.ConfigureTargetsResult; | 
|  | import com.google.devtools.build.lib.skyframe.SkyframeExecutor.FailureToRetrieveIntrospectedValueException; | 
|  | import com.google.devtools.build.lib.skyframe.SkyframeExecutor.TopLevelActionConflictReport; | 
|  | import com.google.devtools.build.lib.skyframe.config.BuildConfigurationKey; | 
|  | import com.google.devtools.build.lib.skyframe.serialization.analysis.RemoteAnalysisCachingDependenciesProvider; | 
|  | import com.google.devtools.build.lib.skyframe.serialization.analysis.RemoteAnalysisCachingDependenciesProvider.DisabledDependenciesProvider; | 
|  | import com.google.devtools.build.lib.util.DetailedExitCode; | 
|  | import com.google.devtools.build.lib.util.DetailedExitCode.DetailedExitCodeComparator; | 
|  | import com.google.devtools.build.lib.util.OrderedSetMultimap; | 
|  | import com.google.devtools.build.skyframe.ErrorInfo; | 
|  | import com.google.devtools.build.skyframe.EvaluationProgressReceiver; | 
|  | import com.google.devtools.build.skyframe.EvaluationResult; | 
|  | import com.google.devtools.build.skyframe.GroupedDeps; | 
|  | import com.google.devtools.build.skyframe.NodeEntry.DirtyType; | 
|  | 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 com.google.devtools.common.options.OptionDefinition; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Collections; | 
|  | import java.util.List; | 
|  | import java.util.Map; | 
|  | import java.util.Map.Entry; | 
|  | import java.util.Objects; | 
|  | import java.util.Optional; | 
|  | import java.util.Set; | 
|  | import java.util.concurrent.ConcurrentHashMap; | 
|  | import java.util.concurrent.atomic.AtomicInteger; | 
|  | import javax.annotation.Nullable; | 
|  |  | 
|  | /** | 
|  | * Skyframe-based driver of analysis. | 
|  | * | 
|  | * <p>Covers enough functionality to work as a substitute for {@code BuildView#configureTargets}. | 
|  | */ | 
|  | public final class SkyframeBuildView { | 
|  | private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); | 
|  |  | 
|  | private final ConfiguredTargetFactory factory; | 
|  | private final ArtifactFactory artifactFactory; | 
|  | private final SkyframeExecutor skyframeExecutor; | 
|  | private final ActionKeyContext actionKeyContext; | 
|  | private RemoteAnalysisCachingDependenciesProvider remoteAnalysisCachingDependenciesProvider = | 
|  | DisabledDependenciesProvider.INSTANCE; | 
|  | private boolean enableAnalysis = false; | 
|  |  | 
|  | // This hack allows us to see when an action lookup node has been invalidated, and thus when the | 
|  | // set of artifact conflicts needs to be recomputed (whenever an action lookup node has been | 
|  | // invalidated or newly evaluated). | 
|  | private final ActionLookupValueProgressReceiver progressReceiver = | 
|  | new ActionLookupValueProgressReceiver(); | 
|  | // Used to see if checks of graph consistency need to be done after analysis. | 
|  | private volatile boolean someActionLookupValueEvaluated = false; | 
|  |  | 
|  | // We keep the set of invalidated action lookup nodes so that we can know if something has been | 
|  | // invalidated after graph pruning has been executed. | 
|  | private Set<ActionLookupKey> dirtiedActionLookupKeys = Sets.newConcurrentHashSet(); | 
|  |  | 
|  | private final ConfiguredRuleClassProvider ruleClassProvider; | 
|  |  | 
|  | // Null until the build configuration is set. | 
|  | @Nullable private BuildConfigurationValue configuration; | 
|  | @Nullable private BuildOptions originalConfigurationOptions; | 
|  |  | 
|  | /** | 
|  | * If the last build was executed with {@code Options#discard_analysis_cache} and we are not | 
|  | * running Skyframe full, we should clear the legacy data since it is out-of-sync. | 
|  | */ | 
|  | private boolean skyframeAnalysisWasDiscarded; | 
|  |  | 
|  | private ImmutableSet<ActionLookupKey> largestTopLevelKeySetCheckedForConflicts = | 
|  | ImmutableSet.of(); | 
|  | private boolean foundActionConflictInLatestCheck; | 
|  |  | 
|  | private final StarlarkTransitionCache starlarkTransitionCache = new StarlarkTransitionCache(); | 
|  |  | 
|  | public SkyframeBuildView( | 
|  | ArtifactFactory artifactFactory, | 
|  | SkyframeExecutor skyframeExecutor, | 
|  | ConfiguredRuleClassProvider ruleClassProvider, | 
|  | ActionKeyContext actionKeyContext) { | 
|  | this.actionKeyContext = actionKeyContext; | 
|  | this.factory = | 
|  | new ConfiguredTargetFactory( | 
|  | ruleClassProvider, | 
|  | () -> | 
|  | skyframeExecutor.getCheckerForConflictCheckingMode( | 
|  | UPON_CONFIGURED_OBJECT_CREATION)); | 
|  | this.artifactFactory = artifactFactory; | 
|  | this.skyframeExecutor = skyframeExecutor; | 
|  | this.ruleClassProvider = ruleClassProvider; | 
|  | } | 
|  |  | 
|  | public void resetProgressReceiver() { | 
|  | progressReceiver.reset(); | 
|  | } | 
|  |  | 
|  | public TotalAndConfiguredTargetOnlyMetric getEvaluatedCounts() { | 
|  | return TotalAndConfiguredTargetOnlyMetric.create( | 
|  | progressReceiver.configuredObjectCount.get(), progressReceiver.configuredTargetCount.get()); | 
|  | } | 
|  |  | 
|  | ConfiguredTargetFactory getConfiguredTargetFactory() { | 
|  | return factory; | 
|  | } | 
|  |  | 
|  | public TotalAndConfiguredTargetOnlyMetric getEvaluatedActionCounts() { | 
|  | return TotalAndConfiguredTargetOnlyMetric.create( | 
|  | progressReceiver.actionCount.get(), progressReceiver.configuredTargetActionCount.get()); | 
|  | } | 
|  |  | 
|  | public ImmutableMap<String, Integer> getEvaluatedActionCountsByMnemonic() { | 
|  | ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder(); | 
|  | for (Map.Entry<String, AtomicInteger> entry : | 
|  | progressReceiver.actionCountByMnemonic.entrySet()) { | 
|  | builder.put(entry.getKey(), entry.getValue().get()); | 
|  | } | 
|  | return builder.buildOrThrow(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns a description of the analysis-cache affecting changes between the current configuration | 
|  | * and the incoming one. | 
|  | * | 
|  | * @param maxDifferencesToShow the maximum number of change-affecting options to include in the | 
|  | *     returned description | 
|  | * @return a description or {@code null} if the configuration has not changed in a way that | 
|  | *     requires the analysis cache to be invalidated | 
|  | */ | 
|  | @Nullable | 
|  | private String describeConfigurationDifference( | 
|  | BuildOptions oldOptions, BuildOptions newOptions, int maxDifferencesToShow) { | 
|  | OptionsDiff diff = OptionsDiff.diff(oldOptions, newOptions); | 
|  |  | 
|  | ImmutableSet<OptionDefinition> nativeCacheInvalidatingDifferences = | 
|  | getNativeCacheInvalidatingDifferences(configuration, diff); | 
|  | if (nativeCacheInvalidatingDifferences.isEmpty() | 
|  | && diff.getChangedStarlarkOptions().isEmpty()) { | 
|  | // The configuration may have changed, but none of the changes required a cache reset. For | 
|  | // example, test trimming was turned on and a test option changed. In this case, nothing needs | 
|  | // to be done. | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (maxDifferencesToShow == 0) { | 
|  | return "Build options have changed"; | 
|  | } | 
|  |  | 
|  | ImmutableList<String> relevantDifferences = | 
|  | Streams.concat( | 
|  | diff.getChangedStarlarkOptions().stream().map(Label::getCanonicalForm), | 
|  | nativeCacheInvalidatingDifferences.stream().map(OptionDefinition::getOptionName)) | 
|  | .map(s -> "--" + s) | 
|  | // Sorting the list to ensure that (if truncated through maxDifferencesToShow) the | 
|  | // options in the message remain stable. | 
|  | .sorted() | 
|  | .collect(toImmutableList()); | 
|  |  | 
|  | if (maxDifferencesToShow > 0 && relevantDifferences.size() > maxDifferencesToShow) { | 
|  | return String.format( | 
|  | "Build options %s%s and %d more have changed", | 
|  | Joiner.on(", ").join(relevantDifferences.subList(0, maxDifferencesToShow)), | 
|  | maxDifferencesToShow == 1 ? "" : ",", | 
|  | relevantDifferences.size() - maxDifferencesToShow); | 
|  | } else if (relevantDifferences.size() == 1) { | 
|  | return String.format( | 
|  | "Build option %s has changed", Iterables.getOnlyElement(relevantDifferences)); | 
|  | } else if (relevantDifferences.size() == 2) { | 
|  | return String.format( | 
|  | "Build options %s have changed", Joiner.on(" and ").join(relevantDifferences)); | 
|  | } else { | 
|  | return String.format( | 
|  | "Build options %s, and %s have changed", | 
|  | Joiner.on(", ").join(relevantDifferences.subList(0, relevantDifferences.size() - 1)), | 
|  | Iterables.getLast(relevantDifferences)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO(schmitt): This method assumes that the only option that can cause multiple target | 
|  | //  configurations is --cpu which (with the presence of split transitions) is no longer true. | 
|  | private ImmutableSet<OptionDefinition> getNativeCacheInvalidatingDifferences( | 
|  | BuildConfigurationValue newConfig, OptionsDiff diff) { | 
|  | return diff.getFirst().keySet().stream() | 
|  | .filter( | 
|  | (definition) -> | 
|  | ruleClassProvider.shouldInvalidateCacheForOptionDiff( | 
|  | newConfig.getOptions(), | 
|  | definition, | 
|  | diff.getFirst().get(definition), | 
|  | Iterables.getOnlyElement(diff.getSecond().get(definition)))) | 
|  | .collect(toImmutableSet()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns whether the analysis results from previous invocations should be discarded or report an | 
|  | * error if it should be, but it's disallowed. | 
|  | * | 
|  | * <p>This should happen when the top-level configuration has changed or if the previous | 
|  | * invocation decided that this should happen. Either way, this method also emits a message | 
|  | * informing the user about this decision. | 
|  | */ | 
|  | public boolean shouldDiscardAnalysisCache( | 
|  | EventHandler eventHandler, | 
|  | BuildOptions newOptions, | 
|  | int maxDifferencesToShow, | 
|  | boolean allowAnalysisCacheDiscards, | 
|  | Optional<AdditionalConfigurationChangeEvent> additionalConfigurationChangeEvent) | 
|  | throws InvalidConfigurationException { | 
|  | if (this.configuration == null) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (skyframeAnalysisWasDiscarded) { | 
|  | logger.atInfo().log("Discarding analysis cache because the previous invocation told us to"); | 
|  | eventHandler.handle( | 
|  | Event.warn( | 
|  | "--discard_analysis_cache was used in the previous build, " | 
|  | + "discarding analysis cache.")); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | String diff = | 
|  | describeConfigurationDifference( | 
|  | originalConfigurationOptions, newOptions, maxDifferencesToShow); | 
|  |  | 
|  | if (diff == null && additionalConfigurationChangeEvent.isPresent()) { | 
|  | diff = additionalConfigurationChangeEvent.get().getChangeDescription(); | 
|  | } | 
|  |  | 
|  | if (diff != null) { | 
|  | if (!allowAnalysisCacheDiscards) { | 
|  | String message = String.format("%s, analysis cache would have been discarded.", diff); | 
|  | throw new InvalidConfigurationException( | 
|  | message, FailureDetails.BuildConfiguration.Code.CONFIGURATION_DISCARDED_ANALYSIS_CACHE); | 
|  | } | 
|  | eventHandler.handle( | 
|  | Event.warn( | 
|  | diff | 
|  | + ", discarding analysis cache (this can be expensive, see" | 
|  | + " https://bazel.build/advanced/performance/iteration-speed).")); | 
|  | logger.atInfo().log( | 
|  | "Discarding analysis cache because the build configuration changed: %s", diff); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** Sets the configuration. Not thread-safe. */ | 
|  | @VisibleForTesting | 
|  | public void setConfiguration( | 
|  | BuildConfigurationValue configuration, | 
|  | BuildOptions originalOptions, | 
|  | boolean discardAnalysisCache) { | 
|  | if (discardAnalysisCache) { | 
|  | // Note that clearing the analysis cache is currently required for correctness. It is also | 
|  | // helpful to save memory. | 
|  | // | 
|  | // If we had more memory, fixing the correctness issue (see also b/144932999) would allow us | 
|  | // to not invalidate the cache, leading to potentially better performance on incremental | 
|  | // builds. | 
|  | this.configuration = configuration; | 
|  | this.originalConfigurationOptions = originalOptions; | 
|  | skyframeExecutor.handleAnalysisInvalidatingChange(); | 
|  | } else if (this.configuration == null) { | 
|  | this.configuration = configuration; | 
|  | this.originalConfigurationOptions = originalOptions; | 
|  | } | 
|  |  | 
|  | skyframeAnalysisWasDiscarded = false; | 
|  | skyframeExecutor.setTopLevelConfiguration(configuration); | 
|  | } | 
|  |  | 
|  | @VisibleForTesting | 
|  | @Nullable | 
|  | public BuildConfigurationValue getBuildConfiguration() { | 
|  | return configuration; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Drops the analysis cache. If building with Skyframe, targets in {@code topLevelTargets} may | 
|  | * remain in the cache for use during the execution phase. | 
|  | * | 
|  | * @see com.google.devtools.build.lib.analysis.AnalysisOptions#discardAnalysisCache | 
|  | */ | 
|  | public void clearAnalysisCache( | 
|  | ImmutableSet<ConfiguredTarget> topLevelTargets, ImmutableSet<AspectKey> topLevelAspects) { | 
|  | // TODO(bazel-team): Consider clearing packages too to save more memory. | 
|  | skyframeAnalysisWasDiscarded = true; | 
|  | try (SilentCloseable c = Profiler.instance().profile("skyframeExecutor.clearAnalysisCache")) { | 
|  | skyframeExecutor.clearAnalysisCache(topLevelTargets, topLevelAspects); | 
|  | } | 
|  | starlarkTransitionCache.clear(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Analyzes the specified targets using Skyframe as the driving framework. | 
|  | * | 
|  | * @return the configured targets that should be built along with a WalkableGraph of the analysis. | 
|  | */ | 
|  | public SkyframeAnalysisResult configureTargets( | 
|  | ExtendedEventHandler eventHandler, | 
|  | ImmutableMap<Label, Target> labelToTargetMap, | 
|  | ImmutableList<ConfiguredTargetKey> ctKeys, | 
|  | ImmutableList<TopLevelAspectsKey> topLevelAspectsKeys, | 
|  | TopLevelArtifactContext topLevelArtifactContextForConflictPruning, | 
|  | EventBus eventBus, | 
|  | BugReporter bugReporter, | 
|  | boolean keepGoing, | 
|  | QuiescingExecutors executors, | 
|  | boolean checkForActionConflicts) | 
|  | throws InterruptedException, ViewCreationFailedException { | 
|  | enableAnalysis(true); | 
|  | ConfigureTargetsResult result; | 
|  | try (SilentCloseable c = Profiler.instance().profile("skyframeExecutor.configureTargets")) { | 
|  | result = | 
|  | skyframeExecutor.configureTargets( | 
|  | eventHandler, labelToTargetMap, ctKeys, topLevelAspectsKeys, keepGoing, executors); | 
|  | } finally { | 
|  | enableAnalysis(false); | 
|  | } | 
|  |  | 
|  | ImmutableSet<ConfiguredTarget> cts = result.configuredTargets(); | 
|  | ImmutableMap<AspectKey, ConfiguredAspect> aspects = result.aspects(); | 
|  | ImmutableSet<AspectKey> aspectKeys = aspects.keySet(); | 
|  | PackageRoots packageRoots = result.packageRoots(); | 
|  | EvaluationResult<ActionLookupValue> evaluationResult = result.evaluationResult(); | 
|  |  | 
|  | ImmutableMap<ActionAnalysisMetadata, ActionConflictException> interTargetConflicts = | 
|  | ImmutableMap.of(); | 
|  | try (SilentCloseable c = | 
|  | Profiler.instance().profile("skyframeExecutor.findArtifactConflicts")) { | 
|  | var newKeys = | 
|  | ImmutableSet.<ActionLookupKey>builderWithExpectedSize(ctKeys.size() + aspectKeys.size()) | 
|  | .addAll(ctKeys) | 
|  | .addAll(aspectKeys) | 
|  | .build(); | 
|  | if (shouldCheckForConflicts(checkForActionConflicts, newKeys)) { | 
|  | largestTopLevelKeySetCheckedForConflicts = newKeys; | 
|  | // This operation is somewhat expensive, so we only do it if the graph might have changed in | 
|  | // some way -- either we analyzed a new target or we invalidated an old one or are building | 
|  | // targets together that haven't been built before. | 
|  | ActionLookupValuesTraversal analysisTraversalResult = | 
|  | skyframeExecutor.collectActionLookupValuesInBuild(ctKeys, aspectKeys); | 
|  | ArtifactConflictFinder.ActionConflictsAndStats conflictsAndStats = | 
|  | ArtifactConflictFinder.findAndStoreArtifactConflicts( | 
|  | analysisTraversalResult.getActionLookupValueShards(), | 
|  | analysisTraversalResult.getActionCount(), | 
|  | actionKeyContext); | 
|  | BuildGraphMetrics buildGraphMetrics = | 
|  | analysisTraversalResult | 
|  | .getMetrics() | 
|  | .setOutputArtifactCount(conflictsAndStats.getOutputArtifactCount()) | 
|  | .build(); | 
|  | eventBus.post(new AnalysisGraphStatsEvent(buildGraphMetrics)); | 
|  | interTargetConflicts = conflictsAndStats.getConflicts(); | 
|  | someActionLookupValueEvaluated = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Intra-target conflict would mean an error in evaluationResult. | 
|  | if (!evaluationResult.hasError() && interTargetConflicts.isEmpty()) { | 
|  | return new SkyframeAnalysisResult( | 
|  | /* hasLoadingError= */ false, | 
|  | /* hasAnalysisError= */ false, | 
|  | /* hasActionConflicts= */ false, | 
|  | cts, | 
|  | evaluationResult.getWalkableGraph(), | 
|  | aspects, | 
|  | result.targetsWithConfiguration(), | 
|  | packageRoots); | 
|  | } | 
|  |  | 
|  | ErrorProcessingResult errorProcessingResult = | 
|  | SkyframeErrorProcessor.processAnalysisErrors( | 
|  | evaluationResult, | 
|  | skyframeExecutor.getCyclesReporter(), | 
|  | eventHandler, | 
|  | keepGoing, | 
|  | skyframeExecutor.tracksStateForIncrementality(), | 
|  | eventBus, | 
|  | bugReporter); | 
|  |  | 
|  | var actionConflicts = | 
|  | ImmutableMap.<ActionAnalysisMetadata, ActionConflictException>builder() | 
|  | .putAll(interTargetConflicts) | 
|  | .putAll(errorProcessingResult.actionConflicts()) // Intra-target conflicts. | 
|  | .buildOrThrow(); | 
|  | foundActionConflictInLatestCheck = !actionConflicts.isEmpty(); | 
|  | ViewCreationFailedException noKeepGoingExceptionDueToConflict = null; | 
|  | // Sometimes there are action conflicts, but the actions aren't actually required to run by the | 
|  | // build. In such cases, the conflict should still be reported to the user. | 
|  | // See OutputArtifactConflictTest#unusedActionsStillConflict. | 
|  | Set<String> reportedActionConflictExceptions = Sets.newHashSet(); | 
|  | for (Entry<ActionAnalysisMetadata, ActionConflictException> bad : actionConflicts.entrySet()) { | 
|  | ActionConflictException ace = bad.getValue(); | 
|  | var detailedExitCode = ace.getDetailedExitCode(); | 
|  | if (reportedActionConflictExceptions.add(ace.getMessage())) { | 
|  | ace.reportTo(eventHandler); | 
|  | if (keepGoing) { | 
|  | eventHandler.handle( | 
|  | Event.warn( | 
|  | "errors encountered while analyzing target '" | 
|  | + bad.getKey().getOwner().getLabel() | 
|  | + "': it will not be built")); | 
|  | } | 
|  | } | 
|  | if (!keepGoing) { | 
|  | noKeepGoingExceptionDueToConflict = | 
|  | new ViewCreationFailedException(detailedExitCode.getFailureDetail(), ace); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (foundActionConflictInLatestCheck) { | 
|  | // In order to determine the set of configured targets transitively error free from action | 
|  | // conflict issues, we run a post-processing update() that uses the bad action map. | 
|  | TopLevelActionConflictReport topLevelActionConflictReport; | 
|  | enableAnalysis(true); | 
|  | try { | 
|  | topLevelActionConflictReport = | 
|  | skyframeExecutor.filterActionConflictsForConfiguredTargetsAndAspects( | 
|  | eventHandler, | 
|  | Iterables.concat(ctKeys, aspectKeys), | 
|  | actionConflicts, | 
|  | topLevelArtifactContextForConflictPruning); | 
|  | } finally { | 
|  | enableAnalysis(false); | 
|  | } | 
|  | // Report an AnalysisFailureEvent to BEP for the top-level targets with discoverable action | 
|  | // conflicts, then finally throw if evaluation is --nokeep_going. | 
|  | for (ActionLookupKey actionLookupKey : Iterables.concat(ctKeys, aspectKeys)) { | 
|  | if (!topLevelActionConflictReport.isErrorFree(actionLookupKey)) { | 
|  | Optional<ActionConflictException> e = | 
|  | topLevelActionConflictReport.getConflictException(actionLookupKey); | 
|  | if (e.isEmpty()) { | 
|  | continue; | 
|  | } | 
|  | ActionConflictException conflictException = e.get(); | 
|  | AnalysisFailedCause failedCause = | 
|  | makeArtifactConflictAnalysisFailedCause(conflictException); | 
|  | boolean targetConfigured = true; | 
|  | // Attempt to promote any ConfiguredTargetKey to the one embedded in the ConfiguredTarget | 
|  | // to reflect any transitions or trimming. | 
|  | if (actionLookupKey instanceof ConfiguredTargetKey) { | 
|  | var value = ((ConfiguredTargetValue) evaluationResult.get(actionLookupKey)); | 
|  | if (value == null) { | 
|  | targetConfigured = false; | 
|  | } else if (value.getConfiguredTarget() != null) { | 
|  | // It's possible that the ConfiguredTarget has been cleared. | 
|  | actionLookupKey = value.getConfiguredTarget().getLookupKey(); | 
|  | } | 
|  | } | 
|  | if (!targetConfigured) { | 
|  | eventBus.post( | 
|  | AnalysisFailureEvent.whileAnalyzingTarget( | 
|  | (ConfiguredTargetKey) actionLookupKey, | 
|  | NestedSetBuilder.create(Order.STABLE_ORDER, failedCause))); | 
|  | } else { | 
|  | eventBus.post( | 
|  | AnalysisFailureEvent.actionConflict( | 
|  | actionLookupKey, NestedSetBuilder.create(Order.STABLE_ORDER, failedCause))); | 
|  | } | 
|  |  | 
|  | if (!keepGoing) { | 
|  | noKeepGoingExceptionDueToConflict = | 
|  | new ViewCreationFailedException( | 
|  | failedCause.getDetailedExitCode().getFailureDetail(), conflictException); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // If we're here and we're --nokeep_going, then there was a conflict due to actions not | 
|  | // discoverable by TopLevelActionLookupConflictFindingFunction. This includes extra actions, | 
|  | // coverage artifacts, and artifacts produced by aspects in output groups not present in | 
|  | // --output_groups. Throw the exception produced by the ArtifactConflictFinder which cannot | 
|  | // identify root-cause top-level keys but does catch all possible conflicts. | 
|  | if (!keepGoing) { | 
|  | skyframeExecutor.resetActionConflictsStoredInSkyframe(); | 
|  | throw Preconditions.checkNotNull(noKeepGoingExceptionDueToConflict); | 
|  | } | 
|  |  | 
|  | // Filter cts and aspects to only error-free keys. Note that any analysis failure - not just | 
|  | // action conflicts - will be observed here and lead to a key's exclusion. | 
|  | cts = | 
|  | ctKeys.stream() | 
|  | .filter(topLevelActionConflictReport::isErrorFree) | 
|  | .map( | 
|  | k -> | 
|  | Preconditions.checkNotNull((ConfiguredTargetValue) evaluationResult.get(k), k) | 
|  | .getConfiguredTarget()) | 
|  | .collect(toImmutableSet()); | 
|  |  | 
|  | aspects = | 
|  | aspects.entrySet().stream() | 
|  | .filter(e -> topLevelActionConflictReport.isErrorFree(e.getKey())) | 
|  | .collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); | 
|  | } | 
|  |  | 
|  | return new SkyframeAnalysisResult( | 
|  | errorProcessingResult.hasLoadingError(), | 
|  | evaluationResult.hasError() || foundActionConflictInLatestCheck, | 
|  | foundActionConflictInLatestCheck, | 
|  | cts, | 
|  | evaluationResult.getWalkableGraph(), | 
|  | aspects, | 
|  | result.targetsWithConfiguration(), | 
|  | packageRoots); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Performs analysis & execution of the CTs and aspects with Skyframe. | 
|  | * | 
|  | * <p>In case of error: --nokeep_going will eventually throw a ViewCreationFailedException, | 
|  | * whereas --keep_going will return a SkyframeAnalysisAndExecutionResult which contains the | 
|  | * failure details. | 
|  | * | 
|  | * <p>TODO(b/199053098) Have a more appropriate return type. | 
|  | */ | 
|  | public SkyframeAnalysisResult analyzeAndExecuteTargets( | 
|  | ExtendedEventHandler eventHandler, | 
|  | List<ConfiguredTargetKey> ctKeys, | 
|  | ImmutableList<TopLevelAspectsKey> topLevelAspectsKeys, | 
|  | @Nullable ImmutableSet<Label> testsToRun, | 
|  | ImmutableMap<Label, Target> labelTargetMap, | 
|  | TopLevelArtifactContext topLevelArtifactContext, | 
|  | ImmutableSet<Label> explicitTargetPatterns, | 
|  | EventBus eventBus, | 
|  | BugReporter bugReporter, | 
|  | ResourceManager resourceManager, | 
|  | BuildResultListener buildResultListener, | 
|  | CoverageReportActionsWrapperSupplier coverageReportActionsWrapperSupplier, | 
|  | boolean keepGoing, | 
|  | boolean skipIncompatibleExplicitTargets, | 
|  | boolean checkForActionConflicts, | 
|  | boolean extraActionTopLevelOnly, | 
|  | QuiescingExecutors executors, | 
|  | boolean shouldDiscardAnalysisCache, | 
|  | BuildDriverKeyTestContext buildDriverKeyTestContext, | 
|  | int skymeldAnalysisOverlapPercentage) | 
|  | throws InterruptedException, | 
|  | ViewCreationFailedException, | 
|  | BuildFailedException, | 
|  | TestExecException { | 
|  | Stopwatch analysisWorkTimer = Stopwatch.createStarted(); | 
|  | EvaluationResult<SkyValue> mainEvaluationResult; | 
|  |  | 
|  | var newKeys = | 
|  | ImmutableSet.<ActionLookupKey>builderWithExpectedSize( | 
|  | ctKeys.size() + topLevelAspectsKeys.size()) | 
|  | .addAll(ctKeys) | 
|  | .addAll(topLevelAspectsKeys) | 
|  | .build(); | 
|  |  | 
|  | ImmutableList<Artifact> workspaceStatusArtifacts = | 
|  | skyframeExecutor.getWorkspaceStatusArtifacts(eventHandler); | 
|  |  | 
|  | skyframeExecutor.setTestTypeResolver( | 
|  | target -> | 
|  | determineTestTypeImpl( | 
|  | testsToRun, | 
|  | labelTargetMap, | 
|  | target.getLabel(), | 
|  | buildDriverKeyTestContext, | 
|  | eventHandler)); | 
|  |  | 
|  | ImmutableSet<BuildDriverKey> buildDriverCTKeys = | 
|  | ctKeys.stream() | 
|  | .map( | 
|  | ctKey -> | 
|  | BuildDriverKey.ofConfiguredTarget( | 
|  | ctKey, | 
|  | topLevelArtifactContext, | 
|  | /* explicitlyRequested= */ explicitTargetPatterns.contains( | 
|  | ctKey.getLabel()), | 
|  | skipIncompatibleExplicitTargets, | 
|  | extraActionTopLevelOnly, | 
|  | keepGoing)) | 
|  | .collect(ImmutableSet.toImmutableSet()); | 
|  |  | 
|  | ImmutableSet<BuildDriverKey> buildDriverAspectKeys = | 
|  | topLevelAspectsKeys.stream() | 
|  | .map( | 
|  | k -> | 
|  | BuildDriverKey.ofTopLevelAspect( | 
|  | k, | 
|  | topLevelArtifactContext, | 
|  | /* explicitlyRequested= */ explicitTargetPatterns.contains(k.getLabel()), | 
|  | skipIncompatibleExplicitTargets, | 
|  | extraActionTopLevelOnly, | 
|  | keepGoing)) | 
|  | .collect(ImmutableSet.toImmutableSet()); | 
|  | List<DetailedExitCode> detailedExitCodes = new ArrayList<>(); | 
|  | MultiThreadPoolsQuiescingExecutor executor = | 
|  | (MultiThreadPoolsQuiescingExecutor) executors.getMergedAnalysisAndExecutionExecutor(); | 
|  | Set<SkyKey> topLevelKeys = | 
|  | Sets.newConcurrentHashSet(Sets.union(buildDriverCTKeys, buildDriverAspectKeys)); | 
|  |  | 
|  | ConflictCheckingMode conflictCheckingMode = | 
|  | shouldCheckForConflicts(checkForActionConflicts, newKeys) | 
|  | ? skyframeExecutor.tracksStateForIncrementality() | 
|  | ? WITH_TRAVERSAL | 
|  | : UPON_CONFIGURED_OBJECT_CREATION | 
|  | : NONE; | 
|  | skyframeExecutor.setConflictCheckingModeInThisBuild(conflictCheckingMode); | 
|  |  | 
|  | try (AnalysisOperationWatcher autoCloseableWatcher = | 
|  | AnalysisOperationWatcher.createAndRegisterWithEventBus( | 
|  | topLevelKeys, | 
|  | eventBus, | 
|  | /* lowerThresholdToSignalForExecution= */ (float) | 
|  | (topLevelKeys.size() * skymeldAnalysisOverlapPercentage / 100.0), | 
|  | /* finisher= */ () -> | 
|  | analysisFinishedCallback( | 
|  | eventBus, | 
|  | buildResultListener, | 
|  | skyframeExecutor, | 
|  | ctKeys, | 
|  | shouldDiscardAnalysisCache, | 
|  | /* measuredAnalysisTime= */ analysisWorkTimer.stop().elapsed().toMillis(), | 
|  | /* conflictCheckingMode= */ conflictCheckingMode), | 
|  | /* executionGoAheadCallback= */ executor::launchQueuedUpExecutionPhaseTasks)) { | 
|  |  | 
|  | try { | 
|  | skyframeExecutor.getIsBuildingExclusiveArtifacts().set(false); | 
|  | resourceManager.resetResourceUsage(); | 
|  | EvaluationResult<SkyValue> additionalArtifactsResult; | 
|  | try (SilentCloseable c = | 
|  | Profiler.instance().profile("skyframeExecutor.evaluateBuildDriverKeys")) { | 
|  | // Will be disabled later by the AnalysisOperationWatcher upon conclusion of analysis. | 
|  | enableAnalysis(true); | 
|  | mainEvaluationResult = | 
|  | skyframeExecutor.evaluateBuildDriverKeys( | 
|  | eventHandler, | 
|  | buildDriverCTKeys, | 
|  | buildDriverAspectKeys, | 
|  | workspaceStatusArtifacts, | 
|  | keepGoing, | 
|  | executors.executionParallelism(), | 
|  | executor); | 
|  | } finally { | 
|  | // Required for incremental correctness. | 
|  | // We unconditionally reset the states here instead of in #analysisFinishedCallback since | 
|  | // in case of --nokeep_going & analysis error, the analysis phase is never finished. | 
|  | skyframeExecutor.clearIncrementalArtifactConflictFindingStates(); | 
|  | skyframeExecutor.resetBuildDriverFunction(); | 
|  | skyframeExecutor.setTestTypeResolver(null); | 
|  |  | 
|  | // These attributes affect whether conflict checking will be done during the next build. | 
|  | if (shouldCheckForConflicts(checkForActionConflicts, newKeys)) { | 
|  | largestTopLevelKeySetCheckedForConflicts = newKeys; | 
|  | } | 
|  | someActionLookupValueEvaluated = false; | 
|  | } | 
|  |  | 
|  | // The exclusive tests whose analysis succeeded i.e. those that can be run. | 
|  | ImmutableSet<ConfiguredTarget> exclusiveTestsToRun = | 
|  | getExclusiveTests(mainEvaluationResult); | 
|  | boolean continueWithExclusiveTests = !mainEvaluationResult.hasError() || keepGoing; | 
|  | boolean hasExclusiveTestsError = false; | 
|  |  | 
|  | if (continueWithExclusiveTests && !exclusiveTestsToRun.isEmpty()) { | 
|  | skyframeExecutor.getIsBuildingExclusiveArtifacts().set(true); | 
|  | // Run exclusive tests sequentially. | 
|  | Iterable<SkyKey> testCompletionKeys = | 
|  | TestCompletionValue.keys( | 
|  | exclusiveTestsToRun, topLevelArtifactContext, /* exclusiveTesting= */ true); | 
|  | for (SkyKey testCompletionKey : testCompletionKeys) { | 
|  | EvaluationResult<SkyValue> testRunResult = | 
|  | skyframeExecutor.runExclusiveTestSkymeld( | 
|  | eventHandler, | 
|  | resourceManager, | 
|  | testCompletionKey, | 
|  | keepGoing, | 
|  | executors.executionParallelism()); | 
|  | if (testRunResult.hasError()) { | 
|  | hasExclusiveTestsError = true; | 
|  | detailedExitCodes.add( | 
|  | SkyframeErrorProcessor.processErrors( | 
|  | testRunResult, | 
|  | skyframeExecutor.getCyclesReporter(), | 
|  | eventHandler, | 
|  | keepGoing, | 
|  | skyframeExecutor.tracksStateForIncrementality(), | 
|  | eventBus, | 
|  | bugReporter, | 
|  | /* includeExecutionPhase= */ true) | 
|  | .executionDetailedExitCode()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Coverage report generation should only be requested after all tests have executed. | 
|  | // When --nokeep_going and there's an earlier error, we should skip this and fail fast. | 
|  | if ((!mainEvaluationResult.hasError() && !hasExclusiveTestsError) || keepGoing) { | 
|  | ImmutableSet<Artifact> coverageReportArtifacts = | 
|  | coverageReportActionsWrapperSupplier.getCoverageReportArtifacts( | 
|  | buildResultListener.getAnalyzedTargets(), buildResultListener.getAnalyzedTests()); | 
|  | eventBus.post(CoverageArtifactsKnownEvent.create(coverageReportArtifacts)); | 
|  | additionalArtifactsResult = | 
|  | skyframeExecutor.evaluateSkyKeys( | 
|  | eventHandler, Artifact.keys(coverageReportArtifacts), keepGoing); | 
|  | if (additionalArtifactsResult.hasError()) { | 
|  | detailedExitCodes.add( | 
|  | SkyframeErrorProcessor.processErrors( | 
|  | additionalArtifactsResult, | 
|  | skyframeExecutor.getCyclesReporter(), | 
|  | eventHandler, | 
|  | keepGoing, | 
|  | skyframeExecutor.tracksStateForIncrementality(), | 
|  | eventBus, | 
|  | bugReporter, | 
|  | /* includeExecutionPhase= */ true) | 
|  | .executionDetailedExitCode()); | 
|  | } | 
|  | } | 
|  | } finally { | 
|  | // No more action execution beyond this point. | 
|  | skyframeExecutor.clearExecutionStatesSkymeld(eventHandler); | 
|  | // Also releases thread locks. | 
|  | resourceManager.resetResourceUsage(); | 
|  | } | 
|  |  | 
|  | if (!mainEvaluationResult.hasError() && detailedExitCodes.isEmpty()) { | 
|  | ImmutableMap<AspectKey, ConfiguredAspect> successfulAspects = | 
|  | getSuccessfulAspectMap( | 
|  | topLevelAspectsKeys.size(), | 
|  | mainEvaluationResult, | 
|  | buildDriverAspectKeys, | 
|  | /* topLevelActionConflictReport= */ null); | 
|  | var targetsWithConfiguration = | 
|  | ImmutableList.<TargetAndConfiguration>builderWithExpectedSize(ctKeys.size()); | 
|  | ImmutableSet<ConfiguredTarget> successfulConfiguredTargets = | 
|  | getSuccessfulConfiguredTargets( | 
|  | ctKeys.size(), | 
|  | mainEvaluationResult, | 
|  | buildDriverCTKeys, | 
|  | labelTargetMap, | 
|  | targetsWithConfiguration, | 
|  | /* topLevelActionConflictReport= */ null); | 
|  |  | 
|  | return SkyframeAnalysisAndExecutionResult.success( | 
|  | successfulConfiguredTargets, | 
|  | mainEvaluationResult.getWalkableGraph(), | 
|  | successfulAspects, | 
|  | targetsWithConfiguration.build(), | 
|  | /* packageRoots= */ null); | 
|  | } | 
|  |  | 
|  | ErrorProcessingResult errorProcessingResult = | 
|  | SkyframeErrorProcessor.processErrors( | 
|  | mainEvaluationResult, | 
|  | skyframeExecutor.getCyclesReporter(), | 
|  | eventHandler, | 
|  | keepGoing, | 
|  | skyframeExecutor.tracksStateForIncrementality(), | 
|  | eventBus, | 
|  | bugReporter, | 
|  | /* includeExecutionPhase= */ true); | 
|  | detailedExitCodes.add(errorProcessingResult.executionDetailedExitCode()); | 
|  |  | 
|  | foundActionConflictInLatestCheck = !errorProcessingResult.actionConflicts().isEmpty(); | 
|  | TopLevelActionConflictReport topLevelActionConflictReport = | 
|  | foundActionConflictInLatestCheck | 
|  | ? handleActionConflicts( | 
|  | eventHandler, | 
|  | mainEvaluationResult.getWalkableGraph(), | 
|  | ctKeys, | 
|  | topLevelAspectsKeys, | 
|  | topLevelArtifactContext, | 
|  | eventBus, | 
|  | keepGoing, | 
|  | errorProcessingResult) | 
|  | : null; | 
|  | ImmutableMap<AspectKey, ConfiguredAspect> successfulAspects = | 
|  | getSuccessfulAspectMap( | 
|  | topLevelAspectsKeys.size(), | 
|  | mainEvaluationResult, | 
|  | buildDriverAspectKeys, | 
|  | topLevelActionConflictReport); | 
|  | var targetsWithConfiguration = | 
|  | ImmutableList.<TargetAndConfiguration>builderWithExpectedSize(ctKeys.size()); | 
|  | ImmutableSet<ConfiguredTarget> successfulConfiguredTargets = | 
|  | getSuccessfulConfiguredTargets( | 
|  | ctKeys.size(), | 
|  | mainEvaluationResult, | 
|  | buildDriverCTKeys, | 
|  | labelTargetMap, | 
|  | targetsWithConfiguration, | 
|  | topLevelActionConflictReport); | 
|  |  | 
|  | return SkyframeAnalysisAndExecutionResult.withErrors( | 
|  | /* hasLoadingError= */ errorProcessingResult.hasLoadingError(), | 
|  | // legacy behavior: action conflicts are considered analysis errors. | 
|  | /* hasAnalysisError= */ errorProcessingResult.hasAnalysisError() | 
|  | || foundActionConflictInLatestCheck, | 
|  | /* hasActionConflicts= */ foundActionConflictInLatestCheck, | 
|  | successfulConfiguredTargets, | 
|  | mainEvaluationResult.getWalkableGraph(), | 
|  | successfulAspects, | 
|  | targetsWithConfiguration.build(), | 
|  | /* packageRoots= */ null, | 
|  | Collections.max(detailedExitCodes, DetailedExitCodeComparator.INSTANCE)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Handles the required steps after all analysis work in this build is done. */ | 
|  | private void analysisFinishedCallback( | 
|  | EventBus eventBus, | 
|  | BuildResultListener buildResultListener, | 
|  | SkyframeExecutor skyframeExecutor, | 
|  | List<ConfiguredTargetKey> configuredTargetKeys, | 
|  | boolean shouldDiscardAnalysisCache, | 
|  | long measuredAnalysisTime, | 
|  | ConflictCheckingMode conflictCheckingMode) | 
|  | throws InterruptedException { | 
|  | if (conflictCheckingMode != NONE) { | 
|  | // Now that we have the full picture, it's time to collect the metrics of the whole graph. | 
|  | BuildGraphMetrics.Builder buildGraphMetricsBuilder = | 
|  | skyframeExecutor | 
|  | .collectActionLookupValuesInBuild( | 
|  | configuredTargetKeys, buildResultListener.getAnalyzedAspects().keySet()) | 
|  | .getMetrics(); | 
|  | IncrementalArtifactConflictFinder incrementalArtifactConflictFinder = | 
|  | skyframeExecutor.getCheckerForConflictCheckingMode(conflictCheckingMode); | 
|  | if (incrementalArtifactConflictFinder != null) { | 
|  | buildGraphMetricsBuilder.setOutputArtifactCount( | 
|  | incrementalArtifactConflictFinder.getOutputArtifactCount()); | 
|  | } | 
|  | eventBus.post(new AnalysisGraphStatsEvent(buildGraphMetricsBuilder.build())); | 
|  | } | 
|  |  | 
|  | if (shouldDiscardAnalysisCache) { | 
|  | clearAnalysisCache( | 
|  | buildResultListener.getAnalyzedTargets(), | 
|  | buildResultListener.getAnalyzedAspects().keySet()); | 
|  | } | 
|  |  | 
|  | // At this point, it's safe to clear objects related to action conflict checking. | 
|  | // Clearing the states here is a performance optimization (reduce peak heap size) and isn't | 
|  | // required for correctness. | 
|  | skyframeExecutor.clearIncrementalArtifactConflictFindingStates(); | 
|  |  | 
|  | // Clearing the syscall cache here to free up some heap space. | 
|  | // TODO(b/273225564) Would this incur more CPU cost for the execution phase cache misses? | 
|  | skyframeExecutor.clearSyscallCache(); | 
|  |  | 
|  | enableAnalysis(false); | 
|  |  | 
|  | eventBus.post( | 
|  | new AnalysisPhaseCompleteEvent( | 
|  | buildResultListener.getAnalyzedTargets(), | 
|  | getEvaluatedCounts(), | 
|  | getEvaluatedActionCounts(), | 
|  | getEvaluatedActionCountsByMnemonic(), | 
|  | measuredAnalysisTime, | 
|  | skyframeExecutor.getPackageManager().getAndClearStatistics(), | 
|  | skyframeExecutor.wasAnalysisCacheInvalidatedAndResetBit())); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Report the appropriate conflicts and return a TopLevelActionConflictReport. | 
|  | * | 
|  | * <p>The TopLevelActionConflictReport is used to determine the set of top level targets that | 
|  | * depend on conflicted actions. | 
|  | */ | 
|  | private TopLevelActionConflictReport handleActionConflicts( | 
|  | ExtendedEventHandler eventHandler, | 
|  | WalkableGraph graph, | 
|  | List<ConfiguredTargetKey> ctKeys, | 
|  | ImmutableList<TopLevelAspectsKey> topLevelAspectsKeys, | 
|  | TopLevelArtifactContext topLevelArtifactContextForConflictPruning, | 
|  | EventBus eventBus, | 
|  | boolean keepGoing, | 
|  | ErrorProcessingResult errorProcessingResult) | 
|  | throws InterruptedException, ViewCreationFailedException { | 
|  | // TODO(b/332898055) Unify with the noskymeld code path. | 
|  | try { | 
|  | // Here we already have the <TopLevelAspectKey, error> mapping, but what we need to fit into | 
|  | // the existing AnalysisFailureEvent is <AspectKey, error>. An extra Skyframe evaluation is | 
|  | // required. | 
|  | // If the conflict is intra-Aspect, the TopLevelAspectValue would be null and the AspectKey | 
|  | // isn't retrievable. It must be supplied via the ErrorProcessingResult. | 
|  | Iterable<ActionLookupKey> effectiveTopLevelKeysForConflictReporting = | 
|  | ImmutableSet.<ActionLookupKey>builder() | 
|  | .addAll(ctKeys) | 
|  | .addAll(getDerivedAspectKeysForConflictReporting(topLevelAspectsKeys)) | 
|  | .addAll(errorProcessingResult.aspectKeysForConflictReporting()) | 
|  | .build(); | 
|  | TopLevelActionConflictReport topLevelActionConflictReport; | 
|  | enableAnalysis(true); | 
|  | // In order to determine the set of configured targets transitively error free from action | 
|  | // conflict issues, we run a post-processing update() that uses the bad action map. | 
|  | try { | 
|  | topLevelActionConflictReport = | 
|  | skyframeExecutor.filterActionConflictsForConfiguredTargetsAndAspects( | 
|  | eventHandler, | 
|  | effectiveTopLevelKeysForConflictReporting, | 
|  | errorProcessingResult.actionConflicts(), | 
|  | topLevelArtifactContextForConflictPruning); | 
|  | } finally { | 
|  | enableAnalysis(false); | 
|  | } | 
|  | reportActionConflictErrors( | 
|  | topLevelActionConflictReport, | 
|  | graph, | 
|  | effectiveTopLevelKeysForConflictReporting, | 
|  | errorProcessingResult.actionConflicts(), | 
|  | eventHandler, | 
|  | eventBus, | 
|  | keepGoing); | 
|  | return topLevelActionConflictReport; | 
|  | } finally { | 
|  | skyframeExecutor.resetActionConflictsStoredInSkyframe(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * From the {@code topLevelActionConflictReport}, report the action conflict errors. | 
|  | * | 
|  | * <p>Throw a ViewCreationFailedException in case of --nokeep_going. | 
|  | */ | 
|  | private static void reportActionConflictErrors( | 
|  | TopLevelActionConflictReport topLevelActionConflictReport, | 
|  | WalkableGraph graph, | 
|  | Iterable<ActionLookupKey> effectiveTopLevelKeysForConflictReporting, | 
|  | ImmutableMap<ActionAnalysisMetadata, ActionConflictException> actionConflicts, | 
|  | ExtendedEventHandler eventHandler, | 
|  | EventBus eventBus, | 
|  | boolean keepGoing) | 
|  | throws ViewCreationFailedException, InterruptedException { | 
|  | // ArtifactPrefixConflictExceptions come in pairs, and only one should be reported. | 
|  | Set<String> reportedActionConflictExceptions = Sets.newHashSet(); | 
|  |  | 
|  | // Sometimes a conflicting action can't be traced to a top level target via | 
|  | // TopLevelActionConflictReport. We therefore need to print the errors from the conflicts | 
|  | // themselves. See SkyframeIntegrationTest#topLevelAspectsAndExtraActionsWithConflict. | 
|  | for (ActionConflictException e : actionConflicts.values()) { | 
|  | if (reportedActionConflictExceptions.add(e.getMessage())) { | 
|  | e.reportTo(eventHandler); | 
|  | if (keepGoing) { | 
|  | eventHandler.handle( | 
|  | Event.warn( | 
|  | String.format( | 
|  | "errors encountered while analyzing target '" | 
|  | + e.getArtifact().getOwnerLabel() | 
|  | + "': it will not be built"))); | 
|  | } | 
|  | } | 
|  | } | 
|  | // Report an AnalysisFailureEvent to BEP for the top-level targets with discoverable action | 
|  | // conflicts, then finally throw. | 
|  | for (ActionLookupKey actionLookupKey : effectiveTopLevelKeysForConflictReporting) { | 
|  | if (topLevelActionConflictReport.isErrorFree(actionLookupKey)) { | 
|  | continue; | 
|  | } | 
|  | Optional<ActionConflictException> e = | 
|  | topLevelActionConflictReport.getConflictException(actionLookupKey); | 
|  | if (e.isEmpty()) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | ActionConflictException conflictException = e.get(); | 
|  | AnalysisFailedCause failedCause = makeArtifactConflictAnalysisFailedCause(conflictException); | 
|  | boolean targetConfigured = true; | 
|  | // Attempt to promote any ConfiguredTargetKey to the one embedded in the ConfiguredTarget to | 
|  | // reflect any transitions or trimming. | 
|  | if (actionLookupKey instanceof ConfiguredTargetKey) { | 
|  | // This is a graph lookup instead of an EvaluationResult lookup because Skymeld's | 
|  | // EvaluationResult does not contain ConfiguredTargetKey. | 
|  | var value = ((ConfiguredTargetValue) graph.getValue(actionLookupKey)); | 
|  | if (value == null) { | 
|  | targetConfigured = false; | 
|  | } else if (value.getConfiguredTarget() != null) { | 
|  | // It's possible that the ConfiguredTarget has been cleared. | 
|  | actionLookupKey = value.getConfiguredTarget().getLookupKey(); | 
|  | } | 
|  | } | 
|  | if (!targetConfigured) { | 
|  | eventBus.post( | 
|  | AnalysisFailureEvent.whileAnalyzingTarget( | 
|  | (ConfiguredTargetKey) actionLookupKey, | 
|  | NestedSetBuilder.create(Order.STABLE_ORDER, failedCause))); | 
|  | } else { | 
|  | eventBus.post( | 
|  | AnalysisFailureEvent.actionConflict( | 
|  | actionLookupKey, NestedSetBuilder.create(Order.STABLE_ORDER, failedCause))); | 
|  | } | 
|  |  | 
|  | if (!keepGoing) { | 
|  | throw new ViewCreationFailedException( | 
|  | failedCause.getDetailedExitCode().getFailureDetail(), conflictException); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private static ImmutableSet<ConfiguredTarget> getExclusiveTests( | 
|  | EvaluationResult<SkyValue> evaluationResult) { | 
|  | ImmutableSet.Builder<ConfiguredTarget> exclusiveTests = ImmutableSet.builder(); | 
|  | for (SkyValue value : evaluationResult.values()) { | 
|  | if (value instanceof ExclusiveTestBuildDriverValue) { | 
|  | exclusiveTests.add( | 
|  | ((ExclusiveTestBuildDriverValue) value).getExclusiveTestConfiguredTarget()); | 
|  | } | 
|  | } | 
|  | return exclusiveTests.build(); | 
|  | } | 
|  |  | 
|  | private static TestType determineTestTypeImpl( | 
|  | ImmutableSet<Label> testsToRun, | 
|  | ImmutableMap<Label, Target> labelTargetMap, | 
|  | Label label, | 
|  | BuildDriverKeyTestContext buildDriverKeyTestContext, | 
|  | ExtendedEventHandler eventHandler) { | 
|  | if (testsToRun == null || !testsToRun.contains(label)) { | 
|  | return TestType.NOT_TEST; | 
|  | } | 
|  | Target target = labelTargetMap.get(label); | 
|  |  | 
|  | if (!(target instanceof Rule rule)) { | 
|  | return TestType.NOT_TEST; | 
|  | } | 
|  |  | 
|  | TestType fromExplicitFlagOrTag; | 
|  | if (buildDriverKeyTestContext.getTestStrategy().equals("exclusive") | 
|  | || TargetUtils.isExclusiveTestRule(rule) | 
|  | || (TargetUtils.isExclusiveIfLocalTestRule(rule) && TargetUtils.isLocalTestRule(rule))) { | 
|  | fromExplicitFlagOrTag = TestType.EXCLUSIVE; | 
|  | } else if (TargetUtils.isExclusiveIfLocalTestRule(rule)) { | 
|  | fromExplicitFlagOrTag = TestType.EXCLUSIVE_IF_LOCAL; | 
|  | } else { | 
|  | fromExplicitFlagOrTag = TestType.PARALLEL; | 
|  | } | 
|  |  | 
|  | if ((fromExplicitFlagOrTag == TestType.EXCLUSIVE | 
|  | && buildDriverKeyTestContext.forceExclusiveTestsInParallel()) | 
|  | || (fromExplicitFlagOrTag == TestType.EXCLUSIVE_IF_LOCAL | 
|  | && buildDriverKeyTestContext.forceExclusiveIfLocalTestsInParallel())) { | 
|  | eventHandler.handle( | 
|  | Event.warn( | 
|  | label | 
|  | + " is tagged " | 
|  | + fromExplicitFlagOrTag.getMsg() | 
|  | + ", but --test_strategy=" | 
|  | + buildDriverKeyTestContext.getTestStrategy() | 
|  | + " forces parallel test execution.")); | 
|  | return TestType.PARALLEL; | 
|  | } | 
|  | return fromExplicitFlagOrTag; | 
|  | } | 
|  |  | 
|  | // When we check for action conflicts that occur with a TopLevelAspectKey, a reference to the | 
|  | // lower-level AspectKeys is required: it could happen that only some AspectKeys, but not | 
|  | // all, that derived from a TopLevelAspectKey has a conflicting action. | 
|  | private ImmutableSet<AspectKey> getDerivedAspectKeysForConflictReporting( | 
|  | ImmutableList<TopLevelAspectsKey> topLevelAspectsKeys) { | 
|  | ImmutableSet.Builder<AspectKey> aspectKeysBuilder = ImmutableSet.builder(); | 
|  | for (TopLevelAspectsKey topLevelAspectsKey : topLevelAspectsKeys) { | 
|  | try { | 
|  | TopLevelAspectsValue topLevelAspectsValue = | 
|  | (TopLevelAspectsValue) | 
|  | skyframeExecutor.getDoneSkyValueForIntrospection(topLevelAspectsKey); | 
|  | aspectKeysBuilder.addAll(topLevelAspectsValue.getTopLevelAspectsMap().keySet()); | 
|  | } catch (FailureToRetrieveIntrospectedValueException e) { | 
|  | // It could happen that the analysis of TopLevelAspectKey wasn't complete: either its own | 
|  | // analysis failed, or another error was raise in --nokeep_going mode. In that case, it | 
|  | // couldn't be involved in the conflict exception anyway, and we just move on. | 
|  | // Unless it's an unexpected interrupt that caused the exception. | 
|  | if (e.getCause() instanceof InterruptedException) { | 
|  | BugReport.sendNonFatalBugReport(e); | 
|  | } | 
|  | } | 
|  | } | 
|  | return aspectKeysBuilder.build(); | 
|  | } | 
|  |  | 
|  | private static ImmutableSet<ConfiguredTarget> getSuccessfulConfiguredTargets( | 
|  | int expectedSize, | 
|  | EvaluationResult<SkyValue> evaluationResult, | 
|  | Set<BuildDriverKey> buildDriverCTKeys, | 
|  | ImmutableMap<Label, Target> labelToTargetMap, | 
|  | ImmutableList.Builder<TargetAndConfiguration> targetsWithConfiguration, | 
|  | @Nullable TopLevelActionConflictReport topLevelActionConflictReport) | 
|  | throws InterruptedException { | 
|  | ImmutableSet.Builder<ConfiguredTarget> cts = ImmutableSet.builderWithExpectedSize(expectedSize); | 
|  | for (BuildDriverKey bdCTKey : buildDriverCTKeys) { | 
|  | if (topLevelActionConflictReport != null | 
|  | && !topLevelActionConflictReport.isErrorFree(bdCTKey.getActionLookupKey())) { | 
|  | continue; | 
|  | } | 
|  | BuildDriverValue value = (BuildDriverValue) evaluationResult.get(bdCTKey); | 
|  | if (value == null) { | 
|  | continue; | 
|  | } | 
|  | ConfiguredTargetValue ctValue = (ConfiguredTargetValue) value.getWrappedSkyValue(); | 
|  | cts.add(ctValue.getConfiguredTarget()); | 
|  |  | 
|  | BuildConfigurationKey configurationKey = ctValue.getConfiguredTarget().getConfigurationKey(); | 
|  | var configuration = | 
|  | configurationKey == null | 
|  | ? null | 
|  | : (BuildConfigurationValue) | 
|  | evaluationResult.getWalkableGraph().getValue(configurationKey); | 
|  | targetsWithConfiguration.add( | 
|  | new TargetAndConfiguration( | 
|  | labelToTargetMap.get(bdCTKey.getActionLookupKey().getLabel()), configuration)); | 
|  | } | 
|  | return cts.build(); | 
|  | } | 
|  |  | 
|  | private static ImmutableMap<AspectKey, ConfiguredAspect> getSuccessfulAspectMap( | 
|  | int expectedSize, | 
|  | EvaluationResult<SkyValue> evaluationResult, | 
|  | Set<BuildDriverKey> buildDriverAspectKeys, | 
|  | @Nullable TopLevelActionConflictReport topLevelActionConflictReport) { | 
|  | // There can't be duplicate Aspects after resolving --aspects, so this is safe. | 
|  | ImmutableMap.Builder<AspectKey, ConfiguredAspect> aspects = | 
|  | ImmutableMap.builderWithExpectedSize(expectedSize); | 
|  | for (BuildDriverKey bdAspectKey : buildDriverAspectKeys) { | 
|  | if (topLevelActionConflictReport != null | 
|  | && !topLevelActionConflictReport.isErrorFree(bdAspectKey.getActionLookupKey())) { | 
|  | continue; | 
|  | } | 
|  | BuildDriverValue value = (BuildDriverValue) evaluationResult.get(bdAspectKey); | 
|  | if (value == null) { | 
|  | // Skip aspects that couldn't be applied to targets. | 
|  | continue; | 
|  | } | 
|  | TopLevelAspectsValue topLevelAspectsValue = (TopLevelAspectsValue) value.getWrappedSkyValue(); | 
|  | aspects.putAll(topLevelAspectsValue.getTopLevelAspectsMap()); | 
|  | } | 
|  | return aspects.buildOrThrow(); | 
|  | } | 
|  |  | 
|  | private static AnalysisFailedCause makeArtifactConflictAnalysisFailedCause( | 
|  | ActionConflictException ace) { | 
|  | DetailedExitCode detailedExitCode = ace.getDetailedExitCode(); | 
|  | Label causeLabel = ace.getArtifact().getArtifactOwner().getLabel(); | 
|  | BuildConfigurationKey causeConfigKey = null; | 
|  | if (ace.getArtifact().getArtifactOwner() instanceof ConfiguredTargetKey) { | 
|  | causeConfigKey = | 
|  | ((ConfiguredTargetKey) ace.getArtifact().getArtifactOwner()).getConfigurationKey(); | 
|  | } | 
|  | return new AnalysisFailedCause( | 
|  | causeLabel, configurationIdMessage(causeConfigKey), detailedExitCode); | 
|  | } | 
|  |  | 
|  | private boolean shouldCheckForConflicts( | 
|  | boolean specifiedValueInRequest, ImmutableSet<ActionLookupKey> newKeys) { | 
|  | if (!specifiedValueInRequest) { | 
|  | // A build request by default enables action conflict checking, except for some cases e.g. | 
|  | // cquery. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (someActionLookupValueEvaluated) { | 
|  | // A top-level target was added and may introduce a conflict, or a top-level target was | 
|  | // recomputed and may introduce or resolve a conflict. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!dirtiedActionLookupKeys.isEmpty()) { | 
|  | // No target was (re)computed but at least one was dirtied. | 
|  | // Example: (//:x //foo:y) are built, and in conflict (//:x creates foo/C and //foo:y | 
|  | // creates C). Then y is removed from foo/BUILD and only //:x is built, so //foo:y is | 
|  | // dirtied but not recomputed, and no other nodes are recomputed (and none are deleted). | 
|  | // Still we must do the conflict checking because previously there was a conflict but now | 
|  | // there isn't. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (foundActionConflictInLatestCheck) { | 
|  | // Example sequence: | 
|  | // 1.  Build (x y z), and there is a conflict. We store (x y z) as the largest checked key | 
|  | //     set, and record the fact that there were bad actions. | 
|  | // 2.  Null-build (x z), so we don't evaluate or dirty anything, but because we know there was | 
|  | //     some conflict last time but don't know exactly which targets conflicted, it could have | 
|  | //     been (x z), so we now check again. The value of foundActionConflictInLatestCheck would | 
|  | //     then be updated for the next build, based on the result of this check. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!largestTopLevelKeySetCheckedForConflicts.containsAll(newKeys)) { | 
|  | // Example sequence: | 
|  | // 1.  Build (x y z), and there is a conflict. We store (x y z) as the largest checked key | 
|  | //     set, and record the fact that there were bad actions. | 
|  | // 2.  Null-build (x z), so we don't evaluate or dirty anything, but we check again for | 
|  | //     conflict because foundActionConflictInLatestCheck is true, and store (x z) as the | 
|  | //     largest checked key set. | 
|  | // 3.  Null-build (y z), so again we don't evaluate or dirty anything, and the previous build | 
|  | //     had no conflicts, so no other condition is true. But because (y z) is not a subset of | 
|  | //     (x z) and we only keep the most recent largest checked key set, we don't know if (y z) | 
|  | //     are conflict free, so we check. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // We believe the conditions above are correct in the sense that we always check for conflicts | 
|  | // when we have to. But they are incomplete, so we sometimes check for conflicts even if we | 
|  | // wouldn't have to. For example: | 
|  | // - if no target was evaluated nor dirtied and build sequence is (x y) [no conflict], (z), | 
|  | //   where z is in the transitive closure of (x y), then we shouldn't check. | 
|  | // - if no target was evaluated nor dirtied and build sequence is (x y) [no conflict], (w), (x), | 
|  | //   then the last build shouldn't conflict-check because (x y) was checked earlier. But it | 
|  | //   does, because after the second build we store (w) as the largest checked set, and (x) is | 
|  | //   not a subset of that. | 
|  |  | 
|  | // Case when we DON'T need to re-check: | 
|  | // - a configured target is deleted. Deletion can only resolve conflicts, not introduce any, and | 
|  | //   if the previous build had a conflict then foundActionConflictInLatestCheck would be true, | 
|  | //   and if the previous build had no conflict then deleting a CT won't change that. | 
|  | //   Example that triggers this scenario: | 
|  | //   1.  genrule(name='x', srcs=['A'], ...) | 
|  | //       genrule(name='y', outs=['A'], ...) | 
|  | //   2.  Build (x y) | 
|  | //   3.  Rename 'x' to 'y', and 'y' to 'z' | 
|  | //   4.  Build (y z) | 
|  | //   5.  Null-build (y z) again | 
|  | // We only delete the old 'x' value in (5), and we don't evaluate nor dirty anything, nor was | 
|  | // (4) bad. So there's no reason to re-check just because we deleted something. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public ArtifactFactory getArtifactFactory() { | 
|  | return artifactFactory; | 
|  | } | 
|  |  | 
|  | CachingAnalysisEnvironment createAnalysisEnvironment( | 
|  | ActionLookupKey owner, | 
|  | ExtendedEventHandler eventHandler, | 
|  | Environment env, | 
|  | BuildConfigurationValue config, | 
|  | StarlarkBuiltinsValue starlarkBuiltinsValue) { | 
|  | boolean extendedSanityChecks = config != null && config.extendedSanityChecks(); | 
|  | boolean allowAnalysisFailures = config != null && config.allowAnalysisFailures(); | 
|  | return new CachingAnalysisEnvironment( | 
|  | artifactFactory, | 
|  | skyframeExecutor.getActionKeyContext(), | 
|  | owner, | 
|  | extendedSanityChecks, | 
|  | allowAnalysisFailures, | 
|  | eventHandler, | 
|  | env, | 
|  | starlarkBuiltinsValue); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Invokes the appropriate constructor to create a {@link ConfiguredTarget} instance. | 
|  | * | 
|  | * <p>For use in {@code ConfiguredTargetFunction}. | 
|  | * | 
|  | * <p>Returns null if Skyframe deps are missing or upon certain errors. | 
|  | */ | 
|  | @Nullable | 
|  | ConfiguredTarget createConfiguredTarget( | 
|  | Target target, | 
|  | BuildConfigurationValue configuration, | 
|  | CachingAnalysisEnvironment analysisEnvironment, | 
|  | ConfiguredTargetKey configuredTargetKey, | 
|  | OrderedSetMultimap<DependencyKind, ConfiguredTargetAndData> prerequisiteMap, | 
|  | ConfigConditions configConditions, | 
|  | @Nullable ToolchainCollection<ResolvedToolchainContext> toolchainContexts, | 
|  | @Nullable NestedSet<Package> transitivePackages, | 
|  | ExecGroupCollection.Builder execGroupCollectionBuilder) | 
|  | throws InterruptedException, | 
|  | ActionConflictException, | 
|  | InvalidExecGroupException, | 
|  | AnalysisFailurePropagationException, | 
|  | StarlarkExecTransitionLoadingException { | 
|  | Preconditions.checkState( | 
|  | enableAnalysis, "Already in execution phase %s %s", target, configuration); | 
|  | Preconditions.checkNotNull(analysisEnvironment); | 
|  | Preconditions.checkNotNull(target); | 
|  | Preconditions.checkNotNull(prerequisiteMap); | 
|  |  | 
|  | Optional<StarlarkAttributeTransitionProvider> starlarkExecTransition = | 
|  | StarlarkExecTransitionLoader.loadStarlarkExecTransition( | 
|  | configuration == null ? null : configuration.getOptions(), | 
|  | (bzlKey) -> | 
|  | (BzlLoadValue) | 
|  | analysisEnvironment | 
|  | .getSkyframeEnv() | 
|  | .getValueOrThrow(bzlKey, BzlLoadFailedException.class)); | 
|  | if (starlarkExecTransition == null) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | return factory.createConfiguredTarget( | 
|  | analysisEnvironment, | 
|  | artifactFactory, | 
|  | target, | 
|  | configuration, | 
|  | configuredTargetKey, | 
|  | prerequisiteMap, | 
|  | configConditions, | 
|  | toolchainContexts, | 
|  | transitivePackages, | 
|  | execGroupCollectionBuilder, | 
|  | starlarkExecTransition.orElse(null)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Workaround to clear all legacy data, like the artifact factory. We need to clear them to avoid | 
|  | * conflicts. TODO(bazel-team): Remove this workaround. [skyframe-execution] | 
|  | */ | 
|  | void clearLegacyData() { | 
|  | artifactFactory.clear(); | 
|  | starlarkTransitionCache.clear(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Clears any data cached in this BuildView. To be called when the attached SkyframeExecutor is | 
|  | * reset. | 
|  | */ | 
|  | public void reset() { | 
|  | configuration = null; | 
|  | originalConfigurationOptions = null; | 
|  | skyframeAnalysisWasDiscarded = false; | 
|  | clearLegacyData(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Hack to invalidate actions in legacy action graph when their values are invalidated in | 
|  | * skyframe. | 
|  | */ | 
|  | EvaluationProgressReceiver getProgressReceiver() { | 
|  | return progressReceiver; | 
|  | } | 
|  |  | 
|  | /** Clear the invalidated action lookup nodes detected during loading and analysis phases. */ | 
|  | public void clearInvalidatedActionLookupKeys() { | 
|  | dirtiedActionLookupKeys = Sets.newConcurrentHashSet(); | 
|  | starlarkTransitionCache.clear(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * {@link #createConfiguredTarget} will only create configured targets if this is set to true. It | 
|  | * should be set to true before any Skyframe update call that might call into {@link | 
|  | * #createConfiguredTarget}, and false immediately after the call. Use it to fail-fast in the case | 
|  | * that a target is requested for analysis not during the analysis phase. | 
|  | */ | 
|  | public void enableAnalysis(boolean enable) { | 
|  | this.enableAnalysis = enable; | 
|  | } | 
|  |  | 
|  | public StarlarkTransitionCache getStarlarkTransitionCache() { | 
|  | return starlarkTransitionCache; | 
|  | } | 
|  |  | 
|  | public void setRemoteAnalysisCachingDependenciesProvider( | 
|  | RemoteAnalysisCachingDependenciesProvider provider) { | 
|  | this.remoteAnalysisCachingDependenciesProvider = provider; | 
|  | } | 
|  |  | 
|  | public RemoteAnalysisCachingDependenciesProvider getRemoteAnalysisCachingDependenciesProvider() { | 
|  | return remoteAnalysisCachingDependenciesProvider; | 
|  | } | 
|  |  | 
|  | private final class ActionLookupValueProgressReceiver implements EvaluationProgressReceiver { | 
|  | private final AtomicInteger configuredObjectCount = new AtomicInteger(); | 
|  | private final AtomicInteger actionCount = new AtomicInteger(); | 
|  | private final AtomicInteger configuredTargetCount = new AtomicInteger(); | 
|  | private final AtomicInteger configuredTargetActionCount = new AtomicInteger(); | 
|  | private final ConcurrentHashMap<String, AtomicInteger> actionCountByMnemonic = | 
|  | new ConcurrentHashMap<>(); | 
|  |  | 
|  | @Override | 
|  | public void dirtied(SkyKey skyKey, DirtyType dirtyType) { | 
|  | if (skyKey instanceof ActionLookupKey) { | 
|  | // If the value was just dirtied and not deleted, then it may not be truly invalid, since | 
|  | // it may later get re-validated. Therefore adding the key to dirtiedConfiguredTargetKeys | 
|  | // is provisional--if the key is later evaluated and the value found to be clean, then we | 
|  | // remove it from the set. | 
|  | dirtiedActionLookupKeys.add((ActionLookupKey) skyKey); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void evaluated( | 
|  | SkyKey skyKey, | 
|  | EvaluationState state, | 
|  | @Nullable SkyValue newValue, | 
|  | @Nullable ErrorInfo newError, | 
|  | @Nullable GroupedDeps directDeps) { | 
|  | // We tolerate any action lookup keys here, although we only expect configured targets, | 
|  | // aspects, and the workspace status value. | 
|  | if (!(skyKey instanceof ActionLookupKey)) { | 
|  | return; | 
|  | } | 
|  | if (!state.versionChanged()) { | 
|  | // ActionLookupValue subclasses don't implement equality, so must have been marked clean. | 
|  | dirtiedActionLookupKeys.remove(skyKey); | 
|  | } else if (state.succeeded()) { | 
|  | boolean isConfiguredTarget = skyKey.functionName().equals(SkyFunctions.CONFIGURED_TARGET); | 
|  | if (isConfiguredTarget) { | 
|  | ConfiguredTargetKey configuredTargetKey = (ConfiguredTargetKey) skyKey; | 
|  | ConfiguredTargetValue configuredTargetValue = (ConfiguredTargetValue) newValue; | 
|  | if (!Objects.equals( | 
|  | configuredTargetKey.getConfigurationKey(), | 
|  | configuredTargetValue.getConfiguredTarget().getConfigurationKey())) { | 
|  | // The node entry performs delegation and doesn't own the value. Skips it to avoid | 
|  | // overcounting. | 
|  | return; | 
|  | } | 
|  | configuredTargetCount.incrementAndGet(); | 
|  | } | 
|  | configuredObjectCount.incrementAndGet(); | 
|  | if (newValue instanceof ActionLookupValue alv) { | 
|  | if (alv instanceof AspectValue) { | 
|  | if (AspectValue.isForAliasTarget((AspectValue) alv)) { | 
|  | // Created actions will be counted from {@link AspectValue} on the original target. | 
|  | return; | 
|  | } | 
|  | } | 
|  | // During multithreaded operation, this is only set to true, so no concurrency issues. | 
|  | someActionLookupValueEvaluated = true; | 
|  | ImmutableList<ActionAnalysisMetadata> actions = alv.getActions(); | 
|  | for (ActionAnalysisMetadata action : actions) { | 
|  | actionCountByMnemonic | 
|  | .computeIfAbsent(action.getMnemonic(), (m) -> new AtomicInteger(0)) | 
|  | .incrementAndGet(); | 
|  | } | 
|  |  | 
|  | int numActions = actions.size(); | 
|  | actionCount.addAndGet(numActions); | 
|  | if (isConfiguredTarget) { | 
|  | configuredTargetActionCount.addAndGet(numActions); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public void reset() { | 
|  | configuredObjectCount.set(0); | 
|  | actionCount.set(0); | 
|  | configuredTargetCount.set(0); | 
|  | configuredTargetActionCount.set(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Provides the list of coverage artifacts to be built. */ | 
|  | @FunctionalInterface | 
|  | public interface CoverageReportActionsWrapperSupplier { | 
|  | ImmutableSet<Artifact> getCoverageReportArtifacts( | 
|  | Set<ConfiguredTarget> configuredTargets, Set<ConfiguredTarget> allTargetsToTest) | 
|  | throws InterruptedException; | 
|  | } | 
|  |  | 
|  | /** Encapsulates the context required to construct a test BuildDriverKey. */ | 
|  | public interface BuildDriverKeyTestContext { | 
|  | String getTestStrategy(); | 
|  |  | 
|  | boolean forceExclusiveTestsInParallel(); | 
|  |  | 
|  | boolean forceExclusiveIfLocalTestsInParallel(); | 
|  | } | 
|  | } |