Compile base classpaths for Bazel Jack support in android_sdk.

This also enables Jack support to compile with the Java bootclasspath
when running over non-Android rules. This is akin to how normal javac
support works - android_ rules are compiled with android.jar, while
java_libraries are compiled with special flags but the normal compile
time bootclasspath.

As of this change, the android_jack attribute on android_sdk is now
deprecated, and has no further effect. Because it was always optional,
this isn't really much of a change, it just means that now it does
nothing even if you DO specify it.

Because Jack support is still experimental, this should have no effect
on most users.

RELNOTES[INC]: android_sdk now compiles android_jack on the fly from
android_jar, which means android_jar must be a jar and android_jack is
now deprecated. The Jack tools (jack, jill, resource_extractor) must
be specified.

--
MOS_MIGRATED_REVID=117386373
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
index bd1dd65..a1bd855 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
@@ -558,7 +558,10 @@
         .setOutputArtifact(
             ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LIBRARY_JACK_FILE))
         // tools
-        .setAndroidSdk(sdk)
+        .setJackBinary(sdk.getJack())
+        .setJillBinary(sdk.getJill())
+        .setResourceExtractorBinary(sdk.getResourceExtractor())
+        .setJackBaseClasspath(sdk.getAndroidBaseClasspathForJack())
         // sources
         .addJavaSources(attributes.getSourceFiles())
         .addSourceJars(attributes.getSourceJars())
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
index c1d12ed..1b964d9 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
@@ -337,15 +337,12 @@
           .add(attr("adb", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec())
           .add(attr("framework_aidl", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE))
           .add(attr("aidl", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec())
-          .add(attr("android_jar", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE))
+          .add(attr("android_jar", LABEL).mandatory().cfg(HOST).allowedFileTypes(JavaSemantics.JAR))
           .add(attr("shrinked_android_jar", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE))
           .add(
               attr("android_jack", LABEL)
                   .cfg(HOST)
-                  .allowedFileTypes(ANY_FILE)
-                  // TODO(bazel-team): Remove defaults and make mandatory when android_sdk targets
-                  // have been updated to include manually specified Jack attributes.
-                  .value(environment.getToolsLabel("//tools/android/jack:android_jack")))
+                  .allowedFileTypes(ANY_FILE))
           .add(attr("annotations_jar", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE))
           .add(attr("main_dex_classes", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE))
           .add(attr("apkbuilder", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec())
@@ -355,19 +352,27 @@
                   .cfg(HOST)
                   .allowedFileTypes(ANY_FILE)
                   .exec()
-                  .value(environment.getToolsLabel("//tools/android/jack:jack")))
+                  .mandatory())
           .add(
               attr("jill", LABEL)
                   .cfg(HOST)
                   .allowedFileTypes(ANY_FILE)
                   .exec()
-                  .value(environment.getToolsLabel("//tools/android/jack:jill")))
+                  .mandatory())
           .add(
               attr("resource_extractor", LABEL)
                   .cfg(HOST)
                   .allowedFileTypes(ANY_FILE)
                   .exec()
-                  .value(environment.getToolsLabel("//tools/android/jack:resource_extractor")))
+                  .mandatory())
+          .add(
+              attr(":java_toolchain", LABEL)
+                  .allowedRuleClasses("java_toolchain")
+                  .value(JavaSemantics.JAVA_TOOLCHAIN))
+          .add(
+              attr("$javac_bootclasspath", LABEL)
+                  .cfg(HOST)
+                  .value(environment.getLabel(JavaSemantics.JAVAC_BOOTCLASSPATH_LABEL)))
           .build();
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java
index 2d61a18..016fedf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package com.google.devtools.build.lib.rules.android;
 
+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.FilesToRunProvider;
@@ -20,13 +21,18 @@
 import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.RunfilesProvider;
+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.AggregatingAttributeMapper;
 import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.rules.java.BaseJavaCompilationHelper;
 import com.google.devtools.build.lib.rules.java.JavaConfiguration;
+import com.google.devtools.build.lib.rules.java.JavaToolchainProvider;
 import com.google.devtools.build.lib.syntax.Type;
 
+import java.util.Collection;
+
 /**
  * Implementation of the {@code android_sdk} rule.
  */
@@ -59,7 +65,20 @@
     Artifact androidJar = ruleContext.getPrerequisiteArtifact("android_jar", Mode.HOST);
     Artifact shrinkedAndroidJar =
         ruleContext.getPrerequisiteArtifact("shrinked_android_jar", Mode.HOST);
-    Artifact androidJack = ruleContext.getPrerequisiteArtifact("android_jack", Mode.HOST);
+    // Because all Jack actions using this android_sdk will need Jack versions of the Android and
+    // Java classpaths, pre-translate the jars for Android and Java targets here. (They will only
+    // be run if needed, as usual for Bazel.)
+    NestedSet<Artifact> androidBaseClasspathForJack =
+        convertClasspathJarsToJack(
+            ruleContext, jack, jill, resourceExtractor, ImmutableList.of(androidJar));
+    NestedSet<Artifact> javaBaseClasspathForJack =
+        convertClasspathJarsToJack(
+            ruleContext,
+            jack,
+            jill,
+            resourceExtractor,
+            BaseJavaCompilationHelper.getBootClasspath(
+                ruleContext, JavaToolchainProvider.fromRuleContext(ruleContext), ""));
     Artifact annotationsJar = ruleContext.getPrerequisiteArtifact("annotations_jar", Mode.HOST);
     Artifact mainDexClasses = ruleContext.getPrerequisiteArtifact("main_dex_classes", Mode.HOST);
 
@@ -75,7 +94,8 @@
                 frameworkAidl,
                 androidJar,
                 shrinkedAndroidJar,
-                androidJack,
+                androidBaseClasspathForJack,
+                javaBaseClasspathForJack,
                 annotationsJar,
                 mainDexClasses,
                 adb,
@@ -93,4 +113,27 @@
         .setFilesToBuild(NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER))
         .build();
   }
+
+  private NestedSet<Artifact> convertClasspathJarsToJack(
+      RuleContext ruleContext,
+      FilesToRunProvider jack,
+      FilesToRunProvider jill,
+      FilesToRunProvider resourceExtractor,
+      Collection<Artifact> jars) {
+    return new JackCompilationHelper.Builder()
+        // bazel infrastructure
+        .setRuleContext(ruleContext)
+        // configuration
+        .setTolerant()
+        // tools
+        .setJackBinary(jack)
+        .setJillBinary(jill)
+        .setResourceExtractorBinary(resourceExtractor)
+        .setJackBaseClasspath(NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER))
+        // sources
+        .addCompiledJars(jars)
+        .build()
+        .compileAsLibrary()
+        .getTransitiveJackClasspathLibraries();
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdkProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdkProvider.java
index 522c41f..2a3065f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdkProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdkProvider.java
@@ -19,6 +19,7 @@
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 
 /**
@@ -31,7 +32,8 @@
   private final Artifact frameworkAidl;
   private final Artifact androidJar;
   private final Artifact shrinkedAndroidJar;
-  private final Artifact androidJack;
+  private final NestedSet<Artifact> androidBaseClasspathForJack;
+  private final NestedSet<Artifact> javaBaseClasspathForJack;
   private final Artifact annotationsJar;
   private final Artifact mainDexClasses;
   private final FilesToRunProvider adb;
@@ -51,7 +53,8 @@
       Artifact frameworkAidl,
       Artifact androidJar,
       Artifact shrinkedAndroidJar,
-      Artifact androidJack,
+      NestedSet<Artifact> androidBaseClasspathForJack,
+      NestedSet<Artifact> javaBaseClasspathForJack,
       Artifact annotationsJar,
       Artifact mainDexClasses,
       FilesToRunProvider adb,
@@ -70,7 +73,8 @@
     this.frameworkAidl = frameworkAidl;
     this.androidJar = androidJar;
     this.shrinkedAndroidJar = shrinkedAndroidJar;
-    this.androidJack = androidJack;
+    this.androidBaseClasspathForJack = androidBaseClasspathForJack;
+    this.javaBaseClasspathForJack = javaBaseClasspathForJack;
     this.annotationsJar = annotationsJar;
     this.mainDexClasses = mainDexClasses;
     this.adb = adb;
@@ -131,8 +135,20 @@
     return shrinkedAndroidJar;
   }
 
-  public Artifact getAndroidJack() {
-    return androidJack;
+  /**
+   * Returns the set of jack files to be used as a base classpath for jack compilation of Android
+   * rules, typically a Jack translation of the jar returned by {@link getAndroidJar}.
+   */
+  public NestedSet<Artifact> getAndroidBaseClasspathForJack() {
+    return androidBaseClasspathForJack;
+  }
+
+  /**
+   * Returns the set of jack files to be used as a base classpath for jack compilation of Java
+   * rules, typically a Jack translation of the jars in the Java bootclasspath.
+   */
+  public NestedSet<Artifact> getJavaBaseClasspathForJack() {
+    return javaBaseClasspathForJack;
   }
 
   public Artifact getAnnotationsJar() {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/JackAspect.java b/src/main/java/com/google/devtools/build/lib/rules/android/JackAspect.java
index b76690d..a167f38 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/JackAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/JackAspect.java
@@ -71,7 +71,10 @@
             // configuration
             .setOutputArtifact(jackLibraryOutput)
             // tools
-            .setAndroidSdk(androidSdk)
+            .setJackBinary(androidSdk.getJack())
+            .setJillBinary(androidSdk.getJill())
+            .setResourceExtractorBinary(androidSdk.getResourceExtractor())
+            .setJackBaseClasspath(androidSdk.getJavaBaseClasspathForJack())
             // sources
             .addJavaSources(sourceProvider.getSourceFiles())
             .addSourceJars(sourceProvider.getSourceJars())
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/JackCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/android/JackCompilationHelper.java
index f49ed04..c067d36 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/JackCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/JackCompilationHelper.java
@@ -76,6 +76,8 @@
   static final String SANITY_CHECKS_OFF = "off";
   /** Value of the sanity checks flag which enables sanity checks. */
   static final String SANITY_CHECKS_ON = "on";
+  /** Flag to enable tolerant mode in Jill, for compiling special jars (e.g., bootclasspath). */
+  static final String TOLERANT = "--tolerant";
 
   /** Flag to indicate the classpath of Jack libraries, separated by semicolons. */
   static final String CLASSPATH = "-cp";
@@ -112,6 +114,8 @@
 
   /** True to use Jack's internal sanity checks, trading speed for crash-on-bugs. */
   private final boolean useSanityChecks;
+  /** True to make Jill more tolerant, when compiling special jars (e.g., bootclasspath) */
+  private final boolean useTolerant;
 
   /** Binary used to extract resources from a jar file. */
   private final FilesToRunProvider resourceExtractorBinary;
@@ -119,11 +123,15 @@
   private final FilesToRunProvider jackBinary;
   /** Binary used to convert jars to Jack libraries. */
   private final FilesToRunProvider jillBinary;
-  /** Jack library containing Android base classes. This will be placed first on the classpath. */
-  private final Artifact androidBaseLibraryForJack;
+  /**
+   * Jack libraries containing Android/Java base classes.
+   *
+   * <p>These will be placed first on the classpath.
+   */
+  private final NestedSet<Artifact> baseClasspath;
 
-  /** The destination for the Jack artifact to be created. */
-  private final Artifact outputArtifact;
+  /** The destination for the Jack artifact to be created, or null to skip this. */
+  @Nullable private final Artifact outputArtifact;
 
   /** Java files for the rule's Jack library. */
   private final ImmutableSet<Artifact> javaSources;
@@ -172,11 +180,12 @@
   private JackCompilationHelper(
       RuleContext ruleContext,
       boolean useSanityChecks,
+      boolean useTolerant,
       FilesToRunProvider resourceExtractorBinary,
       FilesToRunProvider jackBinary,
       FilesToRunProvider jillBinary,
-      Artifact androidJackLibrary,
-      Artifact outputArtifact,
+      NestedSet<Artifact> baseClasspath,
+      @Nullable Artifact outputArtifact,
       ImmutableSet<Artifact> javaSources,
       ImmutableSet<Artifact> sourceJars,
       ImmutableMap<PathFragment, Artifact> resources,
@@ -190,10 +199,11 @@
       ImmutableSet<Artifact> dexJars) {
     this.ruleContext = ruleContext;
     this.useSanityChecks = useSanityChecks;
+    this.useTolerant = useTolerant;
     this.resourceExtractorBinary = resourceExtractorBinary;
     this.jackBinary = jackBinary;
     this.jillBinary = jillBinary;
-    this.androidBaseLibraryForJack = androidJackLibrary;
+    this.baseClasspath = baseClasspath;
     this.outputArtifact = outputArtifact;
     this.javaSources = javaSources;
     this.sourceJars = sourceJars;
@@ -329,24 +339,26 @@
             .addTransitive(classpathJacks)
             .build();
 
-    // android.jack needs to be first in the set's iteration order, as it's the base library.
+    // The base classpath needs to be first in the set's iteration order.
     // Then any jars or jack files specified directly, then dependencies from providers.
     NestedSet<Artifact> classpath =
         new NestedSetBuilder<Artifact>(Order.NAIVE_LINK_ORDER)
-            .add(androidBaseLibraryForJack)
+            .addTransitive(baseClasspath)
             .addTransitive(transitiveClasspath)
             .build();
 
     NestedSetBuilder<Artifact> exports = new NestedSetBuilder<>(Order.NAIVE_LINK_ORDER);
     NestedSetBuilder<Artifact> dexContents = new NestedSetBuilder<>(Order.NAIVE_LINK_ORDER);
 
-    if (javaSources.isEmpty() && sourceJars.isEmpty() && resources.isEmpty()) {
-      // We still have to create SOMETHING to fulfill the artifact, but man, screw it
-      buildEmptyJackAction();
-    } else {
-      buildJackAction(javaSources, sourceJars, resources, classpath);
-      exports.add(outputArtifact);
-      dexContents.add(outputArtifact);
+    if (outputArtifact != null) {
+      if (javaSources.isEmpty() && sourceJars.isEmpty() && resources.isEmpty()) {
+        // We still have to create SOMETHING to fulfill the artifact, but man, screw it
+        buildEmptyJackAction();
+      } else {
+        buildJackAction(javaSources, sourceJars, resources, classpath);
+        exports.add(outputArtifact);
+        dexContents.add(outputArtifact);
+      }
     }
 
     // These need to be added now so that they can be after the outputArtifact (if present).
@@ -374,9 +386,14 @@
         PARTIAL_JACK_DIRECTORY,
         FileSystemUtils.replaceExtension(jar.getRootRelativePath(), ".jack"),
         ruleContext.getBinOrGenfilesDirectory());
-    ruleContext.registerAction(
+    SpawnAction.Builder builder =
         new SpawnAction.Builder()
-            .setExecutable(jillBinary)
+            .setExecutable(jillBinary);
+    if (useTolerant) {
+      builder.addArgument(TOLERANT);
+    }
+    ruleContext.registerAction(
+        builder
             .addArgument(JILL_OUTPUT)
             .addOutputArgument(result)
             .addInputArgument(jar)
@@ -526,8 +543,21 @@
     /** Rule context used to build and register actions. */
     @Nullable private RuleContext ruleContext;
 
-    /** Set of Android tools used to pick up the Jack tools. */
-    @Nullable private AndroidSdkProvider androidSdk;
+    /** Whether to enable tolerant mode in Jill, e.g., when compiling a bootclasspath. */
+    private boolean useTolerant;
+
+    /** Binary used to extract resources from a jar file. */
+    @Nullable private FilesToRunProvider resourceExtractorBinary;
+    /** Binary used to build Jack libraries and dex files. */
+    @Nullable private FilesToRunProvider jackBinary;
+    /** Binary used to convert jars to Jack libraries. */
+    @Nullable private FilesToRunProvider jillBinary;
+    /**
+     * Set of Jack libraries containing Android/Java base classes.
+     *
+     * <p>These will be placed first on the classpath.
+     */
+    @Nullable private NestedSet<Artifact> baseClasspath;
 
     /** The destination for the Jack artifact to be created. */
     @Nullable private Artifact outputArtifact;
@@ -594,6 +624,8 @@
      *
      * <p>The artifact specified will always be generated, although it may be empty if there are no
      * sources.
+     *
+     * <p>This method must be called if any of addJavaSources, addSourceJars, or addResources is.
      */
     public JackCompilationHelper.Builder setOutputArtifact(Artifact outputArtifact) {
       this.outputArtifact = Preconditions.checkNotNull(outputArtifact);
@@ -601,11 +633,43 @@
     }
 
     /**
-     * Sets the tools bundle containing Jack, Jill, the resource extractor, and the Android base
-     * library in Jack format.
+     * Sets the Jack binary used to perform operations on Jack libraries.
      */
-    public JackCompilationHelper.Builder setAndroidSdk(AndroidSdkProvider androidSdk) {
-      this.androidSdk = Preconditions.checkNotNull(androidSdk);
+    public JackCompilationHelper.Builder setJackBinary(FilesToRunProvider jackBinary) {
+      this.jackBinary = Preconditions.checkNotNull(jackBinary);
+      return this;
+    }
+
+    /**
+     * Sets the Jill binary used to translate jars to jack files.
+     */
+    public JackCompilationHelper.Builder setJillBinary(FilesToRunProvider jillBinary) {
+      this.jillBinary = Preconditions.checkNotNull(jillBinary);
+      return this;
+    }
+
+    /**
+     * Sets the resource extractor binary used to extract resources from jars.
+     */
+    public JackCompilationHelper.Builder setResourceExtractorBinary(
+        FilesToRunProvider resourceExtractorBinary) {
+      this.resourceExtractorBinary = Preconditions.checkNotNull(resourceExtractorBinary);
+      return this;
+    }
+
+    /**
+     * Sets the base classpath, containing core classes (android.jar or Java bootclasspath).
+     */
+    public JackCompilationHelper.Builder setJackBaseClasspath(NestedSet<Artifact> baseClasspath) {
+      this.baseClasspath = Preconditions.checkNotNull(baseClasspath);
+      return this;
+    }
+
+    /**
+     * Sets Jill to be tolerant, e.g., when translating a jar from the Java bootclasspath to jack.
+     */
+    public JackCompilationHelper.Builder setTolerant() {
+      this.useTolerant = true;
       return this;
     }
 
@@ -780,25 +844,28 @@
      */
     public JackCompilationHelper build() {
       Preconditions.checkNotNull(ruleContext);
-      Preconditions.checkNotNull(androidSdk);
 
       boolean useSanityChecks =
           ruleContext
               .getFragment(AndroidConfiguration.class)
               .isJackSanityChecked();
-      FilesToRunProvider jackBinary = androidSdk.getJack();
-      FilesToRunProvider jillBinary = androidSdk.getJill();
-      FilesToRunProvider resourceExtractorBinary = androidSdk.getResourceExtractor();
-      Artifact androidBaseLibraryForJack = androidSdk.getAndroidJack();
+
+      // It's okay not to have an outputArtifact if there is nothing to build.
+      // e.g., if only translating jars with Jill, no final jack library will be created.
+      // But if there is something to build, enforce that one has been specified.
+      if (!javaSources.isEmpty() || !sourceJars.isEmpty() || !resources.isEmpty()) {
+        Preconditions.checkNotNull(outputArtifact);
+      }
 
       return new JackCompilationHelper(
           ruleContext,
           useSanityChecks,
+          useTolerant,
           Preconditions.checkNotNull(resourceExtractorBinary),
           Preconditions.checkNotNull(jackBinary),
           Preconditions.checkNotNull(jillBinary),
-          Preconditions.checkNotNull(androidBaseLibraryForJack),
-          Preconditions.checkNotNull(outputArtifact),
+          Preconditions.checkNotNull(baseClasspath),
+          outputArtifact,
           ImmutableSet.copyOf(javaSources),
           ImmutableSet.copyOf(sourceJars),
           ImmutableMap.copyOf(resources),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/BaseJavaCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/BaseJavaCompilationHelper.java
index 4e09ebd..ea59767 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/BaseJavaCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/BaseJavaCompilationHelper.java
@@ -99,9 +99,12 @@
   }
 
   /**
-   * Returns the javac bootclasspath artifacts.
+   * Returns the javac bootclasspath artifacts from the given toolchain (if it has any) or the rule.
    */
-  protected final ImmutableList<Artifact> getBootClasspath() {
+  public static ImmutableList<Artifact> getBootClasspath(
+      RuleContext ruleContext,
+      JavaToolchainProvider javaToolchain,
+      String implicitAttributesSuffix) {
     NestedSet<Artifact> toolchainBootclasspath = javaToolchain.getBootclasspath();
     if (toolchainBootclasspath != null) {
       return ImmutableList.copyOf(toolchainBootclasspath);
@@ -111,6 +114,13 @@
   }
 
   /**
+   * Returns the javac bootclasspath artifacts.
+   */
+  protected final ImmutableList<Artifact> getBootClasspath() {
+    return getBootClasspath(ruleContext, javaToolchain, implicitAttributesSuffix);
+  }
+
+  /**
    * Returns the extdir artifacts.
    */
   protected final ImmutableList<Artifact> getExtdirInputs() {
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
index 7393e71..86c2f97 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
@@ -13,8 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.analysis.mock;
 
-import static com.google.devtools.build.lib.packages.BuildType.LABEL;
-
 import com.google.common.base.Functions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
@@ -39,6 +37,8 @@
 import com.google.devtools.build.lib.rules.objc.J2ObjcConfiguration;
 import com.google.devtools.build.lib.rules.objc.ObjcConfigurationLoader;
 import com.google.devtools.build.lib.rules.python.PythonConfigurationLoader;
+import com.google.devtools.build.lib.testutil.BuildRuleBuilder;
+import com.google.devtools.build.lib.testutil.BuildRuleWithDefaultsBuilder;
 import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
@@ -131,21 +131,16 @@
 
     List<Attribute> attrs = androidSdkRuleClass.getAttributes();
     Builder<String> androidBuildContents = ImmutableList.builder();
-    androidBuildContents
-        .add("android_sdk(")
-        .add("    name = 'sdk',")
-        .add("    android_jack = ':empty',")
-        .add("    jack = ':fail',")
-        .add("    jill = ':fail',")
-        .add("    resource_extractor = ':fail',");
 
-    for (Attribute attr : attrs) {
-      if (attr.getType() == LABEL && attr.isMandatory() && !attr.getName().startsWith(":")) {
-        androidBuildContents.add("    " + attr.getName() + " = ':" + attr.getName() + "',");
-      }
+    BuildRuleWithDefaultsBuilder ruleBuilder =
+        new BuildRuleWithDefaultsBuilder("android_sdk", "sdk")
+            .popuplateAttributes("", false);
+    androidBuildContents.add(ruleBuilder.build());
+    for (BuildRuleBuilder generatedRuleBuilder : ruleBuilder.getRulesToGenerate()) {
+      androidBuildContents.add(generatedRuleBuilder.build());
     }
+
     androidBuildContents
-        .add(")")
         .add("sh_binary(name = 'aar_generator', srcs = ['empty.sh'])")
         .add("sh_binary(name = 'dexbuilder', srcs = ['empty.sh'])")
         .add("sh_binary(name = 'dexmerger', srcs = ['empty.sh'])")
@@ -166,23 +161,7 @@
         .add("          runtime_deps = [ ':PackageParser_import'],")
         .add("          main_class = 'com.google.devtools.build.android.ideinfo.PackageParser')")
         .add("java_import(name = 'PackageParser_import',")
-        .add("          jars = [ 'package_parser_deploy.jar' ])");
-
-    for (Attribute attr : attrs) {
-      if (attr.getType() == LABEL && attr.isMandatory() && !attr.getName().startsWith(":")) {
-        if (attr.isExecutable()) {
-          androidBuildContents
-              .add("sh_binary(name = '" + attr.getName() + "',")
-              .add("          srcs = ['empty.sh'],")
-              .add(")");
-        } else {
-          androidBuildContents
-              .add("filegroup(name = '" + attr.getName() + "',")
-              .add("          srcs = ['fake.file'])");
-        }
-      }
-    }
-    androidBuildContents
+        .add("          jars = [ 'package_parser_deploy.jar' ])")
         .add("java_binary(name = 'IdlClass',")
         .add("            runtime_deps = [ ':idlclass_import' ],")
         .add("            main_class = 'com.google.devtools.build.android.idlclass.IdlClass')")