aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-09-16 22:38:35 +0000
committerGravatar Laszlo Csomor <laszlocsomor@google.com>2016-09-19 07:35:26 +0000
commita51ade354499c084b71030192d8e2f79dbfdc299 (patch)
tree163e8f30b58dae15ab5fd085d88685b727d8482a /src
parentae2197de3af4ebfac8d6ba073667b0948845b522 (diff)
Java 8 support for Android builds, initially with incremental dexing only.
-- MOS_MIGRATED_REVID=133436157
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java44
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java16
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java170
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java1
5 files changed, 193 insertions, 40 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
index 0b6b467aea..ed545f87f1 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
@@ -866,7 +866,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
NestedSet<Artifact> libraryResourceJars = libraryResourceJarsBuilder.build();
Iterable<Artifact> filteredJars = ImmutableList.copyOf(
- filter(jars, not(in(libraryResourceJars.toSet()))));
+ filter(jars, not(in(libraryResourceJars.toSet()))));
AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext);
@@ -1276,11 +1276,14 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
}
/**
- * Returns a {@link DexArchiveProvider} of all transitively generated dex archives as well as
- * dex archives for the Jars produced by the binary target itself.
+ * Returns a {@link DexArchiveProvider} of all transitively generated dex archives as well as dex
+ * archives for the Jars produced by the binary target itself.
*/
private static Function<Artifact, Artifact> collectDexArchives(
- RuleContext ruleContext, AndroidCommon common, List<String> dexopts) {
+ RuleContext ruleContext,
+ AndroidCommon common,
+ List<String> dexopts,
+ JavaTargetAttributes attributes) {
DexArchiveProvider.Builder result = new DexArchiveProvider.Builder()
// Use providers from all attributes that declare DexArchiveAspect
.addTransitiveProviders(
@@ -1289,15 +1292,30 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
DexArchiveAspect.incrementalDexopts(ruleContext, dexopts);
for (Artifact jar : common.getJarsProducedForRuntime()) {
// Create dex archives next to all Jars produced by AndroidCommon for this rule. We need to
- // do this (instead of placing dex archives into the _dx subdirectory like DexArchiveAspect
- // does because for "legacy" ResourceApks, AndroidCommon produces Jars per resource dependency
- // that can theoretically have duplicate basenames, so they go into special directories, and
- // we piggyback on that naming scheme here by placing dex archives into the same directories.
+ // do this (instead of placing dex archives into the _dx subdirectory like DexArchiveAspect)
+ // because for "legacy" ResourceApks, AndroidCommon produces Jars per resource dependency that
+ // can theoretically have duplicate basenames, so they go into special directories, and we
+ // piggyback on that naming scheme here by placing dex archives into the same directories.
PathFragment jarPath = jar.getRootRelativePath();
- Artifact dexArchive = ruleContext.getDerivedArtifact(
- jarPath.replaceName(jarPath.getBaseName() + ".dex.zip"),
- jar.getRoot());
- DexArchiveAspect.createDexArchiveAction(ruleContext, jar, dexArchive, incrementalDexopts);
+ Artifact jarToDex = jar;
+ if (AndroidCommon.getAndroidConfig(ruleContext).desugarJava8()) {
+ // desugar jars first if desired...
+ jarToDex =
+ DexArchiveAspect.desugar(
+ ruleContext,
+ jar,
+ attributes.getBootClassPath(),
+ attributes.getCompileTimeClassPath(),
+ ruleContext.getDerivedArtifact(
+ jarPath.replaceName(jarPath.getBaseName() + "_desugared.jar"), jar.getRoot()));
+ }
+ Artifact dexArchive =
+ DexArchiveAspect.createDexArchiveAction(
+ ruleContext,
+ jarToDex,
+ incrementalDexopts,
+ ruleContext.getDerivedArtifact(
+ jarPath.replaceName(jarPath.getBaseName() + ".dex.zip"), jar.getRoot()));
result.addDexArchive(incrementalDexopts, dexArchive, jar);
}
return result.build().archivesForDexopts(incrementalDexopts);
@@ -1355,7 +1373,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
// there should be very few or no Jar files that still end up in shards. The dexing
// step below will have to deal with those in addition to merging .dex files together.
classpath = Iterables
- .transform(classpath, collectDexArchives(ruleContext, common, dexopts));
+ .transform(classpath, collectDexArchives(ruleContext, common, dexopts, attributes));
shardCommandLine.add("--split_dexed_classes");
}
shardCommandLine.addBeforeEachExecPath("--input_jar", classpath);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
index c7b3650796..90cd5ca391 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
@@ -306,6 +306,16 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
help = "Enables sanity checks for Jack and Jill compilation.")
public boolean jackSanityChecks;
+ // For desugaring lambdas when compiling Java 8 sources. Do not use on the command line.
+ // The idea is that once this option works, we'll flip the default value in a config file, then
+ // once it is proven that it works, remove it from Bazel and said config file.
+ @Option(name = "experimental_desugar_for_android",
+ defaultValue = "false",
+ category = "undocumented",
+ implicitRequirements = "--noexperimental_android_use_jack_for_dexing",
+ help = "Whether to desugar Java 8 bytecode before dexing.")
+ public boolean desugarJava8;
+
@Option(name = "experimental_incremental_dexing",
defaultValue = "off",
category = "undocumented",
@@ -467,6 +477,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
private final ImmutableSet<AndroidBinaryType> incrementalDexingBinaries;
private final ImmutableList<String> dexoptsSupportedInIncrementalDexing;
private final ImmutableList<String> targetDexoptsThatPreventIncrementalDexing;
+ private final boolean desugarJava8;
private final boolean allowAndroidLibraryDepsWithoutSrcs;
private final boolean useAndroidResourceShrinking;
private final boolean useRClassGenerator;
@@ -491,6 +502,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
ImmutableList.copyOf(options.dexoptsSupportedInIncrementalDexing);
this.targetDexoptsThatPreventIncrementalDexing =
ImmutableList.copyOf(options.nonIncrementalPerTargetDexopts);
+ this.desugarJava8 = options.desugarJava8;
this.allowAndroidLibraryDepsWithoutSrcs = options.allowAndroidLibraryDepsWithoutSrcs;
this.useAndroidResourceShrinking = options.useAndroidResourceShrinking;
this.useRClassGenerator = options.useRClassGenerator;
@@ -553,6 +565,10 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
return targetDexoptsThatPreventIncrementalDexing;
}
+ public boolean desugarJava8() {
+ return desugarJava8;
+ }
+
public boolean allowSrcsLessAndroidLibraryDeps() {
return allowAndroidLibraryDepsWithoutSrcs;
}
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 3500ebc5fe..9033b1a87a 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
@@ -629,6 +629,8 @@ public final class AndroidRuleClasses {
.value(env.getToolsLabel(DEFAULT_INCREMENTAL_STUB_APPLICATION)))
.add(attr("$incremental_split_stub_application", LABEL)
.value(env.getToolsLabel(DEFAULT_INCREMENTAL_SPLIT_STUB_APPLICATION)))
+ .add(attr("$desugar", LABEL).cfg(HOST).exec()
+ .value(env.getToolsLabel("//tools/android:desugar_java8")))
/* <!-- #BLAZE_RULE($android_binary_base).ATTRIBUTE(dexopts) -->
Additional command-line flags for the dx tool when generating classes.dex.
Subject to <a href="${link make-variables}">"Make variable"</a> substitution and
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java b/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
index c805da5553..fdfe7c216a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/DexArchiveAspect.java
@@ -23,6 +23,7 @@ import static com.google.devtools.build.lib.rules.android.AndroidCommon.getAndro
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import com.google.common.base.Function;
+import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
@@ -41,6 +42,7 @@ import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
import com.google.devtools.build.lib.analysis.actions.ParameterFileWriteAction;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.packages.AspectDefinition;
import com.google.devtools.build.lib.packages.AspectParameters;
import com.google.devtools.build.lib.packages.AttributeMap;
@@ -50,7 +52,9 @@ import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.TriState;
import com.google.devtools.build.lib.rules.java.JavaCommon;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
+import com.google.devtools.build.lib.rules.java.JavaCompilationInfoProvider;
import com.google.devtools.build.lib.rules.java.JavaRuntimeJarProvider;
+import java.util.LinkedHashMap;
import java.util.Set;
import java.util.TreeSet;
@@ -63,7 +67,7 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
* Function that returns a {@link Rule}'s {@code incremental_dexing} attribute for use by this
* aspect. Must be provided when attaching this aspect to a rule.
*/
- static final Function<Rule, AspectParameters> PARAM_EXTRACTOR =
+ public static final Function<Rule, AspectParameters> PARAM_EXTRACTOR =
new Function<Rule, AspectParameters>() {
@Override
public AspectParameters apply(Rule rule) {
@@ -74,9 +78,13 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
return result.build();
}
};
+ /** Aspect-only label for dexbuidler executable, to avoid name clashes with labels on rules. */
private static final String ASPECT_DEXBUILDER_PREREQ = "$dex_archive_dexbuilder";
+ /** Aspect-only label for desugaring executable, to avoid name clashes with labels on rules. */
+ private static final String ASPECT_DESUGAR_PREREQ = "$aspect_desugar";
private static final ImmutableList<String> TRANSITIVE_ATTRIBUTES =
ImmutableList.of("deps", "exports", "runtime_deps");
+
private final String toolsRepository;
public DexArchiveAspect(String toolsRepository) {
@@ -88,9 +96,11 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
AspectDefinition.Builder result = new AspectDefinition.Builder(NAME)
// Actually we care about JavaRuntimeJarProvider, but rules don't advertise that provider.
.requireProvider(JavaCompilationArgsProvider.class)
+ // Parse labels since we don't have RuleDefinitionEnvironment.getLabel like in a rule
.add(attr(ASPECT_DEXBUILDER_PREREQ, LABEL).cfg(HOST).exec()
- // Parse label here since we don't have RuleDefinitionEnvironment.getLabel like in a rule
.value(Label.parseAbsoluteUnchecked(toolsRepository + "//tools/android:dexbuilder")))
+ .add(attr(ASPECT_DESUGAR_PREREQ, LABEL).cfg(HOST).exec()
+ .value(Label.parseAbsoluteUnchecked(toolsRepository + "//tools/android:desugar_java8")))
.requiresConfigurationFragments(AndroidConfiguration.class);
for (String attr : TRANSITIVE_ATTRIBUTES) {
result.attributeAspect(attr, this);
@@ -121,10 +131,23 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
DexArchiveProvider.Builder result = createArchiveProviderBuilderFromDeps(ruleContext);
JavaRuntimeJarProvider jarProvider = base.getProvider(JavaRuntimeJarProvider.class);
if (jarProvider != null) {
+ Function<Artifact, Artifact> desugaredJars =
+ desugarJarsIfRequested(base, ruleContext, jarProvider);
Set<Set<String>> aspectDexopts = aspectDexopts(ruleContext);
for (Artifact jar : jarProvider.getRuntimeJars()) {
for (Set<String> incrementalDexopts : aspectDexopts) {
- Artifact dexArchive = createDexArchiveAction(ruleContext, jar, incrementalDexopts);
+ // Since we're potentially dexing the same jar multiple times with different flags, we
+ // need to write unique artifacts for each flag combination. Here, it is convenient to
+ // distinguish them by putting the flags that were used for creating the artifacts into
+ // their filenames.
+ String filename = jar.getFilename() + Joiner.on("").join(incrementalDexopts) + ".dex.zip";
+ Artifact dexArchive =
+ createDexArchiveAction(
+ ruleContext,
+ ASPECT_DEXBUILDER_PREREQ,
+ desugaredJars.apply(jar),
+ incrementalDexopts,
+ AndroidBinary.getDxArtifact(ruleContext, filename));
result.addDexArchive(incrementalDexopts, dexArchive, jar);
}
}
@@ -132,6 +155,32 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
return new ConfiguredAspect.Builder(NAME, ruleContext).addProvider(result.build()).build();
}
+ /**
+ * Runs Jars in {@code jarProvider} through desugaring action if flag is set. Note that this
+ * cannot happen in a separate aspect because aspects don't see providers added by other aspects
+ * executed on the same target.
+ */
+ private Function<Artifact, Artifact> desugarJarsIfRequested(
+ ConfiguredTarget base, RuleContext ruleContext, JavaRuntimeJarProvider jarProvider) {
+ if (!getAndroidConfig(ruleContext).desugarJava8()) {
+ return Functions.identity();
+ }
+ // These are all transitive hjars of dependencies and hjar of the jar itself
+ NestedSet<Artifact> compileTimeClasspath = base
+ .getProvider(JavaCompilationArgsProvider.class) // aspect definition requires this
+ .getRecursiveJavaCompilationArgs()
+ .getCompileTimeJars();
+ // For android_* targets we need to honor their bootclasspath (nicer in general to do so)
+ ImmutableList<Artifact> bootclasspath = getBootclasspath(base);
+ LinkedHashMap<Artifact, Artifact> desugaredJars = new LinkedHashMap<>();
+ for (Artifact jar : jarProvider.getRuntimeJars()) {
+ Artifact desugared = createDesugarAction(ruleContext, jar, bootclasspath,
+ compileTimeClasspath);
+ desugaredJars.put(jar, desugared);
+ }
+ return Functions.forMap(desugaredJars);
+ }
+
private static DexArchiveProvider.Builder createArchiveProviderBuilderFromDeps(
RuleContext ruleContext) {
DexArchiveProvider.Builder result = new DexArchiveProvider.Builder();
@@ -144,22 +193,85 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
return result;
}
- private static Artifact createDexArchiveAction(RuleContext ruleContext, Artifact jar,
- Set<String> incrementalDexopts) {
- // Since we're potentially dexing the same jar multiple times with different flags, we need to
- // write out unique artifacts for each flag combinations. Here, it is convenient to distinguish
- // them by putting the flags that were used for creating the artifacts into their filenames.
- String filename = jar.getFilename() + Joiner.on("").join(incrementalDexopts) + ".dex.zip";
- Artifact result = AndroidBinary.getDxArtifact(ruleContext, filename);
- // Aspect must use attribute name for dexbuilder prereq that's different from the prerequisite
- // declared on AndroidBinaryBaseRule because the two prereq's can otherwise name-clash when
- // android_binary targets are built as part of an android_test: building android_test causes
- // the aspect to apply to the android_binary target, but android_binary itself also declares
- // a $dexbuilder prerequisite, so if the aspect also used $dexbuilder then
- // RuleContext.getExecutablePrerequisite would fail with "$dexbuilder produces multiple prereqs"
- // (note they both resolve to the same artifact but that doesn't seem to prevent the exception
- // from being thrown).
- createDexArchiveAction(ruleContext, ASPECT_DEXBUILDER_PREREQ, jar, result, incrementalDexopts);
+ private static ImmutableList<Artifact> getBootclasspath(ConfiguredTarget base) {
+ JavaCompilationInfoProvider compilationInfo =
+ base.getProvider(JavaCompilationInfoProvider.class);
+ if (compilationInfo == null) {
+ return ImmutableList.of();
+ }
+ return compilationInfo.getBootClasspath();
+ }
+
+ private Artifact createDesugarAction(
+ RuleContext ruleContext,
+ Artifact jar,
+ ImmutableList<Artifact> bootclasspath,
+ NestedSet<Artifact> compileTimeClasspath) {
+ return createDesugarAction(
+ ruleContext,
+ ASPECT_DESUGAR_PREREQ,
+ jar,
+ bootclasspath,
+ compileTimeClasspath,
+ AndroidBinary.getDxArtifact(ruleContext, jar.getFilename() + "_desugared.jar"));
+ }
+
+ /**
+ * Desugars the given Jar using an executable prerequisite {@code "$dexbuilder"}. Rules
+ * calling this method must declare the appropriate prerequisite, similar to how
+ * {@link #getDefinition} does it for {@link DexArchiveAspect} under a different name.
+ *
+ * <p>It's useful to have this action separately since callers need to look up classpath and
+ * bootclasspath in a different way than this aspect does it.
+ *
+ * @return the artifact given as {@code result}, which can simplify calling code
+ */
+ static Artifact desugar(
+ RuleContext ruleContext,
+ Artifact jar,
+ ImmutableList<Artifact> bootclasspath,
+ NestedSet<Artifact> classpath,
+ Artifact result) {
+ return createDesugarAction(ruleContext, "$desugar", jar, bootclasspath, classpath, result);
+ }
+
+ private static Artifact createDesugarAction(
+ RuleContext ruleContext,
+ String desugarPrereqName,
+ Artifact jar,
+ ImmutableList<Artifact> bootclasspath,
+ NestedSet<Artifact> classpath,
+ Artifact result) {
+ CustomCommandLine args = new CustomCommandLine.Builder()
+ .addExecPath("--input", jar)
+ .addExecPath("--output", result)
+ .addBeforeEachExecPath("--classpath_entry", classpath)
+ .addBeforeEachExecPath("--bootclasspath_entry", bootclasspath)
+ .build();
+
+ // Just use params file, since classpaths can get long
+ Artifact paramFile =
+ ruleContext.getDerivedArtifact(
+ ParameterFile.derivePath(result.getRootRelativePath()), result.getRoot());
+ ruleContext.registerAction(
+ new ParameterFileWriteAction(
+ ruleContext.getActionOwner(),
+ paramFile,
+ args,
+ ParameterFile.ParameterFileType.UNQUOTED,
+ ISO_8859_1));
+ ruleContext.registerAction(
+ new SpawnAction.Builder()
+ .setExecutable(ruleContext.getExecutablePrerequisite(desugarPrereqName, Mode.HOST))
+ .addArgument("@" + paramFile.getExecPathString())
+ .addInput(jar)
+ .addInput(paramFile)
+ .addInputs(bootclasspath)
+ .addTransitiveInputs(classpath)
+ .addOutput(result)
+ .setMnemonic("Desugar")
+ .setProgressMessage("Desugaring " + jar.prettyPrint() + " for Android")
+ .build(ruleContext));
return result;
}
@@ -167,24 +279,27 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
* Creates a dex archive using an executable prerequisite called {@code "$dexbuilder"}. Rules
* calling this method must declare the appropriate prerequisite, similar to how
* {@link #getDefinition} does it for {@link DexArchiveAspect} under a different name.
+ *
+ * @return the artifact given as {@code result}, which can simplify calling code
*/
// Package-private method for use in AndroidBinary
- static void createDexArchiveAction(RuleContext ruleContext, Artifact jar, Artifact dexArchive,
- Set<String> tokenizedDexopts) {
- createDexArchiveAction(ruleContext, "$dexbuilder", jar, dexArchive, tokenizedDexopts);
+ static Artifact createDexArchiveAction(RuleContext ruleContext, Artifact jar,
+ Set<String> tokenizedDexopts, Artifact result) {
+ return createDexArchiveAction(ruleContext, "$dexbuilder", jar, tokenizedDexopts, result);
}
/**
* Creates a dexbuilder action with the given input, output, and flags. Flags must have been
* filtered and normalized to a set that the dexbuilder tool can understand.
*/
- private static void createDexArchiveAction(RuleContext ruleContext, String dexbuilderPrereq,
- Artifact jar, Artifact dexArchive, Set<String> incrementalDexopts) {
+ private static Artifact createDexArchiveAction(RuleContext ruleContext, String dexbuilderPrereq,
+ Artifact jar, Set<String> incrementalDexopts, Artifact dexArchive) {
// Write command line arguments into a params file for compatibility with WorkerSpawnStrategy
- CustomCommandLine.Builder args = new CustomCommandLine.Builder()
+ CustomCommandLine args = new CustomCommandLine.Builder()
.addExecPath("--input_jar", jar)
.addExecPath("--output_zip", dexArchive)
- .add(incrementalDexopts);
+ .add(incrementalDexopts)
+ .build();
Artifact paramFile =
ruleContext.getDerivedArtifact(
ParameterFile.derivePath(dexArchive.getRootRelativePath()), dexArchive.getRoot());
@@ -192,7 +307,7 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
new ParameterFileWriteAction(
ruleContext.getActionOwner(),
paramFile,
- args.build(),
+ args,
ParameterFile.ParameterFileType.UNQUOTED,
ISO_8859_1));
SpawnAction.Builder dexbuilder =
@@ -208,6 +323,7 @@ public final class DexArchiveAspect extends NativeAspectClass implements Configu
.setProgressMessage(
"Dexing " + jar.prettyPrint() + " with applicable dexopts " + incrementalDexopts);
ruleContext.registerAction(dexbuilder.build(ruleContext));
+ return dexArchive;
}
private static Set<Set<String>> aspectDexopts(RuleContext ruleContext) {
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 5590c64741..86ba04a986 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
@@ -160,6 +160,7 @@ public final class BazelAnalysisMock extends AnalysisMock {
androidBuildContents
.add("sh_binary(name = 'aar_generator', srcs = ['empty.sh'])")
+ .add("sh_binary(name = 'desugar_java8', srcs = ['empty.sh'])")
.add("sh_binary(name = 'dexbuilder', srcs = ['empty.sh'])")
.add("sh_binary(name = 'dexmerger', srcs = ['empty.sh'])")
.add("sh_binary(name = 'manifest_merger', srcs = ['empty.sh'])")