Add jvm_opts attribute to the java_toolchain rule

This allows to override the JVM options given to the Java compiler
invocations.

Fixes #56

--
MOS_MIGRATED_REVID=93981759
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
index a1e41f7..004606c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
@@ -48,6 +48,7 @@
   private JavaTargetAttributes.Builder attributes;
   private JavaTargetAttributes builtAttributes;
   private final ImmutableList<String> customJavacOpts;
+  private final ImmutableList<String> customJavacJvmOpts;
   private final List<Artifact> translations = new ArrayList<>();
   private boolean translationsFrozen = false;
   private final JavaSemantics semantics;
@@ -57,6 +58,8 @@
     super(ruleContext);
     this.attributes = attributes;
     this.customJavacOpts = javacOpts;
+    this.customJavacJvmOpts =
+        ImmutableList.copyOf(JavaToolchainProvider.getDefaultJavacJvmOptions(ruleContext));
     this.semantics = semantics;
   }
 
@@ -85,7 +88,6 @@
   public void createCompileAction(Artifact outputJar, @Nullable Artifact gensrcOutputJar,
       @Nullable Artifact outputDepsProto, @Nullable Artifact outputMetadata) {
     JavaTargetAttributes attributes = getAttributes();
-    List<String> javacOpts = getJavacOpts();
     JavaCompileAction.Builder builder = createJavaCompileActionBuilder(semantics);
     builder.setClasspathEntries(attributes.getCompileTimeClassPath());
     builder.addResources(attributes.getResources());
@@ -106,7 +108,8 @@
     builder.setInstrumentationJars(getInstrumentationJars(semantics));
     builder.addSourceFiles(attributes.getSourceFiles());
     builder.addSourceJars(attributes.getSourceJars());
-    builder.setJavacOpts(javacOpts);
+    builder.setJavacOpts(customJavacOpts);
+    builder.setJavacJvmOpts(customJavacJvmOpts);
     builder.setCompressJar(true);
     builder.setClassDirectory(outputDir(outputJar));
     builder.setSourceGenDirectory(sourceGenDir(outputJar));
@@ -243,6 +246,11 @@
     builder.setClassDirectory(outputDir(resourceJar));
     builder.setTempDirectory(tempDir(resourceJar));
     builder.setJavaBuilderJar(getJavaBuilderJar());
+    builder.setJavacOpts(getDefaultJavacOptsFromRule(getRuleContext()));
+    builder.setJavacJvmOpts(
+        ImmutableList.copyOf(JavaToolchainProvider.getDefaultJavacOptions(getRuleContext())));
+    builder.setJavacJvmOpts(
+        ImmutableList.copyOf(JavaToolchainProvider.getDefaultJavacJvmOptions(getRuleContext())));
     getAnalysisEnvironment().registerAction(builder.build());
     return resourceJar;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
index 572fcc1..85d4647 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
@@ -723,6 +723,7 @@
     private final Collection<Artifact> directJars = new ArrayList<>();
     private final Collection<Artifact> compileTimeDependencyArtifacts = new ArrayList<>();
     private List<String> javacOpts = new ArrayList<>();
+    private ImmutableList<String> javacJvmOpts = ImmutableList.of();
     private boolean compressJar;
     private NestedSet<Artifact> classpathEntries =
         NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER);
@@ -856,7 +857,7 @@
           langtoolsJar,
           instrumentationJars,
           paramFile,
-          javaConfiguration.getDefaultJavaBuilderJvmFlags(),
+          javacJvmOpts,
           semantics.getJavaBuilderMainClass(),
           configuration.getHostPathSeparator());
 
@@ -978,6 +979,11 @@
       return this;
     }
 
+    public Builder setJavacJvmOpts(ImmutableList<String> opts) {
+      this.javacJvmOpts = opts;
+      return this;
+    }
+
     public Builder setCompressJar(boolean compressJar) {
       this.compressJar = compressJar;
       return this;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfigurationLoader.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfigurationLoader.java
index 53fdfdf..b1aa262 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfigurationLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfigurationLoader.java
@@ -55,22 +55,10 @@
     boolean generateJavaDeps = javaOptions.javaDeps ||
         javaOptions.experimentalJavaClasspath != JavaClasspathMode.OFF;
 
-    ImmutableList<String> defaultJavaBuilderJvmOpts = ImmutableList.<String>builder()
-        .addAll(getJavacJvmOptions())
-        .addAll(JavaHelper.tokenizeJavaOptions(javaOptions.javaBuilderJvmOpts))
-        .build();
+    ImmutableList<String> defaultJavaBuilderJvmOpts =
+        ImmutableList.copyOf(JavaHelper.tokenizeJavaOptions(javaOptions.javaBuilderJvmOpts));
 
     return new JavaConfiguration(generateJavaDeps, javaOptions.jvmOpts, javaOptions,
         javaToolchain, javaCpu, defaultJavaBuilderJvmOpts);
   }
-
-  /**
-   * This method returns the list of JVM options when invoking the java compiler.
-   *
-   * <p>TODO(bazel-team): Maybe we should put those options in the java_toolchain rule.
-   */
-  protected ImmutableList<String> getJavacJvmOptions() {
-    return ImmutableList.of("-client");
-  }
-
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java
index 65ed97a..aac1742 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.rules.java;
 
-import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
@@ -39,10 +38,12 @@
     final String encoding = ruleContext.attributes().get("encoding", Type.STRING);
     final List<String> xlint = ruleContext.attributes().get("xlint", Type.STRING_LIST);
     final List<String> misc = ruleContext.attributes().get("misc", Type.STRING_LIST);
+    final List<String> jvmOpts = ruleContext.attributes().get("jvm_opts", Type.STRING_LIST);
+    final JavaToolchainData toolchainData =
+        new JavaToolchainData(source, target, encoding, xlint, misc, jvmOpts);
     final JavaConfiguration configuration = ruleContext.getFragment(JavaConfiguration.class);
-    JavaToolchainProvider provider = new JavaToolchainProvider(source, target, encoding,
-        ImmutableList.copyOf(xlint), ImmutableList.copyOf(misc),
-        configuration.getDefaultJavacFlags());
+    JavaToolchainProvider provider = new JavaToolchainProvider(toolchainData,
+        configuration.getDefaultJavacFlags(), configuration.getDefaultJavaBuilderJvmFlags());
     RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext)
         .add(JavaToolchainProvider.class, provider)
         .setFilesToBuild(new NestedSetBuilder<Artifact>(Order.STABLE_ORDER).build())
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainData.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainData.java
index 0338fb8..f3c94f4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainData.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainData.java
@@ -18,6 +18,8 @@
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 
+import java.util.List;
+
 /**
  * Information about the JDK used by the <code>java_*</code> rules.
  *
@@ -27,9 +29,11 @@
 @Immutable
 public class JavaToolchainData {
   private final ImmutableList<String> options;
+  private final ImmutableList<String> jvmOpts;
 
   public JavaToolchainData(String source, String target, String encoding,
-      ImmutableList<String> xlint, ImmutableList<String> misc) {
+      List<String> xlint, List<String> misc, List<String> jvmOpts) {
+    this.jvmOpts = ImmutableList.copyOf(jvmOpts);
     Builder<String> builder = ImmutableList.<String>builder();
     if (!source.isEmpty()) {
       builder.add("-source", source);
@@ -52,4 +56,11 @@
   public ImmutableList<String> getJavacOptions() {
     return options;
   }
+
+  /**
+   * @return the list of options to be given to the JVM when invoking the java compiler.
+   */
+  public ImmutableList<String> getJavacJvmOptions() {
+    return jvmOpts;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainDataParser.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainDataParser.java
index 64e1eb3..688e02a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainDataParser.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainDataParser.java
@@ -75,6 +75,7 @@
     String encoding = "";
     ImmutableList<String> xlint = ImmutableList.of();
     ImmutableList<String> misc = ImmutableList.of();
+    ImmutableList<String> jvmOpts = ImmutableList.of();
     for (Build.Attribute attribute : rule.getAttributeList()) {
       switch (attribute.getName()) {
         case "source_version":
@@ -92,8 +93,11 @@
         case "misc":
           misc = ImmutableList.copyOf(attribute.getStringListValueList());
           break;
+        case "jvm_opts":
+          jvmOpts = ImmutableList.copyOf(attribute.getStringListValueList());
+          break;
       }
     }
-    return new JavaToolchainData(source, target, encoding, xlint, misc);
+    return new JavaToolchainData(source, target, encoding, xlint, misc, jvmOpts);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainProvider.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainProvider.java
index 3e210d8..3c2c4f3 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainProvider.java
@@ -18,6 +18,7 @@
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.Type;
 
 import java.util.List;
 
@@ -28,17 +29,23 @@
 public final class JavaToolchainProvider implements TransitiveInfoProvider {
 
   private final ImmutableList<String> javacOptions;
+  private final ImmutableList<String> javacJvmOptions;
 
-  public JavaToolchainProvider(String source, String target, String encoding,
-      ImmutableList<String> xlint, ImmutableList<String> misc, List<String> defaultJavacFlags) {
+  public JavaToolchainProvider(JavaToolchainData data, List<String> defaultJavacFlags,
+      List<String> defaultJavacJvmOpts) {
     super();
     // merges the defaultJavacFlags from
     // {@link JavaConfiguration} with the flags from the {@code java_toolchain} rule.
-    JavaToolchainData data = new JavaToolchainData(source, target, encoding, xlint, misc);
     this.javacOptions = ImmutableList.<String>builder()
         .addAll(data.getJavacOptions())
         .addAll(defaultJavacFlags)
         .build();
+    // merges the defaultJavaBuilderJvmFlags from
+    // {@link JavaConfiguration} with the flags from the {@code java_toolchain} rule.
+    this.javacJvmOptions = ImmutableList.<String>builder()
+        .addAll(data.getJavacJvmOptions())
+        .addAll(defaultJavacJvmOpts)
+        .build();
   }
 
   /**
@@ -49,6 +56,13 @@
   }
 
   /**
+   * @return the list of default options for the JVM running the java compiler
+   */
+  public ImmutableList<String> getJavacJvmOptions() {
+    return javacJvmOptions;
+  }
+
+  /**
    * An helper method to construct the list of javac options.
    *
    * @param ruleContext The rule context of the current rule.
@@ -59,10 +73,31 @@
     JavaToolchainProvider javaToolchain =
         ruleContext.getPrerequisite(":java_toolchain", Mode.TARGET, JavaToolchainProvider.class);
     if (javaToolchain == null) {
-      ruleContext.ruleError("No java_toolchain implicit dependency found. This is probably because"
-          + " your java configuration is not up-to-date.");
+      ruleContext.ruleError("The --java_toolchain option does not point to a java_toolchain rule.");
       return ImmutableList.of();
     }
     return javaToolchain.getJavacOptions();
   }
+
+  /**
+   * An helper method to construct the list of options to pass to the JVM running the java compiler.
+   *
+   * @param ruleContext The rule context of the current rule.
+   * @return the list of flags provided by the {@code java_toolchain} rule merged with the one
+   *         provided by the {@link JavaConfiguration} fragment.
+   */
+  public static List<String> getDefaultJavacJvmOptions(RuleContext ruleContext) {
+    if (!ruleContext.getRule().isAttrDefined(":java_toolchain", Type.LABEL))  {
+      // As some rules might not have java_toolchain dependency (e.g., java_import), we silently
+      // ignore it. The rules needing it will error in #getDefaultJavacOptions(RuleContext) anyway.
+      return ImmutableList.of();
+    }
+    JavaToolchainProvider javaToolchain =
+        ruleContext.getPrerequisite(":java_toolchain", Mode.TARGET, JavaToolchainProvider.class);
+    if (javaToolchain == null) {
+      ruleContext.ruleError("The --java_toolchain option does not point to a java_toolchain rule.");
+      return ImmutableList.of();
+    }
+    return javaToolchain.getJavacJvmOptions();
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainRule.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainRule.java
index 5da931d..4a45461 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchainRule.java
@@ -28,6 +28,20 @@
  * Rule definition for {@code java_toolchain}
  */
 public final class JavaToolchainRule implements RuleDefinition {
+  // TODO(dmarting): remove this and make -client the default once the jvm_opts field is released.
+  private final ImmutableList<String> defaultJavacJvmOpts;
+
+  public JavaToolchainRule() {
+    defaultJavacJvmOpts = ImmutableList.<String>of("-client");
+  }
+
+  /**
+   * Construct a {@link JavaToolchainRule} with a different set of default JVM options for Javac.
+   */
+  public JavaToolchainRule(ImmutableList<String> defaultJavacJvmOpts) {
+    this.defaultJavacJvmOpts = defaultJavacJvmOpts;
+  }
+
   @Override
   public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
     return builder.setUndocumented()
@@ -50,11 +64,16 @@
         removes it. Please see the Javac documentation on the -Xlint options for more information.
         <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
         .add(attr("xlint", STRING_LIST).value(ImmutableList.<String>of()))
-        /* <!-- #BLAZE_RULE(java_toolchain).ATTRIBUTE(xlint) -->
+        /* <!-- #BLAZE_RULE(java_toolchain).ATTRIBUTE(misc) -->
         The list of extra arguments for the Java compiler. Please refer to the Java compiler
         documentation for the extensive list of possible Java compiler flags.
         <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
         .add(attr("misc", STRING_LIST).value(ImmutableList.<String>of()))
+        /* <!-- #BLAZE_RULE(java_toolchain).ATTRIBUTE(jvm_opts) -->
+        The list of arguments for the JVM when invoking the Java compiler. Please refer to the Java
+        virtual machine documentation for the extensive list of possible flags for this option.
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("jvm_opts", STRING_LIST).value(defaultJavacJvmOpts))
         .build();
   }