diff options
author | Colin Cross <ccross@android.com> | 2017-12-28 12:23:20 -0800 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2018-01-03 14:15:57 -0800 |
commit | 66dbc0bc32100d9696dd248018683128a1274016 (patch) | |
tree | 2e0886a1a8a90b7f3c5604d309e64c1e91c186ae | |
parent | 10d9930c065d8d12f300eed5b1ca58219a1910f4 (diff) | |
download | build_soong-66dbc0bc32100d9696dd248018683128a1274016.tar.gz build_soong-66dbc0bc32100d9696dd248018683128a1274016.tar.bz2 build_soong-66dbc0bc32100d9696dd248018683128a1274016.zip |
Add R8 support
Add support for R8 to optimize apps and java libraries.
Test: m checkbuild
Change-Id: I2afd5d7a84912d3ab613c32c599bd1ebe60562e0
-rw-r--r-- | androidmk/cmd/androidmk/android.go | 58 | ||||
-rw-r--r-- | androidmk/cmd/androidmk/androidmk_test.go | 36 | ||||
-rw-r--r-- | java/androidmk.go | 3 | ||||
-rw-r--r-- | java/app.go | 10 | ||||
-rw-r--r-- | java/builder.go | 1 | ||||
-rw-r--r-- | java/config/config.go | 2 | ||||
-rw-r--r-- | java/config/makevars.go | 1 | ||||
-rw-r--r-- | java/dex.go | 123 | ||||
-rw-r--r-- | java/java.go | 37 | ||||
-rw-r--r-- | java/java_test.go | 3 |
10 files changed, 252 insertions, 22 deletions
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go index 82b5eb95..3e134b6e 100644 --- a/androidmk/cmd/androidmk/android.go +++ b/androidmk/cmd/androidmk/android.go @@ -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) diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go index 22a52d46..c85aaaac 100644 --- a/androidmk/cmd/androidmk/androidmk_test.go +++ b/androidmk/cmd/androidmk/androidmk_test.go @@ -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 { diff --git a/java/androidmk.go b/java/androidmk.go index 24fe43ad..64ef5054 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -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)") diff --git a/java/app.go b/java/app.go index df53375a..234dcb75 100644 --- a/java/app.go +++ b/java/app.go @@ -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, diff --git a/java/builder.go b/java/builder.go index dae655d7..bf826e1c 100644 --- a/java/builder.go +++ b/java/builder.go @@ -149,7 +149,6 @@ func init() { 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 930e65e4..75176c98 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -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" diff --git a/java/config/makevars.go b/java/config/makevars.go index c382cc1a..7e125d59 100644 --- a/java/config/makevars.go +++ b/java/config/makevars.go @@ -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}") diff --git a/java/dex.go b/java/dex.go index fa5c2eea..2beb2ac3 100644 --- a/java/dex.go +++ b/java/dex.go @@ -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,22 +230,42 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, 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 f06d81d7..106284e0 100644 --- a/java/java.go +++ b/java/java.go @@ -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", diff --git a/java/java_test.go b/java/java_test.go index 6e14a706..e8298a22 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -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, } |