Add a javac_supports_workers attribute to the java_toolchain rule.

If this is set to true (which it is by default), JavaCompilationHelper will set the "supports-workers" tag on the execution info for the Javac SpawnAction. If it is explicitly set to false, this tag will be absent.

In a follow-up CL, the WorkerSpawnStrategy will check whether this tag is present and fallback to non-worker execution if not.

This is needed to safely enable workers by default in Bazel without breaking the older JDK7 JavaBuilder, which does not support workers yet.

--
MOS_MIGRATED_REVID=126290991
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 0d6125d..21b94c9 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
@@ -17,6 +17,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
@@ -150,6 +151,7 @@
     builder.addSourceJars(attributes.getSourceJars());
     builder.setJavacOpts(customJavacOpts);
     builder.setJavacJvmOpts(customJavacJvmOpts);
+    builder.setJavacExecutionInfo(getExecutionInfo());
     builder.setCompressJar(true);
     builder.setSourceGenDirectory(sourceGenDir(outputJar));
     builder.setTempDirectory(tempDir(outputJar));
@@ -164,6 +166,13 @@
     getAnalysisEnvironment().registerAction(builder.build());
   }
 
+  private ImmutableMap<String, String> getExecutionInfo() {
+    if (javaToolchain.getJavacSupportsWorkers()) {
+      return ImmutableMap.of("supports-workers", "1");
+    }
+    return ImmutableMap.of();
+  }
+
   /** Returns the bootclasspath explicit set in attributes if present, or else the default. */
   public ImmutableList<Artifact> getBootclasspathOrDefault() {
     JavaTargetAttributes attributes = getAttributes();
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 c9abbda..d5651ae 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
@@ -149,6 +149,9 @@
   /** The subset of classpath jars provided by direct dependencies. */
   private final NestedSet<Artifact> directJars;
 
+  /** The ExecutionInfo to be used when creating the SpawnAction for this compilation. */
+  private final ImmutableMap<String, String> executionInfo;
+
   /**
    * The level of strict dependency checks (off, warnings, or errors).
    */
@@ -192,6 +195,7 @@
       Collection<Artifact> sourceFiles,
       List<String> javacOpts,
       NestedSet<Artifact> directJars,
+      Map<String, String> executionInfo,
       BuildConfiguration.StrictDepsMode strictJavaDeps,
       Collection<Artifact> compileTimeDependencyArtifacts) {
     super(
@@ -227,6 +231,7 @@
     this.sourceFiles = ImmutableList.copyOf(sourceFiles);
     this.javacOpts = ImmutableList.copyOf(javacOpts);
     this.directJars = checkNotNull(directJars, "directJars must not be null");
+    this.executionInfo = ImmutableMap.copyOf(executionInfo);
     this.strictJavaDeps = strictJavaDeps;
     this.compileTimeDependencyArtifacts = ImmutableList.copyOf(compileTimeDependencyArtifacts);
   }
@@ -292,6 +297,11 @@
   }
 
   @VisibleForTesting
+  public ImmutableMap<String, String> getExecutionInfo() {
+    return executionInfo;
+  }
+
+  @VisibleForTesting
   public NestedSet<Artifact> getDirectJars() {
     return directJars;
   }
@@ -356,7 +366,7 @@
     return new BaseSpawn(
         getCommand(),
         ImmutableMap.of("LC_CTYPE", "en_US.UTF-8"),
-        /*executionInfo=*/ ImmutableMap.<String, String>of(),
+        executionInfo,
         this,
         LOCAL_RESOURCES);
   }
@@ -793,6 +803,7 @@
     private final Collection<Artifact> compileTimeDependencyArtifacts = new ArrayList<>();
     private List<String> javacOpts = new ArrayList<>();
     private ImmutableList<String> javacJvmOpts = ImmutableList.of();
+    private ImmutableMap<String, String> executionInfo = ImmutableMap.of();
     private boolean compressJar;
     private NestedSet<Artifact> classpathEntries =
         NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER);
@@ -964,6 +975,7 @@
           sourceFiles,
           internedJcopts,
           directJars,
+          executionInfo,
           strictJavaDeps,
           compileTimeDependencyArtifacts);
     }
@@ -1068,6 +1080,11 @@
       return this;
     }
 
+    public Builder setJavacExecutionInfo(ImmutableMap<String, String> executionInfo) {
+      this.executionInfo = executionInfo;
+      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/JavaToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java
index 671dcf0..7030f81 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
@@ -29,8 +29,8 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
-import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.rules.java.JavaToolchainData.SupportsWorkers;
 import com.google.devtools.build.lib.syntax.Type;
 
 import java.util.List;
@@ -51,6 +51,8 @@
     final List<String> xlint = ruleContext.attributes().get("xlint", Type.STRING_LIST);
     final List<String> misc = ruleContext.getTokenizedStringListAttr("misc");
     final List<String> jvmOpts = ruleContext.attributes().get("jvm_opts", Type.STRING_LIST);
+    final boolean javacSupportsWorkers =
+        ruleContext.attributes().get("javac_supports_workers", Type.BOOLEAN);
     Artifact javac = getArtifact("javac", ruleContext);
     Artifact javabuilder = getArtifact("javabuilder", ruleContext);
     Artifact headerCompiler = getArtifact("header_compiler", ruleContext);
@@ -69,7 +71,8 @@
             encoding,
             xlint,
             misc,
-            jvmOpts);
+            jvmOpts,
+            javacSupportsWorkers ? SupportsWorkers.YES : SupportsWorkers.NO);
     final JavaConfiguration configuration = ruleContext.getFragment(JavaConfiguration.class);
     JavaToolchainProvider provider =
         new JavaToolchainProvider(
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 49a0663..ba6c3dc 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
@@ -31,6 +31,11 @@
 @Immutable
 public class JavaToolchainData {
 
+  public enum SupportsWorkers {
+    NO,
+    YES
+  }
+
   private final String sourceVersion;
   private final String targetVersion;
   private final Iterable<String> bootclasspath;
@@ -38,6 +43,7 @@
   private final String encoding;
   private final ImmutableList<String> options;
   private final ImmutableList<String> jvmOpts;
+  private boolean javacSupportsWorkers;
 
   public JavaToolchainData(
       String sourceVersion,
@@ -47,7 +53,8 @@
       String encoding,
       List<String> xlint,
       List<String> misc,
-      List<String> jvmOpts) {
+      List<String> jvmOpts,
+      SupportsWorkers javacSupportsWorkers) {
     this.sourceVersion = checkNotNull(sourceVersion, "sourceVersion must not be null");
     this.targetVersion = checkNotNull(targetVersion, "targetVersion must not be null");
     this.bootclasspath = checkNotNull(bootclasspath, "bootclasspath must not be null");
@@ -55,6 +62,7 @@
     this.encoding = checkNotNull(encoding, "encoding must not be null");
 
     this.jvmOpts = ImmutableList.copyOf(jvmOpts);
+    this.javacSupportsWorkers = javacSupportsWorkers.equals(SupportsWorkers.YES);
     Builder<String> builder = ImmutableList.<String>builder();
     if (!sourceVersion.isEmpty()) {
       builder.add("-source", sourceVersion);
@@ -104,4 +112,8 @@
   public String getEncoding() {
     return encoding;
   }
+
+  public boolean getJavacSupportsWorkers() {
+    return javacSupportsWorkers;
+  }
 }
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 1091dc0..4c1558f 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
@@ -18,6 +18,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.query2.proto.proto2api.Build;
 import com.google.devtools.build.lib.query2.proto.proto2api.Build.QueryResult;
+import com.google.devtools.build.lib.rules.java.JavaToolchainData.SupportsWorkers;
 import com.google.devtools.build.lib.shell.ShellUtils;
 import com.google.protobuf.TextFormat;
 import com.google.protobuf.TextFormat.ParseException;
@@ -81,6 +82,7 @@
     ImmutableList<String> xlint = ImmutableList.of();
     ImmutableList<String> misc = ImmutableList.of();
     ImmutableList<String> jvmOpts = ImmutableList.of();
+    SupportsWorkers javacSupportsWorkers = SupportsWorkers.NO;
     for (Build.Attribute attribute : rule.getAttributeList()) {
       switch (attribute.getName()) {
         case "source_version":
@@ -120,9 +122,22 @@
         case "jvm_opts":
           jvmOpts = ImmutableList.copyOf(attribute.getStringListValueList());
           break;
+        case "javac_supports_workers":
+          if (attribute.getBooleanValue()) {
+            javacSupportsWorkers = SupportsWorkers.YES;
+          }
+          break;
       }
     }
     return new JavaToolchainData(
-        source, target, bootclasspath, extclasspath, encoding, xlint, misc, jvmOpts);
+        source,
+        target,
+        bootclasspath,
+        extclasspath,
+        encoding,
+        xlint,
+        misc,
+        jvmOpts,
+        javacSupportsWorkers);
   }
 }
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 aeb9e94..e00f7bb 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
@@ -47,6 +47,7 @@
   private final String encoding;
   private final ImmutableList<String> javacOptions;
   private final ImmutableList<String> javacJvmOptions;
+  private final boolean javacSupportsWorkers;
   private final Artifact javac;
   private final Artifact javaBuilder;
   private final Artifact headerCompiler;
@@ -89,6 +90,7 @@
             .addAll(defaultJavacFlags)
             .build();
     this.javacJvmOptions = data.getJavacJvmOptions();
+    this.javacSupportsWorkers = data.getJavacSupportsWorkers();
   }
 
   /** @return the list of default options for the java compiler */
@@ -101,6 +103,11 @@
     return javacJvmOptions;
   }
 
+  /** @return whether JavaBuilders supports running as a persistent worker or not */
+  public boolean getJavacSupportsWorkers() {
+    return javacSupportsWorkers;
+  }
+
   /** @return the input Java language level */
   public String getSourceVersion() {
     return sourceVersion;
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 db58862..c50fe4c 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
@@ -16,6 +16,7 @@
 import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
 import static com.google.devtools.build.lib.packages.Attribute.attr;
 import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
 import static com.google.devtools.build.lib.syntax.Type.STRING;
 import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
 import static com.google.devtools.build.lib.syntax.Type.STRING_LIST_DICT;
@@ -84,6 +85,10 @@
         virtual machine documentation for the extensive list of possible flags for this option.
         <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
         .add(attr("jvm_opts", STRING_LIST).value(ImmutableList.<String>of("-client")))
+        /* <!-- #BLAZE_RULE(java_toolchain).ATTRIBUTE(javac_supports_workers) -->
+        True if JavaBuilder supports running as a persistent worker, false if it doesn't.
+        <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+        .add(attr("javac_supports_workers", BOOLEAN).value(true))
         /* <!-- #BLAZE_RULE(java_toolchain).ATTRIBUTE(javac) -->
         Label of the javac jar.
         <!-- #END_BLAZE_RULE.ATTRIBUTE --> */