blob: d4a0e89ba553e87fba380a19e481b25041ec19b1 [file] [log] [blame]
// 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.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
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.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.InputFileConfiguredTarget;
import com.google.devtools.build.lib.analysis.configuredtargets.OutputFileConfiguredTarget;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
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.runtime.BlazeRuntime;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
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.
*/
void showBuildResult(
BuildRequest request,
BuildResult result,
Collection<ConfiguredTarget> configuredTargets,
Collection<ConfiguredTarget> configuredTargetsToSkip,
ImmutableMap<AspectKey, ConfiguredAspect> 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.
BlazeRuntime runtime = env.getRuntime();
String productName = runtime.getProductName();
PathPrettyPrinter prettyPrinter =
OutputDirectoryLinksUtils.getPathPrettyPrinter(
runtime.getRuleClassProvider().getSymlinkDefinitions(),
request.getBuildOptions().getSymlinkPrefix(productName),
productName,
env.getWorkspace(),
request.getBuildOptions().printWorkspaceInOutputPathsIfNeeded
? env.getWorkingDirectory()
: env.getWorkspace(),
request.getBuildOptions().experimentalNoProductNameOutSymlink);
OutErr outErr = request.getOutErr();
Collection<ConfiguredTarget> targetsToPrint = filterTargetsToPrint(configuredTargets);
final boolean success;
if (aspects.isEmpty()) {
// Suppress summary if --show_result value is exceeded:
if (targetsToPrint.size() > request.getBuildOptions().maxResultTargets) {
return;
}
// Filter the targets we care about into two buckets:
Collection<ConfiguredTarget> succeeded = new ArrayList<>();
Collection<ConfiguredTarget> failed = new ArrayList<>();
Collection<ConfiguredTarget> successfulTargets = result.getSuccessfulTargets();
for (ConfiguredTarget target : targetsToPrint) {
(successfulTargets.contains(target) ? succeeded : failed).add(target);
}
// TODO(bazel-team): convert these to a new "SKIPPED" status when ready: b/62191890.
failed.addAll(configuredTargetsToSkip);
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()
.toList()) {
if (shouldPrint(artifact)) {
if (headerFlag) {
outErr.printErr("Target " + label + " up-to-date:\n");
headerFlag = false;
}
outErr.printErrLn(formatArtifactForShowResults(prettyPrinter, artifact));
}
}
if (headerFlag) {
outErr.printErr("Target " + 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).
OutputGroupInfo topLevelProvider = OutputGroupInfo.get(target);
if (topLevelProvider != null) {
for (Artifact temp :
topLevelProvider.getOutputGroup(OutputGroupInfo.TEMP_FILES).toList()) {
if (temp.getPath().exists()) {
outErr.printErrLn(" See temp at " + prettyPrinter.getPrettyPath(temp.getPath()));
}
}
}
}
success = failed.isEmpty();
} else {
// Suppress summary if --show_result value is exceeded:
if (aspects.size() > request.getBuildOptions().maxResultTargets) {
return;
}
// Filter the targets we care about into two buckets:
Collection<AspectKey> succeeded = new ArrayList<>();
Collection<AspectKey> failed = new ArrayList<>();
ImmutableSet<AspectKey> successfulAspects = result.getSuccessfulAspects();
for (AspectKey aspect : aspects.keySet()) {
(successfulAspects.contains(aspect) ? succeeded : failed).add(aspect);
}
TopLevelArtifactContext context = request.getTopLevelArtifactContext();
for (AspectKey aspect : succeeded) {
Label label = aspect.getLabel();
ConfiguredAspect configuredAspect = aspects.get(aspect);
String aspectName = aspect.getAspectClass().getName();
boolean headerFlag = true;
NestedSet<Artifact> importantArtifacts =
TopLevelArtifactHelper.getAllArtifactsToBuild(configuredAspect, context)
.getImportantArtifacts();
for (Artifact importantArtifact : importantArtifacts.toList()) {
if (headerFlag) {
outErr.printErr("Aspect " + aspectName + " of " + label + " up-to-date:\n");
headerFlag = false;
}
if (shouldPrint(importantArtifact)) {
outErr.printErrLn(formatArtifactForShowResults(prettyPrinter, importantArtifact));
}
}
if (headerFlag) {
outErr.printErr(
"Aspect " + aspectName + " of " + label + " up-to-date (nothing to build)\n");
}
}
for (AspectKey aspect : failed) {
Label label = aspect.getLabel();
String aspectName = aspect.getAspectClass().getName();
outErr.printErr("Aspect " + aspectName + " of " + label + " failed to build\n");
}
success = failed.isEmpty();
}
if (!success && !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(PathPrettyPrinter prettyPrinter, Artifact artifact) {
return " " + prettyPrinter.getPrettyPath(artifact.getPath());
}
/**
* Prints a flat list of all artifacts built by the passed top-level targets.
*
* <p>This corresponds to the --experimental_show_artifacts flag.
*/
void showArtifacts(
BuildRequest request,
Collection<ConfiguredTarget> configuredTargets,
Collection<ConfiguredAspect> aspects) {
TopLevelArtifactContext context = request.getTopLevelArtifactContext();
Collection<ConfiguredTarget> targetsToPrint = filterTargetsToPrint(configuredTargets);
NestedSetBuilder<Artifact> artifactsBuilder = NestedSetBuilder.stableOrder();
targetsToPrint.forEach(
t ->
artifactsBuilder.addTransitive(
TopLevelArtifactHelper.getAllArtifactsToBuild(t, context).getImportantArtifacts()));
aspects.forEach(
a ->
artifactsBuilder.addTransitive(
TopLevelArtifactHelper.getAllArtifactsToBuild(a, context).getImportantArtifacts()));
OutErr outErr = request.getOutErr();
outErr.printErrLn("Build artifacts:");
NestedSet<Artifact> artifacts = artifactsBuilder.build();
for (Artifact artifact : artifacts.toList()) {
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 configuredTarget : configuredTargets) {
// TODO(bazel-team): this is quite ugly. Add a marker provider for this check.
if (configuredTarget instanceof InputFileConfiguredTarget) {
// Suppress display of source files (because we do no work to build them).
continue;
}
if (configuredTarget instanceof RuleConfiguredTarget) {
RuleConfiguredTarget ruleCt = (RuleConfiguredTarget) configuredTarget;
if (ruleCt.getRuleClassString().contains("$")) {
// Suppress display of hidden rules
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 (CollectionUtils.containsAll(
generatingRule.getProvider(FileProvider.class).getFilesToBuild().toList(),
configuredTarget.getProvider(FileProvider.class).getFilesToBuild().toList())
&& configuredTargets.contains(generatingRule)) {
continue;
}
}
result.add(configuredTarget);
}
return result.build();
}
}