| // 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 com.google.common.base.Preconditions; |
| import com.google.common.collect.Iterables; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.events.Event; |
| import com.google.devtools.build.lib.events.ExtendedEventHandler; |
| import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader; |
| import com.google.devtools.build.lib.skyframe.SkyframeExecutor.SkyframeTransitivePackageLoader; |
| import com.google.devtools.build.skyframe.CyclesReporter; |
| import com.google.devtools.build.skyframe.ErrorInfo; |
| import com.google.devtools.build.skyframe.EvaluationResult; |
| import com.google.devtools.build.skyframe.SkyKey; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.stream.Collectors; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Skyframe-based transitive package loader. |
| */ |
| public final class SkyframeLabelVisitor implements TransitivePackageLoader { |
| |
| private final SkyframeTransitivePackageLoader transitivePackageLoader; |
| private final AtomicReference<CyclesReporter> skyframeCyclesReporter; |
| |
| SkyframeLabelVisitor(SkyframeTransitivePackageLoader transitivePackageLoader, |
| AtomicReference<CyclesReporter> skyframeCyclesReporter) { |
| this.transitivePackageLoader = transitivePackageLoader; |
| this.skyframeCyclesReporter = skyframeCyclesReporter; |
| } |
| |
| @Override |
| public boolean sync( |
| ExtendedEventHandler eventHandler, |
| Set<Label> labelsToVisit, |
| boolean keepGoing, |
| int parallelThreads) |
| throws InterruptedException { |
| return sync(eventHandler, labelsToVisit, keepGoing, parallelThreads, true); |
| } |
| |
| // The only remaining non-test caller of this code is BlazeQueryEnvironment. |
| public boolean sync( |
| ExtendedEventHandler eventHandler, |
| Set<Label> labelsToVisit, |
| boolean keepGoing, |
| int parallelThreads, |
| boolean errorOnCycles) |
| throws InterruptedException { |
| EvaluationResult<TransitiveTargetValue> result = transitivePackageLoader.loadTransitiveTargets( |
| eventHandler, labelsToVisit, keepGoing, parallelThreads); |
| |
| if (!hasErrors(result)) { |
| return true; |
| } |
| |
| Set<Map.Entry<SkyKey, ErrorInfo>> errors = result.errorMap().entrySet(); |
| if (!errorOnCycles) { |
| errors = |
| errors |
| .stream() |
| .filter(error -> Iterables.isEmpty(error.getValue().getCycleInfo())) |
| .collect(Collectors.toSet()); |
| if (errors.isEmpty()) { |
| return true; |
| } |
| } |
| |
| if (!keepGoing) { |
| // We may have multiple errors, but in non keep_going builds, we're obligated to print only |
| // one of them. |
| Preconditions.checkState(!errors.isEmpty(), result); |
| Map.Entry<SkyKey, ErrorInfo> error = errors.iterator().next(); |
| ErrorInfo errorInfo = error.getValue(); |
| SkyKey topLevel = error.getKey(); |
| Label topLevelLabel = ((TransitiveTargetKey) topLevel).getLabel(); |
| if (!Iterables.isEmpty(errorInfo.getCycleInfo())) { |
| skyframeCyclesReporter.get().reportCycles(errorInfo.getCycleInfo(), topLevel, eventHandler); |
| errorAboutLoadingFailure(topLevelLabel, null, eventHandler); |
| } else if (isDirectErrorFromTopLevelLabel(topLevelLabel, labelsToVisit, errorInfo)) { |
| // An error caused by a non-top-level label has already been reported during error |
| // bubbling but an error caused by the top-level non-target label itself hasn't been |
| // reported yet. Note that errors from top-level targets have already been reported |
| // during target parsing. |
| errorAboutLoadingFailure(topLevelLabel, errorInfo.getException(), eventHandler); |
| } |
| return false; |
| } |
| |
| for (Map.Entry<SkyKey, ErrorInfo> errorEntry : errors) { |
| SkyKey key = errorEntry.getKey(); |
| ErrorInfo errorInfo = errorEntry.getValue(); |
| Preconditions.checkState(key.functionName().equals(SkyFunctions.TRANSITIVE_TARGET), errorEntry); |
| Label topLevelLabel = ((TransitiveTargetKey) key).getLabel(); |
| if (!Iterables.isEmpty(errorInfo.getCycleInfo())) { |
| skyframeCyclesReporter.get().reportCycles(errorInfo.getCycleInfo(), key, eventHandler); |
| } |
| if (isDirectErrorFromTopLevelLabel(topLevelLabel, labelsToVisit, errorInfo)) { |
| // Unlike top-level targets, which have already gone through target parsing, |
| // errors directly coming from top-level labels have not been reported yet. |
| // |
| // See the note in the --nokeep_going case above. |
| eventHandler.handle(Event.error(errorInfo.getException().getMessage())); |
| } |
| warnAboutLoadingFailure(topLevelLabel, eventHandler); |
| } |
| for (TransitiveTargetKey topLevelTransitiveTargetKey : result.<TransitiveTargetKey>keyNames()) { |
| TransitiveTargetValue topLevelTransitiveTargetValue = result.get(topLevelTransitiveTargetKey); |
| if (topLevelTransitiveTargetValue.getTransitiveRootCauses() != null) { |
| warnAboutLoadingFailure(topLevelTransitiveTargetKey.getLabel(), eventHandler); |
| } |
| } |
| return false; |
| } |
| |
| private static boolean hasErrors(EvaluationResult<TransitiveTargetValue> result) { |
| if (result.hasError()) { |
| return true; |
| } |
| for (TransitiveTargetValue transitiveTargetValue : result.values()) { |
| if (transitiveTargetValue.getTransitiveRootCauses() != null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean isDirectErrorFromTopLevelLabel(Label label, Set<Label> topLevelLabels, |
| ErrorInfo errorInfo) { |
| return errorInfo.getException() != null && topLevelLabels.contains(label) |
| && Iterables.contains(errorInfo.getRootCauses(), TransitiveTargetKey.of(label)); |
| } |
| |
| private static void errorAboutLoadingFailure( |
| Label topLevelLabel, @Nullable Throwable throwable, ExtendedEventHandler eventHandler) { |
| eventHandler.handle(Event.error( |
| "Loading of target '" + topLevelLabel + "' failed; build aborted" + |
| (throwable == null ? "" : ": " + throwable.getMessage()))); |
| } |
| |
| private static void warnAboutLoadingFailure(Label label, ExtendedEventHandler eventHandler) { |
| eventHandler.handle(Event.warn("errors encountered while loading target '" + label + "'")); |
| } |
| } |