If a TreeFileArtifact is not explicitly in ActionExecutionValue's metadata map, check its TreeArtifactValue cache and return a value if it's there. Much of the time, a TreeFileArtifact's metadata will be in the main map: if it was the output of a templated action, it will be in the main map. It will also be there if it was explicitly statted/injected, either from Google-internal remote execution or as part of local execution.

However, if the tree artifact was injected via ActionMetadataHandler#injectRemoteDirectory, it is not present in the main metadata map.

I considered two other options for this CL:
(1) Explicitly add TreeFileArtifact metadata in ActionMetadataHandler#injectRemoteDirectory. Leads to increased memory usage for tree artifacts with Bazel remote execution (though just on parity with Google-internal/local) and maintains this as a sharp edge that could lead to crashes in the future if another use case is added.
(2) Explicitly add TreeFileArtifact metadata inside OutputStore#putTreeArtifactData. Duplicates work for local/Google-internal remote execution, where the metadata was already added, and has memory disadvantage.

This change seemed better: it makes explicit that TreeFileArtifacts don't have to be added, and saves a bit of memory at the cost of three map lookups instead of one for TreeFileArtifacts with Bazel remote execution. If Google-internal remote execution supported directories natively, we would move in this direction there as well.

PiperOrigin-RevId: 298601895
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java
index 5db4328..33fb340 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java
@@ -124,7 +124,16 @@
    */
   @Nullable
   public FileArtifactValue getArtifactValue(Artifact artifact) {
-    return artifactData.get(artifact);
+    FileArtifactValue result = artifactData.get(artifact);
+    if (result != null || !artifact.hasParent()) {
+      return result;
+    }
+    // In some cases, TreeFileArtifact metadata may not have been injected directly, and is only
+    // available via the parent. However, if this ActionExecutionValue corresponds to a templated
+    // action, as opposed to an action that created a tree artifact itself, the TreeFileArtifact
+    // metadata will be in artifactData, since this value will have no treeArtifactData.
+    TreeArtifactValue treeArtifactValue = treeArtifactData.get(artifact.getParent());
+    return treeArtifactValue == null ? null : treeArtifactValue.getChildValues().get(artifact);
   }
 
   TreeArtifactValue getTreeArtifactValue(Artifact artifact) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
index 3c1a85d..bbf13fe 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
@@ -436,6 +436,8 @@
           throw new IOException(errorMessage, e);
         }
 
+        // TODO(janakr): we don't actually want the metadata for this TreeFileArtifact stored in the
+        //  main metadata cache as of cl/297927844. Refactor and remove.
         // A minor hack: maybeStoreAdditionalData will force the data to be stored via
         // store.putAdditionalOutputData, if the underlying OutputStore supports it.
         fileMetadata = maybeStoreAdditionalData(treeFileArtifact, fileMetadata, null);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
index 1cf4552..bc7bd53 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
@@ -404,6 +404,11 @@
     assertThat(treeValue.getChildPaths())
         .containsExactly(PathFragment.create("foo"), PathFragment.create("bar"));
     assertThat(treeValue.getChildValues().values()).containsExactly(fooValue, barValue);
+    ActionExecutionValue actionExecutionValue =
+        ActionExecutionValue.createFromOutputStore(handler.getOutputStore(), null, null, false);
+    treeValue
+        .getChildren()
+        .forEach(t -> assertThat(actionExecutionValue.getArtifactValue(t)).isNotNull());
   }
 
   @Test
diff --git a/src/test/shell/bazel/remote/remote_execution_test.sh b/src/test/shell/bazel/remote/remote_execution_test.sh
index 822900f..0d97941 100755
--- a/src/test/shell/bazel/remote/remote_execution_test.sh
+++ b/src/test/shell/bazel/remote/remote_execution_test.sh
@@ -166,6 +166,36 @@
       || fail "Remote execution generated different result"
 }
 
+function test_cc_tree() {
+  if [[ "$PLATFORM" == "darwin" ]]; then
+    # TODO(b/37355380): This test is disabled due to RemoteWorker not supporting
+    # setting SDKROOT and DEVELOPER_DIR appropriately, as is required of
+    # action executors in order to select the appropriate Xcode toolchain.
+    return 0
+  fi
+
+  mkdir -p a
+  cat > a/BUILD <<EOF
+load(":tree.bzl", "mytree")
+mytree(name = "tree")
+cc_library(name = "tree_cc", srcs = [":tree"])
+EOF
+  cat > a/tree.bzl <<EOF
+def _tree_impl(ctx):
+    tree = ctx.actions.declare_directory("file.cc")
+    ctx.actions.run_shell(outputs = [tree],
+                          command = "mkdir -p %s && touch %s/one.cc" % (tree.path, tree.path))
+    return [DefaultInfo(files = depset([tree]))]
+
+mytree = rule(implementation = _tree_impl)
+EOF
+  bazel build \
+      --remote_executor=grpc://localhost:${worker_port} \
+      --remote_download_minimal \
+      //a:tree_cc >& "$TEST_log" \
+      || fail "Failed to build //a:tree_cc with minimal downloads"
+}
+
 function test_cc_test() {
   if [[ "$PLATFORM" == "darwin" ]]; then
     # TODO(b/37355380): This test is disabled due to RemoteWorker not supporting