Add a flag for using a custom legacy main dex list generator for android_binary.
RELNOTES: None
PiperOrigin-RevId: 295192505
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 c1919bd..fad73c7 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
@@ -1754,75 +1754,122 @@
@Nullable Artifact mainDexProguardSpec,
@Nullable Artifact proguardOutputMap)
throws InterruptedException {
- // Process the input jar through Proguard into an intermediate, streamlined jar.
- Artifact strippedJar = AndroidBinary.getDxArtifact(ruleContext, "main_dex_intermediate.jar");
+ AndroidConfiguration config = AndroidCommon.getAndroidConfig(ruleContext);
AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext);
- SpawnAction.Builder streamlinedBuilder =
- new SpawnAction.Builder()
- .useDefaultShellEnvironment()
- .addOutput(strippedJar)
- .setExecutable(sdk.getProguard())
- .setProgressMessage("Generating streamlined input jar for main dex classes list")
- .setMnemonic("MainDexClassesIntermediate")
- .addInput(jar)
- .addInput(sdk.getShrinkedAndroidJar());
- CustomCommandLine.Builder streamlinedCommandLine =
- CustomCommandLine.builder()
- .add("-forceprocessing")
- .addExecPath("-injars", jar)
- .addExecPath("-libraryjars", sdk.getShrinkedAndroidJar())
- .addExecPath("-outjars", strippedJar)
- .add("-dontwarn")
- .add("-dontnote")
- .add("-dontoptimize")
- .add("-dontobfuscate");
-
- List<Artifact> specs = new ArrayList<>();
- specs.addAll(
- ruleContext.getPrerequisiteArtifacts("main_dex_proguard_specs", Mode.TARGET).list());
- if (specs.isEmpty()) {
- specs.add(sdk.getMainDexClasses());
- }
- if (mainDexProguardSpec != null) {
- specs.add(mainDexProguardSpec);
- }
-
- for (Artifact spec : specs) {
- streamlinedBuilder.addInput(spec);
- streamlinedCommandLine.addExecPath("-include", spec);
- }
-
- androidSemantics.addMainDexListActionArguments(
- ruleContext, streamlinedBuilder, streamlinedCommandLine, proguardOutputMap);
-
- streamlinedBuilder.addCommandLine(streamlinedCommandLine.build());
- ruleContext.registerAction(streamlinedBuilder.build(ruleContext));
-
// Create the main dex classes list.
Artifact mainDexList = AndroidBinary.getDxArtifact(ruleContext, "main_dex_list.txt");
- SpawnAction.Builder builder =
- new SpawnAction.Builder()
- .setMnemonic("MainDexClasses")
- .setProgressMessage("Generating main dex classes list");
- ruleContext.registerAction(
- builder
- .setExecutable(sdk.getMainDexListCreator())
- .addOutput(mainDexList)
- .addInput(strippedJar)
- .addInput(jar)
- .addCommandLine(
- CustomCommandLine.builder()
- .addExecPath(mainDexList)
- .addExecPath(strippedJar)
- .addExecPath(jar)
- .addAll(
- ruleContext
- .getExpander()
- .withDataLocations()
- .tokenized("main_dex_list_opts"))
- .build())
- .build(ruleContext));
+ List<Artifact> proguardSpecs = new ArrayList<>();
+ proguardSpecs.addAll(
+ ruleContext.getPrerequisiteArtifacts("main_dex_proguard_specs", Mode.TARGET).list());
+ if (proguardSpecs.isEmpty()) {
+ proguardSpecs.add(sdk.getMainDexClasses());
+ }
+ if (mainDexProguardSpec != null) {
+ proguardSpecs.add(mainDexProguardSpec);
+ }
+
+ // If --legacy_main_dex_list_generator is not set, use ProGuard and the main dext list creator
+ // specified by the android_sdk rule. If --legacy_main_dex_list_generator is provided, use that
+ // tool instead.
+ // TODO(b/147692286): Remove the old main-dex list generation that relied on ProGuard.
+ if (config.getLegacyMainDexListGenerator() == null) {
+ // Process the input jar through Proguard into an intermediate, streamlined jar.
+ Artifact strippedJar = AndroidBinary.getDxArtifact(ruleContext, "main_dex_intermediate.jar");
+ SpawnAction.Builder streamlinedBuilder =
+ new SpawnAction.Builder()
+ .useDefaultShellEnvironment()
+ .addOutput(strippedJar)
+ .setExecutable(sdk.getProguard())
+ .setProgressMessage("Generating streamlined input jar for main dex classes list")
+ .setMnemonic("MainDexClassesIntermediate")
+ .addInput(jar)
+ .addInput(sdk.getShrinkedAndroidJar());
+ CustomCommandLine.Builder streamlinedCommandLine =
+ CustomCommandLine.builder()
+ .add("-forceprocessing")
+ .addExecPath("-injars", jar)
+ .addExecPath("-libraryjars", sdk.getShrinkedAndroidJar())
+ .addExecPath("-outjars", strippedJar)
+ .add("-dontwarn")
+ .add("-dontnote")
+ .add("-dontoptimize")
+ .add("-dontobfuscate");
+
+ for (Artifact spec : proguardSpecs) {
+ streamlinedBuilder.addInput(spec);
+ streamlinedCommandLine.addExecPath("-include", spec);
+ }
+
+ androidSemantics.addMainDexListActionArguments(
+ ruleContext, streamlinedBuilder, streamlinedCommandLine, proguardOutputMap);
+
+ streamlinedBuilder.addCommandLine(streamlinedCommandLine.build());
+ ruleContext.registerAction(streamlinedBuilder.build(ruleContext));
+
+ SpawnAction.Builder builder =
+ new SpawnAction.Builder()
+ .setMnemonic("MainDexClasses")
+ .setProgressMessage("Generating main dex classes list");
+
+ ruleContext.registerAction(
+ builder
+ .setExecutable(sdk.getMainDexListCreator())
+ .addOutput(mainDexList)
+ .addInput(strippedJar)
+ .addInput(jar)
+ .addCommandLine(
+ CustomCommandLine.builder()
+ .addExecPath(mainDexList)
+ .addExecPath(strippedJar)
+ .addExecPath(jar)
+ .addAll(
+ ruleContext
+ .getExpander()
+ .withDataLocations()
+ .tokenized("main_dex_list_opts"))
+ .build())
+ .build(ruleContext));
+ } else {
+ FilesToRunProvider legacyMainDexListGenerator =
+ ruleContext.getExecutablePrerequisite(":legacy_main_dex_list_generator", Mode.HOST);
+ // Use the newer legacy multidex main-dex list generation.
+ SpawnAction.Builder actionBuilder =
+ new SpawnAction.Builder()
+ .setMnemonic("MainDexClasses")
+ .setProgressMessage("Generating main dex classes list");
+
+ CustomCommandLine.Builder commandLineBuilder =
+ CustomCommandLine.builder()
+ .addExecPath("--main-dex-list-output", mainDexList)
+ .addExecPath("--lib", sdk.getAndroidJar());
+ if (AndroidCommon.getAndroidConfig(ruleContext).desugarJava8Libs()) {
+ NestedSet<Artifact> legacyApis =
+ ruleContext
+ .getPrerequisite("$desugared_java8_legacy_apis", Mode.TARGET)
+ .getProvider(FileProvider.class)
+ .getFilesToBuild();
+ for (Artifact lib : legacyApis.toList()) {
+ actionBuilder.addInput(lib);
+ commandLineBuilder.addExecPath("--lib", lib);
+ }
+ }
+ for (Artifact spec : proguardSpecs) {
+ actionBuilder.addInput(spec);
+ commandLineBuilder.addExecPath("--main-dex-rules", spec);
+ }
+
+ commandLineBuilder.addExecPath(jar);
+
+ ruleContext.registerAction(
+ actionBuilder
+ .setExecutable(legacyMainDexListGenerator)
+ .addOutput(mainDexList)
+ .addInput(jar)
+ .addInput(sdk.getAndroidJar())
+ .addCommandLine(commandLineBuilder.build())
+ .build(ruleContext));
+ }
return mainDexList;
}
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 8882332..8cc88a9 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
@@ -40,6 +40,7 @@
import com.google.devtools.common.options.OptionEffectTag;
import com.google.devtools.common.options.OptionMetadataTag;
import java.util.List;
+import javax.annotation.Nullable;
/** Configuration fragment for Android rules. */
@Immutable
@@ -883,6 +884,19 @@
help = "Use R.txt from the merging action, instead of from the validation action.")
public boolean useRTxtFromMergedResources;
+ @Option(
+ name = "legacy_main_dex_list_generator",
+ // TODO(b/147692286): Update this default value to R8's GenerateMainDexList binary after
+ // migrating usage.
+ defaultValue = "null",
+ converter = LabelConverter.class,
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Specifies a binary to use to generate the list of classes that must be in the main"
+ + " dex when compiling legacy multidex.")
+ public Label legacyMainDexListGenerator;
+
@Override
public FragmentOptions getHost() {
Options host = (Options) super.getHost();
@@ -979,6 +993,7 @@
private final boolean alwaysFilterDuplicateClassesFromAndroidTest;
private final boolean filterLibraryJarWithProgramJar;
private final boolean useRTxtFromMergedResources;
+ private final Label legacyMainDexListGenerator;
private AndroidConfiguration(Options options) throws InvalidConfigurationException {
this.sdk = options.sdk;
@@ -1032,6 +1047,7 @@
options.alwaysFilterDuplicateClassesFromAndroidTest;
this.filterLibraryJarWithProgramJar = options.filterLibraryJarWithProgramJar;
this.useRTxtFromMergedResources = options.useRTxtFromMergedResources;
+ this.legacyMainDexListGenerator = options.legacyMainDexListGenerator;
if (options.androidAaptVersion != AndroidAaptVersion.AAPT2) {
throw new InvalidConfigurationException(
@@ -1283,4 +1299,14 @@
boolean useRTxtFromMergedResources() {
return useRTxtFromMergedResources;
}
+
+ /** Returns the label provided with --legacy_main_dex_list_generator, if any. */
+ // TODO(b/147692286): Move R8's main dex list tool into tool repository.
+ @SkylarkConfigurationField(
+ name = "legacy_main_dex_list_generator",
+ doc = "Returns the label provided with --legacy_main_dex_list_generator, if any.")
+ @Nullable
+ public Label getLegacyMainDexListGenerator() {
+ return legacyMainDexListGenerator;
+ }
}
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 d022627..433c288 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
@@ -222,6 +222,13 @@
public static final AndroidSplitTransition ANDROID_SPLIT_TRANSITION =
new AndroidSplitTransition();
+ @AutoCodec
+ static final LabelLateBoundDefault<AndroidConfiguration> LEGACY_MAIN_DEX_LIST_GENERATOR =
+ LabelLateBoundDefault.fromTargetConfiguration(
+ AndroidConfiguration.class,
+ null,
+ (rule, attributes, androidConfig) -> androidConfig.getLegacyMainDexListGenerator());
+
/** Android Split configuration transition for properly handling native dependencies */
public static final class AndroidSplitTransition
implements SplitTransition, AndroidSplitTransititionApi {
@@ -909,6 +916,12 @@
.undocumented(
"Do not use this attribute. It's for the migration of "
+ "Android resource processing to Starlark only."))
+ // This comes from the --legacy_main_dex_list_generator flag.
+ .add(
+ attr(":legacy_main_dex_list_generator", LABEL)
+ .cfg(HostTransition.createFactory())
+ .value(LEGACY_MAIN_DEX_LIST_GENERATOR)
+ .exec())
.removeAttribute("data")
.advertiseSkylarkProvider(SkylarkProviderIdentifier.forKey(ApkInfo.PROVIDER.getKey()))
.advertiseSkylarkProvider(SkylarkProviderIdentifier.forKey(JavaInfo.PROVIDER.getKey()))
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
index d2bc7c5..f84b16f 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
@@ -199,6 +199,42 @@
}
@Test
+ public void testLegacyMainDexListGenerator() throws Exception {
+ scratch.file(
+ "java/a/BUILD",
+ "android_binary(",
+ " name = 'a',",
+ " srcs = ['A.java'],",
+ " manifest = 'AndroidManifest.xml',",
+ " multidex = 'legacy')");
+ scratch.file(
+ "tools/fake/BUILD",
+ "cc_binary(",
+ " name = 'generate_main_dex_list',",
+ " srcs = ['main.cc'])");
+ useConfiguration("--legacy_main_dex_list_generator=//tools/fake:generate_main_dex_list");
+
+ ConfiguredTarget binary = getConfiguredTarget("//java/a:a");
+ Artifact mainDexList =
+ ActionsTestUtil.getFirstArtifactEndingWith(
+ actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)), "main_dex_list.txt");
+ List<String> args = getGeneratingSpawnActionArgs(mainDexList);
+ NestedSet<Artifact> mainDexInputs = getGeneratingAction(mainDexList).getInputs();
+
+ MoreAsserts.assertContainsSublist(args, "--lib", getAndroidJarPath());
+ MoreAsserts.assertContainsSublist(args, "--main-dex-rules", getMainDexClassesPath());
+
+ assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs)).contains("generate_main_dex_list");
+ assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs)).contains("a_deploy.jar");
+ assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs)).contains(getAndroidJarFilename());
+ assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs))
+ .contains(getMainDexClassesFilename());
+ assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs))
+ .contains("main_dex_a_proguard.cfg");
+ assertThat(getFirstArtifactEndingWith(mainDexInputs, "main_dex_list_creator")).isNull();
+ }
+
+ @Test
public void testMainDexListObfuscation() throws Exception {
useConfiguration("--noincremental_dexing");
scratch.file("/java/a/list.txt");
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java
index 392bb5f..ebcd8e2 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBuildViewTestCase.java
@@ -337,10 +337,22 @@
return getAndroidSdk().getAndroidJar().getRootRelativePathString();
}
+ protected String getAndroidJarFilename() throws Exception {
+ return getAndroidSdk().getAndroidJar().getFilename();
+ }
+
protected Artifact getProguardBinary() throws Exception {
return getAndroidSdk().getProguard().getExecutable();
}
+ protected String getMainDexClassesPath() throws Exception {
+ return getAndroidSdk().getMainDexClasses().getRootRelativePathString();
+ }
+
+ protected String getMainDexClassesFilename() throws Exception {
+ return getAndroidSdk().getMainDexClasses().getFilename();
+ }
+
private AndroidSdkProvider getAndroidSdk() throws Exception {
Label sdk = targetConfig.getFragment(AndroidConfiguration.class).getSdk();
return getConfiguredTarget(sdk, targetConfig).get(AndroidSdkProvider.PROVIDER);