Encode remaining ActionExecution failures with FailureDetails
This removes the last uses of undetailed ActionExecutionException
constructors by adding details to {Test,User}ExecException.
RELNOTES: None.
PiperOrigin-RevId: 318143448
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 87cb251..73e9d68 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
@@ -51,15 +51,6 @@
}
public ActionExecutionException(
- String message, Throwable cause, ActionAnalysisMetadata action, boolean catastrophe) {
- super(message, cause);
- this.action = action;
- this.catastrophe = catastrophe;
- this.detailedExitCode = DetailedExitCode.justExitCode(ExitCode.BUILD_FAILURE);
- this.rootCauses = rootCausesFromAction(action, detailedExitCode);
- }
-
- public ActionExecutionException(
String message,
Throwable cause,
ActionAnalysisMetadata action,
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Spawns.java b/src/main/java/com/google/devtools/build/lib/actions/Spawns.java
index dea5930..d84b8fc 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Spawns.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Spawns.java
@@ -14,6 +14,9 @@
package com.google.devtools.build.lib.actions;
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Spawn.Code;
import com.google.devtools.build.lib.util.CommandDescriptionForm;
import com.google.devtools.build.lib.util.CommandFailureUtils;
import com.google.devtools.build.lib.vfs.Path;
@@ -94,22 +97,17 @@
return customValue != null ? customValue : spawn.getMnemonic();
}
- /** Parse the timeout key in the spawn execution info, if it exists. Otherwise, return -1. */
+ /**
+ * Parse the timeout key in the spawn execution info, if it exists. Otherwise, return {@link
+ * Duration#ZERO}.
+ */
public static Duration getTimeout(Spawn spawn) throws ExecException {
- String timeoutStr = spawn.getExecutionInfo().get(ExecutionRequirements.TIMEOUT);
- if (timeoutStr == null) {
- return Duration.ZERO;
- }
- try {
- return Duration.ofSeconds(Integer.parseInt(timeoutStr));
- } catch (NumberFormatException e) {
- throw new UserExecException("could not parse timeout: ", e);
- }
+ return getTimeout(spawn, Duration.ZERO);
}
/**
* Parse the timeout key in the spawn execution info, if it exists. Otherwise, return
- * defaultTimeout, or 0 if that is null.
+ * defaultTimeout, or {@code Duration.ZERO} if that is null.
*/
public static Duration getTimeout(Spawn spawn, Duration defaultTimeout) throws ExecException {
String timeoutStr = spawn.getExecutionInfo().get(ExecutionRequirements.TIMEOUT);
@@ -119,7 +117,12 @@
try {
return Duration.ofSeconds(Integer.parseInt(timeoutStr));
} catch (NumberFormatException e) {
- throw new UserExecException("could not parse timeout: ", e);
+ throw new UserExecException(
+ e,
+ FailureDetail.newBuilder()
+ .setMessage("could not parse timeout")
+ .setSpawn(FailureDetails.Spawn.newBuilder().setCode(Code.INVALID_TIMEOUT))
+ .build());
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/TestExecException.java b/src/main/java/com/google/devtools/build/lib/actions/TestExecException.java
index db2cb5d..8381507 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/TestExecException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/TestExecException.java
@@ -13,20 +13,30 @@
// limitations under the License.
package com.google.devtools.build.lib.actions;
-/**
- * An TestExecException that is related to the failure of a TestAction.
- */
-public final class TestExecException extends ExecException {
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.TestAction;
+import com.google.devtools.build.lib.util.DetailedExitCode;
- public TestExecException(String message) {
+/** An TestExecException that is related to the failure of a TestAction. */
+public final class TestExecException extends ExecException {
+ private final FailureDetails.TestAction.Code detailedCode;
+
+ public TestExecException(String message, FailureDetails.TestAction.Code detailedCode) {
super(message);
+ this.detailedCode = detailedCode;
}
@Override
- public ActionExecutionException toActionExecutionException(String messagePrefix,
- boolean verboseFailures, Action action) {
- String message = messagePrefix + " failed";
- return new ActionExecutionException(
- message + ": " + getMessage(), this, action, isCatastrophic());
+ public ActionExecutionException toActionExecutionException(
+ String messagePrefix, boolean verboseFailures, Action action) {
+ String message = String.format("%s: %s", messagePrefix + " failed", getMessage());
+ DetailedExitCode code =
+ DetailedExitCode.of(
+ FailureDetail.newBuilder()
+ .setMessage(message)
+ .setTestAction(TestAction.newBuilder().setCode(detailedCode))
+ .build());
+ return new ActionExecutionException(message, this, action, isCatastrophic(), code);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/UserExecException.java b/src/main/java/com/google/devtools/build/lib/actions/UserExecException.java
index f58f9e1..caaeb66 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/UserExecException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/UserExecException.java
@@ -14,29 +14,33 @@
package com.google.devtools.build.lib.actions;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.util.DetailedExitCode;
+
/**
- * An ExecException that is related to the failure of an Action and therefore
- * very likely the user's fault.
+ * An ExecException that is related to the failure of an Action and therefore very likely the user's
+ * fault.
*/
public class UserExecException extends ExecException {
- public UserExecException(String message) {
- super(message);
- }
-
- public UserExecException(Throwable cause) {
- super(cause);
+ private final FailureDetail failureDetail;
+
+ public UserExecException(FailureDetail failureDetail) {
+ super(failureDetail.getMessage());
+ this.failureDetail = failureDetail;
}
- public UserExecException(String message, Throwable cause) {
- super(message, cause);
+ public UserExecException(Throwable cause, FailureDetail failureDetail) {
+ super(failureDetail.getMessage(), cause);
+ this.failureDetail = failureDetail;
}
@Override
- public ActionExecutionException toActionExecutionException(String messagePrefix,
- boolean verboseFailures, Action action) {
- String message = messagePrefix + " failed";
+ public ActionExecutionException toActionExecutionException(
+ String messagePrefix, boolean verboseFailures, Action action) {
+ String message = String.format("%s failed: %s", messagePrefix, getMessage());
+ FailureDetail failureDetailWithPrefix = failureDetail.toBuilder().setMessage(message).build();
return new ActionExecutionException(
- message + ": " + getMessage(), this, action, isCatastrophic());
+ message, this, action, isCatastrophic(), DetailedExitCode.of(failureDetailWithPrefix));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index abd3dba..b9598c2 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -1326,6 +1326,7 @@
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
"//src/main/java/com/google/devtools/build/lib/syntax:evaluator",
"//src/main/java/com/google/devtools/build/lib/util",
+ "//src/main/protobuf:failure_details_java_proto",
"//third_party:guava",
],
)
@@ -1978,6 +1979,7 @@
"//src/main/java/com/google/devtools/build/lib/concurrent",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
"//src/main/java/com/google/devtools/build/lib/util",
+ "//src/main/protobuf:failure_details_java_proto",
"//third_party:guava",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/ParameterFileWriteAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/ParameterFileWriteAction.java
index 5ca54b0..92162aa 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/ParameterFileWriteAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/ParameterFileWriteAction.java
@@ -18,6 +18,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionKeyContext;
@@ -34,6 +35,9 @@
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.Immutable;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Spawn;
+import com.google.devtools.build.lib.server.FailureDetails.Spawn.Code;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
import com.google.devtools.build.lib.syntax.EvalException;
@@ -138,7 +142,12 @@
ArtifactExpander artifactExpander = Preconditions.checkNotNull(ctx.getArtifactExpander());
arguments = commandLine.arguments(artifactExpander);
} catch (CommandLineExpansionException e) {
- throw new UserExecException(e);
+ throw new UserExecException(
+ e,
+ FailureDetail.newBuilder()
+ .setMessage(Strings.nullToEmpty(e.getMessage()))
+ .setSpawn(Spawn.newBuilder().setCode(Code.COMMAND_LINE_EXPANSION_FAILURE))
+ .build());
}
return new ParamFileWriter(arguments, type);
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/StarlarkAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/StarlarkAction.java
index 474822d..c4abbeb 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/StarlarkAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/StarlarkAction.java
@@ -170,10 +170,11 @@
.toPath(unusedInputsListArtifact)
.getInputStream();
} catch (FileNotFoundException e) {
- throw new UserExecException(
+ String message =
"Action did not create expected output file listing unused inputs: "
- + unusedInputsListArtifact.getExecPathString(),
- e);
+ + unusedInputsListArtifact.getExecPathString();
+ throw new UserExecException(
+ e, createFailureDetail(message, Code.UNUSED_INPUT_LIST_FILE_NOT_FOUND));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/extra/ExtraActionInfoFileWriteAction.java b/src/main/java/com/google/devtools/build/lib/analysis/extra/ExtraActionInfoFileWriteAction.java
index 5c1263c..7b86e16 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/extra/ExtraActionInfoFileWriteAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/extra/ExtraActionInfoFileWriteAction.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.analysis.extra;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionKeyContext;
@@ -28,6 +29,9 @@
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.Immutable;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Spawn;
+import com.google.devtools.build.lib.server.FailureDetails.Spawn.Code;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.Fingerprint;
@@ -62,7 +66,12 @@
return new ProtoDeterministicWriter(
shadowedAction.getExtraActionInfo(ctx.getActionKeyContext()).build());
} catch (CommandLineExpansionException e) {
- throw new UserExecException(e);
+ throw new UserExecException(
+ e,
+ FailureDetail.newBuilder()
+ .setMessage(Strings.nullToEmpty(e.getMessage()))
+ .setSpawn(Spawn.newBuilder().setCode(Code.COMMAND_LINE_EXPANSION_FAILURE))
+ .build());
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/platform/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/platform/BUILD
index eed1d3a..90481d9 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/platform/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/platform/BUILD
@@ -42,6 +42,7 @@
deps = [
"//src/main/java/com/google/devtools/build/lib/actions",
"//src/main/java/com/google/devtools/build/lib/remote/options",
+ "//src/main/protobuf:failure_details_java_proto",
"//third_party:guava",
"//third_party:jsr305",
"//third_party/protobuf:protobuf_java",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformUtils.java b/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformUtils.java
index 78dd897..621a598 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/platform/PlatformUtils.java
@@ -22,6 +22,9 @@
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.UserExecException;
import com.google.devtools.build.lib.remote.options.RemoteOptions;
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Spawn.Code;
import com.google.protobuf.TextFormat;
import com.google.protobuf.TextFormat.ParseException;
import java.util.Comparator;
@@ -84,11 +87,12 @@
TextFormat.getParser()
.merge(spawn.getExecutionPlatform().remoteExecutionProperties(), platformBuilder);
} catch (ParseException e) {
- throw new UserExecException(
+ String message =
String.format(
"Failed to parse remote_execution_properties from platform %s",
- spawn.getExecutionPlatform().label()),
- e);
+ spawn.getExecutionPlatform().label());
+ throw new UserExecException(
+ e, createFailureDetail(message, Code.INVALID_REMOTE_EXECUTION_PROPERTIES));
}
} else {
for (Map.Entry<String, String> property : defaultExecProperties.entrySet()) {
@@ -100,4 +104,11 @@
sortPlatformProperties(platformBuilder);
return platformBuilder.build();
}
+
+ private static FailureDetail createFailureDetail(String message, Code detailedCode) {
+ return FailureDetail.newBuilder()
+ .setMessage(message)
+ .setSpawn(FailureDetails.Spawn.newBuilder().setCode(detailedCode))
+ .build();
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
index fd3ada0..e79d8d3 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
@@ -56,6 +56,7 @@
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.server.FailureDetails.Execution.Code;
+import com.google.devtools.build.lib.server.FailureDetails.TestAction;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.LoggingUtil;
import com.google.devtools.build.lib.util.Pair;
@@ -1165,7 +1166,8 @@
testRunnerSpawn.finalizeTest(result, failedAttempts);
if (!keepGoing && testResult != TestAttemptResult.Result.PASSED) {
- throw new TestExecException("Test failed: aborting");
+ throw new TestExecException(
+ "Test failed: aborting", TestAction.Code.NO_KEEP_GOING_TEST_FAILURE);
}
return ActionContinuationOrResult.of(ActionResult.create(spawnResults));
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestStrategy.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestStrategy.java
index 6b49745..b9fc71b 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestStrategy.java
@@ -16,6 +16,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -39,6 +40,9 @@
import com.google.devtools.build.lib.exec.TestLogHelper;
import com.google.devtools.build.lib.exec.TestXmlOutputParser;
import com.google.devtools.build.lib.exec.TestXmlOutputParserException;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.TestAction;
+import com.google.devtools.build.lib.server.FailureDetails.TestAction.Code;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.io.OutErr;
@@ -141,7 +145,12 @@
try {
return expandedArgsFromAction(testAction);
} catch (CommandLineExpansionException e) {
- throw new UserExecException(e);
+ throw new UserExecException(
+ e,
+ FailureDetail.newBuilder()
+ .setMessage(Strings.nullToEmpty(e.getMessage()))
+ .setTestAction(TestAction.newBuilder().setCode(Code.COMMAND_LINE_EXPANSION_FAILURE))
+ .build());
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetProperties.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetProperties.java
index 214969c..15a422f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetProperties.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetProperties.java
@@ -28,6 +28,9 @@
import com.google.devtools.build.lib.packages.TestSize;
import com.google.devtools.build.lib.packages.TestTimeout;
import com.google.devtools.build.lib.packages.Type;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.TestAction;
+import com.google.devtools.build.lib.server.FailureDetails.TestAction.Code;
import java.util.List;
import java.util.Map;
@@ -145,10 +148,11 @@
String cpus = ExecutionRequirements.CPU.parseIfMatches(tag);
if (cpus != null) {
if (testResourcesFromTag != null) {
- throw new UserExecException(
+ String message =
String.format(
"%s has more than one '%s' tag, but duplicate tags aren't allowed",
- label, ExecutionRequirements.CPU.userFriendlyName()));
+ label, ExecutionRequirements.CPU.userFriendlyName());
+ throw new UserExecException(createFailureDetail(message, Code.DUPLICATE_CPU_TAGS));
}
testResourcesFromTag =
ResourceSet.create(
@@ -157,19 +161,27 @@
testResourcesFromSize.getLocalTestCount());
}
} catch (ValidationException e) {
- throw new UserExecException(
+ String message =
String.format(
"%s has a '%s' tag, but its value '%s' didn't pass validation: %s",
label,
ExecutionRequirements.CPU.userFriendlyName(),
e.getTagValue(),
- e.getMessage()));
+ e.getMessage());
+ throw new UserExecException(createFailureDetail(message, Code.INVALID_CPU_TAG));
}
}
return testResourcesFromTag != null ? testResourcesFromTag : testResourcesFromSize;
}
+ private static FailureDetail createFailureDetail(String message, Code detailedCode) {
+ return FailureDetail.newBuilder()
+ .setMessage(message)
+ .setTestAction(TestAction.newBuilder().setCode(detailedCode))
+ .build();
+ }
+
/**
* Returns a map of execution info. See {@link
* com.google.devtools.build.lib.actions.Spawn#getExecutionInfo}.
diff --git a/src/main/java/com/google/devtools/build/lib/dynamic/LegacyDynamicSpawnStrategy.java b/src/main/java/com/google/devtools/build/lib/dynamic/LegacyDynamicSpawnStrategy.java
index 69379a1..f22c120 100644
--- a/src/main/java/com/google/devtools/build/lib/dynamic/LegacyDynamicSpawnStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/dynamic/LegacyDynamicSpawnStrategy.java
@@ -16,6 +16,7 @@
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -243,9 +244,13 @@
}));
} catch (ExecutionException e) {
Throwables.propagateIfPossible(e.getCause(), InterruptedException.class);
- // DynamicExecutionCallable.callImpl only declares InterruptedException, so this should never
+ // DynamicExecutionCallable.call only declares InterruptedException, so this should never
// happen.
- exceptionDuringExecution = new UserExecException(e.getCause());
+ exceptionDuringExecution =
+ new UserExecException(
+ e.getCause(),
+ createFailureDetail(
+ Strings.nullToEmpty(e.getCause().getMessage()), Code.RUN_FAILURE));
} finally {
bothTasksFinished.arriveAndAwaitAdvance();
if (dynamicExecutionResult.execException() != null) {
@@ -273,7 +278,10 @@
String strategyName = winningStrategy.name().toLowerCase();
if (exceptionDuringExecution == null) {
throw new UserExecException(
- String.format("Could not move action logs from %s execution", strategyName), e);
+ e,
+ createFailureDetail(
+ String.format("Could not move action logs from %s execution", strategyName),
+ Code.ACTION_LOG_MOVE_FAILURE));
} else {
actionExecutionContext
.getEventHandler()
@@ -442,7 +450,11 @@
Throwables.throwIfInstanceOf(e, InterruptedException.class);
return DynamicExecutionResult.create(
strategyIdentifier,
- fileOutErr, e instanceof ExecException ? (ExecException) e : new UserExecException(e),
+ fileOutErr,
+ e instanceof ExecException
+ ? (ExecException) e
+ : new UserExecException(
+ e, createFailureDetail(Strings.nullToEmpty(e.getMessage()), Code.RUN_FAILURE)),
/*spawnResults=*/ ImmutableList.of());
} finally {
try {
diff --git a/src/main/java/com/google/devtools/build/lib/exec/BUILD b/src/main/java/com/google/devtools/build/lib/exec/BUILD
index 355bc3a..9251478 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/exec/BUILD
@@ -287,6 +287,7 @@
deps = [
":spawn_strategy_registry",
"//src/main/java/com/google/devtools/build/lib/actions",
+ "//src/main/protobuf:failure_details_java_proto",
"//third_party:guava",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnStrategyResolver.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnStrategyResolver.java
index 902206c..da5a9ca 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnStrategyResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnStrategyResolver.java
@@ -23,6 +23,9 @@
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.SpawnStrategy;
import com.google.devtools.build.lib.actions.UserExecException;
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Spawn.Code;
import java.util.List;
import java.util.stream.Collectors;
@@ -91,13 +94,18 @@
.collect(Collectors.toList());
if (strategies.isEmpty()) {
- throw new UserExecException(
+ String message =
String.format(
"No usable spawn strategy found for spawn with mnemonic %s. Your"
+ " --spawn_strategy, --genrule_strategy and/or --strategy flags are probably too"
+ " strict. Visit https://github.com/bazelbuild/bazel/issues/7480 for"
+ " migration advice",
- spawn.getMnemonic()));
+ spawn.getMnemonic());
+ throw new UserExecException(
+ FailureDetail.newBuilder()
+ .setMessage(message)
+ .setSpawn(FailureDetails.Spawn.newBuilder().setCode(Code.NO_USABLE_STRATEGY_FOUND))
+ .build());
}
return strategies;
diff --git a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
index c11ca37..e0d24d1 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
@@ -47,6 +47,7 @@
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.server.FailureDetails.Execution.Code;
+import com.google.devtools.build.lib.server.FailureDetails.TestAction;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileStatus;
@@ -95,7 +96,9 @@
public TestRunnerSpawn createTestRunnerSpawn(
TestRunnerAction action, ActionExecutionContext actionExecutionContext) throws ExecException {
if (action.getExecutionSettings().getInputManifest() == null) {
- throw new TestExecException("cannot run local tests with --nobuild_runfile_manifests");
+ throw new TestExecException(
+ "cannot run local tests with --nobuild_runfile_manifests",
+ TestAction.Code.LOCAL_TEST_PREREQ_UNMET);
}
Path execRoot = actionExecutionContext.getExecRoot();
ArtifactPathResolver pathResolver = actionExecutionContext.getPathResolver();
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
index 92d1e32..afc1562 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
@@ -356,12 +356,9 @@
ExecException execEx =
new EnvironmentalExecException(
ioEx,
- FailureDetail.newBuilder()
- .setMessage("Failed to delete output files after incomplete download")
- .setRemoteExecution(
- RemoteExecution.newBuilder()
- .setCode(Code.INCOMPLETE_OUTPUT_DOWNLOAD_CLEANUP_FAILURE))
- .build());
+ createFailureDetail(
+ "Failed to delete output files after incomplete download",
+ Code.INCOMPLETE_OUTPUT_DOWNLOAD_CLEANUP_FAILURE));
execEx.addSuppressed(e);
throw execEx;
}
@@ -968,12 +965,13 @@
private void illegalOutput(Path what) throws ExecException {
String kind = what.isSymbolicLink() ? "symbolic link" : "special file";
- throw new UserExecException(
+ String message =
String.format(
"Output %s is a %s. Only regular files and directories may be "
+ "uploaded to a remote cache. "
+ "Change the file type or use --remote_allow_symlink_upload.",
- what.relativeTo(execRoot), kind));
+ what.relativeTo(execRoot), kind);
+ throw new UserExecException(createFailureDetail(message, Code.ILLEGAL_OUTPUT));
}
}
@@ -983,6 +981,13 @@
cacheProtocol.close();
}
+ private static FailureDetail createFailureDetail(String message, Code detailedCode) {
+ return FailureDetail.newBuilder()
+ .setMessage(message)
+ .setRemoteExecution(RemoteExecution.newBuilder().setCode(detailedCode))
+ .build();
+ }
+
/**
* Creates an {@link OutputStream} that isn't actually opened until the first data is written.
* This is useful to only have as many open file descriptors as necessary at a time to avoid
diff --git a/src/main/java/com/google/devtools/build/lib/remote/options/BUILD b/src/main/java/com/google/devtools/build/lib/remote/options/BUILD
index 5a714d7..1e7be66 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/options/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/remote/options/BUILD
@@ -18,6 +18,7 @@
"//src/main/java/com/google/devtools/build/lib/util",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
"//src/main/java/com/google/devtools/common/options",
+ "//src/main/protobuf:failure_details_java_proto",
"//third_party:guava",
"//third_party/protobuf:protobuf_java",
"@remoteapis//:build_bazel_remote_execution_v2_remote_execution_java_proto",
diff --git a/src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java b/src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java
index d31bfa7..0c400d1 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java
@@ -19,6 +19,9 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSortedMap;
import com.google.devtools.build.lib.actions.UserExecException;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.RemoteExecution;
+import com.google.devtools.build.lib.server.FailureDetails.RemoteExecution.Code;
import com.google.devtools.build.lib.util.OptionsUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.common.options.Converters;
@@ -454,9 +457,11 @@
if (hasExecProperties && hasPlatformProperties) {
throw new UserExecException(
- "Setting both --remote_default_platform_properties and "
- + "--remote_default_exec_properties is not allowed. Prefer setting "
- + "--remote_default_exec_properties.");
+ createFailureDetail(
+ "Setting both --remote_default_platform_properties and "
+ + "--remote_default_exec_properties is not allowed. Prefer setting "
+ + "--remote_default_exec_properties.",
+ Code.INVALID_EXEC_AND_PLATFORM_PROPERTIES));
}
if (hasExecProperties) {
@@ -470,10 +475,11 @@
TextFormat.getParser().merge(remoteDefaultPlatformProperties, builder);
platform = builder.build();
} catch (ParseException e) {
- throw new UserExecException(
+ String message =
"Failed to parse --remote_default_platform_properties "
- + remoteDefaultPlatformProperties,
- e);
+ + remoteDefaultPlatformProperties;
+ throw new UserExecException(
+ e, createFailureDetail(message, Code.REMOTE_DEFAULT_PLATFORM_PROPERTIES_PARSE_FAILURE));
}
ImmutableSortedMap.Builder<String, String> builder = ImmutableSortedMap.naturalOrder();
@@ -485,4 +491,11 @@
return ImmutableSortedMap.of();
}
+
+ private static FailureDetail createFailureDetail(String message, Code detailedCode) {
+ return FailureDetail.newBuilder()
+ .setMessage(message)
+ .setRemoteExecution(RemoteExecution.newBuilder().setCode(detailedCode))
+ .build();
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanning.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanning.java
index 37078d0..4d53e00 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanning.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanning.java
@@ -143,8 +143,10 @@
continue;
}
throw new UserExecException(
- "illegal absolute path to include file: "
- + actionExecutionContext.getInputPath(included));
+ createFailureDetail(
+ "illegal absolute path to include file: "
+ + actionExecutionContext.getInputPath(included),
+ Code.ILLEGAL_ABSOLUTE_PATH));
}
if (included.hasParent() && included.getParent().isTreeArtifact()) {
// Note that this means every file in the TreeArtifact becomes an input to the action, and
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
index 4a667a4..886f82c 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
@@ -36,6 +36,9 @@
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.profiler.SilentCloseable;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Sandbox;
+import com.google.devtools.build.lib.server.FailureDetails.Sandbox.Code;
import com.google.devtools.build.lib.shell.ExecutionStatistics;
import com.google.devtools.build.lib.shell.Subprocess;
import com.google.devtools.build.lib.shell.SubprocessBuilder;
@@ -86,7 +89,10 @@
SandboxedSpawn sandbox = prepareSpawn(spawn, context);
return runSpawn(spawn, sandbox, context);
} catch (IOException e) {
- throw new UserExecException("I/O exception during sandboxed execution", e);
+ FailureDetail failureDetail =
+ createFailureDetail(
+ "I/O exception during sandboxed execution", Code.EXECUTION_IO_EXCEPTION);
+ throw new UserExecException(e, failureDetail);
}
}
@@ -347,4 +353,11 @@
}
}
}
+
+ static FailureDetail createFailureDetail(String message, Code detailedCode) {
+ return FailureDetail.newBuilder()
+ .setMessage(message)
+ .setSandbox(Sandbox.newBuilder().setCode(detailedCode))
+ .build();
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java
index 39da19d..f319b53 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java
@@ -37,6 +37,7 @@
import com.google.devtools.build.lib.runtime.ProcessWrapper;
import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs;
import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs;
+import com.google.devtools.build.lib.server.FailureDetails.Sandbox.Code;
import com.google.devtools.build.lib.shell.Command;
import com.google.devtools.build.lib.shell.CommandException;
import com.google.devtools.build.lib.util.OS;
@@ -232,16 +233,21 @@
String baseImageName = dockerContainerFromSpawn(spawn).orElse(this.defaultImage);
if (baseImageName.isEmpty()) {
throw new UserExecException(
- String.format(
- "Cannot execute %s mnemonic with Docker, because no "
- + "image could be found in the remote_execution_properties of the platform and "
- + "no default image was set via --experimental_docker_image",
- spawn.getMnemonic()));
+ createFailureDetail(
+ String.format(
+ "Cannot execute %s mnemonic with Docker, because no image could be found in the"
+ + " remote_execution_properties of the platform and no default image was set"
+ + " via --experimental_docker_image",
+ spawn.getMnemonic()),
+ Code.NO_DOCKER_IMAGE));
}
String customizedImageName = getOrCreateCustomizedImage(baseImageName);
if (customizedImageName == null) {
- throw new UserExecException("Could not prepare Docker image for execution");
+ throw new UserExecException(
+ createFailureDetail(
+ "Could not prepare Docker image for execution",
+ Code.DOCKER_IMAGE_PREPARATION_FAILURE));
}
DockerCommandLineBuilder cmdLine = new DockerCommandLineBuilder();
@@ -369,8 +375,8 @@
try {
cmd.executeAsync(stdIn, stdOut, stdErr, Command.KILL_SUBPROCESS_ON_INTERRUPT).get();
} catch (CommandException e) {
- throw new UserExecException(
- "Running command " + cmd.toDebugString() + " failed: " + stdErr, e);
+ String message = String.format("Running command %s failed: %s", cmd.toDebugString(), stdErr);
+ throw new UserExecException(e, createFailureDetail(message, Code.DOCKER_COMMAND_FAILURE));
}
return stdOut.toString().trim();
}
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
index 3332796..114063a 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
@@ -33,6 +33,7 @@
import com.google.devtools.build.lib.runtime.CommandEnvironment;
import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs;
import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs;
+import com.google.devtools.build.lib.server.FailureDetails.Sandbox.Code;
import com.google.devtools.build.lib.shell.Command;
import com.google.devtools.build.lib.shell.CommandException;
import com.google.devtools.build.lib.util.OS;
@@ -277,7 +278,9 @@
bindMounts.put(mountTarget, mountSource);
} catch (IllegalArgumentException e) {
throw new UserExecException(
- String.format("Error occurred when analyzing bind mount pairs. %s", e.getMessage()));
+ createFailureDetail(
+ String.format("Error occurred when analyzing bind mount pairs. %s", e.getMessage()),
+ Code.BIND_MOUNT_ANALYSIS_FAILURE));
}
}
for (Path inaccessiblePath : getInaccessiblePaths()) {
@@ -305,7 +308,10 @@
final Path target = bindMount.getKey();
// Mount source should exist in the file system
if (!source.exists()) {
- throw new UserExecException(String.format("Mount source '%s' does not exist.", source));
+ throw new UserExecException(
+ createFailureDetail(
+ String.format("Mount source '%s' does not exist.", source),
+ Code.MOUNT_SOURCE_DOES_NOT_EXIST));
}
// If target exists, but is not of the same type as the source, then we cannot mount it.
if (target.exists()) {
@@ -316,18 +322,22 @@
if (!(areBothDirectories || areBothFiles)) {
// Source and target are not of the same type; we cannot mount it.
throw new UserExecException(
- String.format(
- "Mount target '%s' is not of the same type as mount source '%s'.",
- target, source));
+ createFailureDetail(
+ String.format(
+ "Mount target '%s' is not of the same type as mount source '%s'.",
+ target, source),
+ Code.MOUNT_SOURCE_TARGET_TYPE_MISMATCH));
}
} else {
// Mount target should exist in the file system
throw new UserExecException(
- String.format(
- "Mount target '%s' does not exist. Bazel only supports bind mounting on top of "
- + "existing files/directories. Please create an empty file or directory at "
- + "the mount target path according to the type of mount source.",
- target));
+ createFailureDetail(
+ String.format(
+ "Mount target '%s' does not exist. Bazel only supports bind mounting on top of "
+ + "existing files/directories. Please create an empty file or directory at "
+ + "the mount target path according to the type of mount source.",
+ target),
+ Code.MOUNT_TARGET_DOES_NOT_EXIST));
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/worker/BUILD b/src/main/java/com/google/devtools/build/lib/worker/BUILD
index 27f6381..0d3a13f 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/worker/BUILD
@@ -30,6 +30,7 @@
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
"//src/main/java/com/google/devtools/common/options",
+ "//src/main/protobuf:failure_details_java_proto",
"//src/main/protobuf:worker_protocol_java_proto",
"//third_party:apache_commons_pool2",
"//third_party:auto_value",
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerMultiplexerManager.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerMultiplexerManager.java
index 028f031..d3582d4 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerMultiplexerManager.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerMultiplexerManager.java
@@ -15,11 +15,14 @@
package com.google.devtools.build.lib.worker;
import com.google.devtools.build.lib.actions.UserExecException;
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Worker.Code;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
-/** A manager to instantiate and distroy multiplexers. */
+/** A manager to instantiate and destroy multiplexers. */
public class WorkerMultiplexerManager {
/**
* There should only be one WorkerMultiplexer corresponding to workers with the same mnemonic. If
@@ -67,12 +70,8 @@
multiplexerInstance.remove(workerHash);
}
} catch (Exception e) {
- throw new UserExecException(
- ErrorMessage.builder()
- .message("NullPointerException while accessing non-existent multiplexer instance.")
- .exception(e)
- .build()
- .toString());
+ String message = "NullPointerException while accessing non-existent multiplexer instance.";
+ throw createUserExecException(e, message, Code.MULTIPLEXER_INSTANCE_REMOVAL_FAILURE);
} finally {
semMultiplexer.release();
}
@@ -82,12 +81,8 @@
try {
return multiplexerInstance.get(workerHash).getWorkerMultiplexer();
} catch (NullPointerException e) {
- throw new UserExecException(
- ErrorMessage.builder()
- .message("NullPointerException while accessing non-existent multiplexer instance.")
- .exception(e)
- .build()
- .toString());
+ String message = "NullPointerException while accessing non-existent multiplexer instance.";
+ throw createUserExecException(e, message, Code.MULTIPLEXER_DOES_NOT_EXIST);
}
}
@@ -95,12 +90,8 @@
try {
return multiplexerInstance.get(workerHash).getRefCount();
} catch (NullPointerException e) {
- throw new UserExecException(
- ErrorMessage.builder()
- .message("NullPointerException while accessing non-existent multiplexer instance.")
- .exception(e)
- .build()
- .toString());
+ String message = "NullPointerException while accessing non-existent multiplexer instance.";
+ throw createUserExecException(e, message, Code.MULTIPLEXER_DOES_NOT_EXIST);
}
}
@@ -108,6 +99,15 @@
return multiplexerInstance.keySet().size();
}
+ private static UserExecException createUserExecException(
+ Exception e, String message, Code detailedCode) {
+ return new UserExecException(
+ FailureDetail.newBuilder()
+ .setMessage(ErrorMessage.builder().message(message).exception(e).build().toString())
+ .setWorker(FailureDetails.Worker.newBuilder().setCode(detailedCode))
+ .build());
+ }
+
/** Contains the WorkerMultiplexer instance and reference count */
static class InstanceInfo {
private WorkerMultiplexer workerMultiplexer;
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
index f4f8631..ee2f6ea 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
@@ -44,6 +44,9 @@
import com.google.devtools.build.lib.sandbox.SandboxHelpers;
import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs;
import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs;
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.Worker.Code;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -149,8 +152,9 @@
private SpawnResult actuallyExec(Spawn spawn, SpawnExecutionContext context)
throws ExecException, IOException, InterruptedException {
if (spawn.getToolFiles().isEmpty()) {
- throw new UserExecException(
- String.format(ERROR_MESSAGE_PREFIX + REASON_NO_TOOLS, spawn.getMnemonic()));
+ throw createUserExecException(
+ String.format(ERROR_MESSAGE_PREFIX + REASON_NO_TOOLS, spawn.getMnemonic()),
+ Code.NO_TOOLS);
}
runfilesTreeUpdater.updateRunfilesDirectory(
@@ -239,8 +243,9 @@
}
if (flagFiles.isEmpty()) {
- throw new UserExecException(
- String.format(ERROR_MESSAGE_PREFIX + REASON_NO_FLAGFILE, spawn.getMnemonic()));
+ throw createUserExecException(
+ String.format(ERROR_MESSAGE_PREFIX + REASON_NO_FLAGFILE, spawn.getMnemonic()),
+ Code.NO_FLAGFILE);
}
return workerArgs
@@ -333,12 +338,8 @@
try {
inputFiles.materializeVirtualInputs(execRoot);
} catch (IOException e) {
- throw new UserExecException(
- ErrorMessage.builder()
- .message("IOException while materializing virtual inputs:")
- .exception(e)
- .build()
- .toString());
+ String message = "IOException while materializing virtual inputs:";
+ throw createUserExecException(e, message, Code.VIRTUAL_INPUT_MATERIALIZATION_FAILURE);
}
try {
@@ -346,23 +347,15 @@
request =
createWorkRequest(spawn, context, flagFiles, inputFileCache, worker.getWorkerId());
} catch (IOException e) {
- throw new UserExecException(
- ErrorMessage.builder()
- .message("IOException while borrowing a worker from the pool:")
- .exception(e)
- .build()
- .toString());
+ String message = "IOException while borrowing a worker from the pool:";
+ throw createUserExecException(e, message, Code.BORROW_FAILURE);
}
try {
context.prefetchInputs();
} catch (IOException e) {
- throw new UserExecException(
- ErrorMessage.builder()
- .message("IOException while prefetching for worker:")
- .exception(e)
- .build()
- .toString());
+ String message = "IOException while prefetching for worker:";
+ throw createUserExecException(e, message, Code.PREFETCH_FAILURE);
}
try (ResourceHandle handle =
@@ -371,19 +364,20 @@
try {
worker.prepareExecution(inputFiles, outputs, key.getWorkerFilesWithHashes().keySet());
} catch (IOException e) {
- throw new UserExecException(
+ String message =
ErrorMessage.builder()
.message("IOException while preparing the execution environment of a worker:")
.logFile(worker.getLogFile())
.exception(e)
.build()
- .toString());
+ .toString();
+ throw createUserExecException(message, Code.PREPARE_FAILURE);
}
try {
worker.putRequest(request);
} catch (IOException e) {
- throw new UserExecException(
+ String message =
ErrorMessage.builder()
.message(
"Worker process quit or closed its stdin stream when we tried to send a"
@@ -391,7 +385,8 @@
.logFile(worker.getLogFile())
.exception(e)
.build()
- .toString());
+ .toString();
+ throw createUserExecException(message, Code.REQUEST_FAILURE);
}
try {
@@ -401,7 +396,7 @@
// to stdout - it's probably a stack trace or some kind of error message that will help
// the user figure out why the compiler is failing.
String recordingStreamMessage = worker.getRecordingStreamMessage();
- throw new UserExecException(
+ String message =
ErrorMessage.builder()
.message(
"Worker process returned an unparseable WorkResponse!\n\n"
@@ -411,32 +406,35 @@
.logText(recordingStreamMessage)
.exception(e)
.build()
- .toString());
+ .toString();
+ throw createUserExecException(message, Code.PARSE_RESPONSE_FAILURE);
}
}
if (response == null) {
- throw new UserExecException(
+ String message =
ErrorMessage.builder()
.message("Worker process did not return a WorkResponse:")
.logFile(worker.getLogFile())
.logSizeLimit(4096)
.build()
- .toString());
+ .toString();
+ throw createUserExecException(message, Code.NO_RESPONSE);
}
try {
context.lockOutputFiles();
worker.finishExecution(execRoot);
} catch (IOException e) {
- throw new UserExecException(
+ String message =
ErrorMessage.builder()
.message("IOException while finishing worker execution:")
.exception(e)
.build()
- .toString());
+ .toString();
+ throw createUserExecException(message, Code.FINISH_FAILURE);
}
- } catch (ExecException e) {
+ } catch (UserExecException e) {
if (worker != null) {
try {
workers.invalidateObject(key, worker);
@@ -455,4 +453,18 @@
return response;
}
+
+ private static UserExecException createUserExecException(
+ IOException e, String message, Code detailedCode) {
+ return createUserExecException(
+ ErrorMessage.builder().message(message).exception(e).build().toString(), detailedCode);
+ }
+
+ private static UserExecException createUserExecException(String message, Code detailedCode) {
+ return new UserExecException(
+ FailureDetail.newBuilder()
+ .setMessage(message)
+ .setWorker(FailureDetails.Worker.newBuilder().setCode(detailedCode))
+ .build());
+ }
}
diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto
index 0530743..6147481 100644
--- a/src/main/protobuf/failure_details.proto
+++ b/src/main/protobuf/failure_details.proto
@@ -139,6 +139,8 @@
SymlinkAction symlink_action = 167;
CppLink cpp_link = 168;
LtoAction lto_action = 169;
+ TestAction test_action = 172;
+ Worker worker = 173;
}
reserved 102; // For internal use
@@ -199,6 +201,9 @@
REMOTE_CACHE_FAILED = 6 [(metadata) = { exit_code: 34 }];
COMMAND_LINE_EXPANSION_FAILURE = 7 [(metadata) = { exit_code: 1 }];
EXEC_IO_EXCEPTION = 8 [(metadata) = { exit_code: 36 }];
+ INVALID_TIMEOUT = 9 [(metadata) = { exit_code: 1 }];
+ INVALID_REMOTE_EXECUTION_PROPERTIES = 10 [(metadata) = { exit_code: 1 }];
+ NO_USABLE_STRATEGY_FOUND = 11 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
@@ -345,6 +350,10 @@
[(metadata) = { exit_code: 2 }];
INCOMPLETE_OUTPUT_DOWNLOAD_CLEANUP_FAILURE = 13
[(metadata) = { exit_code: 36 }];
+ REMOTE_DEFAULT_PLATFORM_PROPERTIES_PARSE_FAILURE = 14
+ [(metadata) = { exit_code: 1 }];
+ ILLEGAL_OUTPUT = 15 [(metadata) = { exit_code: 1 }];
+ INVALID_EXEC_AND_PLATFORM_PROPERTIES = 16 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
@@ -627,6 +636,14 @@
enum Code {
SANDBOX_FAILURE_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
INITIALIZATION_FAILURE = 1 [(metadata) = { exit_code: 36 }];
+ EXECUTION_IO_EXCEPTION = 2 [(metadata) = { exit_code: 1 }];
+ DOCKER_COMMAND_FAILURE = 3 [(metadata) = { exit_code: 1 }];
+ NO_DOCKER_IMAGE = 4 [(metadata) = { exit_code: 1 }];
+ DOCKER_IMAGE_PREPARATION_FAILURE = 5 [(metadata) = { exit_code: 1 }];
+ BIND_MOUNT_ANALYSIS_FAILURE = 6 [(metadata) = { exit_code: 1 }];
+ MOUNT_SOURCE_DOES_NOT_EXIST = 7 [(metadata) = { exit_code: 1 }];
+ MOUNT_SOURCE_TARGET_TYPE_MISMATCH = 8 [(metadata) = { exit_code: 1 }];
+ MOUNT_TARGET_DOES_NOT_EXIST = 9 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
@@ -639,6 +656,7 @@
SCANNING_IO_EXCEPTION = 2 [(metadata) = { exit_code: 36 }];
INCLUDE_HINTS_FILE_NOT_IN_PACKAGE = 3 [(metadata) = { exit_code: 36 }];
INCLUDE_HINTS_READ_FAILURE = 4 [(metadata) = { exit_code: 36 }];
+ ILLEGAL_ABSOLUTE_PATH = 5 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
@@ -882,6 +900,7 @@
enum Code {
STARLARK_ACTION_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
UNUSED_INPUT_LIST_READ_FAILURE = 1 [(metadata) = { exit_code: 36 }];
+ UNUSED_INPUT_LIST_FILE_NOT_FOUND = 2 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
@@ -901,6 +920,8 @@
enum Code {
DYNAMIC_EXECUTION_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
XCODE_RELATED_PREREQ_UNMET = 1 [(metadata) = { exit_code: 36 }];
+ ACTION_LOG_MOVE_FAILURE = 2 [(metadata) = { exit_code: 1 }];
+ RUN_FAILURE = 3 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
@@ -948,3 +969,36 @@
Code code = 1;
}
+
+message TestAction {
+ enum Code {
+ TEST_ACTION_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
+ NO_KEEP_GOING_TEST_FAILURE = 1 [(metadata) = { exit_code: 1 }];
+ LOCAL_TEST_PREREQ_UNMET = 2 [(metadata) = { exit_code: 1 }];
+ COMMAND_LINE_EXPANSION_FAILURE = 3 [(metadata) = { exit_code: 1 }];
+ DUPLICATE_CPU_TAGS = 4 [(metadata) = { exit_code: 1 }];
+ INVALID_CPU_TAG = 5 [(metadata) = { exit_code: 1 }];
+ }
+
+ Code code = 1;
+}
+
+message Worker {
+ enum Code {
+ WORKER_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
+ MULTIPLEXER_INSTANCE_REMOVAL_FAILURE = 1 [(metadata) = { exit_code: 1 }];
+ MULTIPLEXER_DOES_NOT_EXIST = 2 [(metadata) = { exit_code: 1 }];
+ NO_TOOLS = 3 [(metadata) = { exit_code: 1 }];
+ NO_FLAGFILE = 4 [(metadata) = { exit_code: 1 }];
+ VIRTUAL_INPUT_MATERIALIZATION_FAILURE = 5 [(metadata) = { exit_code: 1 }];
+ BORROW_FAILURE = 6 [(metadata) = { exit_code: 1 }];
+ PREFETCH_FAILURE = 7 [(metadata) = { exit_code: 1 }];
+ PREPARE_FAILURE = 8 [(metadata) = { exit_code: 1 }];
+ REQUEST_FAILURE = 9 [(metadata) = { exit_code: 1 }];
+ PARSE_RESPONSE_FAILURE = 10 [(metadata) = { exit_code: 1 }];
+ NO_RESPONSE = 11 [(metadata) = { exit_code: 1 }];
+ FINISH_FAILURE = 12 [(metadata) = { exit_code: 1 }];
+ }
+
+ Code code = 1;
+}
\ No newline at end of file
diff --git a/src/test/java/com/google/devtools/build/lib/dynamic/BUILD b/src/test/java/com/google/devtools/build/lib/dynamic/BUILD
index 3338aa1..f02079a 100644
--- a/src/test/java/com/google/devtools/build/lib/dynamic/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/dynamic/BUILD
@@ -28,6 +28,7 @@
"//src/main/java/com/google/devtools/build/lib/util/io",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/common/options",
+ "//src/main/protobuf:failure_details_java_proto",
"//src/test/java/com/google/devtools/build/lib/actions/util",
"//src/test/java/com/google/devtools/build/lib/testutil:TestThread",
"//src/test/java/com/google/devtools/build/lib/testutil:TestUtils",
diff --git a/src/test/java/com/google/devtools/build/lib/dynamic/DynamicSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/dynamic/DynamicSpawnStrategyTest.java
index 244d202..d3901c0 100644
--- a/src/test/java/com/google/devtools/build/lib/dynamic/DynamicSpawnStrategyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/dynamic/DynamicSpawnStrategyTest.java
@@ -49,6 +49,9 @@
import com.google.devtools.build.lib.exec.ExecutionOptions;
import com.google.devtools.build.lib.exec.ModuleActionContextRegistry;
import com.google.devtools.build.lib.exec.SpawnStrategyRegistry;
+import com.google.devtools.build.lib.server.FailureDetails.DynamicExecution;
+import com.google.devtools.build.lib.server.FailureDetails.DynamicExecution.Code;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.lib.testutil.TestThread;
import com.google.devtools.build.lib.testutil.TestUtils;
import com.google.devtools.build.lib.util.AbruptExitException;
@@ -152,7 +155,7 @@
} catch (IOException e) {
throw new IllegalStateException(e);
}
- throw new UserExecException(name + " failed to execute the Spawn");
+ throw new UserExecException(createFailureDetail(name + " failed to execute the Spawn"));
}
@Override
@@ -989,7 +992,7 @@
Spawn spawn = newDynamicSpawn();
Exception e = assertThrows(expectedException.getClass(), () -> strategyAndContext.exec(spawn));
- assertThat(e).hasMessageThat().matches(expectedException.getMessage());
+ assertThat(e).hasMessageThat().contains(expectedException.getMessage());
Spawn executedSpawn = localStrategy.getExecutedSpawn();
executedSpawn = executedSpawn == null ? remoteStrategy.getExecutedSpawn() : executedSpawn;
@@ -1013,7 +1016,9 @@
};
assertThatStrategyPropagatesException(
- localExec, remoteExec, legacyBehavior ? new UserExecException(e) : e);
+ localExec,
+ remoteExec,
+ legacyBehavior ? new UserExecException(e, createFailureDetail("")) : e);
}
@Test
@@ -1031,7 +1036,16 @@
};
assertThatStrategyPropagatesException(
- localExec, remoteExec, legacyBehavior ? new UserExecException(e) : e);
+ localExec,
+ remoteExec,
+ legacyBehavior ? new UserExecException(e, createFailureDetail("")) : e);
+ }
+
+ private static FailureDetail createFailureDetail(String message) {
+ return FailureDetail.newBuilder()
+ .setMessage(message)
+ .setDynamicExecution(DynamicExecution.newBuilder().setCode(Code.RUN_FAILURE))
+ .build();
}
@AutoValue