Add R8 support
Add support for R8 to optimize apps and java libraries.
Test: m checkbuild
Change-Id: I2afd5d7a84912d3ab613c32c599bd1ebe60562e0
diff --git a/java/androidmk.go b/java/androidmk.go
index 24fe43a..64ef505 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -165,6 +165,9 @@
if app.jacocoReportClassesFile != nil {
fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", app.jacocoReportClassesFile.String())
}
+ if app.proguardDictionary != nil {
+ fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", app.proguardDictionary.String())
+ }
if app.Name() == "framework-res" {
fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)")
diff --git a/java/app.go b/java/app.go
index df53375..234dcb7 100644
--- a/java/app.go
+++ b/java/app.go
@@ -125,14 +125,13 @@
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
- //if !ctx.ContainsProperty("proguard.enabled") {
- // a.properties.Proguard.Enabled = true
- //}
-
if String(a.appProperties.Instrumentation_for) == "" {
a.properties.Instrument = true
}
+ a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles,
+ proguardOptionsFile)
+
if ctx.ModuleName() != "framework-res" {
a.Module.compile(ctx, a.aaptSrcJar)
}
@@ -324,6 +323,9 @@
func AndroidAppFactory() android.Module {
module := &AndroidApp{}
+ module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
+ module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
+
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
diff --git a/java/builder.go b/java/builder.go
index dae655d..bf826e1 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -149,7 +149,6 @@
type javaBuilderFlags struct {
javacFlags string
- dxFlags string
bootClasspath classpath
classpath classpath
systemModules classpath
diff --git a/java/config/config.go b/java/config/config.go
index 930e65e..75176c9 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -106,8 +106,8 @@
return path.String(), nil
}
})
-
pctx.HostBinToolVariable("D8Cmd", "d8")
+ pctx.HostBinToolVariable("R8Cmd", "r8-compat-proguard")
pctx.VariableFunc("TurbineJar", func(config android.Config) (string, error) {
turbine := "turbine.jar"
diff --git a/java/config/makevars.go b/java/config/makevars.go
index c382cc1..7e125d5 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -55,6 +55,7 @@
ctx.Strict("DX_COMMAND", "${DxCmd} -JXms16M -JXmx2048M")
ctx.Strict("USE_D8_DESUGAR", "false")
}
+ ctx.Strict("R8_COMPAT_PROGUARD", "${R8Cmd}")
ctx.Strict("TURBINE", "${TurbineJar}")
diff --git a/java/dex.go b/java/dex.go
index fa5c2ee..2beb2ac 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -108,6 +108,23 @@
},
"outDir", "dxFlags")
+var r8 = pctx.AndroidStaticRule("r8",
+ blueprint.RuleParams{
+ Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
+ `${config.R8Cmd} -injars $in --output $outDir ` +
+ `--force-proguard-compatibility ` +
+ `-printmapping $outDict ` +
+ `$dxFlags $r8Flags && ` +
+ `${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -D $outDir && ` +
+ `${config.MergeZipsCmd} -D -stripFile "*.class" $out $outDir/classes.dex.jar $in`,
+ CommandDeps: []string{
+ "${config.R8Cmd}",
+ "${config.SoongZipCmd}",
+ "${config.MergeZipsCmd}",
+ },
+ },
+ "outDir", "outDict", "dxFlags", "r8Flags")
+
func (j *Module) dxFlags(ctx android.ModuleContext, fullD8 bool) []string {
flags := j.deviceProperties.Dxflags
if fullD8 {
@@ -144,10 +161,64 @@
return flags
}
+func (j *Module) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
+ opt := j.deviceProperties.Optimize
+
+ // When an app contains references to APIs that are not in the SDK specified by
+ // its LOCAL_SDK_VERSION for example added by support library or by runtime
+ // classes added by desugar, we artifically raise the "SDK version" "linked" by
+ // ProGuard, to
+ // - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.
+ // - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
+ // See b/20667396
+ var proguardRaiseDeps classpath
+ ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(dep android.Module) {
+ proguardRaiseDeps = append(proguardRaiseDeps, dep.(Dependency).HeaderJars()...)
+ })
+
+ r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
+ r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
+ r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
+ r8Flags = append(r8Flags, "-forceprocessing")
+
+ flagFiles := android.Paths{
+ android.PathForSource(ctx, "build/make/core/proguard.flags"),
+ }
+
+ flagFiles = append(flagFiles, j.extraProguardFlagFiles...)
+ // TODO(ccross): static android library proguard files
+
+ r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
+ r8Deps = append(r8Deps, flagFiles...)
+
+ // TODO(b/70942988): This is included from build/make/core/proguard.flags
+ r8Deps = append(r8Deps, android.PathForSource(ctx,
+ "build/make/core/proguard_basic_keeps.flags"))
+
+ r8Flags = append(r8Flags, j.deviceProperties.Optimize.Proguard_flags...)
+
+ // TODO(ccross): Don't shrink app instrumentation tests by default.
+ if !Bool(opt.Shrink) {
+ r8Flags = append(r8Flags, "-dontshrink")
+ }
+
+ if !Bool(opt.Optimize) {
+ r8Flags = append(r8Flags, "-dontoptimize")
+ }
+
+ // TODO(ccross): error if obufscation + app instrumentation test.
+ if !Bool(opt.Obfuscate) {
+ r8Flags = append(r8Flags, "-dontobfuscate")
+ }
+
+ return r8Flags, r8Deps
+}
+
func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
classesJar android.Path, jarName string) android.Path {
- fullD8 := ctx.Config().UseD8Desugar()
+ useR8 := Bool(j.deviceProperties.Optimize.Enabled)
+ fullD8 := useR8 || ctx.Config().UseD8Desugar()
if !fullD8 {
classesJar = j.desugar(ctx, flags, classesJar, jarName)
@@ -159,22 +230,42 @@
javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
outDir := android.PathForModuleOut(ctx, "dex")
- rule := dx
- desc := "dx"
- if fullD8 {
- rule = d8
- desc = "d8"
+ if useR8 {
+ // TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
+ // dictionary of the app and move the app from libraryjars to injars.
+ j.proguardDictionary = android.PathForModuleOut(ctx, "proguard_dictionary")
+ r8Flags, r8Deps := j.r8Flags(ctx, flags)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: r8,
+ Description: "r8",
+ Output: javalibJar,
+ Input: classesJar,
+ Implicits: r8Deps,
+ Args: map[string]string{
+ "dxFlags": strings.Join(dxFlags, " "),
+ "r8Flags": strings.Join(r8Flags, " "),
+ "outDict": j.proguardDictionary.String(),
+ "outDir": outDir.String(),
+ },
+ })
+ } else {
+ rule := dx
+ desc := "dx"
+ if fullD8 {
+ rule = d8
+ desc = "d8"
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rule,
+ Description: desc,
+ Output: javalibJar,
+ Input: classesJar,
+ Args: map[string]string{
+ "dxFlags": strings.Join(dxFlags, " "),
+ "outDir": outDir.String(),
+ },
+ })
}
- ctx.Build(pctx, android.BuildParams{
- Rule: rule,
- Description: desc,
- Output: javalibJar,
- Input: classesJar,
- Args: map[string]string{
- "dxFlags": strings.Join(dxFlags, " "),
- "outDir": outDir.String(),
- },
- })
j.dexJarFile = javalibJar
return javalibJar
diff --git a/java/java.go b/java/java.go
index f06d81d..106284e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -191,6 +191,32 @@
Profile *string
}
+ Optimize struct {
+ // If false, disable all optimization. Defaults to true for apps, false for
+ // libraries and tests.
+ Enabled *bool
+
+ // If true, optimize for size by removing unused code. Defaults to true for apps,
+ // false for libraries and tests.
+ Shrink *bool
+
+ // If true, optimize bytecode. Defaults to false.
+ Optimize *bool
+
+ // If true, obfuscate bytecode. Defaults to false.
+ Obfuscate *bool
+
+ // If true, do not use the flag files generated by aapt that automatically keep
+ // classes referenced by the app manifest. Defaults to false.
+ No_aapt_flags *bool
+
+ // Flags to pass to proguard.
+ Proguard_flags []string
+
+ // Specifies the locations of files containing proguard flags.
+ Proguard_flags_files []string
+ }
+
// When targeting 1.9, override the modules to use with --system
System_modules *string
}
@@ -216,6 +242,9 @@
// output file containing uninstrumented classes that will be instrumented by jacoco
jacocoReportClassesFile android.Path
+ // output file containing mapping of obfuscated names
+ proguardDictionary android.Path
+
// output file suitable for installing or running
outputFile android.Path
@@ -229,6 +258,9 @@
// list of .java files and srcjars that was passed to javac
compiledJavaSrcs android.Paths
compiledSrcJars android.Paths
+
+ // list of extra progurad flag files
+ extraProguardFlagFiles android.Paths
}
func (j *Module) Srcs() android.Paths {
@@ -260,6 +292,7 @@
systemModulesTag = dependencyTag{name: "system modules"}
frameworkResTag = dependencyTag{name: "framework-res"}
kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
+ proguardRaiseTag = dependencyTag{name: "proguard-raise"}
)
type sdkDep struct {
@@ -377,6 +410,10 @@
ctx.AddDependency(ctx.Module(), systemModulesTag, sdkDep.systemModules)
}
ctx.AddDependency(ctx.Module(), bootClasspathTag, sdkDep.module)
+ if Bool(j.deviceProperties.Optimize.Enabled) {
+ ctx.AddDependency(ctx.Module(), proguardRaiseTag, config.DefaultBootclasspathLibraries...)
+ ctx.AddDependency(ctx.Module(), proguardRaiseTag, config.DefaultLibraries...)
+ }
}
} else if j.deviceProperties.System_modules == nil {
ctx.PropertyErrorf("no_standard_libs",
diff --git a/java/java_test.go b/java/java_test.go
index 6e14a70..e8298a2 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -157,6 +157,9 @@
"build/soong/scripts/jar-wrapper.sh": nil,
+ "build/make/core/proguard.flags": nil,
+ "build/make/core/proguard_basic_keeps.flags": nil,
+
"jdk8/jre/lib/jce.jar": nil,
"jdk8/jre/lib/rt.jar": nil,
}