Logical roll-forward of https://github.com/bazelbuild/bazel/commit/32a97a653735cf7c9eff5abdf0f7632f9a6525e2 (order tree artifacts).
Fixes #5686.

Should give us deterministic action keys for action template expansions.

RELNOTES: Makes TreeArtifact deterministic.
PiperOrigin-RevId: 239311465
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
index ab82a31..c5f15b2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
@@ -13,13 +13,13 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
-import com.google.common.base.Function;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
+import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.Maps;
-import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
 import com.google.devtools.build.lib.actions.FileArtifactValue;
 import com.google.devtools.build.lib.actions.cache.DigestUtils;
@@ -45,22 +45,16 @@
  */
 @AutoCodec
 public class TreeArtifactValue implements SkyValue {
-  private static final Function<Artifact, PathFragment> PARENT_RELATIVE_PATHS =
-      new Function<Artifact, PathFragment>() {
-        @Override
-        public PathFragment apply(Artifact artifact) {
-            return artifact.getParentRelativePath();
-        }
-      };
 
   private final byte[] digest;
-  private final Map<TreeFileArtifact, FileArtifactValue> childData;
+  private final ImmutableSortedMap<TreeFileArtifact, FileArtifactValue> childData;
   private BigInteger valueFingerprint;
 
   @AutoCodec.VisibleForSerialization
-  TreeArtifactValue(byte[] digest, Map<TreeFileArtifact, FileArtifactValue> childData) {
+  TreeArtifactValue(
+      byte[] digest, ImmutableSortedMap<TreeFileArtifact, FileArtifactValue> childData) {
     this.digest = digest;
-    this.childData = ImmutableMap.copyOf(childData);
+    this.childData = childData;
   }
 
   /**
@@ -76,7 +70,7 @@
 
     return new TreeArtifactValue(
         DigestUtils.fromMetadata(digestBuilder).getDigestBytesUnsafe(),
-        ImmutableMap.copyOf(childFileValues));
+        ImmutableSortedMap.copyOf(childFileValues));
   }
 
   FileArtifactValue getSelfData() {
@@ -87,8 +81,10 @@
     return getSelfData();
   }
 
-  Set<PathFragment> getChildPaths() {
-    return ImmutableSet.copyOf(Iterables.transform(childData.keySet(), PARENT_RELATIVE_PATHS));
+  ImmutableSet<PathFragment> getChildPaths() {
+    return childData.keySet().stream()
+        .map(TreeFileArtifact::getParentRelativePath)
+        .collect(toImmutableSet());
   }
 
   @Nullable
@@ -100,7 +96,7 @@
     return childData.keySet();
   }
 
-  Map<TreeFileArtifact, FileArtifactValue> getChildValues() {
+  ImmutableMap<TreeFileArtifact, FileArtifactValue> getChildValues() {
     return childData;
   }
 
@@ -150,7 +146,7 @@
    * Java's concurrent collections disallow null members.
    */
   static final TreeArtifactValue MISSING_TREE_ARTIFACT =
-      new TreeArtifactValue(null, ImmutableMap.<TreeFileArtifact, FileArtifactValue>of()) {
+      new TreeArtifactValue(null, ImmutableSortedMap.of()) {
         @Override
         FileArtifactValue getSelfData() {
           throw new UnsupportedOperationException();
@@ -162,7 +158,7 @@
         }
 
         @Override
-        Map<TreeFileArtifact, FileArtifactValue> getChildValues() {
+        ImmutableMap<TreeFileArtifact, FileArtifactValue> getChildValues() {
           throw new UnsupportedOperationException();
         }
 
@@ -172,7 +168,7 @@
         }
 
         @Override
-        Set<PathFragment> getChildPaths() {
+        ImmutableSet<PathFragment> getChildPaths() {
           throw new UnsupportedOperationException();
         }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
index 04b3211..4a6261f 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
@@ -56,9 +56,14 @@
 import com.google.devtools.build.skyframe.SkyValue;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -126,6 +131,24 @@
   }
 
   @Test
+  public void testTreeArtifactOrdering() throws Exception {
+    int rangeSize = 100;
+    int attempts = 10;
+    List<PathFragment> children =
+        IntStream.range(0, rangeSize)
+            .mapToObj(i -> PathFragment.create("file" + i))
+            .collect(Collectors.toList());
+
+    for (int i = 0; i < attempts; i++) {
+      Collections.shuffle(children, new Random());
+      Artifact treeArtifact = createTreeArtifact("out");
+      TreeArtifactValue value = evaluateTreeArtifact(treeArtifact, children);
+      assertThat(value.getChildPaths()).containsExactlyElementsIn(children);
+      assertThat(value.getChildPaths()).isOrdered(Comparator.naturalOrder());
+    }
+  }
+
+  @Test
   public void testEqualTreeArtifacts() throws Exception {
     Artifact treeArtifact = createTreeArtifact("out");
     ImmutableList<PathFragment> children =