Propagate a persistent worker's worker protocol format from its rule's execution requirements

RELNOTES: None
PiperOrigin-RevId: 325481408
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java b/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java
index 4b32bcd..3150e2c 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java
@@ -157,6 +157,9 @@
 
   public static final String SUPPORTS_MULTIPLEX_WORKERS = "supports-multiplex-workers";
 
+  /** Specify the type of worker protocol the worker uses. */
+  public static final String WORKER_PROTOCOL = "worker-protocol";
+
   /** Denotes what the type of worker protocol the worker uses. */
   public enum WorkerProtocolFormat {
     JSON,
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Spawns.java b/src/main/java/com/google/devtools/build/lib/actions/Spawns.java
index d84b8fc..0f4f8c1 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Spawns.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Spawns.java
@@ -20,6 +20,7 @@
 import com.google.devtools.build.lib.util.CommandDescriptionForm;
 import com.google.devtools.build.lib.util.CommandFailureUtils;
 import com.google.devtools.build.lib.vfs.Path;
+import java.io.IOException;
 import java.time.Duration;
 import java.util.Collection;
 import java.util.Map;
@@ -91,6 +92,29 @@
         .equals(spawn.getExecutionInfo().get(ExecutionRequirements.SUPPORTS_MULTIPLEX_WORKERS));
   }
 
+  /**
+   * Returns which worker protocol format a Spawn claims a persistent worker uses. Defaults to proto
+   * if the protocol format is not specified.
+   */
+  public static ExecutionRequirements.WorkerProtocolFormat getWorkerProtocolFormat(Spawn spawn)
+      throws IOException {
+    String protocolFormat = spawn.getExecutionInfo().get(ExecutionRequirements.WORKER_PROTOCOL);
+
+    if (protocolFormat != null) {
+      switch (protocolFormat) {
+        case "json":
+          return ExecutionRequirements.WorkerProtocolFormat.JSON;
+        case "proto":
+          return ExecutionRequirements.WorkerProtocolFormat.PROTO;
+        default:
+          throw new IOException(
+              "protocol-format must be set to a valid worker protocol format: json or proto");
+      }
+    } else {
+      return ExecutionRequirements.WorkerProtocolFormat.PROTO;
+    }
+  }
+
   /** Returns the mnemonic that should be used in the worker's key. */
   public static String getWorkerKeyMnemonic(Spawn spawn) {
     String customValue = spawn.getExecutionInfo().get(ExecutionRequirements.WORKER_KEY_MNEMONIC);
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerOptions.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerOptions.java
index 7eaf64e..6dd1eb2 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerOptions.java
@@ -46,6 +46,16 @@
       })
   public Void experimentalPersistentJavac;
 
+  @Option(
+      name = "experimental_allow_json_worker_protocol",
+      defaultValue = "false",
+      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
+      help =
+          "Allows workers to use the JSON worker protocol until it is determined to be"
+              + " stable.")
+  public boolean experimentalJsonWorkerProtocol;
+
   /**
    * Defines a resource converter for named values in the form [name=]value, where the value is
    * {@link ResourceConverter.FLAG_SYNTAX}. If no name is provided (used when setting a default),
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
index 627dcd2..6f96c69 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
@@ -193,8 +193,14 @@
             context.getInputMapping(), spawn, context.getArtifactExpander(), execRoot);
     SandboxOutputs outputs = helpers.getOutputs(spawn);
 
-    /** TODO(karlgray): Determine protocolFormat from Spawn. */
-    WorkerProtocolFormat protocolFormat = WorkerProtocolFormat.PROTO;
+    WorkerProtocolFormat protocolFormat = Spawns.getWorkerProtocolFormat(spawn);
+    if (!workerOptions.experimentalJsonWorkerProtocol) {
+      if (protocolFormat == WorkerProtocolFormat.JSON) {
+        throw new IOException(
+            "Persistent worker protocol format must be set to proto unless"
+                + " --experimentalJsonWorkerProtocol is used");
+      }
+    }
 
     WorkerKey key =
         new WorkerKey(
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BUILD b/src/test/java/com/google/devtools/build/lib/analysis/BUILD
index c88111a..f9c5131 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/analysis/BUILD
@@ -40,6 +40,7 @@
         "//src/main/java/com/google/devtools/build/lib/actions",
         "//src/main/java/com/google/devtools/build/lib/actions:action_lookup_key",
         "//src/main/java/com/google/devtools/build/lib/actions:artifacts",
+        "//src/main/java/com/google/devtools/build/lib/actions:execution_requirements",
         "//src/main/java/com/google/devtools/build/lib/analysis:actions/binary_file_write_action",
         "//src/main/java/com/google/devtools/build/lib/analysis:actions/compression",
         "//src/main/java/com/google/devtools/build/lib/analysis:actions/custom_command_line",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java
index ff01b59..c960aa9 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTest.java
@@ -27,6 +27,7 @@
 import com.google.devtools.build.lib.actions.ActionInput;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.CommandLine;
+import com.google.devtools.build.lib.actions.ExecutionRequirements.WorkerProtocolFormat;
 import com.google.devtools.build.lib.actions.ParamFileInfo;
 import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
 import com.google.devtools.build.lib.actions.RunfilesSupplier;
@@ -506,6 +507,32 @@
   }
 
   @Test
+  public void testWorkerProtocolFormat_defaultIsProto() throws Exception {
+    SpawnAction spawn =
+        createWorkerSupportSpawn(ImmutableMap.<String, String>of("supports-workers", "1"));
+    assertThat(Spawns.getWorkerProtocolFormat(spawn.getSpawn()))
+        .isEqualTo(WorkerProtocolFormat.PROTO);
+  }
+
+  @Test
+  public void testWorkerProtocolFormat_explicitProto() throws Exception {
+    SpawnAction spawn =
+        createWorkerSupportSpawn(
+            ImmutableMap.<String, String>of("supports-workers", "1", "worker-protocol", "proto"));
+    assertThat(Spawns.getWorkerProtocolFormat(spawn.getSpawn()))
+        .isEqualTo(WorkerProtocolFormat.PROTO);
+  }
+
+  @Test
+  public void testWorkerProtocolFormat_explicitJson() throws Exception {
+    SpawnAction spawn =
+        createWorkerSupportSpawn(
+            ImmutableMap.<String, String>of("supports-workers", "1", "worker-protocol", "json"));
+    assertThat(Spawns.getWorkerProtocolFormat(spawn.getSpawn()))
+        .isEqualTo(WorkerProtocolFormat.JSON);
+  }
+
+  @Test
   public void testWorkerMnemonicDefault() throws Exception {
     SpawnAction defaultMnemonicSpawn = createWorkerSupportSpawn(ImmutableMap.<String, String>of());
     assertThat(Spawns.getWorkerKeyMnemonic(defaultMnemonicSpawn.getSpawn()))