From fcfcb929366dd3faac9643302b19c88bcf871ec6 Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Fri, 8 Sep 2023 14:51:31 -0700 Subject: [PATCH] Add fields "filtered_deploy_jar" and "shuffled_java_resource_jar" to AndroidDexInfo and wire them up in rule android_binary. PiperOrigin-RevId: 563858224 Change-Id: I1c77631b83530ef0295cba82e2586c16c1c7450b --- .../lib/rules/android/AndroidBinary.java | 230 ++++++++++-------- .../lib/rules/android/AndroidDexInfo.java | 28 ++- .../android/AndroidDexInfoApi.java | 42 +++- 3 files changed, 200 insertions(+), 100 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 606a21ed562fed..cb7e0d33ec0a34 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 @@ -657,97 +657,115 @@ public static RuleConfiguredTargetBuilder createAndroidBinary( } Artifact jarToDex = proguardOutput.getOutputJar(); - DexingOutput dexingOutput = - dex( - ruleContext, - androidSemantics, - binaryJar, - jarToDex, - isBinaryJarFiltered, - androidCommon, - resourceApk.getMainDexProguardConfig(), - resourceClasses, - derivedJarFunction, - proguardOutputMap, - postProcessingOutputMap, - proguardOutput.getLibraryJar(), - proguardOutput.getStartupProfileRewritten(), - !proguardSpecs.isEmpty()); - - DexPostprocessingOutput dexPostprocessingOutput = - androidSemantics.postprocessClassesDexZip( - ruleContext, - filesBuilder, - dexingOutput.classesDexZip, - proguardOutput, - postProcessingOutputMap, - dexingOutput.mainDexList); - - + DexingOutput dexingOutput = null; + DexPostprocessingOutput dexPostprocessingOutput = null; + ImmutableList finalShardDexZips = null; + Java8LegacyDexOutput java8LegacyDexOutput = null; // Compute the final DEX files by appending Java 8 legacy .dex if used. final Artifact finalClassesDex; - Java8LegacyDexOutput java8LegacyDexOutput = null; - ImmutableList finalShardDexZips = dexingOutput.shardDexZips; + if (androidDexInfo != null) { finalClassesDex = androidDexInfo.getFinalClassesDexZip(); - } else if (AndroidCommon.getAndroidConfig(ruleContext).desugarJava8Libs() - && dexPostprocessingOutput.classesDexZip().getFilename().endsWith(".zip")) { - if (binaryJar.equals(jarToDex)) { - // No shrinking: use canned Java 8 legacy .dex file - java8LegacyDexOutput = Java8LegacyDexOutput.getCanned(ruleContext); - } else { - // Shrinking is used: build custom Java 8 legacy .dex file - java8LegacyDexOutput = buildJava8LegacyDex(ruleContext, jarToDex); - - // Merge the mapping files from shrinking the program and Java 8 legacy .dex file. - if (finalProguardOutputMap != null) { - ruleContext.registerAction( - createSpawnActionBuilder(ruleContext) - .useDefaultShellEnvironment() - .setExecutable(ruleContext.getExecutablePrerequisite("$merge_proguard_maps")) - .addInput(dexPostprocessingOutput.proguardMap()) - .addInput(java8LegacyDexOutput.getMap()) - .addOutput(finalProguardOutputMap) - .addCommandLine( - CustomCommandLine.builder() - .addExecPath("--pg-map", dexPostprocessingOutput.proguardMap()) - .addExecPath("--pg-map", java8LegacyDexOutput.getMap()) - .addExecPath("--pg-map-output", finalProguardOutputMap) - .build()) - .setMnemonic("MergeProguardMaps") - .setProgressMessage( - "Merging app and desugared library Proguard maps for %{label}") - .build(ruleContext)); - } + finalShardDexZips = ImmutableList.of(); + if (androidDexInfo.getShuffledJavaResourceJar() != null) { + // Symlink to the java resource jar created by this android_binary's android_binary_internal + // target to satisfy its implicit output of android_binary. + ruleContext.registerAction( + SymlinkAction.toArtifact( + ruleContext.getActionOwner(), + androidDexInfo.getShuffledJavaResourceJar(), // target + ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.JAVA_RESOURCES_JAR), // symlink + "Symlinking Android shuffled java resources jar")); } + } else { + dexingOutput = + dex( + ruleContext, + androidSemantics, + binaryJar, + jarToDex, + isBinaryJarFiltered, + androidCommon, + resourceApk.getMainDexProguardConfig(), + resourceClasses, + derivedJarFunction, + proguardOutputMap, + postProcessingOutputMap, + proguardOutput.getLibraryJar(), + proguardOutput.getStartupProfileRewritten(), + !proguardSpecs.isEmpty()); - // Append legacy .dex library to app's .dex files - finalClassesDex = getDxArtifact(ruleContext, "_final_classes.dex.zip"); - ruleContext.registerAction( - createSpawnActionBuilder(ruleContext) - .useDefaultShellEnvironment() - .setMnemonic("AppendJava8LegacyDex") - .setProgressMessage("Adding Java 8 legacy library for %s", ruleContext.getLabel()) - .setExecutable(ruleContext.getExecutablePrerequisite("$merge_dexzips")) - .addInput(dexPostprocessingOutput.classesDexZip()) - .addInput(java8LegacyDexOutput.getDex()) - .addOutput(finalClassesDex) - // Order matters here: we want java8LegacyDex to be the highest-numbered classesN.dex - .addCommandLine( - CustomCommandLine.builder() - .addExecPath("--input_zip", dexPostprocessingOutput.classesDexZip()) - .addExecPath("--input_zip", java8LegacyDexOutput.getDex()) - .addExecPath("--output_zip", finalClassesDex) - .build()) - .build(ruleContext)); - finalShardDexZips = - ImmutableList.builder() - .addAll(finalShardDexZips) - .add(java8LegacyDexOutput.getDex()) - .build(); + dexPostprocessingOutput = + androidSemantics.postprocessClassesDexZip( + ruleContext, + filesBuilder, + dexingOutput.classesDexZip, + proguardOutput, + postProcessingOutputMap, + dexingOutput.mainDexList); + + finalShardDexZips = dexingOutput.shardDexZips; + if (AndroidCommon.getAndroidConfig(ruleContext).desugarJava8Libs() + && dexPostprocessingOutput.classesDexZip().getFilename().endsWith(".zip")) { + if (binaryJar.equals(jarToDex)) { + // No shrinking: use canned Java 8 legacy .dex file + java8LegacyDexOutput = Java8LegacyDexOutput.getCanned(ruleContext); + } else { + // Shrinking is used: build custom Java 8 legacy .dex file + java8LegacyDexOutput = buildJava8LegacyDex(ruleContext, jarToDex); + + // Merge the mapping files from shrinking the program and Java 8 legacy .dex file. + if (finalProguardOutputMap != null) { + ruleContext.registerAction( + createSpawnActionBuilder(ruleContext) + .useDefaultShellEnvironment() + .setExecutable(ruleContext.getExecutablePrerequisite("$merge_proguard_maps")) + .addInput(dexPostprocessingOutput.proguardMap()) + .addInput(java8LegacyDexOutput.getMap()) + .addOutput(finalProguardOutputMap) + .addCommandLine( + CustomCommandLine.builder() + .addExecPath("--pg-map", dexPostprocessingOutput.proguardMap()) + .addExecPath("--pg-map", java8LegacyDexOutput.getMap()) + .addExecPath("--pg-map-output", finalProguardOutputMap) + .build()) + .setMnemonic("MergeProguardMaps") + .setProgressMessage( + "Merging app and desugared library Proguard maps for %{label}") + .build(ruleContext)); + } + } - } else { - finalClassesDex = dexPostprocessingOutput.classesDexZip(); + // Append legacy .dex library to app's .dex files + finalClassesDex = getDxArtifact(ruleContext, "_final_classes.dex.zip"); + ruleContext.registerAction( + createSpawnActionBuilder(ruleContext) + .useDefaultShellEnvironment() + .setMnemonic("AppendJava8LegacyDex") + .setProgressMessage("Adding Java 8 legacy library for %{label}") + .setExecutable(ruleContext.getExecutablePrerequisite("$merge_dexzips")) + .addInput(dexPostprocessingOutput.classesDexZip()) + .addInput(java8LegacyDexOutput.getDex()) + .addOutput(finalClassesDex) + // Order matters here: we want java8LegacyDex to be the highest-numbered + // classesN.dex + .addCommandLine( + CustomCommandLine.builder() + .addExecPath("--input_zip", dexPostprocessingOutput.classesDexZip()) + .addExecPath("--input_zip", java8LegacyDexOutput.getDex()) + .addExecPath("--output_zip", finalClassesDex) + .build()) + .build(ruleContext)); + finalShardDexZips = + ImmutableList.builder() + .addAll(finalShardDexZips) + .add(java8LegacyDexOutput.getDex()) + .build(); + + } else { + finalClassesDex = dexPostprocessingOutput.classesDexZip(); + } } if (hasProguardSpecs) { @@ -900,7 +918,12 @@ public static RuleConfiguredTargetBuilder createAndroidBinary( androidCommon.isNeverLink(), /* isLibrary = */ false); - if (dexPostprocessingOutput.proguardMap() != null) { + ProguardMappingProvider proguardMappingProvider = + ruleContext.getPrerequisite("application_resources", ProguardMappingProvider.PROVIDER); + + if (proguardMappingProvider != null) { + builder.addNativeDeclaredProvider(proguardMappingProvider); + } else if (dexPostprocessingOutput != null && dexPostprocessingOutput.proguardMap() != null) { builder.addNativeDeclaredProvider( new ProguardMappingProvider(dexPostprocessingOutput.proguardMap())); } @@ -913,7 +936,9 @@ public static RuleConfiguredTargetBuilder createAndroidBinary( AndroidBinaryMobileInstall.addMobileInstall( ruleContext, builder, - dexingOutput.javaResourceJar, + androidDexInfo == null + ? dexingOutput.javaResourceJar + : androidDexInfo.getJavaResourceJar(), finalShardDexZips, javaSemantics, nativeLibs, @@ -2551,15 +2576,30 @@ private static Artifact getFilteredDeployJar(RuleContext ruleContext, Artifact d .getPreDexJar(); Artifact filteredDeployJar = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_TEST_FILTERED_JAR); - AndroidCommon.createZipFilterAction( - ruleContext, - deployJar, - filterJar, - filteredDeployJar, - CheckHashMismatchMode.NONE, - ruleContext - .getFragment(AndroidConfiguration.class) - .removeRClassesFromInstrumentationTestJar()); + + AndroidDexInfo androidDexInfo = + ruleContext.getPrerequisite("application_resources", AndroidDexInfo.PROVIDER); + + if (androidDexInfo != null && androidDexInfo.getFilteredDeployJar() != null) { + // Symlink to the filtered deploy jar created by this android_binary's android_binary_internal + // target to satisfy the filtered deploy jar implicit output of android_binary. + ruleContext.registerAction( + SymlinkAction.toArtifact( + ruleContext.getActionOwner(), + androidDexInfo.getFilteredDeployJar(), // target + filteredDeployJar, // symlink + "Symlinking Android filtered deploy jar")); + } else { + AndroidCommon.createZipFilterAction( + ruleContext, + deployJar, + filterJar, + filteredDeployJar, + CheckHashMismatchMode.NONE, + ruleContext + .getFragment(AndroidConfiguration.class) + .removeRClassesFromInstrumentationTestJar()); + } return filteredDeployJar; } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDexInfo.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDexInfo.java index 10ef9234c87757..8b08bde11900be 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDexInfo.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDexInfo.java @@ -32,18 +32,24 @@ public class AndroidDexInfo extends NativeInfo implements AndroidDexInfoApi implements AndroidDexInfoApi.Provider { @@ -90,15 +108,19 @@ public String getName() { public AndroidDexInfo createInfo( Artifact deployJar, Object finalClassesDexZip, + Object filteredDeployJar, Object finalProguardOutputMap, - Object javaResourceJar) + Object javaResourceJar, + Object shuffledJavaResourceJar) throws EvalException { return new AndroidDexInfo( deployJar, fromNoneable(finalClassesDexZip, Artifact.class), + fromNoneable(filteredDeployJar, Artifact.class), fromNoneable(finalProguardOutputMap, Artifact.class), - fromNoneable(javaResourceJar, Artifact.class)); + fromNoneable(javaResourceJar, Artifact.class), + fromNoneable(shuffledJavaResourceJar, Artifact.class)); } } } diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDexInfoApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDexInfoApi.java index 5c94a296fc746a..b5a526ca6205b5 100644 --- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDexInfoApi.java +++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/android/AndroidDexInfoApi.java @@ -53,6 +53,15 @@ public interface AndroidDexInfoApi extends StructApi { allowReturnNones = true) FileT getFinalClassesDexZip(); + @Nullable + @StarlarkMethod( + name = "filtered_deploy_jar", + doc = "The filtered deploy jar.", + documented = false, + structField = true, + allowReturnNones = true) + FileT getFilteredDeployJar(); + @Nullable @StarlarkMethod( name = "final_proguard_output_map", @@ -71,6 +80,15 @@ public interface AndroidDexInfoApi extends StructApi { allowReturnNones = true) FileT getJavaResourceJar(); + @Nullable + @StarlarkMethod( + name = "shuffled_java_resource_jar", + doc = "The output java resource jar after shuffling the proguarded jar.", + documented = false, + structField = true, + allowReturnNones = true) + FileT getShuffledJavaResourceJar(); + /** Provider for {@link AndroidDexInfoApi}. */ @StarlarkBuiltin( name = "Provider", @@ -100,6 +118,15 @@ interface Provider extends ProviderApi { }, named = true, doc = "The zip file containing the final dex classes."), + @Param( + name = "filtered_deploy_jar", + allowedTypes = { + @ParamType(type = FileApi.class), + @ParamType(type = NoneType.class), + }, + named = true, + doc = "The filtered deploy jar.", + defaultValue = "None"), @Param( name = "final_proguard_output_map", allowedTypes = { @@ -117,15 +144,26 @@ interface Provider extends ProviderApi { }, named = true, doc = "The final Java resource jar.", - defaultValue = "None") + defaultValue = "None"), + @Param( + name = "shuffled_java_resource_jar", + allowedTypes = { + @ParamType(type = FileApi.class), + @ParamType(type = NoneType.class), + }, + named = true, + doc = "The output java resource jar after shuffling the proguarded jar.", + defaultValue = "None"), }, selfCall = true) @StarlarkConstructor AndroidDexInfoApi createInfo( FileT deployJar, Object finalClassesDexZip, + Object filteredDeployJar, Object finalProguardOutputMap, - Object javaResourceJar) + Object javaResourceJar, + Object shuffledJavaResourceJar) throws EvalException; } }