Move CommandLine, CommandLineItem, and ParamFileInfo from lib.analysis.actions -> lib.actions.

These are fundamental types that want to sit alongside types like Spawn.

RELNOTES: None
PiperOrigin-RevId: 185887971
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionKeyContext.java b/src/main/java/com/google/devtools/build/lib/actions/ActionKeyContext.java
index 053c120..21ddc43 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionKeyContext.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionKeyContext.java
@@ -14,7 +14,6 @@
 
 package com.google.devtools.build.lib.actions;
 
-import com.google.devtools.build.lib.analysis.actions.CommandLineItem;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetFingerprintCache;
 import com.google.devtools.build.lib.util.Fingerprint;
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
index a3da211..91b5313 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -27,7 +27,6 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Streams;
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
-import com.google.devtools.build.lib.analysis.actions.CommandLineItem;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.shell.ShellUtils;
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 f936712..287dad9 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -12,18 +12,21 @@
 # Action graph and cache.
 java_library(
     name = "actions",
-    srcs = glob([
-        "*.java",
-        "cache/*.java",
-    ]),
+    srcs = glob(
+        [
+            "*.java",
+            "cache/*.java",
+        ],
+        exclude = ["CommandLineItem.java"],
+    ),
     deps = [
-        "//src/main/java/com/google/devtools/build/lib:commandline_item",
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
         "//src/main/java/com/google/devtools/build/lib:skylarkinterface",
         "//src/main/java/com/google/devtools/build/lib:unix",
         "//src/main/java/com/google/devtools/build/lib:util",
+        "//src/main/java/com/google/devtools/build/lib/actions:commandline_item",
         "//src/main/java/com/google/devtools/build/lib/analysis/platform",
         "//src/main/java/com/google/devtools/build/lib/buildeventstream",
         "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto",
@@ -48,3 +51,8 @@
         "//third_party/protobuf:protobuf_java",
     ],
 )
+
+java_library(
+    name = "commandline_item",
+    srcs = ["CommandLineItem.java"],
+)
diff --git a/src/main/java/com/google/devtools/build/lib/actions/CommandLine.java b/src/main/java/com/google/devtools/build/lib/actions/CommandLine.java
new file mode 100644
index 0000000..dcf1006
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/CommandLine.java
@@ -0,0 +1,184 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.actions;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
+import com.google.devtools.build.lib.collect.CollectionUtils;
+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.util.Fingerprint;
+
+/** A representation of a list of arguments. */
+@AutoCodec(strategy = Strategy.POLYMORPHIC)
+public abstract class CommandLine {
+  public static final ObjectCodec<CommandLine> CODEC = new CommandLine_AutoCodec();
+
+  @AutoCodec
+  @VisibleForSerialization
+  static class EmptyCommandLine extends CommandLine {
+    public static final ObjectCodec<EmptyCommandLine> CODEC =
+        new CommandLine_EmptyCommandLine_AutoCodec();
+
+    @Override
+    public Iterable<String> arguments() throws CommandLineExpansionException {
+      return ImmutableList.of();
+    }
+  }
+
+  public static final CommandLine EMPTY = new EmptyCommandLine();
+
+  /** Returns the command line. */
+  public abstract Iterable<String> arguments() throws CommandLineExpansionException;
+
+  /**
+   * Returns the evaluated command line with enclosed artifacts expanded by {@code artifactExpander}
+   * at execution time.
+   *
+   * <p>By default, this method just delegates to {@link #arguments()}, without performing any
+   * artifact expansion. Subclasses should override this method if they contain TreeArtifacts and
+   * need to expand them for proper argument evaluation.
+   */
+  public Iterable<String> arguments(ArtifactExpander artifactExpander)
+      throws CommandLineExpansionException {
+    return arguments();
+  }
+
+  public void addToFingerprint(ActionKeyContext actionKeyContext, Fingerprint fingerprint)
+      throws CommandLineExpansionException {
+    for (String s : arguments()) {
+      fingerprint.addString(s);
+    }
+  }
+
+  @AutoCodec
+  @VisibleForSerialization
+  static class ArgumentCommandLine extends CommandLine {
+    public static final ObjectCodec<ArgumentCommandLine> CODEC =
+        new CommandLine_ArgumentCommandLine_AutoCodec();
+
+    private Iterable<String> args;
+
+    ArgumentCommandLine(Iterable<String> args) {
+      this.args = args;
+    }
+
+    @Override
+    public Iterable<String> arguments() throws CommandLineExpansionException {
+      return args;
+    }
+  }
+
+  /** Returns a {@link CommandLine} backed by a copy of the given list of arguments. */
+  public static CommandLine of(Iterable<String> arguments) {
+    final Iterable<String> immutableArguments = CollectionUtils.makeImmutable(arguments);
+    return new ArgumentCommandLine(immutableArguments);
+  }
+
+  @AutoCodec
+  @VisibleForSerialization
+  static class ConcatenatedCommandLine extends CommandLine {
+    public static final ObjectCodec<ConcatenatedCommandLine> CODEC =
+        new CommandLine_ConcatenatedCommandLine_AutoCodec();
+
+    private ImmutableList<String> executableArgs;
+    private CommandLine commandLine;
+
+    @VisibleForSerialization
+    ConcatenatedCommandLine(ImmutableList<String> executableArgs, CommandLine commandLine) {
+      this.executableArgs = executableArgs;
+      this.commandLine = commandLine;
+    }
+
+    @Override
+    public Iterable<String> arguments() throws CommandLineExpansionException {
+      return Iterables.concat(executableArgs, commandLine.arguments());
+    }
+
+    @Override
+    public Iterable<String> arguments(ArtifactExpander artifactExpander)
+        throws CommandLineExpansionException {
+      return Iterables.concat(executableArgs, commandLine.arguments(artifactExpander));
+    }
+  }
+
+  /**
+   * Returns a {@link CommandLine} that is constructed by prepending the {@code executableArgs} to
+   * {@code commandLine}.
+   */
+  public static CommandLine concat(
+      final ImmutableList<String> executableArgs, final CommandLine commandLine) {
+    if (executableArgs.isEmpty()) {
+      return commandLine;
+    }
+    return new ConcatenatedCommandLine(executableArgs, commandLine);
+  }
+
+  @AutoCodec
+  @VisibleForSerialization
+  static class ReverseConcatenatedCommandLine extends CommandLine {
+    public static final ObjectCodec<ReverseConcatenatedCommandLine> CODEC =
+        new CommandLine_ReverseConcatenatedCommandLine_AutoCodec();
+
+    private ImmutableList<String> executableArgs;
+    private CommandLine commandLine;
+
+    @VisibleForSerialization
+    ReverseConcatenatedCommandLine(ImmutableList<String> executableArgs, CommandLine commandLine) {
+      this.executableArgs = executableArgs;
+      this.commandLine = commandLine;
+    }
+
+    @Override
+    public Iterable<String> arguments() throws CommandLineExpansionException {
+      return Iterables.concat(commandLine.arguments(), executableArgs);
+    }
+
+    @Override
+    public Iterable<String> arguments(ArtifactExpander artifactExpander)
+        throws CommandLineExpansionException {
+      return Iterables.concat(commandLine.arguments(artifactExpander), executableArgs);
+    }
+  }
+
+  /**
+   * Returns a {@link CommandLine} that is constructed by appending the {@code args} to {@code
+   * commandLine}.
+   */
+  public static CommandLine concat(
+      final CommandLine commandLine, final ImmutableList<String> args) {
+    if (args.isEmpty()) {
+      return commandLine;
+    }
+    return new ReverseConcatenatedCommandLine(args, commandLine);
+  }
+
+  /**
+   * This helps when debugging Blaze code that uses {@link CommandLine}s, as you can see their
+   * content directly in the variable inspector.
+   */
+  @Override
+  public String toString() {
+    try {
+      return Joiner.on(' ').join(arguments());
+    } catch (CommandLineExpansionException e) {
+      return "Error in expanding command line";
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/CommandLineItem.java b/src/main/java/com/google/devtools/build/lib/actions/CommandLineItem.java
new file mode 100644
index 0000000..3cd455f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/CommandLineItem.java
@@ -0,0 +1,84 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.actions;
+
+import java.util.function.Consumer;
+
+/** An interface for an object that customizes how it is expanded into a command line. */
+public interface CommandLineItem {
+  /**
+   * A map function that allows caller customization how a type is expanded into the command line.
+   */
+  interface MapFn<T> {
+    MapFn<Object> DEFAULT =
+        (Object object, Consumer<String> args) ->
+            args.accept(CommandLineItem.expandToCommandLine(object));
+
+    void expandToCommandLine(T object, Consumer<String> args);
+  }
+
+  /**
+   * Use this map function when parametrizing over a limited set of values.
+   *
+   * <p>The user promises that the number of distinct instances constructed is closer to O(rule
+   * class count) than O(rule count).
+   *
+   * <p>Without this, {@link
+   * com.google.devtools.build.lib.collect.nestedset.NestedSetFingerprintCache} will refuse to cache
+   * your {@link MapFn} computations.
+   */
+  abstract class ParametrizedMapFn<T> implements MapFn<T> {
+    @Override
+    public abstract boolean equals(Object obj);
+
+    @Override
+    public abstract int hashCode();
+
+    /**
+     * This method controls the max number of distinct instances allowed. If the system sees any
+     * more than this, it will throw.
+     *
+     * <p>Override and set this to something low. You want this to represent the small number of
+     * preallocated static instances used in this blaze instance. 3 is an OK number, 100 is a bad
+     * number.
+     */
+    public abstract int maxInstancesAllowed();
+  }
+
+  /**
+   * Use this map function when your map function needs to capture per-rule information.
+   *
+   * <p>Use of this class prevents sharing sub-computations over shared NestedSets, since the map
+   * function is per-target. This will make your action key computations become O(N^2). Please avoid
+   * if possible.
+   */
+  interface CapturingMapFn<T> extends MapFn<T> {}
+
+  /** Expands the object into the command line as a string. */
+  String expandToCommandLine();
+
+  /**
+   * The default method of expanding types.
+   *
+   * <p>If the object is a {@link CommandLineItem} we use its {@link
+   * CommandLineItem#expandToCommandLine} method, else we call {@link Object#toString()}.
+   */
+  static String expandToCommandLine(Object object) {
+    if (object instanceof CommandLineItem) {
+      return ((CommandLineItem) object).expandToCommandLine();
+    } else {
+      return object.toString();
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ParamFileInfo.java b/src/main/java/com/google/devtools/build/lib/actions/ParamFileInfo.java
new file mode 100644
index 0000000..27f4d45
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/ParamFileInfo.java
@@ -0,0 +1,130 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.actions;
+
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+
+import com.google.common.base.Preconditions;
+import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
+import java.nio.charset.Charset;
+import java.util.Objects;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * An object that encapsulates how a params file should be constructed: what is the filetype,
+ * what charset to use and what prefix (typically "@") to use.
+ */
+@Immutable
+public final class ParamFileInfo {
+  private final ParameterFileType fileType;
+  private final Charset charset;
+  private final String flagFormatString;
+  private final boolean always;
+
+  private ParamFileInfo(
+      ParameterFileType fileType, Charset charset, String flagFormatString, boolean always) {
+    this.fileType = Preconditions.checkNotNull(fileType);
+    this.charset = Preconditions.checkNotNull(charset);
+    this.flagFormatString = Preconditions.checkNotNull(flagFormatString);
+    this.always = always;
+  }
+
+  /**
+   * Returns the file type.
+   */
+  public ParameterFileType getFileType() {
+    return fileType;
+  }
+
+  /**
+   * Returns the charset.
+   */
+  public Charset getCharset() {
+    return charset;
+  }
+
+  /** Returns the format string for the params filename on the command line (typically "@%s"). */
+  public String getFlagFormatString() {
+    return flagFormatString;
+  }
+
+  /** Returns true if a params file should always be used. */
+  public boolean always() {
+    return always;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(charset, flagFormatString, fileType, always);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (!(obj instanceof ParamFileInfo)) {
+      return false;
+    }
+    ParamFileInfo other = (ParamFileInfo) obj;
+    return fileType.equals(other.fileType)
+        && charset.equals(other.charset)
+        && flagFormatString.equals(other.flagFormatString)
+        && always == other.always;
+  }
+
+  public static Builder builder(ParameterFileType parameterFileType) {
+    return new Builder(parameterFileType);
+  }
+
+  /** Builder for a ParamFileInfo. */
+  public static class Builder {
+    private final ParameterFileType fileType;
+    private Charset charset = ISO_8859_1;
+    private String flagFormatString = "@%s";
+    private boolean always;
+
+    private Builder(ParameterFileType fileType) {
+      this.fileType = fileType;
+    }
+
+    /** Sets the encoding to write the parameter file with. */
+    public Builder setCharset(Charset charset) {
+      this.charset = charset;
+      return this;
+    }
+
+    /**
+     * Sets a format string to use for the flag that is passed to original command.
+     *
+     * <p>The format string must have a single "%s" that will be replaced by the execution path to
+     * the param file.
+     */
+    public Builder setFlagFormatString(String flagFormatString) {
+      this.flagFormatString = flagFormatString;
+      return this;
+    }
+
+    /** Set whether the parameter file is always used, regardless of parameter file length. */
+    public Builder setUseAlways(boolean always) {
+      this.always = always;
+      return this;
+    }
+
+    public ParamFileInfo build() {
+      return new ParamFileInfo(fileType, charset, flagFormatString, always);
+    }
+  }
+}