Support proxy tree artifact values in the action cache.
When a non-empty `TreeArtifactValue` is comprised of only `ProxyFileArtifactValue` children, record it as a proxy output and reconstruct it using `ProxyMetadataFactory`.
PiperOrigin-RevId: 811937775
Change-Id: I394af43428017ba8cb716722993a4a7dc7e0cae1
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 3c2251a..2273101 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
@@ -39,6 +39,7 @@
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.skyframe.TreeArtifactValue;
import com.google.devtools.build.lib.skyframe.TreeArtifactValue.ArchivedRepresentation;
+import com.google.devtools.build.lib.vfs.Dirent;
import com.google.devtools.build.lib.vfs.OutputPermissions;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.FileNotFoundException;
@@ -326,6 +327,17 @@
for (Artifact artifact : action.getOutputs()) {
if (artifact.isTreeArtifact()) {
SpecialArtifact parent = (SpecialArtifact) artifact;
+
+ if (proxyOutputs.contains(parent.getExecPathString())) {
+ try {
+ TreeArtifactValue metadata = constructProxyTreeMetadata(parent);
+ mergedTreeMetadata.put(parent, metadata);
+ } catch (IOException e) {
+ // Ignore - we'll get an action cache miss.
+ }
+ continue;
+ }
+
SerializableTreeArtifactValue cachedTreeMetadata = entry.getOutputTree(parent);
if (cachedTreeMetadata == null) {
continue;
@@ -410,6 +422,24 @@
mergedFileMetadata.buildOrThrow(), mergedTreeMetadata.buildOrThrow());
}
+ private TreeArtifactValue constructProxyTreeMetadata(SpecialArtifact parent)
+ throws IOException, InterruptedException {
+ TreeArtifactValue.Builder tree = TreeArtifactValue.newBuilder(parent);
+ TreeArtifactValue.visitTree(
+ parent.getPath(),
+ (parentRelativePath, type, traversedSymlink) -> {
+ if (type != Dirent.Type.DIRECTORY) {
+ TreeFileArtifact child = TreeFileArtifact.createTreeOutput(parent, parentRelativePath);
+ FileArtifactValue metadata = proxyMetadataFactory.createProxyMetadata(child);
+ // visitTree() uses multiple threads and putChild() is not thread-safe
+ synchronized (tree) {
+ tree.putChild(child, metadata);
+ }
+ }
+ });
+ return tree.build();
+ }
+
/**
* Checks whether {@code action} needs to be executed and returns a non-null {@link Token} if so.
*
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java b/src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java
index 5a3e008..1249c94 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java
@@ -168,10 +168,7 @@
/**
* Returns a list of exec path strings for {@linkplain ProxyFileArtifactValue proxied} outputs.
- *
- * <p>Tree artifacts are not currently supported and are never included here.
*/
- // TODO: b/440119558 - Support action caching of proxied tree artifacts.
public ImmutableList<String> getProxyOutputs() {
checkState(!isCorrupted());
return proxyOutputs;
@@ -371,7 +368,13 @@
checkArgument(output.isTreeArtifact(), "artifact must be a tree artifact: %s", output);
String execPath = output.getExecPathString();
if (saveTreeMetadata) {
- outputTreeMetadata.put(execPath, SerializableTreeArtifactValue.create(metadata));
+ if (!metadata.getChildValues().isEmpty()
+ && metadata.getChildValues().values().stream()
+ .allMatch(ProxyFileArtifactValue.class::isInstance)) {
+ proxyOutputs.add(output.getExecPathString());
+ } else {
+ outputTreeMetadata.put(execPath, SerializableTreeArtifactValue.create(metadata));
+ }
}
metadataMap.put(execPath, metadata.getMetadata());
return this;