Teach ActionExecutionException and downstream structures about DetailedExitCode
I intend this change to be a pure refactoring. It should entail no
externally observable change in behavior.
This replaces ActionExecutionException's handling of ExitCode values. It
now handles DetailedExitCode values.
The upstream effects include:
* SpawnExecException's translation to ActionExecutionException gives it a
DetailedExitCode instead of an ExitCode.
SpawnExecException could relay its SpawnResult's FailureDetail, but to
keep this change pure, it does not, yet.
SpawnExecException and ActionExecutionException continue to encode the
idea of a "user error" failure with a null DetailedExitCode value, as
they encoded that idea before with a null ExitCode value. For detailed
user errors this must change, but it isn't changed yet.
The downstream effects include:
* BuildResult handles DetailedExitCode instead.
* BlazeCommandResult now preferably handles DetailedExitCode. It retains
its ExitCode-only factory methods, for now.
* BuildFailedException handles DetailedExitCode instead.
RELNOTES: None.
PiperOrigin-RevId: 298643766
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 938890f..f077ea8 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -308,6 +308,7 @@
":exitcode-external",
":failure_detail_util",
"//src/main/protobuf:failure_details_java_proto",
+ "//third_party:guava",
"//third_party:jsr305",
],
)
@@ -587,6 +588,7 @@
":bug-report",
":build-request-options",
":command-utils",
+ ":detailed_exit_code",
":events",
":exitcode-external",
":keep-going-option",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
index f16d441..c9becf1 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
@@ -20,6 +20,8 @@
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import javax.annotation.Nullable;
@@ -33,14 +35,14 @@
private final Action action;
private final NestedSet<Cause> rootCauses;
private final boolean catastrophe;
- @Nullable private final ExitCode exitCode;
+ @Nullable private final DetailedExitCode detailedExitCode;
public ActionExecutionException(Throwable cause, Action action, boolean catastrophe) {
super(cause.getMessage(), cause);
this.action = action;
this.rootCauses = rootCausesFromAction(action);
this.catastrophe = catastrophe;
- this.exitCode = null;
+ this.detailedExitCode = null;
}
public ActionExecutionException(String message,
@@ -49,17 +51,20 @@
this.action = action;
this.rootCauses = rootCausesFromAction(action);
this.catastrophe = catastrophe;
- this.exitCode = null;
+ this.detailedExitCode = null;
}
- public ActionExecutionException(String message,
- Throwable cause, Action action, boolean catastrophe,
- ExitCode exitCode) {
+ public ActionExecutionException(
+ String message,
+ Throwable cause,
+ Action action,
+ boolean catastrophe,
+ DetailedExitCode detailedExitCode) {
super(message, cause);
this.action = action;
this.rootCauses = rootCausesFromAction(action);
this.catastrophe = catastrophe;
- this.exitCode = exitCode;
+ this.detailedExitCode = detailedExitCode;
}
public ActionExecutionException(String message, Action action, boolean catastrophe) {
@@ -67,7 +72,7 @@
this.action = action;
this.rootCauses = rootCausesFromAction(action);
this.catastrophe = catastrophe;
- this.exitCode = null;
+ this.detailedExitCode = null;
}
public ActionExecutionException(String message, Action action, boolean catastrophe,
@@ -76,7 +81,7 @@
this.action = action;
this.rootCauses = rootCausesFromAction(action);
this.catastrophe = catastrophe;
- this.exitCode = exitCode;
+ this.detailedExitCode = DetailedExitCode.justExitCode(exitCode);
}
public ActionExecutionException(
@@ -85,7 +90,7 @@
this.action = action;
this.rootCauses = rootCauses;
this.catastrophe = catastrophe;
- this.exitCode = null;
+ this.detailedExitCode = null;
}
public ActionExecutionException(
@@ -98,7 +103,7 @@
this.action = action;
this.rootCauses = rootCauses;
this.catastrophe = catastrophe;
- this.exitCode = null;
+ this.detailedExitCode = null;
}
public ActionExecutionException(
@@ -107,15 +112,15 @@
Action action,
NestedSet<Cause> rootCauses,
boolean catastrophe,
- ExitCode exitCode) {
+ DetailedExitCode detailedExitCode) {
super(message, cause);
this.action = action;
this.rootCauses = rootCauses;
this.catastrophe = catastrophe;
- this.exitCode = exitCode;
+ this.detailedExitCode = detailedExitCode;
}
- static NestedSet<Cause> rootCausesFromAction(Action action) {
+ private static NestedSet<Cause> rootCausesFromAction(Action action) {
return action == null || action.getOwner() == null || action.getOwner().getLabel() == null
? NestedSetBuilder.<Cause>emptySet(Order.STABLE_ORDER)
: NestedSetBuilder.<Cause>create(
@@ -155,8 +160,33 @@
return catastrophe;
}
- @Nullable public ExitCode getExitCode() {
- return exitCode;
+ /**
+ * Returns the exit code to return from this Bazel invocation because of this action execution
+ * failure.
+ *
+ * <p>Returns {@code null} if the exception is not intended to cause the invocation to fail, or if
+ * the failure is attributable to the user. (In the latter case, ExitCode.BUILD_FAILURE with
+ * numeric value 1 will be returned.)
+ */
+ @Nullable
+ public ExitCode getExitCode() {
+ return detailedExitCode == null ? null : detailedExitCode.getExitCode();
+ }
+
+ /**
+ * Returns the pair of {@link ExitCode} and optional {@link FailureDetail} to return from this
+ * Bazel invocation because of this action execution failure.
+ *
+ * <p>Returns {@code null} if the exception is not intended to cause the invocation to fail, or if
+ * the failure is attributable to the user. (In the latter case, ExitCode.BUILD_FAILURE with
+ * numeric value 1 will be returned for an exit code, and no FailureDetail will be returned.)
+ */
+ // TODO(b/138456686): for detailed user failures, this must be able to return non-null for
+ // user-attributable failures. The meaning of "null" must be changed in code paths handling this
+ // returned value.
+ @Nullable
+ public DetailedExitCode getDetailedExitCode() {
+ return detailedExitCode;
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/actions/AlreadyReportedActionExecutionException.java b/src/main/java/com/google/devtools/build/lib/actions/AlreadyReportedActionExecutionException.java
index 3efc919..fa9cd46 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/AlreadyReportedActionExecutionException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/AlreadyReportedActionExecutionException.java
@@ -29,8 +29,13 @@
public class AlreadyReportedActionExecutionException extends ActionExecutionException {
public AlreadyReportedActionExecutionException(ActionExecutionException cause) {
- super(cause.getMessage(), cause.getCause(), cause.getAction(), cause.getRootCauses(),
- cause.isCatastrophe(), cause.getExitCode());
+ super(
+ cause.getMessage(),
+ cause.getCause(),
+ cause.getAction(),
+ cause.getRootCauses(),
+ cause.isCatastrophe(),
+ cause.getDetailedExitCode());
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD
index 741afaa..f922d67 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -34,6 +34,7 @@
":localhost_capacity",
"//src/main/java/com/google/devtools/build/lib:bug-report",
"//src/main/java/com/google/devtools/build/lib:command-utils",
+ "//src/main/java/com/google/devtools/build/lib:detailed_exit_code",
"//src/main/java/com/google/devtools/build/lib:events",
"//src/main/java/com/google/devtools/build/lib:packages-internal",
"//src/main/java/com/google/devtools/build/lib:unix",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java b/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
index 622f249..d0aa35b 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
@@ -19,6 +19,8 @@
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.ThreadSafety.ThreadSafe;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import javax.annotation.Nullable;
@@ -33,7 +35,7 @@
*
* <p>This exception typically leads to Bazel termination with exit code {@link
* ExitCode#BUILD_FAILURE}. However, if a more specific exit code is appropriate, it can be
- * propagated by specifying the exit code to the constructor.
+ * propagated by specifying the exit code to the constructor using a {@link DetailedExitCode}.
*/
@ThreadSafe
public class BuildFailedException extends Exception {
@@ -41,7 +43,7 @@
private final Action action;
private final NestedSet<Cause> rootCauses;
private final boolean errorAlreadyShown;
- @Nullable private final ExitCode exitCode;
+ @Nullable private final DetailedExitCode detailedExitCode;
public BuildFailedException() {
this(null);
@@ -51,8 +53,14 @@
this(message, false, null, NestedSetBuilder.emptySet(Order.STABLE_ORDER), false, null);
}
- public BuildFailedException(String message, ExitCode exitCode) {
- this(message, false, null, NestedSetBuilder.emptySet(Order.STABLE_ORDER), false, exitCode);
+ public BuildFailedException(String message, DetailedExitCode detailedExitCode) {
+ this(
+ message,
+ false,
+ null,
+ NestedSetBuilder.emptySet(Order.STABLE_ORDER),
+ false,
+ detailedExitCode);
}
public BuildFailedException(String message, boolean catastrophic) {
@@ -65,13 +73,13 @@
Action action,
NestedSet<Cause> rootCauses,
boolean errorAlreadyShown,
- ExitCode exitCode) {
+ @Nullable DetailedExitCode detailedExitCode) {
super(message);
this.catastrophic = catastrophic;
this.rootCauses = rootCauses;
this.action = action;
this.errorAlreadyShown = errorAlreadyShown;
- this.exitCode = exitCode;
+ this.detailedExitCode = detailedExitCode;
}
public boolean isCatastrophic() {
@@ -90,7 +98,19 @@
return errorAlreadyShown || getMessage() == null;
}
- @Nullable public ExitCode getExitCode() {
- return exitCode;
+ /**
+ * Returns the pair of {@link ExitCode} and optional {@link FailureDetail} to return from this
+ * Bazel invocation.
+ *
+ * <p>Returns {@code null} if the failure is attributable to the user. (In this case,
+ * ExitCode.BUILD_FAILURE with numeric value 1 will be returned for an exit code, and no
+ * FailureDetail will be returned.)
+ */
+ // TODO(b/138456686): for detailed user failures, this must be able to return non-null for
+ // user-attributable failures. The meaning of "null" must be changed in code paths handling this
+ // returned value.
+ @Nullable
+ public DetailedExitCode getDetailedExitCode() {
+ return detailedExitCode;
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildResult.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildResult.java
index c4e7686..0b50ebf 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildResult.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildResult.java
@@ -26,6 +26,7 @@
import com.google.devtools.build.lib.buildeventstream.BuildToolLogs;
import com.google.devtools.build.lib.buildeventstream.BuildToolLogs.LogFileEntry;
import com.google.devtools.build.lib.skyframe.AspectValue;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.Path;
@@ -49,7 +50,8 @@
private Throwable crash = null;
private boolean catastrophe = false;
private boolean stopOnFirstFailure;
- private ExitCode exitCondition = ExitCode.BLAZE_INTERNAL_ERROR;
+ private DetailedExitCode detailedExitCode =
+ DetailedExitCode.justExitCode(ExitCode.BLAZE_INTERNAL_ERROR);
private BuildConfigurationCollection configurations;
private Collection<ConfiguredTarget> actualTargets;
@@ -101,22 +103,21 @@
return wasSuspended;
}
- public void setExitCondition(ExitCode exitCondition) {
- this.exitCondition = exitCondition;
+ public void setDetailedExitCode(DetailedExitCode detailedExitCode) {
+ this.detailedExitCode = detailedExitCode;
}
- /**
- * True iff the build request has been successfully completed.
- */
+ /** True iff the build request has been successfully completed. */
public boolean getSuccess() {
- return exitCondition.equals(ExitCode.SUCCESS);
+ return detailedExitCode.isSuccess();
}
/**
- * Gets the Blaze exit condition.
+ * Gets the {@link DetailedExitCode} containing the {@link ExitCode} and optional failure detail
+ * to complete the command with.
*/
- public ExitCode getExitCondition() {
- return exitCondition;
+ public DetailedExitCode getDetailedExitCode() {
+ return detailedExitCode;
}
/**
@@ -283,7 +284,7 @@
.add("stopTimeMillis", stopTimeMillis)
.add("crash", crash)
.add("catastrophe", catastrophe)
- .add("exitCondition", exitCondition)
+ .add("detailedExitCode", detailedExitCode)
.add("actualTargets", actualTargets)
.add("testTargets", testTargets)
.add("successfulTargets", successfulTargets)
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
index d625154..45f7607 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
@@ -47,6 +47,7 @@
import com.google.devtools.build.lib.runtime.BlazeRuntime;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
import com.google.devtools.build.lib.util.AbruptExitException;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.common.options.OptionsProvider;
@@ -283,10 +284,11 @@
maybeSetStopOnFirstFailure(request, result);
int startSuspendCount = suspendCount();
Throwable catastrophe = null;
- ExitCode exitCode = ExitCode.BLAZE_INTERNAL_ERROR;
+ DetailedExitCode detailedExitCode =
+ DetailedExitCode.justExitCode(ExitCode.BLAZE_INTERNAL_ERROR);
try {
buildTargets(request, result, validator);
- exitCode = ExitCode.SUCCESS;
+ detailedExitCode = DetailedExitCode.justExitCode(ExitCode.SUCCESS);
} catch (BuildFailedException e) {
if (e.isErrorAlreadyShown()) {
// The actual error has already been reported by the Builder.
@@ -296,34 +298,38 @@
if (e.isCatastrophic()) {
result.setCatastrophe();
}
- exitCode = e.getExitCode() != null ? e.getExitCode() : ExitCode.BUILD_FAILURE;
+ detailedExitCode =
+ e.getDetailedExitCode() != null
+ ? e.getDetailedExitCode()
+ : DetailedExitCode.justExitCode(ExitCode.BUILD_FAILURE);
} catch (InterruptedException e) {
// We may have been interrupted by an error, or the user's interruption may have raced with
// an error, so check to see if we should report that error code instead.
- exitCode = env.getPendingExitCode();
- if (exitCode == null) {
- exitCode = ExitCode.INTERRUPTED;
+ ExitCode environmentPendingExitCode = env.getPendingExitCode();
+ if (environmentPendingExitCode == null) {
+ detailedExitCode = DetailedExitCode.justExitCode(ExitCode.INTERRUPTED);
env.getReporter().handle(Event.error("build interrupted"));
env.getEventBus().post(new BuildInterruptedEvent());
} else {
// Report the exception from the environment - the exception we're handling here is just an
// interruption.
+ detailedExitCode = DetailedExitCode.justExitCode(environmentPendingExitCode);
reportExceptionError(env.getPendingException());
result.setCatastrophe();
}
} catch (TargetParsingException | LoadingFailedException | ViewCreationFailedException e) {
- exitCode = ExitCode.PARSING_FAILURE;
+ detailedExitCode = DetailedExitCode.justExitCode(ExitCode.PARSING_FAILURE);
reportExceptionError(e);
} catch (PostAnalysisQueryCommandLineException e) {
- exitCode = ExitCode.COMMAND_LINE_ERROR;
+ detailedExitCode = DetailedExitCode.justExitCode(ExitCode.COMMAND_LINE_ERROR);
reportExceptionError(e);
} catch (TestExecException e) {
// ExitCode.SUCCESS means that build was successful. Real return code of program
// is going to be calculated in TestCommand.doTest().
- exitCode = ExitCode.SUCCESS;
+ detailedExitCode = DetailedExitCode.justExitCode(ExitCode.SUCCESS);
reportExceptionError(e);
} catch (InvalidConfigurationException e) {
- exitCode = ExitCode.COMMAND_LINE_ERROR;
+ detailedExitCode = DetailedExitCode.justExitCode(ExitCode.COMMAND_LINE_ERROR);
reportExceptionError(e);
// TODO(gregce): With "global configurations" we cannot tie a configuration creation failure
// to a single target and have to halt the entire build. Once configurations are genuinely
@@ -331,14 +337,14 @@
// target(s) that triggered them.
result.setCatastrophe();
} catch (AbruptExitException e) {
- exitCode = e.getExitCode();
+ detailedExitCode = DetailedExitCode.justExitCode(e.getExitCode());
reportExceptionError(e);
result.setCatastrophe();
} catch (Throwable throwable) {
catastrophe = throwable;
Throwables.propagate(throwable);
} finally {
- stopRequest(result, catastrophe, exitCode, startSuspendCount);
+ stopRequest(result, catastrophe, detailedExitCode, startSuspendCount);
}
return result;
@@ -377,17 +383,20 @@
*
* @param result result to update
* @param crash any unexpected {@link RuntimeException} or {@link Error}. May be null
- * @param exitCondition a suggested exit condition from either the build logic or a thrown
- * exception somewhere along the way
+ * @param detailedExitCode describes the exit code and an optional detailed failure value to add
+ * to {@code result}
* @param startSuspendCount number of suspensions before the build started
*/
public void stopRequest(
- BuildResult result, Throwable crash, ExitCode exitCondition, int startSuspendCount) {
- Preconditions.checkState((crash == null) || !exitCondition.equals(ExitCode.SUCCESS));
+ BuildResult result,
+ Throwable crash,
+ DetailedExitCode detailedExitCode,
+ int startSuspendCount) {
+ Preconditions.checkState((crash == null) || !detailedExitCode.isSuccess());
int stopSuspendCount = suspendCount();
Preconditions.checkState(startSuspendCount <= stopSuspendCount);
result.setUnhandledThrowable(crash);
- result.setExitCondition(exitCondition);
+ result.setDetailedExitCode(detailedExitCode);
InterruptedException ie = null;
try {
env.getSkyframeExecutor().notifyCommandComplete(env.getReporter());
@@ -410,14 +419,13 @@
// modules add their data to the collection.
env.getEventBus().post(result.getBuildToolLogCollection().freeze().toEvent());
if (ie != null) {
- if (exitCondition.equals(ExitCode.SUCCESS)) {
- result.setExitCondition(ExitCode.INTERRUPTED);
- } else if (!exitCondition.equals(ExitCode.INTERRUPTED)) {
+ if (detailedExitCode.isSuccess()) {
+ result.setDetailedExitCode(DetailedExitCode.justExitCode(ExitCode.INTERRUPTED));
+ } else if (!detailedExitCode.getExitCode().equals(ExitCode.INTERRUPTED)) {
logger.log(
Level.WARNING,
- "Suppressed interrupted exception during stop request because already failing with exit"
- + " code "
- + exitCondition,
+ "Suppressed interrupted exception during stop request because already failing with: "
+ + detailedExitCode,
ie);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
index 529e983..0cb1821 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
@@ -47,7 +47,7 @@
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
import com.google.devtools.build.lib.skyframe.TopDownActionCache;
import com.google.devtools.build.lib.util.AbruptExitException;
-import com.google.devtools.build.lib.util.ExitCode;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.LoggingUtil;
import com.google.devtools.build.lib.vfs.ModifiedFileSet;
import com.google.devtools.build.skyframe.CycleInfo;
@@ -55,10 +55,10 @@
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.common.options.OptionsProvider;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -129,7 +129,7 @@
.getEventBus()
.post(new ExecutionProgressReceiverAvailableEvent(executionProgressReceiver));
- List<ExitCode> exitCodes = new LinkedList<>();
+ List<DetailedExitCode> detailedExitCodes = new ArrayList<>();
EvaluationResult<?> result;
ActionExecutionStatusReporter statusReporter = ActionExecutionStatusReporter.create(
@@ -168,15 +168,15 @@
executionProgressReceiver,
topLevelArtifactContext);
// progressReceiver is finished, so unsynchronized access to builtTargets is now safe.
- Optional<ExitCode> exitCode =
+ Optional<DetailedExitCode> detailedExitCode =
processResult(
reporter,
result,
options.getOptions(KeepGoingOption.class).keepGoing,
skyframeExecutor);
- if (exitCode != null) {
- exitCodes.add(exitCode.orNull());
+ if (detailedExitCode != null) {
+ detailedExitCodes.add(detailedExitCode.orNull());
}
// Run exclusive tests: either tagged as "exclusive" or is run in an invocation with
@@ -196,20 +196,20 @@
topDownActionCache,
null,
topLevelArtifactContext);
- exitCode =
+ detailedExitCode =
processResult(
reporter,
result,
options.getOptions(KeepGoingOption.class).keepGoing,
skyframeExecutor);
Preconditions.checkState(
- exitCode != null || !result.keyNames().isEmpty(),
+ detailedExitCode != null || !result.keyNames().isEmpty(),
"Build reported as successful but test %s not executed: %s",
exclusiveTest,
result);
- if (exitCode != null) {
- exitCodes.add(exitCode.orNull());
+ if (detailedExitCode != null) {
+ detailedExitCodes.add(detailedExitCode.orNull());
}
}
} finally {
@@ -218,11 +218,11 @@
statusReporter.unregisterFromEventBus();
}
- if (!exitCodes.isEmpty()) {
+ if (!detailedExitCodes.isEmpty()) {
if (options.getOptions(KeepGoingOption.class).keepGoing) {
// Use the exit code with the highest priority.
throw new BuildFailedException(
- null, Collections.max(exitCodes, ExitCodeComparator.INSTANCE));
+ null, Collections.max(detailedExitCodes, DetailedExitCodeComparator.INSTANCE));
} else {
throw new BuildFailedException();
}
@@ -230,15 +230,22 @@
}
/**
- * Process the Skyframe update, taking into account the keepGoing setting.
+ * Process an {@link EvaluationResult}, taking into account the keepGoing setting.
*
- * <p>Returns optional {@link ExitCode} based on following conditions: 1. null, if result had no
- * errors. 2. Optional.absent(), if result had errors but none of the errors specified an exit
- * code. 3. Optional.of(e), if result had errors and one of them specified exit code 'e'. Throws
- * on fail-fast failures.
+ * <p>Returns a nullable optional {@link DetailedExitCode} value, as follows:
+ *
+ * <ol>
+ * <li>{@code null}, if {@code result} had no errors
+ * <li>{@code Optional.absent()}, if result had errors but none of the errors specified a {@link
+ * DetailedExitCode}
+ * <li>{@code Optional.of(e)} if result had errors and one of them specified a {@link
+ * DetailedExitCode} value {@code e}.
+ * </ol>
+ *
+ * <p>Throws on catastrophic failures.
*/
@Nullable
- private static Optional<ExitCode> processResult(
+ private static Optional<DetailedExitCode> processResult(
ExtendedEventHandler eventHandler,
EvaluationResult<?> result,
boolean keepGoing,
@@ -259,21 +266,21 @@
// 1. First infrastructure error with non-null exit code
// 2. First non-infrastructure error with non-null exit code
// 3. Null (later default to 1)
- ExitCode exitCode = null;
+ DetailedExitCode detailedExitCode = null;
for (Map.Entry<SkyKey, ErrorInfo> error : result.errorMap().entrySet()) {
Throwable cause = error.getValue().getException();
if (cause instanceof ActionExecutionException) {
ActionExecutionException actionExecutionCause = (ActionExecutionException) cause;
- ExitCode code = actionExecutionCause.getExitCode();
+ DetailedExitCode thisCode = actionExecutionCause.getDetailedExitCode();
// Update global exit code when current exit code is not null and global exit code has
// a lower 'reporting' priority.
- if (ExitCodeComparator.INSTANCE.compare(code, exitCode) > 0) {
- exitCode = code;
+ if (DetailedExitCodeComparator.INSTANCE.compare(thisCode, detailedExitCode) > 0) {
+ detailedExitCode = thisCode;
}
}
}
- return Optional.fromNullable(exitCode);
+ return Optional.fromNullable(detailedExitCode);
}
ErrorInfo errorInfo = Preconditions.checkNotNull(result.getError(), result);
Exception exception = errorInfo.getException();
@@ -312,7 +319,7 @@
actionExecutionCause.getAction(),
actionExecutionCause.getRootCauses(),
/*errorAlreadyShown=*/ !actionExecutionCause.showError(),
- actionExecutionCause.getExitCode());
+ actionExecutionCause.getDetailedExitCode());
} else if (cause instanceof MissingInputFileException) {
throw new BuildFailedException(cause.getMessage());
} else if (cause instanceof BuildFileNotFoundException) {
@@ -342,24 +349,24 @@
}
/**
- * A comparator to determine the reporting priority of {@link ExitCode}.
+ * A comparator to determine the reporting priority of {@link DetailedExitCode}.
*
- * <p> Priority: infrastructure exit codes > non-infrastructure exit codes > null exit codes.
+ * <p>Priority: infrastructure exit codes > non-infrastructure exit codes > null exit codes.
*/
- private static class ExitCodeComparator implements Comparator<ExitCode> {
- private static final ExitCodeComparator INSTANCE = new ExitCodeComparator();
+ private static class DetailedExitCodeComparator implements Comparator<DetailedExitCode> {
+ private static final DetailedExitCodeComparator INSTANCE = new DetailedExitCodeComparator();
@Override
- public int compare(ExitCode c1, ExitCode c2) {
+ public int compare(DetailedExitCode c1, DetailedExitCode c2) {
// returns POSITIVE result when the priority of c1 is HIGHER than the priority of c2
return getPriority(c1) - getPriority(c2);
}
- private int getPriority(ExitCode code) {
+ private static int getPriority(DetailedExitCode code) {
if (code == null) {
return 0;
} else {
- return code.isInfrastructureFailure() ? 2 : 1;
+ return code.getExitCode().isInfrastructureFailure() ? 2 : 1;
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/BuildCompleteEvent.java b/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/BuildCompleteEvent.java
index 86516aa..c4b6744 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/BuildCompleteEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/BuildCompleteEvent.java
@@ -25,19 +25,23 @@
/**
* This event is fired from BuildTool#stopRequest().
*
- * <p>This class also implements the {@link BuildFinished} event of the build event protocol (BEP).
+ * <p>This class also implements the {@link BuildCompletingEvent} of the build event protocol (BEP).
*/
public final class BuildCompleteEvent extends BuildCompletingEvent {
private final BuildResult result;
/** Construct the BuildCompleteEvent. */
public BuildCompleteEvent(BuildResult result, Collection<BuildEventId> children) {
- super(result.getExitCondition(), result.getStopTime(), children, result.getWasSuspended());
+ super(
+ result.getDetailedExitCode().getExitCode(),
+ result.getStopTime(),
+ children,
+ result.getWasSuspended());
this.result = checkNotNull(result);
}
public BuildCompleteEvent(BuildResult result) {
- this(result, ImmutableList.<BuildEventId>of());
+ this(result, ImmutableList.of());
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnExecException.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnExecException.java
index 6beec44..091e0df 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnExecException.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnExecException.java
@@ -23,6 +23,7 @@
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.SpawnResult.Status;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
/**
@@ -68,14 +69,15 @@
String message =
result.getDetailMessage(
messagePrefix, getMessage(), verboseFailures, isCatastrophic(), forciblyRunRemotely);
- return new ActionExecutionException(message, this, action, isCatastrophic(), getExitCode());
+ return new ActionExecutionException(
+ message, this, action, isCatastrophic(), getDetailedExitCode());
}
/** Return exit code depending on the spawn result. */
- protected ExitCode getExitCode() {
+ private DetailedExitCode getDetailedExitCode() {
if (result.status().isConsideredUserError()) {
return null;
}
- return ExitCode.REMOTE_ERROR;
+ return DetailedExitCode.justExitCode(ExitCode.REMOTE_ERROR);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandResult.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandResult.java
index 4292557..69646e4 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandResult.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandResult.java
@@ -82,6 +82,10 @@
return new BlazeCommandResult(DetailedExitCode.of(failureDetail), null, false);
}
+ public static BlazeCommandResult detailedExitCode(DetailedExitCode detailedExitCode) {
+ return new BlazeCommandResult(detailedExitCode, null, false);
+ }
+
public static BlazeCommandResult execute(ExecRequest execDescription) {
return new BlazeCommandResult(
DetailedExitCode.justExitCode(ExitCode.SUCCESS),
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/AqueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/AqueryCommand.java
index 8984e2d..dd5c0c9 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/AqueryCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/AqueryCommand.java
@@ -31,6 +31,7 @@
import com.google.devtools.build.lib.runtime.BlazeRuntime;
import com.google.devtools.build.lib.runtime.Command;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.common.options.OptionPriority.PriorityCategory;
import com.google.devtools.common.options.OptionsParser;
@@ -136,8 +137,9 @@
return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR);
}
}
- ExitCode exitCode = aqueryBuildTool.processRequest(request, null).getExitCondition();
- return BlazeCommandResult.exitCode(exitCode);
+ DetailedExitCode detailedExitCode =
+ aqueryBuildTool.processRequest(request, null).getDetailedExitCode();
+ return BlazeCommandResult.detailedExitCode(detailedExitCode);
}
private ImmutableMap<String, QueryFunction> getFunctionsMap(CommandEnvironment env) {
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java
index b2405e0..3ccc1ba 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/BuildCommand.java
@@ -32,7 +32,7 @@
import com.google.devtools.build.lib.runtime.CommandEnvironment;
import com.google.devtools.build.lib.runtime.KeepGoingOption;
import com.google.devtools.build.lib.runtime.LoadingPhaseThreadsOption;
-import com.google.devtools.build.lib.util.ExitCode;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.common.options.OptionsParsingResult;
import java.util.List;
@@ -91,7 +91,8 @@
targets,
env.getReporter().getOutErr(), env.getCommandId(), env.getCommandStartTime());
}
- ExitCode exitCode = new BuildTool(env).processRequest(request, null).getExitCondition();
- return BlazeCommandResult.exitCode(exitCode);
+ DetailedExitCode detailedExitCode =
+ new BuildTool(env).processRequest(request, null).getDetailedExitCode();
+ return BlazeCommandResult.detailedExitCode(detailedExitCode);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java
index 92b7277..918989b 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/CqueryCommand.java
@@ -30,6 +30,7 @@
import com.google.devtools.build.lib.runtime.BlazeRuntime;
import com.google.devtools.build.lib.runtime.Command;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.common.options.OptionPriority.PriorityCategory;
import com.google.devtools.common.options.OptionsParser;
@@ -128,8 +129,8 @@
env.getReporter().getOutErr(),
env.getCommandId(),
env.getCommandStartTime());
- ExitCode exitCode =
- new CqueryBuildTool(env, expr).processRequest(request, null).getExitCondition();
- return BlazeCommandResult.exitCode(exitCode);
+ DetailedExitCode detailedExitCode =
+ new CqueryBuildTool(env, expr).processRequest(request, null).getDetailedExitCode();
+ return BlazeCommandResult.detailedExitCode(detailedExitCode);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
index 3d0c2cd..4c4e2ae 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
@@ -50,6 +50,7 @@
import com.google.devtools.build.lib.runtime.Command;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
import com.google.devtools.build.lib.runtime.KeepGoingOption;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.io.OutErr;
import com.google.devtools.common.options.Option;
@@ -104,7 +105,7 @@
PrintActionRunner runner = new PrintActionRunner(loadingOptions.compileOneDependency, options,
env.getReporter().getOutErr(),
options.getResidue(), Sets.newHashSet(printActionOptions.printActionMnemonics));
- return BlazeCommandResult.exitCode(runner.printActionsForTargets(env));
+ return BlazeCommandResult.detailedExitCode(runner.printActionsForTargets(env));
}
/**
@@ -141,22 +142,22 @@
};
}
- private ExitCode printActionsForTargets(CommandEnvironment env) {
+ private DetailedExitCode printActionsForTargets(CommandEnvironment env) {
BuildResult result = gatherActionsForTargets(env, requestedTargets);
if (result == null) {
- return ExitCode.PARSING_FAILURE;
+ return DetailedExitCode.justExitCode(ExitCode.PARSING_FAILURE);
}
if (hasFatalBuildFailure(result)) {
env.getReporter().handle(Event.error("Build failed when printing actions"));
- return result.getExitCondition();
+ return result.getDetailedExitCode();
}
String action = TextFormat.printToString(summaryBuilder);
if (!action.isEmpty()) {
outErr.printOut(action);
- return result.getExitCondition();
+ return result.getDetailedExitCode();
} else {
env.getReporter().handle(Event.error("no actions to print were found"));
- return ExitCode.PARSING_FAILURE;
+ return DetailedExitCode.justExitCode(ExitCode.PARSING_FAILURE);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
index c5e8b50..f76eb13 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
@@ -289,7 +289,7 @@
if (!result.getSuccess()) {
env.getReporter().handle(Event.error("Build failed. Not running target"));
- return BlazeCommandResult.exitCode(result.getExitCondition());
+ return BlazeCommandResult.detailedExitCode(result.getDetailedExitCode());
}
// Make sure that we have exactly 1 built target (excluding --run_under),
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java
index 20b998a..ecc37a9 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/TestCommand.java
@@ -40,6 +40,7 @@
import com.google.devtools.build.lib.runtime.TestResultNotifier;
import com.google.devtools.build.lib.runtime.TestSummaryPrinter.TestLogPathFormatter;
import com.google.devtools.build.lib.runtime.UiOptions;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.io.AnsiTerminalPrinter;
import com.google.devtools.build.lib.vfs.Path;
@@ -136,13 +137,17 @@
// (original exitcode=BUILD_FAILURE) or if there weren't but --noanalyze was given
// (original exitcode=SUCCESS).
env.getReporter().handle(Event.error("Couldn't start the build. Unable to run tests"));
- ExitCode exitCode =
- buildResult.getSuccess() ? ExitCode.PARSING_FAILURE : buildResult.getExitCondition();
+ DetailedExitCode detailedExitCode =
+ buildResult.getSuccess()
+ ? DetailedExitCode.justExitCode(ExitCode.PARSING_FAILURE)
+ : buildResult.getDetailedExitCode();
env.getEventBus()
.post(
new TestingCompleteEvent(
- exitCode, buildResult.getStopTime(), buildResult.getWasSuspended()));
- return BlazeCommandResult.exitCode(exitCode);
+ detailedExitCode.getExitCode(),
+ buildResult.getStopTime(),
+ buildResult.getWasSuspended()));
+ return BlazeCommandResult.detailedExitCode(detailedExitCode);
}
// TODO(bazel-team): the check above shadows NO_TESTS_FOUND, but switching the conditions breaks
// more tests
@@ -150,12 +155,17 @@
env.getReporter().handle(Event.error(
null, "No test targets were found, yet testing was requested"));
- ExitCode exitCode =
- buildResult.getSuccess() ? ExitCode.NO_TESTS_FOUND : buildResult.getExitCondition();
+ DetailedExitCode detailedExitCode =
+ buildResult.getSuccess()
+ ? DetailedExitCode.justExitCode(ExitCode.NO_TESTS_FOUND)
+ : buildResult.getDetailedExitCode();
env.getEventBus()
.post(
- new NoTestsFound(exitCode, buildResult.getStopTime(), buildResult.getWasSuspended()));
- return BlazeCommandResult.exitCode(exitCode);
+ new NoTestsFound(
+ detailedExitCode.getExitCode(),
+ buildResult.getStopTime(),
+ buildResult.getWasSuspended()));
+ return BlazeCommandResult.detailedExitCode(detailedExitCode);
}
boolean buildSuccess = buildResult.getSuccess();
@@ -171,14 +181,19 @@
+ AnsiTerminalPrinter.Mode.DEFAULT);
}
- ExitCode exitCode = buildSuccess
- ? (testSuccess ? ExitCode.SUCCESS : ExitCode.TESTS_FAILED)
- : buildResult.getExitCondition();
+ DetailedExitCode detailedExitCode =
+ buildSuccess
+ ? (testSuccess
+ ? DetailedExitCode.justExitCode(ExitCode.SUCCESS)
+ : DetailedExitCode.justExitCode(ExitCode.TESTS_FAILED))
+ : buildResult.getDetailedExitCode();
env.getEventBus()
.post(
new TestingCompleteEvent(
- exitCode, buildResult.getStopTime(), buildResult.getWasSuspended()));
- return BlazeCommandResult.exitCode(exitCode);
+ detailedExitCode.getExitCode(),
+ buildResult.getStopTime(),
+ buildResult.getWasSuspended()));
+ return BlazeCommandResult.detailedExitCode(detailedExitCode);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/mobileinstall/MobileInstallCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/mobileinstall/MobileInstallCommand.java
index 5493872..93fe3cd 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/mobileinstall/MobileInstallCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/mobileinstall/MobileInstallCommand.java
@@ -41,6 +41,7 @@
import com.google.devtools.build.lib.shell.BadExitStatusException;
import com.google.devtools.build.lib.shell.CommandException;
import com.google.devtools.build.lib.util.CommandBuilder;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.io.OutErr;
import com.google.devtools.build.lib.vfs.Path;
@@ -181,8 +182,9 @@
env.getReporter().getOutErr(),
env.getCommandId(),
env.getCommandStartTime());
- ExitCode exitCode = new BuildTool(env).processRequest(request, null).getExitCondition();
- return BlazeCommandResult.exitCode(exitCode);
+ DetailedExitCode detailedExitCode =
+ new BuildTool(env).processRequest(request, null).getDetailedExitCode();
+ return BlazeCommandResult.detailedExitCode(detailedExitCode);
}
// This list should look like: ["//executable:target", "arg1", "arg2"]
@@ -211,7 +213,7 @@
if (!result.getSuccess()) {
env.getReporter().handle(Event.error("Build failed. Not running target"));
- return BlazeCommandResult.exitCode(result.getExitCondition());
+ return BlazeCommandResult.detailedExitCode(result.getDetailedExitCode());
}
Collection<ConfiguredTarget> targetsBuilt = result.getSuccessfulTargets();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
index ed36d95..91b1f31 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
@@ -1266,7 +1266,7 @@
action,
rootCauses.build(),
firstActionExecutionException.isCatastrophe(),
- firstActionExecutionException.getExitCode());
+ firstActionExecutionException.getDetailedExitCode());
}
if (missingCount > 0) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index d6ae9c6..ef4cb7b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -1886,7 +1886,6 @@
getConfigurations(eventHandler, ImmutableList.of(options), options, keepGoing));
}
- @VisibleForTesting
public BuildConfiguration getConfiguration(
ExtendedEventHandler eventHandler, BuildConfigurationValue.Key configurationKey) {
if (configurationKey == null) {
diff --git a/src/main/java/com/google/devtools/build/lib/util/DetailedExitCode.java b/src/main/java/com/google/devtools/build/lib/util/DetailedExitCode.java
index 520a0cf..de37710 100644
--- a/src/main/java/com/google/devtools/build/lib/util/DetailedExitCode.java
+++ b/src/main/java/com/google/devtools/build/lib/util/DetailedExitCode.java
@@ -14,6 +14,8 @@
package com.google.devtools.build.lib.util;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import javax.annotation.Nullable;
@@ -36,8 +38,42 @@
return failureDetail;
}
+ public boolean isSuccess() {
+ return exitCode.equals(ExitCode.SUCCESS);
+ }
+
+ /**
+ * Returns a {@link DetailedExitCode} specifying {@link ExitCode} but no {@link FailureDetail}.
+ *
+ * <p>This method exists in order to allow for code which has not yet been wired for {@link
+ * FailureDetail) support to interact with {@link FailureDetail}-handling code infrastructure.
+ *
+ * <p>Callsites should migrate to using either:
+ *
+ * <ul>
+ * <li>{@link #of(ExitCode, FailureDetail)}, when they're wired for {@link FailureDetail}
+ * support but not yet ready to have {@link FailureDetail} metadata determine exit code behavior
+ * <li>{@link #of(FailureDetail)}, when changing exit code behavior is desired.
+ * </ul>
+ *
+ */
public static DetailedExitCode justExitCode(ExitCode exitCode) {
- return new DetailedExitCode(exitCode, null);
+ return new DetailedExitCode(checkNotNull(exitCode), null);
+ }
+
+ /**
+ * Returns a {@link DetailedExitCode} combining the provided {@link FailureDetail} and {@link
+ * ExitCode}.
+ *
+ * <p>This method exists in order to allow for the introduction of new {@link
+ * FailureDetail)-handling code infrastructure without requiring any simultaneous change in exit
+ * code behavior.
+ *
+ * <p>Callsites should migrate to using {@link #of(FailureDetail)} instead.
+ */
+ // TODO(b/138456686): consider controlling this behavior by flag if migration appears risky.
+ public static DetailedExitCode of(ExitCode exitCode, FailureDetail failureDetail) {
+ return new DetailedExitCode(checkNotNull(exitCode), checkNotNull(failureDetail));
}
/**
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 4fba77d..9e59e72 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -1261,6 +1261,7 @@
"//src/main/java/com/google/devtools/build/lib:bazel-modules",
"//src/main/java/com/google/devtools/build/lib:bazel-rules",
"//src/main/java/com/google/devtools/build/lib:build-base",
+ "//src/main/java/com/google/devtools/build/lib:detailed_exit_code",
"//src/main/java/com/google/devtools/build/lib:loading-phase-threads-option",
"//src/main/java/com/google/devtools/build/lib:packages",
"//src/main/java/com/google/devtools/build/lib:runtime",
diff --git a/src/test/java/com/google/devtools/build/lib/buildtool/BUILD b/src/test/java/com/google/devtools/build/lib/buildtool/BUILD
index b6337e2..0dfef12 100644
--- a/src/test/java/com/google/devtools/build/lib/buildtool/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/buildtool/BUILD
@@ -21,6 +21,7 @@
"//src/main/java/com/google/devtools/build/lib:build-base",
"//src/main/java/com/google/devtools/build/lib:build-request-options",
"//src/main/java/com/google/devtools/build/lib:command-utils",
+ "//src/main/java/com/google/devtools/build/lib:detailed_exit_code",
"//src/main/java/com/google/devtools/build/lib:events",
"//src/main/java/com/google/devtools/build/lib:exitcode-external",
"//src/main/java/com/google/devtools/build/lib:keep-going-option",
diff --git a/src/test/java/com/google/devtools/build/lib/buildtool/util/BlazeRuntimeWrapper.java b/src/test/java/com/google/devtools/build/lib/buildtool/util/BlazeRuntimeWrapper.java
index b75366a..8cb02f5 100644
--- a/src/test/java/com/google/devtools/build/lib/buildtool/util/BlazeRuntimeWrapper.java
+++ b/src/test/java/com/google/devtools/build/lib/buildtool/util/BlazeRuntimeWrapper.java
@@ -57,6 +57,7 @@
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
import com.google.devtools.build.lib.sandbox.SandboxOptions;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.io.OutErr;
import com.google.devtools.build.lib.vfs.OutputService;
@@ -370,7 +371,9 @@
buildTool.stopRequest(
lastResult,
null,
- success ? ExitCode.SUCCESS : ExitCode.BUILD_FAILURE,
+ success
+ ? DetailedExitCode.justExitCode(ExitCode.SUCCESS)
+ : DetailedExitCode.justExitCode(ExitCode.BUILD_FAILURE),
/*startSuspendCount=*/ 0);
getSkyframeExecutor().notifyCommandComplete(env.getReporter());
}
diff --git a/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java b/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java
index a87d2021..d78c92a 100644
--- a/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java
@@ -67,6 +67,7 @@
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.NestedSetView;
import com.google.devtools.build.lib.testutil.FoundationTestCase;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.Path;
@@ -1326,7 +1327,7 @@
private BuildCompleteEvent buildCompleteEvent(
ExitCode exitCode, boolean stopOnFailure, Throwable crash, boolean catastrophe) {
BuildResult result = new BuildResult(0);
- result.setExitCondition(exitCode);
+ result.setDetailedExitCode(DetailedExitCode.justExitCode(exitCode));
result.setStopOnFirstFailure(stopOnFailure);
if (catastrophe) {
result.setCatastrophe();
diff --git a/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java b/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java
index fb052c6..4382cf4 100644
--- a/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/runtime/UiStateTrackerTest.java
@@ -52,6 +52,7 @@
import com.google.devtools.build.lib.skyframe.PackageProgressReceiver;
import com.google.devtools.build.lib.testutil.FoundationTestCase;
import com.google.devtools.build.lib.testutil.ManualClock;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.io.LoggingTerminalWriter;
@@ -1128,7 +1129,7 @@
BuildEventTransport transport2 = newBepTransport("BuildEventTransport2");
BuildEventTransport transport3 = newBepTransport("BuildEventTransport3");
BuildResult buildResult = new BuildResult(clock.currentTimeMillis());
- buildResult.setExitCondition(ExitCode.SUCCESS);
+ buildResult.setDetailedExitCode(DetailedExitCode.justExitCode(ExitCode.SUCCESS));
clock.advanceMillis(TimeUnit.SECONDS.toMillis(1));
buildResult.setStopTime(clock.currentTimeMillis());
@@ -1198,7 +1199,7 @@
BuildEventTransport transport1 = newBepTransport(Strings.repeat("A", 61));
BuildEventTransport transport2 = newBepTransport("BuildEventTransport");
BuildResult buildResult = new BuildResult(clock.currentTimeMillis());
- buildResult.setExitCondition(ExitCode.SUCCESS);
+ buildResult.setDetailedExitCode(DetailedExitCode.justExitCode(ExitCode.SUCCESS));
LoggingTerminalWriter terminalWriter = new LoggingTerminalWriter(true);
UiStateTracker stateTracker = new UiStateTracker(clock, 60);
stateTracker.buildStarted(null);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
index 3ecb0d9..00b62f1 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -82,6 +82,7 @@
"//src/main/java/com/google/devtools/build/lib:bazel-rules",
"//src/main/java/com/google/devtools/build/lib:build-base",
"//src/main/java/com/google/devtools/build/lib:build-request-options",
+ "//src/main/java/com/google/devtools/build/lib:detailed_exit_code",
"//src/main/java/com/google/devtools/build/lib:events",
"//src/main/java/com/google/devtools/build/lib:keep-going-option",
"//src/main/java/com/google/devtools/build/lib:packages",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
index 249764e..901a0c1 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
@@ -116,6 +116,7 @@
import com.google.devtools.build.lib.syntax.StarlarkSemantics;
import com.google.devtools.build.lib.testutil.MoreAsserts;
import com.google.devtools.build.lib.testutil.TestUtils;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
@@ -1480,8 +1481,12 @@
@Override
public ActionResult execute(ActionExecutionContext actionExecutionContext)
throws ActionExecutionException {
- throw new ActionExecutionException("message", new Exception("just cause"), this,
- /*catastrophe=*/true, expectedExitCode);
+ throw new ActionExecutionException(
+ "message",
+ new Exception("just cause"),
+ this,
+ /*catastrophe=*/ true,
+ DetailedExitCode.justExitCode(expectedExitCode));
}
}
@@ -1582,7 +1587,8 @@
null));
// The catastrophic exception should be propagated into the BuildFailedException whether or not
// --keep_going is set.
- assertThat(e.getExitCode()).isEqualTo(CatastrophicAction.expectedExitCode);
+ assertThat(e.getDetailedExitCode().getExitCode())
+ .isEqualTo(CatastrophicAction.expectedExitCode);
assertThat(builtTargets).isEmpty();
assertThat(markerRan.get()).isFalse();
}
@@ -1715,7 +1721,8 @@
null));
// The catastrophic exception should be propagated into the BuildFailedException whether or not
// --keep_going is set.
- assertThat(e.getExitCode()).isEqualTo(CatastrophicAction.expectedExitCode);
+ assertThat(e.getDetailedExitCode().getExitCode())
+ .isEqualTo(CatastrophicAction.expectedExitCode);
assertThat(builtTargets).isEmpty();
}
@@ -1845,7 +1852,8 @@
OutputGroupInfo.determineOutputGroups(ImmutableList.of(), true))));
// The catastrophic exception should be propagated into the BuildFailedException whether or not
// --keep_going is set.
- assertThat(e.getExitCode()).isEqualTo(CatastrophicAction.expectedExitCode);
+ assertThat(e.getDetailedExitCode().getExitCode())
+ .isEqualTo(CatastrophicAction.expectedExitCode);
assertThat(builtTargets).isEmpty();
}
@@ -1952,7 +1960,8 @@
null));
// The catastrophic exception should be propagated into the BuildFailedException whether or not
// --keep_going is set.
- assertThat(e.getExitCode()).isEqualTo(CatastrophicAction.expectedExitCode);
+ assertThat(e.getDetailedExitCode().getExitCode())
+ .isEqualTo(CatastrophicAction.expectedExitCode);
assertThat(builtTargets).isEmpty();
}
@@ -1969,7 +1978,11 @@
public ActionResult execute(ActionExecutionContext actionExecutionContext)
throws ActionExecutionException {
throw new ActionExecutionException(
- "foo", new Exception("bar"), this, /*catastrophe=*/ false, exitCode);
+ "foo",
+ new Exception("bar"),
+ this,
+ /*catastrophe=*/ false,
+ DetailedExitCode.justExitCode(exitCode));
}
}
@@ -2060,7 +2073,7 @@
null));
// The exit code should be propagated into the BuildFailedException whether or not --keep_going
// is set.
- assertThat(e.getExitCode()).isEqualTo(USER_EXIT_CODE);
+ assertThat(e.getDetailedExitCode().getExitCode()).isEqualTo(USER_EXIT_CODE);
}
/**
@@ -2157,7 +2170,7 @@
null));
// The exit code should be propagated into the BuildFailedException whether or not --keep_going
// is set.
- assertThat(e.getExitCode()).isEqualTo(INFRA_EXIT_CODE);
+ assertThat(e.getDetailedExitCode().getExitCode()).isEqualTo(INFRA_EXIT_CODE);
}
/**
@@ -2628,7 +2641,6 @@
return null;
}
- @SuppressWarnings("unchecked")
@Override
public <T extends Info> T get(NativeProvider<T> provider) {
return provider.getValueClass().cast(get(provider.getKey()));