Add R8 support

Add support for R8 to optimize apps and java libraries.

Test: m checkbuild
Change-Id: I2afd5d7a84912d3ab613c32c599bd1ebe60562e0
This commit is contained in:
Colin Cross 2017-12-28 12:23:20 -08:00
parent 10d9930c06
commit 66dbc0bc32
10 changed files with 252 additions and 22 deletions

View file

@ -53,6 +53,7 @@ var rewriteProperties = map[string](func(variableAssignmentContext) error){
"LOCAL_SANITIZE_DIAG": sanitize("diag."),
"LOCAL_CFLAGS": cflags,
"LOCAL_UNINSTALLABLE_MODULE": invert("installable"),
"LOCAL_PROGUARD_ENABLED": proguardEnabled,
// composite functions
"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
@ -136,6 +137,9 @@ func init() {
"LOCAL_ANNOTATION_PROCESSORS": "annotation_processors",
"LOCAL_ANNOTATION_PROCESSOR_CLASSES": "annotation_processor_classes",
"LOCAL_PROGUARD_FLAGS": "optimize.proguard_flags",
"LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flag_files",
})
addStandardProperties(bpparser.BoolType,
map[string]string{
@ -517,6 +521,60 @@ func cflags(ctx variableAssignmentContext) error {
return includeVariableNow(bpVariable{"cflags", bpparser.ListType}, ctx)
}
func proguardEnabled(ctx variableAssignmentContext) error {
val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
if err != nil {
return err
}
list, ok := val.(*bpparser.List)
if !ok {
return fmt.Errorf("unsupported proguard expression")
}
set := func(prop string, value bool) {
bpValue := &bpparser.Bool{
Value: value,
}
setVariable(ctx.file, false, ctx.prefix, prop, bpValue, true)
}
enable := false
for _, v := range list.Values {
s, ok := v.(*bpparser.String)
if !ok {
return fmt.Errorf("unsupported proguard expression")
}
switch s.Value {
case "disabled":
set("optimize.enabled", false)
case "obfuscation":
enable = true
set("optimize.obfuscate", true)
case "optimization":
enable = true
set("optimize.optimize", true)
case "full":
enable = true
case "custom":
set("optimize.no_aapt_flags", true)
enable = true
default:
return fmt.Errorf("unsupported proguard value %q", s)
}
}
if enable {
// This is only necessary for libraries which default to false, but we can't
// tell the difference between a library and an app here.
set("optimize.enabled", true)
}
return nil
}
func invert(name string) func(ctx variableAssignmentContext) error {
return func(ctx variableAssignmentContext) error {
val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.BoolType)

View file

@ -438,6 +438,42 @@ include $(call all-makefiles-under,$(LOCAL_PATH))
`,
expected: ``,
},
{
desc: "proguard options for java library",
in: `
include $(CLEAR_VARS)
# Empty
LOCAL_PROGUARD_ENABLED :=
# Disabled
LOCAL_PROGUARD_ENABLED := disabled
# Full
LOCAL_PROGUARD_ENABLED := full
# Obfuscation and optimization
LOCAL_PROGUARD_ENABLED := obfuscation optimization
# Custom
LOCAL_PROGUARD_ENABLED := custom
include $(BUILD_JAVA_LIBRARY)
`,
expected: `
java_library {
// Empty
// Disabled
optimize: {
enabled: false,
// Full
enabled: true,
// Obfuscation and optimization
obfuscate: true,
optimize: true,
enabled: true,
// Custom
no_aapt_flags: true,
enabled: true,
},
}
`,
},
}
func reformatBlueprint(input string) string {

View file

@ -165,6 +165,9 @@ func (app *AndroidApp) AndroidMk() android.AndroidMkData {
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)")

View file

@ -125,14 +125,13 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// 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 (a *AndroidApp) aapt2Flags(ctx android.ModuleContext) (flags []string, deps
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,

View file

@ -149,7 +149,6 @@ func init() {
type javaBuilderFlags struct {
javacFlags string
dxFlags string
bootClasspath classpath
classpath classpath
systemModules classpath

View file

@ -106,8 +106,8 @@ func init() {
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"

View file

@ -55,6 +55,7 @@ func makeVarsProvider(ctx android.MakeVarsContext) {
ctx.Strict("DX_COMMAND", "${DxCmd} -JXms16M -JXmx2048M")
ctx.Strict("USE_D8_DESUGAR", "false")
}
ctx.Strict("R8_COMPAT_PROGUARD", "${R8Cmd}")
ctx.Strict("TURBINE", "${TurbineJar}")

View file

@ -108,6 +108,23 @@ var d8 = pctx.AndroidStaticRule("d8",
},
"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 @@ func (j *Module) dxFlags(ctx android.ModuleContext, fullD8 bool) []string {
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,6 +230,25 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
outDir := android.PathForModuleOut(ctx, "dex")
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 {
@ -175,6 +265,7 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
"outDir": outDir.String(),
},
})
}
j.dexJarFile = javalibJar
return javalibJar

View file

@ -191,6 +191,32 @@ type CompilerDeviceProperties struct {
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 @@ type Module struct {
// 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 @@ type Module struct {
// 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 @@ var (
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 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) {
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",

View file

@ -157,6 +157,9 @@ func testContext(config android.Config, bp string,
"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,
}