Make Metadata an interface for FileArtifactValue
Replace all previous uses of Metadata with FileArtifactValue (or a simple inner
class in the case of ActionCacheChecker.CONSTANT_METADATA).
Care was taken to make the equals method obey the equals contract, even in the
presence of multiple implementations.
PiperOrigin-RevId: 160115080
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java b/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
index 57abd35..1a6c4c8 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
@@ -50,6 +50,28 @@
* otherwise lightweight, and should be constructed anew and discarded for each build request.
*/
public class ActionCacheChecker {
+ private static final Metadata CONSTANT_METADATA = new Metadata() {
+ @Override
+ public boolean isFile() {
+ return false;
+ }
+
+ @Override
+ public byte[] getDigest() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getSize() {
+ return 0;
+ }
+
+ @Override
+ public long getModifiedTime() {
+ return -1;
+ }
+ };
+
private final ActionCache actionCache;
private final Predicate<? super Action> executionFilter;
private final ArtifactResolver artifactResolver;
@@ -294,7 +316,7 @@
private static Metadata getMetadataOrConstant(MetadataHandler metadataHandler, Artifact artifact)
throws IOException {
if (artifact.isConstantMetadata()) {
- return Metadata.CONSTANT_METADATA;
+ return CONSTANT_METADATA;
} else {
return metadataHandler.getMetadata(artifact);
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java b/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
index 0f5a037..642bd29 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
@@ -14,104 +14,43 @@
package com.google.devtools.build.lib.actions.cache;
-import com.google.common.io.BaseEncoding;
-import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
-import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
-import com.google.devtools.build.lib.util.Preconditions;
-import java.util.Arrays;
-import java.util.Date;
-
/**
- * A class to represent file metadata.
- * ActionCacheChecker may assume that, for a given file, equal
- * metadata at different moments implies equal file-contents,
- * where metadata equality is computed using Metadata.equals().
- * <p>
- * NB! Several other parts of Blaze are relying on the fact that metadata
- * uses mtime and not ctime. If metadata is ever changed
- * to use ctime, all uses of Metadata must be carefully examined.
+ * An interface to represent the state of a file (or directory). This is used to determine whether a
+ * file has changed or not.
+ *
+ * <p>NB! Several other parts of Blaze are relying on the fact that metadata uses mtime and not
+ * ctime. If metadata is ever changed to use ctime, all uses of Metadata must be carefully examined.
*/
-@Immutable @ThreadSafe
-public final class Metadata {
- private final long mtime;
- private final byte[] digest;
-
- // Convenience object for use with volatile files that we do not want checked
- // (e.g. the build-changelist.txt)
- public static final Metadata CONSTANT_METADATA = new Metadata(-1);
-
+public interface Metadata {
/**
- * Construct an instance for a directory with the specified mtime. The {@link #isFile} method
- * returns true if and only if a digest is set.
+ * Marker interface for singleton implementations of the Metadata interface. This is only needed
+ * for a correct implementation of {@code equals}.
*/
- public Metadata(long mtime) {
- this.mtime = mtime;
- this.digest = null;
+ public interface Singleton {
}
/**
- * Construct an instance for a file with the specified digest. The {@link #isFile} method returns
- * true if and only if a digest is set.
+ * Whether the underlying file system object is a file or a symlink to a file, rather than a
+ * directory. All files are guaranteed to have a digest, and {@link #getDigest} must only be
+ * called on files.
*/
- public Metadata(byte[] digest) {
- this.mtime = 0L;
- this.digest = Preconditions.checkNotNull(digest);
- }
-
- public boolean isFile() {
- return digest != null;
- }
+ boolean isFile();
/**
- * Returns the digest for the underlying file system object.
+ * Returns the file's digest; must only be called on objects for which {@link #isFile} returns
+ * true.
*
* <p>The return value is owned by the cache and must not be modified.
*/
- public byte[] getDigest() {
- Preconditions.checkState(digest != null);
- return digest;
- }
+ byte[] getDigest();
- public long getModifiedTime() {
- return mtime;
- }
+ /** Returns the file's size, or 0 if the underlying file system object is not a file. */
+ // TODO(ulfjack): Throw an exception if it's not a file.
+ long getSize();
- @Override
- public int hashCode() {
- int hash = 0;
- if (digest != null) {
- // We are already dealing with the digest so we can just use portion of it
- // as a hash code.
- hash += digest[0] + (digest[1] << 8) + (digest[2] << 16) + (digest[3] << 24);
- } else {
- // Inlined hashCode for Long, so we don't
- // have to construct an Object, just to compute
- // a 32-bit hash out of a 64 bit value.
- hash = (int) (mtime ^ (mtime >>> 32));
- }
- return hash;
- }
-
- @Override
- public boolean equals(Object that) {
- if (this == that) {
- return true;
- }
- if (!(that instanceof Metadata)) {
- return false;
- }
- // Do a strict comparison - both digest and mtime should match
- return Arrays.equals(this.digest, ((Metadata) that).digest)
- && this.mtime == ((Metadata) that).mtime;
- }
-
- @Override
- public String toString() {
- if (digest != null) {
- return "MD5 " + BaseEncoding.base16().lowerCase().encode(digest);
- } else if (mtime > 0) {
- return "timestamp " + new Date(mtime);
- }
- return "no metadata";
- }
+ /**
+ * Returns the last modified time; must only be called on objects for which {@link #isFile}
+ * returns false.
+ */
+ long getModifiedTime();
}