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; } }
diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto index a7b20dc..aad05b5 100644 --- a/src/main/protobuf/failure_details.proto +++ b/src/main/protobuf/failure_details.proto
@@ -141,7 +141,7 @@ reserved 138; // For internal use reserved 143; // For internal use reserved 149; // For internal use - reserved 155 to 156; // For internal use + reserved 155 to 157; // For internal use } message Interrupted {