// 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 com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.FileProvider; import com.google.devtools.build.lib.analysis.InputFileConfiguredTarget; import com.google.devtools.build.lib.analysis.OutputFileConfiguredTarget; import com.google.devtools.build.lib.analysis.OutputGroupProvider; 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.cmdline.Label; import com.google.devtools.build.lib.collect.CollectionUtils; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.exec.ExecutionOptions; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.skyframe.AspectValue; import com.google.devtools.build.lib.util.io.OutErr; import java.util.ArrayList; import java.util.Collection; /** * 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. */ public void showBuildResult( BuildRequest request, BuildResult result, Collection<ConfiguredTarget> configuredTargets, Collection<AspectValue> aspects) { // 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. Collection<ConfiguredTarget> targetsToPrint = filterTargetsToPrint(configuredTargets); Collection<AspectValue> aspectsToPrint = filterAspectsToPrint(aspects); // Filter the targets we care about into two buckets: Collection<ConfiguredTarget> succeeded = new ArrayList<>(); Collection<ConfiguredTarget> failed = new ArrayList<>(); for (ConfiguredTarget target : targetsToPrint) { Collection<ConfiguredTarget> successfulTargets = result.getSuccessfulTargets(); (successfulTargets.contains(target) ? succeeded : failed).add(target); } // Suppress summary if --show_result value is exceeded: if (succeeded.size() + failed.size() + aspectsToPrint.size() > request.getBuildOptions().maxResultTargets) { return; } OutErr outErr = request.getOutErr(); TopLevelArtifactContext context = request.getTopLevelArtifactContext(); for (ConfiguredTarget target : succeeded) { Label label = target.getLabel(); // For up-to-date targets report generated artifacts, but only // if they have associated action and not middleman artifacts. boolean headerFlag = true; for (Artifact artifact : TopLevelArtifactHelper.getAllArtifactsToBuild(target, context).getImportantArtifacts()) { if (shouldPrint(artifact)) { if (headerFlag) { outErr.printErr("Target " + label + " up-to-date:\n"); headerFlag = false; } outErr.printErrLn(formatArtifactForShowResults(artifact, request)); } } if (headerFlag) { outErr.printErr("Target " + label + " up-to-date (nothing to build)\n"); } } for (AspectValue aspect : aspectsToPrint) { Label label = aspect.getLabel(); String aspectName = aspect.getConfiguredAspect().getName(); boolean headerFlag = true; NestedSet<Artifact> importantArtifacts = TopLevelArtifactHelper.getAllArtifactsToBuild(aspect, context).getImportantArtifacts(); for (Artifact importantArtifact : importantArtifacts) { if (headerFlag) { outErr.printErr("Aspect " + aspectName + " of " + label + " up-to-date:\n"); headerFlag = false; } if (shouldPrint(importantArtifact)) { outErr.printErrLn(formatArtifactForShowResults(importantArtifact, request)); } } if (headerFlag) { outErr.printErr( "Aspect " + aspectName + " of " + label + " up-to-date (nothing to build)\n"); } } for (ConfiguredTarget target : failed) { outErr.printErr("Target " + target.getLabel() + " failed to build\n"); // For failed compilation, it is still useful to examine temp artifacts, // (ie, preprocessed and assembler files). OutputGroupProvider topLevelProvider = OutputGroupProvider.get(target); String productName = env.getRuntime().getProductName(); if (topLevelProvider != null) { for (Artifact temp : topLevelProvider.getOutputGroup(OutputGroupProvider.TEMP_FILES)) { if (temp.getPath().exists()) { outErr.printErrLn(" See temp at " + OutputDirectoryLinksUtils.getPrettyPath(temp.getPath(), env.getWorkspaceName(), env.getWorkspace(), request.getBuildOptions().getSymlinkPrefix(productName), productName)); } } } } if (!failed.isEmpty() && !request.getOptions(ExecutionOptions.class).verboseFailures) { outErr.printErr("Use --verbose_failures to see the command lines of failed build steps.\n"); } } private boolean shouldPrint(Artifact artifact) { return !artifact.isSourceArtifact() && !artifact.isMiddlemanArtifact(); } private String formatArtifactForShowResults(Artifact artifact, BuildRequest request) { String productName = env.getRuntime().getProductName(); return " " + OutputDirectoryLinksUtils.getPrettyPath(artifact.getPath(), env.getWorkspaceName(), env.getWorkspace(), request.getBuildOptions().getSymlinkPrefix(productName), productName); } /** * Prints a flat list of all artifacts built by the passed top-level targets. * * <p>This corresponds to the --experimental_show_artifacts flag. */ public void showArtifacts( BuildRequest request, Collection<ConfiguredTarget> configuredTargets, Collection<AspectValue> aspects) { TopLevelArtifactContext context = request.getTopLevelArtifactContext(); Collection<ConfiguredTarget> targetsToPrint = filterTargetsToPrint(configuredTargets); Collection<AspectValue> aspectsToPrint = filterAspectsToPrint(aspects); NestedSetBuilder<Artifact> artifactsBuilder = NestedSetBuilder.stableOrder(); for (ConfiguredTarget target : targetsToPrint) { artifactsBuilder.addTransitive( TopLevelArtifactHelper.getAllArtifactsToBuild(target, context).getImportantArtifacts()); } for (AspectValue aspect : aspectsToPrint) { artifactsBuilder.addTransitive( TopLevelArtifactHelper.getAllArtifactsToBuild(aspect, context).getImportantArtifacts()); } OutErr outErr = request.getOutErr(); outErr.printErrLn("Build artifacts:"); NestedSet<Artifact> artifacts = artifactsBuilder.build(); for (Artifact artifact : artifacts) { if (!artifact.isSourceArtifact()) { outErr.printErrLn(">>>" + artifact.getPath()); } } } /** * 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 target : configuredTargets) { // TODO(bazel-team): this is quite ugly. Add a marker provider for this check. if (target instanceof InputFileConfiguredTarget) { // Suppress display of source files (because we do no work to build them). continue; } if (target.getTarget() instanceof Rule) { Rule rule = (Rule) target.getTarget(); if (rule.getRuleClass().contains("$")) { // Suppress display of hidden rules continue; } } if (target 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) target).getGeneratingRule(); if (CollectionUtils.containsAll( generatingRule.getProvider(FileProvider.class).getFilesToBuild(), target.getProvider(FileProvider.class).getFilesToBuild()) && configuredTargets.contains(generatingRule)) { continue; } } result.add(target); } return result.build(); } private Collection<AspectValue> filterAspectsToPrint(Collection<AspectValue> aspects) { return aspects; } }