Clean the API of ProtoCompileAction, in preparation for making the proto-compiler use a params file.

--
MOS_MIGRATED_REVID=119193368
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 5eac0d6..efd1379 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -720,6 +720,7 @@
    *
    * @return the {@link FilesToRunProvider} interface of the prerequisite.
    */
+  @Nullable
   public FilesToRunProvider getExecutablePrerequisite(String attributeName, Mode mode) {
     Attribute ruleDefinition = getAttribute(attributeName);
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java
deleted file mode 100644
index 94aca1c..0000000
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2015 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.rules.proto;
-
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.isEmpty;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ResourceSet;
-import com.google.devtools.build.lib.analysis.FilesToRunProvider;
-import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
-import com.google.devtools.build.lib.analysis.RuleContext;
-import com.google.devtools.build.lib.analysis.actions.CommandLine;
-import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
-import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.CustomMultiArgv;
-import com.google.devtools.build.lib.analysis.actions.SpawnAction;
-import com.google.devtools.build.lib.util.LazyString;
-
-import java.util.List;
-
-/**
- * An action to run the protocol compiler to generate sources from .proto files.
- */
-public final class ProtoCompileAction {
-
-  private static final String MNEMONIC = "GenProto";
-  private static final ResourceSet GENPROTO_RESOURCE_SET =
-      ResourceSet.createWithRamCpuIo(100, .1, .0);
-
-  private final RuleContext ruleContext;
-  private final SupportData supportData;
-  private final String language;
-  private final Iterable<Artifact> outputs;
-  private final List<? extends CharSequence> prefixArguments;
-  private final FilesToRunProvider langPluginTarget;
-  private final FilesToRunProvider compilerTarget;
-  private final List<String> suffixArguments;
-
-  public static class Builder {
-    private RuleContext ruleContext;
-    private SupportData supportData;
-    private String language;
-    private String langPrefix;
-    private Iterable<Artifact> outputs;
-    private String langParameter;
-    private String langPluginName;
-    private String langPluginParameter;
-    private Supplier<String> langPluginParameterSupplier;
-    private boolean hasServices;
-
-    public Builder setRuleContext(RuleContext ruleContext) {
-      this.ruleContext = ruleContext;
-      return this;
-    }
-
-    public Builder setSupportData(SupportData supportData) {
-      this.supportData = supportData;
-      return this;
-    }
-
-    public Builder setLanguage(String language) {
-      this.language = language;
-      return this;
-    }
-
-    public Builder setLangPrefix(String langPrefix) {
-      this.langPrefix = langPrefix;
-      return this;
-    }
-
-    public Builder setHasServices(boolean hasServices) {
-      this.hasServices = hasServices;
-      return this;
-    }
-
-    public Builder setOutputs(Iterable<Artifact> outputs) {
-      this.outputs = outputs;
-      return this;
-    }
-
-    public Builder setLangParameter(String langParameter) {
-      this.langParameter = langParameter;
-      return this;
-    }
-
-    public Builder setLangPluginName(String langPluginName) {
-      this.langPluginName = langPluginName;
-      return this;
-    }
-
-    public Builder setLangPluginParameter(String langPluginParameter) {
-      this.langPluginParameter = langPluginParameter;
-      return this;
-    }
-
-    public Builder setLangPluginParameterSupplier(Supplier<String> langPluginParameterSupplier) {
-      this.langPluginParameterSupplier = langPluginParameterSupplier;
-      return this;
-    }
-
-    public Builder(RuleContext ruleContext, SupportData supportData, String language,
-        String langPrefix, Iterable<Artifact> outputs) {
-      this.ruleContext = ruleContext;
-      this.supportData = supportData;
-      this.language = language;
-      this.langPrefix = langPrefix;
-      this.outputs = outputs;
-    }
-
-    /** Static class to avoid keeping a reference to this builder after build() is called. */
-    private static class LazyLangPluginFlag extends LazyString {
-      private final String langPrefix;
-      private final Supplier<String> langPluginParameter1;
-
-      LazyLangPluginFlag(String langPrefix, Supplier<String> langPluginParameter1) {
-        this.langPrefix = langPrefix;
-        this.langPluginParameter1 = langPluginParameter1;
-      }
-
-      @Override
-      public String toString() {
-        return String.format("--%s_out=%s", langPrefix, langPluginParameter1.get());
-      }
-    }
-
-    public Optional<ProtoCompileAction> build() {
-      checkState(langPluginParameter == null || langPluginParameterSupplier == null,
-          "Only one of {langPluginParameter, langPluginParameterSupplier} should be set.");
-
-      final Supplier<String> langPluginParameter1 =
-          langPluginParameter == null
-              ? langPluginParameterSupplier
-              : Suppliers.ofInstance(langPluginParameter);
-      if (isEmpty(outputs)) {
-        return absent();
-      }
-
-      FilesToRunProvider langPluginTarget = null;
-      List<? extends CharSequence> prefixArguments;
-      if (langPluginName != null) {
-        Preconditions.checkArgument(langParameter == null);
-        Preconditions.checkArgument(langPluginParameter1 != null);
-        // We pass a separate langPluginName as there are plugins that cannot be overridden
-        // and thus we have to deal with "$xx_plugin" and "xx_plugin".
-        langPluginTarget = ruleContext.getExecutablePrerequisite(langPluginName, Mode.HOST);
-        if (ruleContext.hasErrors()) {
-          return absent();
-        }
-        prefixArguments =
-            ImmutableList.of(
-                String.format(
-                    "--plugin=protoc-gen-%s=%s",
-                    langPrefix,
-                    langPluginTarget.getExecutable().getExecPathString()),
-                new LazyLangPluginFlag(langPrefix, langPluginParameter1));
-      } else {
-        prefixArguments =
-            (langParameter != null) ? ImmutableList.of(langParameter) : ImmutableList.<String>of();
-      }
-
-      List<String> suffixArguments =
-          hasServices ? ImmutableList.<String>of() : ImmutableList.of("--disallow_services");
-
-      FilesToRunProvider compilerTarget =
-          ruleContext.getExecutablePrerequisite("$compiler", Mode.HOST);
-
-      if (ruleContext.hasErrors()) {
-        return absent();
-      }
-
-      return Optional.of(
-          new ProtoCompileAction(
-              ruleContext,
-              supportData,
-              language,
-              suffixArguments,
-              outputs,
-              prefixArguments,
-              langPluginTarget,
-              compilerTarget));
-    }
-  }
-
-  /**
-   * A convenience method to register an action, if it's present.
-   * @param protoCompileActionOptional
-   */
-  public static void registerAction(Optional<ProtoCompileAction> protoCompileActionOptional) {
-    if (protoCompileActionOptional.isPresent()) {
-      protoCompileActionOptional.get().registerAction();
-    }
-  }
-
-  public ProtoCompileAction(
-      RuleContext ruleContext,
-      SupportData supportData,
-      String language,
-      List<String> suffixArguments,
-      Iterable<Artifact> outputs,
-      List<? extends CharSequence> prefixArguments,
-      FilesToRunProvider langPluginTarget,
-      FilesToRunProvider compilerTarget) {
-    this.ruleContext = ruleContext;
-    this.supportData = supportData;
-    this.language = language;
-    this.suffixArguments = suffixArguments;
-    this.outputs = outputs;
-    this.prefixArguments = prefixArguments;
-    this.langPluginTarget = langPluginTarget;
-    this.compilerTarget = compilerTarget;
-  }
-
-  /**
-   * Registers a proto compile action with the RuleContext.
-   */
-  public void registerAction() {
-    SpawnAction.Builder action = createAction(protoCompileCommandLine().build());
-    ruleContext.registerAction(action.build(ruleContext));
-  }
-
-  public SpawnAction.Builder createAction(CommandLine commandLine) {
-    SpawnAction.Builder builder =
-        new SpawnAction.Builder().addTransitiveInputs(supportData.getTransitiveImports());
-
-    // We also depend on the strict protodeps result to ensure this is run.
-    if (supportData.getUsedDirectDeps() != null) {
-      builder.addInput(supportData.getUsedDirectDeps());
-    }
-
-    if (langPluginTarget != null) {
-      builder.addTool(langPluginTarget);
-    }
-
-    builder
-        .addOutputs(outputs)
-        .setResources(GENPROTO_RESOURCE_SET)
-        .useDefaultShellEnvironment()
-        .setExecutable(compilerTarget)
-        .setCommandLine(commandLine)
-        .setProgressMessage("Generating " + language + " proto_library " + ruleContext.getLabel())
-        .setMnemonic(MNEMONIC);
-
-    return builder;
-  }
-
-  /**
-   * Static inner class since these objects live into the execution phase and so they must not
-   * keep alive references to the surrounding analysis-phase objects.
-   */
-  private static class ProtoCommandLineArgv extends CustomMultiArgv {
-    private final Iterable<Artifact> transitiveImports;
-
-    ProtoCommandLineArgv(Iterable<Artifact> transitiveImports) {
-      this.transitiveImports = transitiveImports;
-    }
-
-    @Override
-    public Iterable<String> argv() {
-      ImmutableList.Builder<String> builder = ImmutableList.builder();
-      for (Artifact artifact : transitiveImports) {
-        builder.add(
-            "-I"
-                + artifact.getRootRelativePath().getPathString()
-                + "="
-                + artifact.getExecPathString());
-      }
-      return builder.build();
-    }
-  }
-
-  /* Commandline generator for protoc invocations. */
-  public CustomCommandLine.Builder protoCompileCommandLine() {
-    CustomCommandLine.Builder arguments = CustomCommandLine.builder();
-    for (CharSequence charSequence : prefixArguments) {
-      arguments.add(charSequence);
-    }
-    arguments.add(ruleContext.getFragment(ProtoConfiguration.class).protocOpts());
-
-    // Add include maps
-    arguments.add(new ProtoCommandLineArgv(supportData.getTransitiveImports()));
-
-    for (Artifact src : supportData.getDirectProtoSources()) {
-      arguments.addPath(src.getRootRelativePath());
-    }
-
-    arguments.add(suffixArguments);
-    return arguments;
-  }
-}
-
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilder.java
new file mode 100644
index 0000000..a743bc4
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilder.java
@@ -0,0 +1,297 @@
+// Copyright 2016 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.rules.proto;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.isEmpty;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ResourceSet;
+import com.google.devtools.build.lib.analysis.FilesToRunProvider;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
+import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+import com.google.devtools.build.lib.util.LazyString;
+
+import javax.annotation.Nullable;
+
+/**
+ * Constructs actions to run the protocol compiler to generate sources from .proto files.
+ */
+public class ProtoCompileActionBuilder {
+  private static final String MNEMONIC = "GenProto";
+  private static final ResourceSet GENPROTO_RESOURCE_SET =
+      ResourceSet.createWithRamCpuIo(100, .1, .0);
+  private static final Action[] NO_ACTIONS = new Action[0];
+
+  private RuleContext ruleContext;
+  private SupportData supportData;
+  private String language;
+  private String langPrefix;
+  private Iterable<Artifact> outputs;
+  private String langParameter;
+  private String langPluginName;
+  private String langPluginParameter;
+  private Supplier<String> langPluginParameterSupplier;
+  private boolean hasServices;
+  private Iterable<String> additionalCommandLineArguments;
+  private Iterable<FilesToRunProvider> additionalTools;
+
+  public ProtoCompileActionBuilder setRuleContext(RuleContext ruleContext) {
+    this.ruleContext = ruleContext;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setSupportData(SupportData supportData) {
+    this.supportData = supportData;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setLanguage(String language) {
+    this.language = language;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setLangPrefix(String langPrefix) {
+    this.langPrefix = langPrefix;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setHasServices(boolean hasServices) {
+    this.hasServices = hasServices;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setOutputs(Iterable<Artifact> outputs) {
+    this.outputs = outputs;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setLangParameter(String langParameter) {
+    this.langParameter = langParameter;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setLangPluginName(String langPluginName) {
+    this.langPluginName = langPluginName;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setLangPluginParameter(String langPluginParameter) {
+    this.langPluginParameter = langPluginParameter;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setLangPluginParameterSupplier(
+      Supplier<String> langPluginParameterSupplier) {
+    this.langPluginParameterSupplier = langPluginParameterSupplier;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setAdditionalCommandLineArguments(
+      Iterable<String> additionalCmdLine) {
+    this.additionalCommandLineArguments = additionalCmdLine;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder setAdditionalTools(
+      Iterable<FilesToRunProvider> additionalTools) {
+    this.additionalTools = additionalTools;
+    return this;
+  }
+
+  public ProtoCompileActionBuilder(
+      RuleContext ruleContext,
+      SupportData supportData,
+      String language,
+      String langPrefix,
+      Iterable<Artifact> outputs) {
+    this.ruleContext = ruleContext;
+    this.supportData = supportData;
+    this.language = language;
+    this.langPrefix = langPrefix;
+    this.outputs = outputs;
+  }
+
+  /**
+   * Static class to avoid keeping a reference to this builder after build() is called.
+   */
+  private static class LazyLangPluginFlag extends LazyString {
+    private final String langPrefix;
+    private final Supplier<String> langPluginParameter1;
+
+    LazyLangPluginFlag(String langPrefix, Supplier<String> langPluginParameter1) {
+      this.langPrefix = langPrefix;
+      this.langPluginParameter1 = langPluginParameter1;
+    }
+
+    @Override
+    public String toString() {
+      return String.format("--%s_out=%s", langPrefix, langPluginParameter1.get());
+    }
+  }
+
+  public Action[] build() {
+    checkState(
+        langPluginParameter == null || langPluginParameterSupplier == null,
+        "Only one of {langPluginParameter, langPluginParameterSupplier} should be set.");
+
+    if (isEmpty(outputs)) {
+      return NO_ACTIONS;
+    }
+
+    try {
+      return createAction().build(ruleContext);
+    } catch (MissingPrerequisiteException e) {
+      return NO_ACTIONS;
+    }
+  }
+
+  private SpawnAction.Builder createAction() {
+    SpawnAction.Builder result =
+        new SpawnAction.Builder().addTransitiveInputs(supportData.getTransitiveImports());
+
+    // We also depend on the strict protodeps result to ensure this is run.
+    if (supportData.getUsedDirectDeps() != null) {
+      result.addInput(supportData.getUsedDirectDeps());
+    }
+
+    FilesToRunProvider langPluginTarget = getLangPluginTarget();
+    if (langPluginTarget != null) {
+      result.addTool(langPluginTarget);
+    }
+
+    FilesToRunProvider compilerTarget =
+        ruleContext.getExecutablePrerequisite("$compiler", RuleConfiguredTarget.Mode.HOST);
+    if (ruleContext.hasErrors()) {
+      throw new MissingPrerequisiteException();
+    }
+
+    if (this.additionalTools != null) {
+      for (FilesToRunProvider tool : additionalTools) {
+        result.addTool(tool);
+      }
+    }
+
+    result
+        .addOutputs(outputs)
+        .setResources(GENPROTO_RESOURCE_SET)
+        .useDefaultShellEnvironment()
+        .setExecutable(compilerTarget)
+        .setCommandLine(createProtoCompilerCommandLine().build())
+        .setProgressMessage("Generating " + language + " proto_library " + ruleContext.getLabel())
+        .setMnemonic(MNEMONIC);
+
+    return result;
+  }
+
+  @Nullable
+  private FilesToRunProvider getLangPluginTarget() {
+    if (langPluginName == null) {
+      return null;
+    }
+    FilesToRunProvider result =
+        ruleContext.getExecutablePrerequisite(langPluginName, RuleConfiguredTarget.Mode.HOST);
+    if (ruleContext.hasErrors()) {
+      throw new MissingPrerequisiteException();
+    }
+    return result;
+  }
+
+  /**
+   * Commandline generator for protoc invocations.
+   */
+  public CustomCommandLine.Builder createProtoCompilerCommandLine() {
+    CustomCommandLine.Builder result = CustomCommandLine.builder();
+
+    if (langPluginName == null) {
+      if (langParameter != null) {
+        result.add(langParameter);
+      }
+    } else {
+      FilesToRunProvider langPluginTarget = getLangPluginTarget();
+      Supplier<String> langPluginParameter1 =
+          langPluginParameter == null
+              ? langPluginParameterSupplier
+              : Suppliers.ofInstance(langPluginParameter);
+
+      Preconditions.checkArgument(langParameter == null);
+      Preconditions.checkArgument(langPluginParameter1 != null);
+      // We pass a separate langPluginName as there are plugins that cannot be overridden
+      // and thus we have to deal with "$xx_plugin" and "xx_plugin".
+      result.add(
+          String.format(
+              "--plugin=protoc-gen-%s=%s",
+              langPrefix,
+              langPluginTarget.getExecutable().getExecPathString()));
+      result.add(new LazyLangPluginFlag(langPrefix, langPluginParameter1));
+    }
+
+    result.add(ruleContext.getFragment(ProtoConfiguration.class).protocOpts());
+
+    // Add include maps
+    result.add(new ProtoCommandLineArgv(supportData.getTransitiveImports()));
+
+    for (Artifact src : supportData.getDirectProtoSources()) {
+      result.addPath(src.getRootRelativePath());
+    }
+
+    if (!hasServices) {
+      result.add("--disallow_services");
+    }
+
+    if (additionalCommandLineArguments != null) {
+      result.add(additionalCommandLineArguments);
+    }
+
+    return result;
+  }
+
+  /**
+   * Static inner class since these objects live into the execution phase and so they must not
+   * keep alive references to the surrounding analysis-phase objects.
+   */
+  private static class ProtoCommandLineArgv extends CustomCommandLine.CustomMultiArgv {
+    private final Iterable<Artifact> transitiveImports;
+
+    ProtoCommandLineArgv(Iterable<Artifact> transitiveImports) {
+      this.transitiveImports = transitiveImports;
+    }
+
+    @Override
+    public Iterable<String> argv() {
+      ImmutableList.Builder<String> builder = ImmutableList.builder();
+      for (Artifact artifact : transitiveImports) {
+        builder.add(
+            "-I"
+                + artifact.getRootRelativePath().getPathString()
+                + "="
+                + artifact.getExecPathString());
+      }
+      return builder.build();
+    }
+  }
+
+  /**
+   * Signifies that a prerequisite could not be satisfied.
+   */
+  private static class MissingPrerequisiteException extends RuntimeException {}
+}