Refactor filesystem traversal failures
Only DanglingSymlinkException remains a subtype. It's the only one that
meaningfully appears in catch clauses and method throws lists.
This CL also equips SkyframeAwareAction exceptions with
DetailedExitCodes.
RELNOTES: None.
PiperOrigin-RevId: 315324370
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 679cdf7..a3c458e 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
@@ -48,6 +48,18 @@
}
public ActionExecutionException(
+ Throwable cause,
+ ActionAnalysisMetadata action,
+ boolean catastrophe,
+ DetailedExitCode detailedExitCode) {
+ super(cause.getMessage(), cause);
+ this.action = action;
+ this.detailedExitCode = detailedExitCode;
+ this.rootCauses = rootCausesFromAction(action, detailedExitCode);
+ this.catastrophe = catastrophe;
+ }
+
+ public ActionExecutionException(
String message, Throwable cause, ActionAnalysisMetadata action, boolean catastrophe) {
super(message, cause);
this.action = action;
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 c4e2eb0..b25b364 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
@@ -72,6 +72,7 @@
import com.google.devtools.build.lib.skyframe.ArtifactNestedSetFunction.ArtifactNestedSetEvalException;
import com.google.devtools.build.lib.skyframe.SkyframeActionExecutor.ActionPostprocessing;
import com.google.devtools.build.lib.syntax.StarlarkSemantics;
+import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
@@ -1111,7 +1112,8 @@
try {
return skyframeAwareAction.processSkyframeValues(keys, values, env.valuesMissing());
} catch (SkyframeAwareAction.ExceptionBase e) {
- throw new ActionExecutionException(e, action, false);
+ throw new ActionExecutionException(
+ e, action, false, DetailedExitCode.of(e.getFailureDetail()));
}
}
return null;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index e1d21ef..77e0c68 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -2250,6 +2250,7 @@
deps = [
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
+ "//src/main/protobuf:failure_details_java_proto",
"//third_party:guava",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
index c611700..8b27387 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
@@ -73,34 +73,39 @@
private static final FileInfo NON_EXISTENT_FILE_INFO =
new FileInfo(FileType.NONEXISTENT, NON_EXISTENT_HAS_DIGEST, null, null);
- /** Base class for exceptions that {@link RecursiveFilesystemTraversalFunctionException} wraps. */
- public abstract static class RecursiveFilesystemTraversalException extends Exception {
- protected RecursiveFilesystemTraversalException(String message) {
- super(message);
- }
- }
+ /** The exception that {@link RecursiveFilesystemTraversalFunctionException} wraps. */
+ public static class RecursiveFilesystemTraversalException extends Exception {
- /** Thrown when a generated directory's root-relative path conflicts with a package's path. */
- public static final class GeneratedPathConflictException extends
- RecursiveFilesystemTraversalException {
- GeneratedPathConflictException(TraversalRequest traversal) {
- super(
- String.format(
- "Generated directory %s conflicts with package under the same path. "
- + "Additional info: %s",
- traversal.root.asRootedPath().getRootRelativePath().getPathString(),
- traversal.errorInfo != null ? traversal.errorInfo : traversal.toString()));
- }
- }
+ /**
+ * Categories of errors that prevent normal {@link RecursiveFilesystemTraversalFunction}
+ * evaluation.
+ */
+ public enum Type {
+ /**
+ * The traversal encountered a subdirectory with a BUILD file but is not allowed to recurse
+ * into it. See {@code PackageBoundaryMode#REPORT_ERROR}.
+ */
+ CANNOT_CROSS_PACKAGE_BOUNDARY,
- /**
- * Thrown when the traversal encounters a subdirectory with a BUILD file but is not allowed to
- * recurse into it. See {@code PackageBoundaryMode#REPORT_ERROR}.
- */
- public static final class CannotCrossPackageBoundaryException extends
- RecursiveFilesystemTraversalException {
- CannotCrossPackageBoundaryException(String message) {
+ /** A dangling symlink was dereferenced. */
+ DANGLING_SYMLINK,
+
+ /** A file operation failed. */
+ FILE_OPERATION_FAILURE,
+
+ /** A generated directory's root-relative path conflicts with a package's path. */
+ GENERATED_PATH_CONFLICT,
+ }
+
+ private final Type type;
+
+ RecursiveFilesystemTraversalException(String message, Type type) {
super(message);
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
}
}
@@ -111,29 +116,14 @@
* and it's not easy to merge the two because of the dependency structure. The other one will
* probably be removed along with the rest of the legacy Fileset code.
*/
- public static final class DanglingSymlinkException extends RecursiveFilesystemTraversalException {
- public final String path;
- public final String unresolvedLink;
-
- public DanglingSymlinkException(String path, String unresolvedLink) {
+ static final class DanglingSymlinkException extends RecursiveFilesystemTraversalException {
+ DanglingSymlinkException(String path, String unresolvedLink) {
super(
String.format(
- "Found dangling symlink: %s, unresolved path: \"%s\"", path, unresolvedLink));
+ "Found dangling symlink: %s, unresolved path: \"%s\"", path, unresolvedLink),
+ Type.DANGLING_SYMLINK);
Preconditions.checkArgument(path != null && !path.isEmpty());
Preconditions.checkArgument(unresolvedLink != null && !unresolvedLink.isEmpty());
- this.path = path;
- this.unresolvedLink = unresolvedLink;
- }
-
- public String getPath() {
- return path;
- }
- }
-
- /** Thrown when we encounter errors from underlying File operations */
- public static final class FileOperationException extends RecursiveFilesystemTraversalException {
- public FileOperationException(String message) {
- super(message);
}
}
@@ -189,8 +179,7 @@
if (pkgLookupResult.isConflicting()) {
// The traversal was requested for an output directory whose root-relative path conflicts
// with a source package. We can't handle that, bail out.
- throw new RecursiveFilesystemTraversalFunctionException(
- new GeneratedPathConflictException(traversal));
+ throw createGeneratedPathConflictException(traversal);
} else if (pkgLookupResult.isPackage() && !traversal.skipTestingForSubpackage) {
// The traversal was requested for a directory that defines a package.
String msg =
@@ -208,7 +197,8 @@
case REPORT_ERROR:
// We cannot traverse the subpackage and should complain loudly (display an error).
throw new RecursiveFilesystemTraversalFunctionException(
- new CannotCrossPackageBoundaryException(msg));
+ new RecursiveFilesystemTraversalException(
+ msg, RecursiveFilesystemTraversalException.Type.CANNOT_CROSS_PACKAGE_BOUNDARY));
default:
throw new IllegalStateException(traversal.toString());
}
@@ -221,13 +211,28 @@
rootInfo,
traverseChildren(env, dependentKeys, /*inline=*/ traversal.isRootGenerated));
} catch (IOException e) {
+ String message = "Error while traversing fileset: " + e.getMessage();
throw new RecursiveFilesystemTraversalFunctionException(
- new FileOperationException("Error while traversing fileset: " + e.getMessage()));
+ new RecursiveFilesystemTraversalException(
+ message, RecursiveFilesystemTraversalException.Type.FILE_OPERATION_FAILURE));
} catch (MissingDepException e) {
return null;
}
}
+ private static RecursiveFilesystemTraversalFunctionException createGeneratedPathConflictException(
+ TraversalRequest traversal) {
+ String message =
+ String.format(
+ "Generated directory %s conflicts with package under the same path. "
+ + "Additional info: %s",
+ traversal.root.asRootedPath().getRootRelativePath().getPathString(),
+ traversal.errorInfo != null ? traversal.errorInfo : traversal.toString());
+ return new RecursiveFilesystemTraversalFunctionException(
+ new RecursiveFilesystemTraversalException(
+ message, RecursiveFilesystemTraversalException.Type.GENERATED_PATH_CONFLICT));
+ }
+
@Override
public String extractTag(SkyKey skyKey) {
return null;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAwareAction.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAwareAction.java
index 572abb3..13b88b4 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAwareAction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAwareAction.java
@@ -15,6 +15,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.graph.ImmutableGraph;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.skyframe.SkyFunction.Environment;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.ValueOrException;
@@ -46,12 +47,15 @@
/** Wrapper and/or base class for exceptions raised in {@link #processSkyframeValues}. */
class ExceptionBase extends Exception {
- public ExceptionBase(String message) {
- super(message);
+ private final FailureDetail failureDetail;
+
+ public ExceptionBase(Throwable cause, FailureDetail failureDetail) {
+ super(cause.getMessage(), cause);
+ this.failureDetail = failureDetail;
}
- public ExceptionBase(Throwable cause) {
- super(cause.getMessage(), cause);
+ public FailureDetail getFailureDetail() {
+ return failureDetail;
}
}