Serialize MiddlemanAction and add strategy=Strategy.Polymorphic for Action and
AbstractAction.

PiperOrigin-RevId: 186316435
diff --git a/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java b/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
index 5aba95b..4844dc9 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
@@ -29,6 +29,9 @@
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.packages.AspectDescriptor;
+import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.Strategy;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
@@ -53,19 +56,23 @@
 
 /**
  * Abstract implementation of Action which implements basic functionality: the inputs, outputs, and
- * toString method. Both input and output sets are immutable. Subclasses must be generally
- * immutable - see the documentation on {@link Action}.
+ * toString method. Both input and output sets are immutable. Subclasses must be generally immutable
+ * - see the documentation on {@link Action}.
  */
-@Immutable @ThreadSafe
+@Immutable
+@ThreadSafe
 @SkylarkModule(
-    name = "Action",
-    category = SkylarkModuleCategory.BUILTIN,
-    doc = "An action created on a <a href=\"ctx.html\">ctx</a> object. You can retrieve these "
-        + "using the <a href=\"globals.html#Actions\">Actions</a> provider. Some fields are only "
-        + "applicable for certain kinds of actions. Fields that are inapplicable are set to "
-        + "<code>None</code>."
+  name = "Action",
+  category = SkylarkModuleCategory.BUILTIN,
+  doc =
+      "An action created on a <a href=\"ctx.html\">ctx</a> object. You can retrieve these "
+          + "using the <a href=\"globals.html#Actions\">Actions</a> provider. Some fields are only "
+          + "applicable for certain kinds of actions. Fields that are inapplicable are set to "
+          + "<code>None</code>."
 )
+@AutoCodec(strategy = Strategy.POLYMORPHIC)
 public abstract class AbstractAction implements Action, SkylarkValue {
+  public static final ObjectCodec<AbstractAction> CODEC = new AbstractAction_AutoCodec();
 
   /**
    * An arbitrary default resource set. Currently 250MB of memory, 50% CPU and 0% of total I/O.
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Action.java b/src/main/java/com/google/devtools/build/lib/actions/Action.java
index 217ce5d..54f562a 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Action.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Action.java
@@ -17,6 +17,9 @@
 import com.google.devtools.build.lib.actions.extra.ExtraActionInfo;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ConditionallyThreadCompatible;
 import com.google.devtools.build.lib.profiler.Describable;
+import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.Strategy;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.skyframe.SkyFunction;
@@ -36,6 +39,7 @@
  * a new custom subclass.
  *
  * <p>These are the most important requirements for subclasses:
+ *
  * <ul>
  *   <li>Actions must be generally immutable; we currently make an exception for C++, and that has
  *       been a constant source of correctness issues; there are still ongoing incremental
@@ -75,7 +79,9 @@
  * known set of fields is covered, not that all fields are covered), so carefully check all changes
  * to action subclasses.
  */
+@AutoCodec(strategy = Strategy.POLYMORPHIC)
 public interface Action extends ActionExecutionMetadata, Describable {
+  public static final ObjectCodec<Action> CODEC = new Action_AutoCodec();
 
   /**
    * Prepares for executing this action; called by the Builder prior to executing the Action itself.
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD
index 287dad9..35bea72 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -34,6 +34,7 @@
         "//src/main/java/com/google/devtools/build/lib/collect",
         "//src/main/java/com/google/devtools/build/lib/collect/nestedset",
         "//src/main/java/com/google/devtools/build/lib/collect/nestedset:fingerprint_cache",
+        "//src/main/java/com/google/devtools/build/lib/collect/nestedset:serialization",
         "//src/main/java/com/google/devtools/build/lib/concurrent",
         "//src/main/java/com/google/devtools/build/lib/profiler",
         "//src/main/java/com/google/devtools/build/lib/shell",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/MiddlemanAction.java b/src/main/java/com/google/devtools/build/lib/actions/MiddlemanAction.java
index 787f052..addbf6e 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/MiddlemanAction.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/MiddlemanAction.java
@@ -14,17 +14,23 @@
 package com.google.devtools.build.lib.actions;
 
 import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
 
 /**
- * An action that depends on a set of inputs and creates a single output file whenever it
- * runs. This is useful for bundling up a bunch of dependencies that are shared
- * between individual targets in the action graph; for example generated header files.
+ * An action that depends on a set of inputs and creates a single output file whenever it runs. This
+ * is useful for bundling up a bunch of dependencies that are shared between individual targets in
+ * the action graph; for example generated header files.
  */
 @Immutable
+@AutoCodec
 public final class MiddlemanAction extends AbstractAction {
+  public static final ObjectCodec<MiddlemanAction> CODEC = new MiddlemanAction_AutoCodec();
 
   public static final String MIDDLEMAN_MNEMONIC = "Middleman";
   private final String description;
@@ -43,9 +49,20 @@
    */
   public MiddlemanAction(ActionOwner owner, Iterable<Artifact> inputs, Artifact stampFile,
       String description, MiddlemanType middlemanType) {
-    super(owner, inputs, ImmutableList.of(stampFile));
+    this(owner, inputs, ImmutableSet.of(stampFile), description, middlemanType);
+  }
+
+  @VisibleForSerialization
+  @AutoCodec.Instantiator
+  MiddlemanAction(
+      ActionOwner owner,
+      Iterable<Artifact> inputs,
+      ImmutableSet<Artifact> outputs,
+      String description,
+      MiddlemanType middlemanType) {
+    super(owner, inputs, outputs);
     Preconditions.checkNotNull(middlemanType);
-    Preconditions.checkArgument(stampFile.isMiddlemanArtifact(), stampFile);
+    Preconditions.checkArgument(Iterables.getOnlyElement(outputs).isMiddlemanArtifact(), outputs);
     this.description = description;
     this.middlemanType = middlemanType;
   }
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 6e0f993..fad138c 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -1290,6 +1290,7 @@
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib/actions",
         "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils",
         "//src/main/java/com/google/devtools/build/lib/vfs",
         "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
         "//src/main/java/com/google/devtools/common/options",
diff --git a/src/test/java/com/google/devtools/build/lib/exec/MiddlemanActionTest.java b/src/test/java/com/google/devtools/build/lib/exec/MiddlemanActionTest.java
index dade507..42a9a1f 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/MiddlemanActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/MiddlemanActionTest.java
@@ -26,8 +26,10 @@
 import com.google.devtools.build.lib.analysis.util.AnalysisTestUtil;
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
 import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.devtools.build.lib.skyframe.serialization.testutils.ObjectCodecTester;
 import com.google.devtools.build.lib.testutil.Suite;
 import com.google.devtools.build.lib.testutil.TestSpec;
+import com.google.devtools.build.lib.vfs.FileSystem;
 import java.util.ArrayList;
 import java.util.Arrays;
 import org.junit.Before;
@@ -135,4 +137,19 @@
     assertThat(Sets.newHashSet(middlemanActionForD.getOutputs()))
         .isNotEqualTo(Sets.newHashSet(middlemanActionForC.getOutputs()));
   }
+
+  @Test
+  public void testCodec() throws Exception {
+    ObjectCodecTester.newBuilder(MiddlemanAction.CODEC)
+        .addSubjects((MiddlemanAction) getGeneratingAction(middle))
+        .addDependency(FileSystem.class, scratch.getFileSystem())
+        .verificationFunction(
+            (first, second) -> {
+              assertThat(first.getActionType()).isEqualTo(second.getActionType());
+              assertThat(first.getInputs()).isEqualTo(second.getInputs());
+              assertThat(first.getOutputs()).isEqualTo(second.getOutputs());
+              assertThat(first.getOwner()).isEqualTo(second.getOwner());
+            })
+        .buildAndRunTests();
+  }
 }