| // 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.buildtool; | |
| import static com.google.common.collect.ImmutableSet.toImmutableSet; | |
| import static java.util.stream.Collectors.joining; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.ImmutableListMultimap; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.collect.ImmutableSet; | |
| import com.google.common.collect.Multimaps; | |
| import com.google.devtools.build.lib.actions.Artifact; | |
| import com.google.devtools.build.lib.analysis.AspectCollection; | |
| import com.google.devtools.build.lib.analysis.ConfiguredAspect; | |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; | |
| import com.google.devtools.build.lib.analysis.FileProvider; | |
| import com.google.devtools.build.lib.analysis.OutputGroupInfo; | |
| import com.google.devtools.build.lib.analysis.ProviderCollection; | |
| import com.google.devtools.build.lib.analysis.TopLevelArtifactContext; | |
| import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper; | |
| import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; | |
| import com.google.devtools.build.lib.analysis.configuredtargets.OutputFileConfiguredTarget; | |
| import com.google.devtools.build.lib.causes.ActionFailed; | |
| 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.exec.ExecutionOptions; | |
| import com.google.devtools.build.lib.runtime.BlazeRuntime; | |
| import com.google.devtools.build.lib.runtime.CommandEnvironment; | |
| import com.google.devtools.build.lib.sandbox.SandboxOptions; | |
| import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey; | |
| import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; | |
| import com.google.devtools.build.lib.util.io.OutErr; | |
| import java.io.IOException; | |
| import java.util.ArrayList; | |
| import java.util.Collection; | |
| import java.util.Objects; | |
| import java.util.stream.Stream; | |
| /** Handles --show_result and --experimental_show_artifacts. */ | |
| class BuildResultPrinter { | |
| private final CommandEnvironment env; | |
| BuildResultPrinter(CommandEnvironment env) { | |
| this.env = env; | |
| } | |
| /** | |
| * Shows the result of the build. Information includes the list of up-to-date and failed targets | |
| * and list of output artifacts for successful targets | |
| * | |
| * <p>This corresponds to the --show_result flag. | |
| */ | |
| void showBuildResult( | |
| BuildRequest request, | |
| BuildResult result, | |
| Collection<ConfiguredTarget> configuredTargets, | |
| Collection<ConfiguredTarget> configuredTargetsToSkip, | |
| ImmutableMap<AspectKey, ConfiguredAspect> aspects, | |
| ImmutableMap<ConfiguredTargetKey, NestedSet<Cause>> targetRootCauses, | |
| ImmutableMap<AspectKey, NestedSet<Cause>> aspectRootCauses, | |
| boolean hasSandboxedActionFailures) { | |
| // NOTE: be careful what you print! We don't want to create a consistency | |
| // problem where the summary message and the exit code disagree. The logic | |
| // here is already complex. | |
| boolean ok = | |
| outputTargets( | |
| request, | |
| result, | |
| configuredTargets, | |
| configuredTargetsToSkip, | |
| aspects, | |
| targetRootCauses, | |
| aspectRootCauses); | |
| if (!ok && hasActionFailures(targetRootCauses, aspectRootCauses)) { | |
| SandboxOptions sandboxOptions = request.getOptions(SandboxOptions.class); | |
| boolean verboseFailures = request.getOptions(ExecutionOptions.class).getVerboseFailures(); | |
| boolean sandboxDebug = sandboxOptions != null && sandboxOptions.getSandboxDebug(); | |
| if (!verboseFailures && !sandboxDebug) { | |
| request | |
| .getOutErr() | |
| .printErr("Use --verbose_failures to see the command lines of failed build steps.\n"); | |
| } | |
| if (sandboxOptions != null && !sandboxDebug && hasSandboxedActionFailures) { | |
| request | |
| .getOutErr() | |
| .printErr( | |
| "Use --sandbox_debug to see verbose messages from the sandbox and retain the" | |
| + " sandbox build root for debugging\n"); | |
| } | |
| } | |
| } | |
| private static boolean hasActionFailures( | |
| ImmutableMap<ConfiguredTargetKey, NestedSet<Cause>> targetRootCauses, | |
| ImmutableMap<AspectKey, NestedSet<Cause>> aspectRootCauses) { | |
| return Stream.concat(targetRootCauses.values().stream(), aspectRootCauses.values().stream()) | |
| .flatMap(causes -> causes.toList().stream()) | |
| .anyMatch(ActionFailed.class::isInstance); | |
| } | |
| /** | |
| * Outputs per-target and per-aspect build results bounded by {@code --show_result} ({@code | |
| * getMaxResultTargets()}). | |
| * | |
| * <p>Output filtering follows two separate {@code --show_result} budgets: | |
| * | |
| * <ul> | |
| * <li><b>Successful and skipped results ({@code essentialBudget}):</b> Targets that produced | |
| * output artifacts and skipped targets decrement {@code essentialBudget} (targets with | |
| * {@code (nothing to build)} are omitted instead of consuming budget when total targets | |
| * exceed {@code --show_result}). If {@code essentialBudget < 0}, all successful and skipped | |
| * per-target lines are suppressed. | |
| * <li><b>Failed results:</b> Tracked against a separate budget equal to {@code --show_result} | |
| * so that successful targets do not starve failure output in mixed builds. Under {@code | |
| * --nokeep_going}, unbuilt targets/aspects that were merely aborted without failing | |
| * evaluation are excluded. If total failed targets and aspects exceed {@code | |
| * --show_result}, per-target failure lines are suppressed. | |
| * </ul> | |
| * | |
| * @return {@code true} if no target or aspect failures occurred ({@code !hasFailures}), | |
| * regardless of whether per-target output was suppressed by {@code --show_result}. | |
| */ | |
| private boolean outputTargets( | |
| BuildRequest request, | |
| BuildResult result, | |
| Collection<ConfiguredTarget> configuredTargets, | |
| Collection<ConfiguredTarget> configuredTargetsToSkip, | |
| ImmutableMap<AspectKey, ConfiguredAspect> aspects, | |
| ImmutableMap<ConfiguredTargetKey, NestedSet<Cause>> targetRootCauses, | |
| ImmutableMap<AspectKey, NestedSet<Cause>> aspectRootCauses) { | |
| BlazeRuntime runtime = env.getRuntime(); | |
| String productName = runtime.getProductName(); | |
| PathPrettyPrinter prettyPrinter = | |
| new PathPrettyPrinter( | |
| env.getRelativeWorkingDirectory(), | |
| request.getBuildOptions().getSymlinkPrefix(productName), | |
| result.getConvenienceSymlinks()); | |
| OutErr outErr = request.getOutErr(); | |
| // Filter and split aspects to display. | |
| ImmutableSet<String> aspectsToIgnore = | |
| ImmutableSet.copyOf(request.getBuildOptions().getHideAspectResults()); | |
| PartitionedAspectKeys partitionedAspectKeys = | |
| partitionAspectKeys( | |
| request.useValidationAspect(), | |
| aspects.keySet().stream() | |
| .filter(k -> !aspectsToIgnore.contains(k.getAspectClass().getName())) | |
| .collect(toImmutableSet())); | |
| Collection<ConfiguredTarget> targetsToPrint = filterTargetsToPrint(configuredTargets); | |
| TopLevelArtifactContext context = request.getTopLevelArtifactContext(); | |
| ImmutableSet<String> outputGroupsToHide = | |
| ImmutableSet.copyOf(request.getBuildOptions().getHideOutputGroupResults()); | |
| // `essentialBudget` tracks the number of non-empty successful/skipped results that can be | |
| // printed under --show_result. | |
| int essentialBudget = request.getBuildOptions().getMaxResultTargets(); | |
| // Splits the targets we care about into three buckets. Targets are only considered successful | |
| // if they and their validation aspects succeeded. | |
| var skipped = new ArrayList<ConfiguredTarget>(); | |
| var succeeded = new ArrayList<ConfiguredTarget>(); | |
| var artifactsToPrintPerTarget = new ArrayList<ArrayList<Artifact>>(); | |
| var failed = new ArrayList<ConfiguredTarget>(); | |
| essentialBudget = | |
| splitConfiguredTargetsByResultReturnRemaining( | |
| targetsToPrint, | |
| result, | |
| context, | |
| outputGroupsToHide, | |
| configuredTargetsToSkip, | |
| partitionedAspectKeys.validationAspects, | |
| targetRootCauses, | |
| aspectRootCauses, | |
| skipped, | |
| succeeded, | |
| artifactsToPrintPerTarget, | |
| failed, | |
| essentialBudget); | |
| // Splits the aspects we care about into two buckets. | |
| var successfulAspects = new ArrayList<AspectKey>(); | |
| var failedAspects = new ArrayList<AspectKey>(); | |
| var artifactsToPrintPerAspect = new ArrayList<ArrayList<Artifact>>(successfulAspects.size()); | |
| essentialBudget = | |
| splitAspectsByResultReturnRemaining( | |
| partitionedAspectKeys.aspectsToPrint, | |
| aspects, | |
| context, | |
| outputGroupsToHide, | |
| result, | |
| aspectRootCauses, | |
| successfulAspects, | |
| artifactsToPrintPerAspect, | |
| failedAspects, | |
| essentialBudget); | |
| // Compute whether any failure occurred before clearing lists for --show_result budgeting so | |
| // that the return value accurately reflects build success/failure (e.g. to print the | |
| // --verbose_failures hint even when per-target lines are suppressed by --show_result=0). | |
| boolean hasFailures = | |
| (succeeded.size() + skipped.size() < targetsToPrint.size()) | |
| || (successfulAspects.size() < partitionedAspectKeys.aspectsToPrint.size()) | |
| || !targetRootCauses.isEmpty() | |
| || !aspectRootCauses.isEmpty() | |
| || !failed.isEmpty() | |
| || !failedAspects.isEmpty(); | |
| boolean budgetExceeded = essentialBudget < 0; | |
| if (budgetExceeded) { | |
| skipped.clear(); | |
| succeeded.clear(); | |
| artifactsToPrintPerTarget.clear(); | |
| successfulAspects.clear(); | |
| artifactsToPrintPerAspect.clear(); | |
| } | |
| // Apply a separate --show_result budget to failed targets/aspects so successful targets do not | |
| // starve failure reporting, while still bounding output on mass failures. | |
| if (failed.size() + failedAspects.size() > request.getBuildOptions().getMaxResultTargets()) { | |
| failed.clear(); | |
| failedAspects.clear(); | |
| } | |
| // Omits "nothing to build" values if it enables staying under --show_result. | |
| boolean omitNothingToBuild = | |
| (targetsToPrint.size() + partitionedAspectKeys.aspectsToPrint.size()) | |
| > request.getBuildOptions().getMaxResultTargets(); | |
| outputConfiguredTargets( | |
| outErr, | |
| prettyPrinter, | |
| context, | |
| succeeded, | |
| artifactsToPrintPerTarget, | |
| failed, | |
| skipped, | |
| omitNothingToBuild, | |
| partitionedAspectKeys.validationAspects, | |
| targetRootCauses, | |
| aspectRootCauses); | |
| outputAspects( | |
| outErr, | |
| prettyPrinter, | |
| context, | |
| aspects, | |
| successfulAspects, | |
| artifactsToPrintPerAspect, | |
| failedAspects, | |
| omitNothingToBuild, | |
| aspectRootCauses); | |
| return !hasFailures; | |
| } | |
| private static int splitConfiguredTargetsByResultReturnRemaining( | |
| Collection<ConfiguredTarget> configuredTargets, | |
| BuildResult result, | |
| TopLevelArtifactContext context, | |
| ImmutableSet<String> outputGroupsToHide, | |
| Collection<ConfiguredTarget> configuredTargetsToSkip, | |
| ImmutableList<AspectKey> validationAspects, | |
| ImmutableMap<ConfiguredTargetKey, NestedSet<Cause>> targetRootCauses, | |
| ImmutableMap<AspectKey, NestedSet<Cause>> aspectRootCauses, | |
| ArrayList<ConfiguredTarget> skipped, | |
| ArrayList<ConfiguredTarget> succeeded, | |
| ArrayList<ArrayList<Artifact>> artifactsToPrintPerTarget, | |
| ArrayList<ConfiguredTarget> failed, | |
| int essentialBudget) { | |
| ImmutableSet<ConfiguredTargetKey> unsuccessfulValidationTargets = | |
| validationAspects.stream() | |
| .filter(k -> !result.getSuccessfulAspects().contains(k)) | |
| .map(AspectKey::getBaseConfiguredTargetKey) | |
| .collect(toImmutableSet()); | |
| ImmutableSet<ConfiguredTargetKey> failedValidationTargets = | |
| result.getStopOnFirstFailure() | |
| ? validationAspects.stream() | |
| .filter(aspectRootCauses::containsKey) | |
| .map(AspectKey::getBaseConfiguredTargetKey) | |
| .collect(toImmutableSet()) | |
| : unsuccessfulValidationTargets; | |
| Collection<ConfiguredTarget> successfulTargets = result.getSuccessfulTargets(); | |
| for (ConfiguredTarget target : configuredTargets) { | |
| ConfiguredTargetKey targetKey = ConfiguredTargetKey.fromConfiguredTarget(target); | |
| if (configuredTargetsToSkip.contains(target)) { | |
| skipped.add(target); | |
| essentialBudget--; | |
| } else if (successfulTargets.contains(target) | |
| && !unsuccessfulValidationTargets.contains(targetKey)) { | |
| succeeded.add(target); | |
| ArrayList<Artifact> artifactsToPrint = | |
| getArtifactsToPrint(target, context, outputGroupsToHide); | |
| artifactsToPrintPerTarget.add(artifactsToPrint); | |
| if (!artifactsToPrint.isEmpty()) { | |
| essentialBudget--; | |
| } | |
| } else { | |
| boolean actuallyFailed = | |
| !result.getStopOnFirstFailure() | |
| || targetRootCauses.containsKey(targetKey) | |
| || failedValidationTargets.contains(targetKey); | |
| if (actuallyFailed) { | |
| failed.add(target); | |
| } | |
| } | |
| } | |
| return essentialBudget; | |
| } | |
| private static ArrayList<Artifact> getArtifactsToPrint( | |
| ProviderCollection target, | |
| TopLevelArtifactContext context, | |
| ImmutableSet<String> outputGroupsToHide) { | |
| var artifacts = new ArrayList<Artifact>(); | |
| // For up-to-date targets report generated artifacts, but only if they have associated action | |
| // and not runfiles trees. | |
| for (Artifact artifact : | |
| TopLevelArtifactHelper.getAllArtifactsToBuild(target, context) | |
| .getImportantArtifactsExcluding(outputGroupsToHide) | |
| .toList()) { | |
| if (TopLevelArtifactHelper.shouldDisplay(artifact)) { | |
| artifacts.add(artifact); | |
| } | |
| } | |
| return artifacts; | |
| } | |
| /** | |
| * Returns the message printed in place of the artifacts of a target or aspect that has none to | |
| * show. | |
| * | |
| * <p>Artifacts in output groups prefixed with {@link OutputGroupInfo#HIDDEN_OUTPUT_GROUP_PREFIX} | |
| * are never shown, but are still built, so the build may well have executed actions on this | |
| * target's behalf. | |
| */ | |
| private static String nothingToBuildMessage( | |
| ProviderCollection target, TopLevelArtifactContext context) { | |
| boolean ranValidationActions = false; | |
| boolean builtInternalOutputGroups = false; | |
| for (var outputGroup : | |
| TopLevelArtifactHelper.getAllArtifactsToBuild(target, context) | |
| .getAllArtifactsByOutputGroup() | |
| .entrySet()) { | |
| if (outputGroup.getValue().areImportant()) { | |
| continue; | |
| } | |
| if (outputGroup.getKey().equals(OutputGroupInfo.VALIDATION) | |
| || outputGroup.getKey().equals(OutputGroupInfo.VALIDATION_TOP_LEVEL)) { | |
| ranValidationActions = true; | |
| } else { | |
| builtInternalOutputGroups = true; | |
| } | |
| } | |
| if (ranValidationActions && builtInternalOutputGroups) { | |
| return "nothing to build except validation outputs and other internal output groups, use" | |
| + " --norun_validations to skip validations"; | |
| } | |
| if (ranValidationActions) { | |
| return "nothing to build except validation outputs, use --norun_validations to skip them"; | |
| } | |
| if (builtInternalOutputGroups) { | |
| return "nothing to build except internal output groups"; | |
| } | |
| return "nothing to build"; | |
| } | |
| private static int splitAspectsByResultReturnRemaining( | |
| Collection<AspectKey> aspectsToPrint, | |
| ImmutableMap<AspectKey, ConfiguredAspect> aspects, | |
| TopLevelArtifactContext context, | |
| ImmutableSet<String> outputGroupsToHide, | |
| BuildResult result, | |
| ImmutableMap<AspectKey, NestedSet<Cause>> aspectRootCauses, | |
| ArrayList<AspectKey> succeeded, | |
| ArrayList<ArrayList<Artifact>> artifactsToPrintPerAspect, | |
| ArrayList<AspectKey> failed, | |
| int essentialBudget) { | |
| ImmutableSet<AspectKey> successfulAspects = result.getSuccessfulAspects(); | |
| for (AspectKey aspect : aspectsToPrint) { | |
| if (successfulAspects.contains(aspect)) { | |
| succeeded.add(aspect); | |
| ArrayList<Artifact> artifactsToPrint = | |
| getArtifactsToPrint(aspects.get(aspect), context, outputGroupsToHide); | |
| artifactsToPrintPerAspect.add(artifactsToPrint); | |
| if (!artifactsToPrint.isEmpty()) { | |
| essentialBudget--; | |
| } | |
| } else if (!result.getStopOnFirstFailure() || aspectRootCauses.containsKey(aspect)) { | |
| failed.add(aspect); | |
| } | |
| } | |
| return essentialBudget; | |
| } | |
| private static void outputConfiguredTargets( | |
| OutErr outErr, | |
| PathPrettyPrinter prettyPrinter, | |
| TopLevelArtifactContext context, | |
| ArrayList<ConfiguredTarget> succeeded, | |
| ArrayList<ArrayList<Artifact>> artifactsToPrintPerTarget, | |
| ArrayList<ConfiguredTarget> failed, | |
| ArrayList<ConfiguredTarget> skipped, | |
| boolean omitNothingToBuild, | |
| ImmutableList<AspectKey> validationAspects, | |
| ImmutableMap<ConfiguredTargetKey, NestedSet<Cause>> targetRootCauses, | |
| ImmutableMap<AspectKey, NestedSet<Cause>> aspectRootCauses) { | |
| for (ConfiguredTarget target : skipped) { | |
| outErr.printErr("Target " + target.getOriginalLabel() + " was skipped\n"); | |
| } | |
| for (int i = 0; i < succeeded.size(); ++i) { | |
| ConfiguredTarget target = succeeded.get(i); | |
| Label label = target.getLabel(); | |
| ArrayList<Artifact> artifacts = artifactsToPrintPerTarget.get(i); | |
| if (artifacts.isEmpty()) { | |
| if (!omitNothingToBuild) { | |
| outErr.printErr( | |
| String.format( | |
| "Target %s up-to-date (%s)\n", label, nothingToBuildMessage(target, context))); | |
| } | |
| continue; | |
| } | |
| outErr.printErr("Target " + label + " up-to-date:\n"); | |
| for (Artifact artifact : artifacts) { | |
| outErr.printErrLn(formatArtifactForShowResults(prettyPrinter, artifact)); | |
| } | |
| } | |
| ImmutableListMultimap<ConfiguredTargetKey, AspectKey> validationAspectsByTarget = | |
| failed.isEmpty() | |
| ? ImmutableListMultimap.of() | |
| : Multimaps.index(validationAspects, AspectKey::getBaseConfiguredTargetKey); | |
| for (ConfiguredTarget target : failed) { | |
| outErr.printErr("Target " + target.getLabel() + " failed to build\n"); | |
| ConfiguredTargetKey targetKey = ConfiguredTargetKey.fromConfiguredTarget(target); | |
| NestedSet<Cause> rootCauses = targetRootCauses.get(targetKey); | |
| ImmutableSet.Builder<Label> rootCauseLabelsBuilder = ImmutableSet.builder(); | |
| if (rootCauses != null) { | |
| rootCauses.toList().stream() | |
| .filter(cause -> cause instanceof ActionFailed) | |
| .map(Cause::getLabel) | |
| .filter(Objects::nonNull) | |
| .filter(label -> !label.equals(target.getLabel())) | |
| .forEach(rootCauseLabelsBuilder::add); | |
| } | |
| for (AspectKey validationAspect : validationAspectsByTarget.get(targetKey)) { | |
| NestedSet<Cause> aspectCauses = aspectRootCauses.get(validationAspect); | |
| if (aspectCauses != null) { | |
| aspectCauses.toList().stream() | |
| .filter(cause -> cause instanceof ActionFailed) | |
| .map(Cause::getLabel) | |
| .filter(Objects::nonNull) | |
| .filter(label -> !label.equals(target.getLabel())) | |
| .forEach(rootCauseLabelsBuilder::add); | |
| } | |
| } | |
| ImmutableSet<Label> rootCauseLabels = rootCauseLabelsBuilder.build(); | |
| if (!rootCauseLabels.isEmpty()) { | |
| String labelList = | |
| rootCauseLabels.stream().map(Label::toString).sorted().collect(joining(", ")); | |
| outErr.printErr(" due to action in " + labelList + "\n"); | |
| } | |
| // For failed compilation, it is still useful to examine temp artifacts, (ie, preprocessed and | |
| // assembler files). | |
| OutputGroupInfo topLevelProvider = OutputGroupInfo.get(target); | |
| if (topLevelProvider != null) { | |
| for (Artifact temp : topLevelProvider.getOutputGroup(OutputGroupInfo.TEMP_FILES).toList()) { | |
| boolean exists; | |
| try { | |
| exists = temp.getPath().exists(); | |
| } catch (IOException e) { | |
| // TODO(tjgq): Propagate this error. | |
| exists = false; | |
| } | |
| if (exists) { | |
| outErr.printErrLn( | |
| " See temp at " + prettyPrinter.getPrettyPath(temp.getPath().asFragment())); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| private static void outputAspects( | |
| OutErr outErr, | |
| PathPrettyPrinter prettyPrinter, | |
| TopLevelArtifactContext context, | |
| ImmutableMap<AspectKey, ConfiguredAspect> aspects, | |
| ArrayList<AspectKey> succeeded, | |
| ArrayList<ArrayList<Artifact>> artifactsToPrintPerAspect, | |
| ArrayList<AspectKey> failed, | |
| boolean omitNothingToBuild, | |
| ImmutableMap<AspectKey, NestedSet<Cause>> aspectRootCauses) { | |
| for (int i = 0; i < succeeded.size(); ++i) { | |
| AspectKey aspect = succeeded.get(i); | |
| Label label = aspect.getLabel(); | |
| String aspectName = aspect.getAspectClass().getName(); | |
| ArrayList<Artifact> artifacts = artifactsToPrintPerAspect.get(i); | |
| if (artifacts.isEmpty()) { | |
| if (!omitNothingToBuild) { | |
| outErr.printErr( | |
| String.format( | |
| "Aspect %s of %s up-to-date (%s)\n", | |
| aspectName, label, nothingToBuildMessage(aspects.get(aspect), context))); | |
| } | |
| continue; | |
| } | |
| outErr.printErr("Aspect " + aspectName + " of " + label + " up-to-date:\n"); | |
| for (Artifact artifact : artifacts) { | |
| outErr.printErrLn(formatArtifactForShowResults(prettyPrinter, artifact)); | |
| } | |
| } | |
| for (AspectKey aspect : failed) { | |
| Label label = aspect.getLabel(); | |
| String aspectName = aspect.getAspectClass().getName(); | |
| outErr.printErr("Aspect " + aspectName + " of " + label + " failed to build\n"); | |
| NestedSet<Cause> rootCauses = aspectRootCauses.get(aspect); | |
| ImmutableSet<Label> rootCauseLabels = | |
| rootCauses == null | |
| ? ImmutableSet.of() | |
| : rootCauses.toList().stream() | |
| .filter(cause -> cause instanceof ActionFailed) | |
| .map(Cause::getLabel) | |
| .filter(Objects::nonNull) | |
| .filter(causeLabel -> !causeLabel.equals(label)) | |
| .collect(toImmutableSet()); | |
| if (!rootCauseLabels.isEmpty()) { | |
| String labelList = | |
| rootCauseLabels.stream().map(Label::toString).sorted().collect(joining(", ")); | |
| outErr.printErr(" due to action in " + labelList + "\n"); | |
| } | |
| } | |
| } | |
| private static String formatArtifactForShowResults( | |
| PathPrettyPrinter prettyPrinter, Artifact artifact) { | |
| return " " + prettyPrinter.getPrettyPath(artifact.getPath().asFragment()); | |
| } | |
| /** | |
| * Returns a list of configured targets that should participate in printing. | |
| * | |
| * <p>Hidden rules and other inserted targets are ignored. | |
| */ | |
| private Collection<ConfiguredTarget> filterTargetsToPrint( | |
| Collection<ConfiguredTarget> configuredTargets) { | |
| ImmutableList.Builder<ConfiguredTarget> result = ImmutableList.builder(); | |
| for (ConfiguredTarget configuredTarget : configuredTargets) { | |
| if (!TopLevelArtifactHelper.shouldConsiderForDisplay(configuredTarget)) { | |
| continue; | |
| } | |
| if (configuredTarget instanceof OutputFileConfiguredTarget) { | |
| // Suppress display of generated files (because they appear underneath | |
| // their generating rule), EXCEPT those ones which are not part of the | |
| // filesToBuild of their generating rule (e.g. .par, _deploy.jar | |
| // files), OR when a user explicitly requests an output file but not | |
| // its rule. | |
| TransitiveInfoCollection generatingRule = | |
| ((OutputFileConfiguredTarget) configuredTarget).getGeneratingRule(); | |
| if (generatingRule | |
| .getProvider(FileProvider.class) | |
| .getFilesToBuild() | |
| .toSet() | |
| .containsAll( | |
| configuredTarget.getProvider(FileProvider.class).getFilesToBuild().toList()) | |
| && configuredTargets.contains(generatingRule)) { | |
| continue; | |
| } | |
| } | |
| result.add(configuredTarget); | |
| } | |
| return result.build(); | |
| } | |
| /** Splits aspects based on whether they are validation aspects. */ | |
| private static PartitionedAspectKeys partitionAspectKeys( | |
| boolean useValidationAspects, ImmutableSet<AspectKey> keys) { | |
| if (!useValidationAspects) { | |
| return new PartitionedAspectKeys(keys, ImmutableList.of()); | |
| } | |
| var aspectsToPrintBuilder = ImmutableSet.<AspectKey>builder(); | |
| var validationAspectsBuilder = ImmutableList.<AspectKey>builder(); | |
| for (AspectKey key : keys) { | |
| if (Objects.equals(key.getAspectClass().getName(), AspectCollection.VALIDATION_ASPECT_NAME)) { | |
| validationAspectsBuilder.add(key); | |
| } else { | |
| aspectsToPrintBuilder.add(key); | |
| } | |
| } | |
| return new PartitionedAspectKeys( | |
| aspectsToPrintBuilder.build(), validationAspectsBuilder.build()); | |
| } | |
| private static class PartitionedAspectKeys { | |
| private final ImmutableSet<AspectKey> aspectsToPrint; | |
| private final ImmutableList<AspectKey> validationAspects; | |
| private PartitionedAspectKeys( | |
| ImmutableSet<AspectKey> aspectsToPrint, ImmutableList<AspectKey> validationAspects) { | |
| this.aspectsToPrint = aspectsToPrint; | |
| this.validationAspects = validationAspects; | |
| } | |
| } | |
| } |