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);