Sorting the Action output files.

PiperOrigin-RevId: 161925075
diff --git a/src/main/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunner.java
index 95a3404..8b0376a 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/CachedLocalSpawnRunner.java
@@ -40,6 +40,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.SortedMap;
 import java.util.TreeSet;
@@ -138,11 +139,13 @@
     Action.Builder action = Action.newBuilder();
     action.setCommandDigest(command);
     action.setInputRootDigest(inputRoot);
-    // Somewhat ugly: we rely on the stable order of outputs here for remote action caching.
+    ArrayList<String> outputPaths = new ArrayList<>();
     for (ActionInput output : outputs) {
-      // TODO: output directories should be handled here, when they are supported.
-      action.addOutputFiles(output.getExecPathString());
+      outputPaths.add(output.getExecPathString());
     }
+    Collections.sort(outputPaths);
+    // TODO: output directories should be handled here, when they are supported.
+    action.addAllOutputFiles(outputPaths);
     if (platform != null) {
       action.setPlatform(platform);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
index 7ba5e5e..7a50e1c 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
@@ -39,6 +39,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.SortedMap;
 import java.util.TreeSet;
@@ -159,11 +160,13 @@
     Action.Builder action = Action.newBuilder();
     action.setCommandDigest(command);
     action.setInputRootDigest(inputRoot);
-    // Somewhat ugly: we rely on the stable order of outputs here for remote action caching.
+    ArrayList<String> outputPaths = new ArrayList<>();
     for (ActionInput output : outputs) {
-      // TODO: output directories should be handled here, when they are supported.
-      action.addOutputFiles(output.getExecPathString());
+      outputPaths.add(output.getExecPathString());
     }
+    Collections.sort(outputPaths);
+    // TODO: output directories should be handled here, when they are supported.
+    action.addAllOutputFiles(outputPaths);
     if (platform != null) {
       action.setPlatform(platform);
     }
diff --git a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java
index 8732235..2a39ba9 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java
@@ -167,7 +167,29 @@
             ImmutableMap.of("VARIABLE", "value"),
             /*executionInfo=*/ ImmutableMap.<String, String>of(),
             /*inputs=*/ ImmutableList.of(ActionInputHelper.fromPath("input")),
-            /*outputs=*/ ImmutableList.<ActionInput>of(),
+            /*outputs=*/ ImmutableList.<ActionInput>of(
+                new ActionInput() {
+                  @Override
+                  public String getExecPathString() {
+                    return "foo";
+                  }
+
+                  @Override
+                  public PathFragment getExecPath() {
+                    return null; // unused here.
+                  }
+                },
+                new ActionInput() {
+                  @Override
+                  public String getExecPathString() {
+                    return "bar";
+                  }
+
+                  @Override
+                  public PathFragment getExecPath() {
+                    return null; // unused here.
+                  }
+                }),
             ResourceSet.ZERO);
 
     Path stdout = fs.getPath("/tmp/stdout");
@@ -335,6 +357,10 @@
         new ExecutionImplBase() {
           @Override
           public void execute(ExecuteRequest request, StreamObserver<Operation> responseObserver) {
+            // Check that the output files are sorted.
+            assertThat(request.getAction().getOutputFilesList())
+                .containsExactly("bar", "foo")
+                .inOrder();
             responseObserver.onNext(
                 Operation.newBuilder()
                     .setDone(true)