Rename `SourceFileArtifactValue` to `SymlinkToSourceFileArtifactValue` and store the target `SourceArtifact` when possible.
The new name makes it more clear that it represents the metadata of a symlink to a source file. Storing the `SourceArtifact` facilitates optimizations in followups.
PiperOrigin-RevId: 691027133
Change-Id: Idd6560ce9696b29fee7b3f248bccb97e15aa0486
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD
index 050b6c4..9970d7c 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -463,6 +463,7 @@
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:serialization-constant",
"//src/main/java/com/google/devtools/build/lib/util",
+ "//src/main/java/com/google/devtools/build/lib/util:hash_codes",
"//src/main/java/com/google/devtools/build/lib/util:var_int",
"//src/main/java/com/google/devtools/build/lib/util/io",
"//src/main/java/com/google/devtools/build/lib/vfs",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java b/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java
index 9d670a6..24a6c84 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java
@@ -15,17 +15,19 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
import com.google.common.hash.HashFunction;
import com.google.common.io.BaseEncoding;
+import com.google.devtools.build.lib.actions.Artifact.SourceArtifact;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.util.HashCodes;
import com.google.devtools.build.lib.vfs.DigestUtils;
import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.Path;
@@ -197,8 +199,8 @@
Artifact artifact, FileValue fileValue, XattrProvider xattrProvider) throws IOException {
// Artifacts with known generating actions should obtain the derived artifact's SkyValue
// from the generating action, instead.
- Preconditions.checkState(!artifact.hasKnownGeneratingAction());
- Preconditions.checkState(!artifact.isConstantMetadata());
+ checkState(!artifact.hasKnownGeneratingAction());
+ checkState(!artifact.isConstantMetadata());
boolean isFile = fileValue.isFile();
return create(
artifact.getPath(),
@@ -255,7 +257,7 @@
if (digest == null) {
digest = DigestUtils.getDigestWithManualFallback(path, xattrProvider);
}
- Preconditions.checkState(digest != null, path);
+ checkState(digest != null, path);
return createForNormalFile(digest, proxy, size);
}
@@ -300,7 +302,7 @@
* {@link ActionCacheChecker}.
*/
public static FileArtifactValue createProxy(byte[] digest) {
- Preconditions.checkNotNull(digest);
+ checkNotNull(digest);
return createForNormalFile(digest, /* proxy= */ null, /* size= */ 0);
}
@@ -532,7 +534,7 @@
long size,
int locationIndex,
@Nullable PathFragment materializationExecPath) {
- this.digest = Preconditions.checkNotNull(digest);
+ this.digest = checkNotNull(digest);
this.size = size;
this.locationIndex = locationIndex;
this.materializationExecPath = materializationExecPath;
@@ -694,7 +696,7 @@
@Override
public void extendExpireAtEpochMilli(long expireAtEpochMilli) {
- Preconditions.checkState(expireAtEpochMilli > this.expireAtEpochMilli);
+ checkState(expireAtEpochMilli > this.expireAtEpochMilli);
this.expireAtEpochMilli = expireAtEpochMilli;
}
@@ -798,8 +800,8 @@
private final byte[] digest;
private InlineFileArtifactValue(byte[] data, byte[] digest) {
- this.data = Preconditions.checkNotNull(data);
- this.digest = Preconditions.checkNotNull(digest);
+ this.data = checkNotNull(data);
+ this.digest = checkNotNull(digest);
}
@Override
@@ -854,23 +856,43 @@
}
/**
- * Used to resolve source symlinks when diskless.
- *
- * <p>When the optional per-action file system creates symlinks, it relies on metadata ({@link
- * FileArtifactValue}) to resolve the actual underlying data. In the case of remote or inline
- * files, this information is self-contained. However, in the case of source files, the path is
- * required to resolve the content.
+ * Metadata for an output of {@link com.google.devtools.build.lib.analysis.actions.SymlinkAction}
+ * that resolves to a source file.
*/
- public static final class SourceFileArtifactValue extends FileArtifactValue {
- private final PathFragment path;
- private final byte[] digest;
- private final long size;
+ public static final class SymlinkToSourceFileArtifactValue extends FileArtifactValue {
- public SourceFileArtifactValue(PathFragment path, byte[] digest, long size) {
- Preconditions.checkArgument(path.isAbsolute(), "path %s isn't absolute", path);
- this.path = path;
- this.digest = Preconditions.checkNotNull(digest);
- this.size = size;
+ /** Creates metadata for a symlink pointing to a known {@link SourceArtifact}. */
+ public static SymlinkToSourceFileArtifactValue toSourceArtifact(
+ SourceArtifact sourceArtifact, FileArtifactValue sourceFileMetadata) {
+ return new SymlinkToSourceFileArtifactValue(
+ sourceArtifact.getPath().asFragment(), sourceFileMetadata, sourceArtifact.getExecPath());
+ }
+
+ /**
+ * Creates metadata for a symlink pointing to a source file that is not a known {@link
+ * SourceArtifact}.
+ *
+ * <p>This is only expected to happen for a symlink to an FDO profile file when {@code
+ * --fdo_profile} is specified as an absolute path.
+ */
+ public static SymlinkToSourceFileArtifactValue toUnknownSourceFile(
+ PathFragment resolvedPath, FileArtifactValue sourceFileMetadata) {
+ return new SymlinkToSourceFileArtifactValue(
+ resolvedPath, sourceFileMetadata, /* sourceArtifactExecPath= */ null);
+ }
+
+ private final PathFragment resolvedPath;
+ private final FileArtifactValue sourceFileMetadata;
+ @Nullable private final PathFragment sourceArtifactExecPath;
+
+ private SymlinkToSourceFileArtifactValue(
+ PathFragment resolvedPath,
+ FileArtifactValue sourceFileMetadata,
+ @Nullable PathFragment sourceArtifactExecPath) {
+ checkArgument(resolvedPath.isAbsolute(), "Resolved path must be absolute: %s", resolvedPath);
+ this.resolvedPath = resolvedPath;
+ this.sourceFileMetadata = checkNotNull(sourceFileMetadata);
+ this.sourceArtifactExecPath = sourceArtifactExecPath;
}
@Override
@@ -878,52 +900,73 @@
if (this == o) {
return true;
}
- if (!(o instanceof SourceFileArtifactValue that)) {
+ if (!(o instanceof SymlinkToSourceFileArtifactValue that)) {
return false;
}
- return path.equals(that.path) && Arrays.equals(digest, that.digest) && size == that.size;
+ return resolvedPath.equals(that.resolvedPath)
+ && sourceFileMetadata.equals(that.sourceFileMetadata)
+ && Objects.equals(sourceArtifactExecPath, that.sourceArtifactExecPath);
}
@Override
public int hashCode() {
- int result = path.hashCode();
- result = 31 * result + Arrays.hashCode(digest);
- result = 31 * result + Long.hashCode(size);
- return result;
+ return HashCodes.hashObjects(resolvedPath, sourceFileMetadata, sourceArtifactExecPath);
}
- public PathFragment getPath() {
- return path;
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("resolvedPath", resolvedPath)
+ .add("sourceFileMetadata", sourceFileMetadata)
+ .add("sourceArtifactExecPath", sourceArtifactExecPath)
+ .toString();
+ }
+
+ /** Returns the absolute path to which the symlink resolves. */
+ public PathFragment getResolvedPath() {
+ return resolvedPath;
+ }
+
+ /**
+ * If the symlink resolves to a {@link SourceArtifact}, returns that artifact's exec path.
+ *
+ * <p>Returns {@code null} when the symlink does not resolve to a known {@link SourceArtifact}.
+ * See {@link #toUnknownSourceFile}.
+ */
+ @Nullable
+ public PathFragment getSourceArtifactExecPath() {
+ return sourceArtifactExecPath;
}
@Override
public FileStateType getType() {
- return FileStateType.REGULAR_FILE;
+ return sourceFileMetadata.getType();
}
+ @Nullable
@Override
public byte[] getDigest() {
- return digest;
+ return sourceFileMetadata.getDigest();
}
@Override
public FileContentsProxy getContentsProxy() {
- throw new UnsupportedOperationException();
+ return sourceFileMetadata.getContentsProxy();
}
@Override
public long getSize() {
- return size;
+ return sourceFileMetadata.getSize();
}
@Override
public long getModifiedTime() {
- throw new UnsupportedOperationException();
+ return sourceFileMetadata.getModifiedTime();
}
@Override
- public boolean wasModifiedSinceDigest(Path path) {
- throw new UnsupportedOperationException();
+ public boolean wasModifiedSinceDigest(Path path) throws IOException {
+ return sourceFileMetadata.wasModifiedSinceDigest(path);
}
}