Reduce filesystem stats in `SymlinkAction`.
`SymlinkAction` checks whether the symlink target is an executable file. If the filesystem implementation supports returning permission bits in the stat (supported in the common case, i.e. `UnixFileSystem`), then a single stat suffices for both `isFile()` and `isExecutable()`.
Moreover, for a source artifact, we can use the build's `SyscallCache` since it was already statted by `FileStateFunction`.
PiperOrigin-RevId: 690819972
Change-Id: Ibd1f8d7aa9c3bd185a965f5a2ec015513ef315be
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 76d8d05..5537966 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -1607,6 +1607,7 @@
"//src/main/java/com/google/devtools/build/lib/analysis/platform",
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
"//src/main/java/com/google/devtools/build/lib/exec:spawn_log_context",
+ "//src/main/java/com/google/devtools/build/lib/unix",
"//src/main/java/com/google/devtools/build/lib/util",
"//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
"//src/main/java/com/google/devtools/build/lib/vfs",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/SymlinkAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/SymlinkAction.java
index 76f0df3..f6ca7b9 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/SymlinkAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/SymlinkAction.java
@@ -14,6 +14,8 @@
package com.google.devtools.build.lib.analysis.actions;
+import static com.google.devtools.build.lib.unix.FileStatus.S_IXUSR;
+
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -34,8 +36,11 @@
import com.google.devtools.build.lib.server.FailureDetails.SymlinkAction.Code;
import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Symlinks;
+import com.google.devtools.build.lib.vfs.SyscallCache;
import java.io.IOException;
import javax.annotation.Nullable;
@@ -246,19 +251,32 @@
return;
}
- Path inputPath = actionExecutionContext.getInputPath(getPrimaryInput());
+ Artifact primaryInput = getPrimaryInput();
+ Path inputPath = actionExecutionContext.getInputPath(primaryInput);
+
+ // Source artifacts are probably in the syscall cache. Generated artifacts are probably not.
+ SyscallCache syscallCache =
+ primaryInput.isSourceArtifact()
+ ? actionExecutionContext.getSyscallCache()
+ : SyscallCache.NO_CACHE;
try {
- // Validate that input path is a file with the executable bit set.
- if (!inputPath.isFile()) {
- String message = String.format("'%s' is not a file", getPrimaryInput().getExecPathString());
+ FileStatus stat = syscallCache.statIfFound(inputPath, Symlinks.FOLLOW);
+ if (stat == null || !stat.isFile()) {
+ String message = String.format("'%s' is not a file", primaryInput.getExecPathString());
throw new ActionExecutionException(
message, this, false, createDetailedExitCode(message, Code.EXECUTABLE_INPUT_NOT_FILE));
}
- if (!inputPath.isExecutable()) {
+ boolean isExecutable;
+ if (stat.getPermissions() != -1) {
+ isExecutable = (stat.getPermissions() & S_IXUSR) != 0;
+ } else {
+ isExecutable = inputPath.isExecutable();
+ }
+ if (!isExecutable) {
String message =
String.format(
"failed to create symbolic link '%s': file '%s' is not executable",
- getPrimaryOutput().getExecPathString(), getPrimaryInput().getExecPathString());
+ getPrimaryOutput().getExecPathString(), primaryInput.getExecPathString());
throw new ActionExecutionException(
message, this, false, createDetailedExitCode(message, Code.EXECUTABLE_INPUT_IS_NOT));
}
@@ -267,7 +285,7 @@
String.format(
"failed to create symbolic link '%s' to the '%s' due to I/O error: %s",
getPrimaryOutput().getExecPathString(),
- getPrimaryInput().getExecPathString(),
+ primaryInput.getExecPathString(),
e.getMessage());
DetailedExitCode detailedExitCode =
createDetailedExitCode(message, Code.EXECUTABLE_INPUT_CHECK_IO_EXCEPTION);