Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable r8 support in bazel #14741

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
Expand Down Expand Up @@ -445,6 +446,9 @@ public static RuleConfiguredTargetBuilder createAndroidBinary(
proguardDeps);
boolean hasProguardSpecs = !proguardSpecs.isEmpty();

// Only use R8 if we are actually proguarding, otherwise fallback to old behavior (D8)
boolean useR8 = AndroidCommon.getAndroidConfig(ruleContext).enableR8() && hasProguardSpecs;

// TODO(bazel-team): Verify that proguard spec files don't contain -printmapping directions
// which this -printmapping command line flag will override.
Artifact proguardOutputMap = null;
Expand Down Expand Up @@ -527,20 +531,23 @@ public static RuleConfiguredTargetBuilder createAndroidBinary(

resourceApk = maybeOptimizeResources(dataContext, resourceApk, hasProguardSpecs);

// R8 takes care of dexing so only dex if r8 is not enabled
Artifact jarToDex = proguardOutput.getOutputJar();
DexingOutput dexingOutput =
dex(
ruleContext,
androidSemantics,
binaryJar,
jarToDex,
isBinaryJarFiltered,
androidCommon,
resourceApk.getMainDexProguardConfig(),
resourceClasses,
derivedJarFunction,
proguardOutputMap);

DexingOutput dexingOutput = null;
if (!useR8) {
dexingOutput =
dex(
ruleContext,
androidSemantics,
binaryJar,
jarToDex,
isBinaryJarFiltered,
androidCommon,
resourceApk.getMainDexProguardConfig(),
resourceClasses,
derivedJarFunction,
proguardOutputMap);
}
// Collect all native shared libraries across split transitions. Some AARs contain shared
// libraries across multiple architectures, e.g. x86 and armeabi-v7a, and need to be packed
// into the APK.
Expand All @@ -556,79 +563,85 @@ public static RuleConfiguredTargetBuilder createAndroidBinary(
}
NestedSet<Artifact> nativeLibsAar = transitiveNativeLibs.build();

DexPostprocessingOutput dexPostprocessingOutput =
DexPostprocessingOutput dexPostprocessingOutput = null;
if (!useR8) {
dexPostprocessingOutput =
androidSemantics.postprocessClassesDexZip(
ruleContext,
filesBuilder,
dexingOutput.classesDexZip,
proguardOutput,
postProcessingOutputMap);
}

// Compute the final DEX files by appending Java 8 legacy .dex if used.
Artifact finalClassesDex;
Artifact finalClassesDex = null;
ImmutableList<Artifact> finalShardDexZips = null;
Java8LegacyDexOutput java8LegacyDexOutput;
ImmutableList<Artifact> 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));
if (!useR8) {
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));
}
}
}

// 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.<Artifact>builder()
.addAll(finalShardDexZips)
.add(java8LegacyDexOutput.getDex())
.build();
// 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.<Artifact>builder()
.addAll(finalShardDexZips)
.add(java8LegacyDexOutput.getDex())
.build();

} else {
finalClassesDex = dexPostprocessingOutput.classesDexZip();
} else {
finalClassesDex = dexPostprocessingOutput.classesDexZip();
}
}

if (hasProguardSpecs) {
if (!useR8 && hasProguardSpecs) {
proguardOutput.addAllToSet(filesBuilder, finalProguardOutputMap);
}

Expand All @@ -647,10 +660,8 @@ public static RuleConfiguredTargetBuilder createAndroidBinary(
FilesToRunProvider resourceExtractor =
ruleContext.getExecutablePrerequisite("$resource_extractor");

ApkActionsBuilder.create("apk")
.setClassesDex(finalClassesDex)
ApkActionsBuilder apkBuilder = ApkActionsBuilder.create("apk")
.addInputZip(resourceApk.getArtifact())
.setJavaResourceZip(dexingOutput.javaResourceJar, resourceExtractor)
.addInputZips(nativeLibsAar.toList())
.setNativeLibs(nativeLibs)
.setUnsignedApk(unsignedApk)
Expand All @@ -660,8 +671,16 @@ public static RuleConfiguredTargetBuilder createAndroidBinary(
.setSigningKeyRotationMinSdk(keyRotationMinSdk)
.setV4Signature(v4Signature)
.setZipalignApk(true)
.setDeterministicSigning(androidSemantics.deterministicSigning())
.registerActions(ruleContext);
.setDeterministicSigning(androidSemantics.deterministicSigning());

// R8 outputs a single jar with dexed classes and resources
if (useR8) {
apkBuilder.setJavaResourceZip(proguardOutput.getOutputJar(), resourceExtractor);
} else {
apkBuilder.setClassesDex(finalClassesDex);
apkBuilder.setJavaResourceZip(dexingOutput.javaResourceJar, resourceExtractor);
}
apkBuilder.registerActions(ruleContext);

filesBuilder.add(binaryJar);
filesBuilder.add(unsignedApk);
Expand Down Expand Up @@ -736,16 +755,19 @@ public static RuleConfiguredTargetBuilder createAndroidBinary(
androidCommon.isNeverLink(),
/* isLibrary = */ false);

if (dexPostprocessingOutput.proguardMap() != null) {
builder.addNativeDeclaredProvider(
new ProguardMappingProvider(dexPostprocessingOutput.proguardMap()));
if (!useR8) {
if (dexPostprocessingOutput.proguardMap() != null) {
builder.addNativeDeclaredProvider(
new ProguardMappingProvider(dexPostprocessingOutput.proguardMap()));
}
}

if (oneVersionEnforcementArtifact != null) {
builder.addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, oneVersionEnforcementArtifact);
}

if (mobileInstallResourceApks != null) {
// Mobile-Install relies on sharding implementation when manually dexing jars. Disable when using R8.
if (!useR8 && mobileInstallResourceApks != null) {
AndroidBinaryMobileInstall.addMobileInstall(
ruleContext,
builder,
Expand Down Expand Up @@ -967,25 +989,31 @@ private static ProguardOutput applyProguard(
}
libraryJars.addTransitive(common.getTransitiveNeverLinkLibraries());

Artifact proguardSeeds =
Artifact proguardSeeds = null;
Artifact proguardUsage = null;
if (!AndroidCommon.getAndroidConfig(ruleContext).enableR8()) {
proguardSeeds =
ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_PROGUARD_SEEDS);
Artifact proguardUsage =
proguardUsage =
ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_PROGUARD_USAGE);
}
Artifact proguardDictionary = ruleContext.getPrerequisiteArtifact("proguard_apply_dictionary");
return ProguardHelper.createOptimizationActions(
ruleContext,
sdk.getProguard(),
deployJarArtifact,
proguardSpecs,
proguardSeeds,
proguardUsage,
proguardMapping,
proguardDictionary,
libraryJars.build(),
proguardOutputJar,
javaSemantics,
getProguardOptimizationPasses(ruleContext),
proguardOutputMap);
ProguardOutput result =
ProguardHelper.createOptimizationActions(
ruleContext,
sdk.getProguard(),
deployJarArtifact,
proguardSpecs,
proguardSeeds,
proguardUsage,
proguardMapping,
proguardDictionary,
libraryJars.build(),
proguardOutputJar,
javaSemantics,
getProguardOptimizationPasses(ruleContext),
proguardOutputMap);
return result;
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,21 @@ public static class Options extends FragmentOptions {
+ "Use incremental_dexing attribute to override default for a particular "
+ "android_binary.")
public boolean incrementalDexingAfterProguardByDefault;

@Option(
name = "experimental_enable_r8",
defaultValue = "false",
metadataTags = {OptionMetadataTag.EXPERIMENTAL},
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
help =
"Whether to use R8 instead of proguarding. "
+ "This only has effect if proguard_specs are specified. "
+ " Legacy multidex and mobile-install are not supported with R8.")
public boolean enableR8;




// TODO(b/31711689): Remove this flag when this optimization is proven to work globally.
@Option(
Expand Down Expand Up @@ -982,6 +997,7 @@ public FragmentOptions getHost() {
host.incrementalDexingShardsAfterProguard = incrementalDexingShardsAfterProguard;
host.incrementalDexingUseDexSharder = incrementalDexingUseDexSharder;
host.incrementalDexingAfterProguardByDefault = incrementalDexingAfterProguardByDefault;
host.enableR8 = enableR8;
host.assumeMinSdkVersion = assumeMinSdkVersion;
host.nonIncrementalPerTargetDexopts = nonIncrementalPerTargetDexopts;
host.dexoptsSupportedInIncrementalDexing = dexoptsSupportedInIncrementalDexing;
Expand All @@ -1008,6 +1024,7 @@ public FragmentOptions getHost() {
private final int incrementalDexingShardsAfterProguard;
private final boolean incrementalDexingUseDexSharder;
private final boolean incrementalDexingAfterProguardByDefault;
private final boolean enableR8;
private final boolean assumeMinSdkVersion;
private final ImmutableList<String> dexoptsSupportedInIncrementalDexing;
private final ImmutableList<String> targetDexoptsThatPreventIncrementalDexing;
Expand Down Expand Up @@ -1060,6 +1077,7 @@ public AndroidConfiguration(BuildOptions buildOptions) throws InvalidConfigurati
this.incrementalDexingShardsAfterProguard = options.incrementalDexingShardsAfterProguard;
this.incrementalDexingUseDexSharder = options.incrementalDexingUseDexSharder;
this.incrementalDexingAfterProguardByDefault = options.incrementalDexingAfterProguardByDefault;
this.enableR8 = options.enableR8;
this.assumeMinSdkVersion = options.assumeMinSdkVersion;
this.dexoptsSupportedInIncrementalDexing =
ImmutableList.copyOf(options.dexoptsSupportedInIncrementalDexing);
Expand Down Expand Up @@ -1165,6 +1183,12 @@ public boolean incrementalDexingAfterProguardByDefault() {
return incrementalDexingAfterProguardByDefault;
}

/** Whether to dex class files using the r8 binary. */
@Override
public boolean enableR8() {
return enableR8;
}

/**
* Returns true if an -assumevalues should be generated for Proguard based on the minSdkVersion of
* the merged AndroidManifest.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ public interface AndroidConfigurationApi extends StarlarkValue {
documented = false)
boolean incrementalDexingAfterProguardByDefault();

@StarlarkMethod(
name = "experimental_enable_r8",
structField = true,
doc = "When true R8 is used for dexing and proguarding. "
+ "Only in effect when proguard_specs are specified.",
documented = false)
boolean enableR8();

@StarlarkMethod(name = "apk_signing_method_v1", structField = true, doc = "", documented = false)
boolean apkSigningMethodV1();

Expand Down