diff options
Diffstat (limited to 'dexpreopt')
| -rw-r--r-- | dexpreopt/Android.bp | 1 | ||||
| -rw-r--r-- | dexpreopt/config.go | 240 | ||||
| -rw-r--r-- | dexpreopt/dexpreopt.go | 165 | ||||
| -rw-r--r-- | dexpreopt/dexpreopt_gen/dexpreopt_gen.go | 24 | ||||
| -rw-r--r-- | dexpreopt/dexpreopt_test.go | 36 | ||||
| -rw-r--r-- | dexpreopt/testing.go | 47 |
6 files changed, 377 insertions, 136 deletions
diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp index c5f24e273..b8f7ea682 100644 --- a/dexpreopt/Android.bp +++ b/dexpreopt/Android.bp @@ -4,6 +4,7 @@ bootstrap_go_package { srcs: [ "config.go", "dexpreopt.go", + "testing.go", ], testSrcs: [ "dexpreopt_test.go", diff --git a/dexpreopt/config.go b/dexpreopt/config.go index e35387841..98850e51a 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -16,14 +16,16 @@ package dexpreopt import ( "encoding/json" + "fmt" "strings" + "github.com/google/blueprint" + "android/soong/android" ) // GlobalConfig stores the configuration for dex preopting. The fields are set -// from product variables via dex_preopt_config.mk, except for SoongConfig -// which come from CreateGlobalSoongConfig. +// from product variables via dex_preopt_config.mk. type GlobalConfig struct { DisablePreopt bool // disable preopt for all modules DisablePreoptModules []string // modules with preopt disabled by product-specific config @@ -81,8 +83,6 @@ type GlobalConfig struct { BootFlags string // extra flags to pass to dex2oat for the boot image Dex2oatImageXmx string // max heap size for dex2oat for the boot image Dex2oatImageXms string // initial heap size for dex2oat for the boot image - - SoongConfig GlobalSoongConfig // settings read from dexpreopt_soong.config } // GlobalSoongConfig contains the global config that is generated from Soong, @@ -178,14 +178,11 @@ func constructWritablePath(ctx android.PathContext, path string) android.Writabl return constructPath(ctx, path).(android.WritablePath) } -// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig -// struct, except the SoongConfig field which is set from the provided -// soongConfig argument. LoadGlobalConfig is used directly in Soong and in -// dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by -// Make. -func LoadGlobalConfig(ctx android.PathContext, data []byte, soongConfig GlobalSoongConfig) (GlobalConfig, error) { +// ParseGlobalConfig parses the given data assumed to be read from the global +// dexpreopt.config file into a GlobalConfig struct. +func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, error) { type GlobalJSONConfig struct { - GlobalConfig + *GlobalConfig // Copies of entries in GlobalConfig that are not constructable without extra parameters. They will be // used to construct the real value manually below. @@ -203,19 +200,70 @@ func LoadGlobalConfig(ctx android.PathContext, data []byte, soongConfig GlobalSo config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects)) config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles) - // Set this here to force the caller to provide a value for this struct (from - // either CreateGlobalSoongConfig or LoadGlobalSoongConfig). - config.GlobalConfig.SoongConfig = soongConfig - return config.GlobalConfig, nil } -// LoadModuleConfig reads a per-module dexpreopt.config file into a ModuleConfig struct. It is not used in Soong, which -// receives a ModuleConfig struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called from oMake to -// read the module dexpreopt.config written by Make. -func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error) { +type globalConfigAndRaw struct { + global *GlobalConfig + data []byte +} + +// GetGlobalConfig returns the global dexpreopt.config that's created in the +// make config phase. It is loaded once the first time it is called for any +// ctx.Config(), and returns the same data for all future calls with the same +// ctx.Config(). A value can be inserted for tests using +// setDexpreoptTestGlobalConfig. +func GetGlobalConfig(ctx android.PathContext) *GlobalConfig { + return getGlobalConfigRaw(ctx).global +} + +// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns +// the literal content of dexpreopt.config. +func GetGlobalConfigRawData(ctx android.PathContext) []byte { + return getGlobalConfigRaw(ctx).data +} + +var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig") +var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig") + +func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw { + return ctx.Config().Once(globalConfigOnceKey, func() interface{} { + if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil { + panic(err) + } else if data != nil { + globalConfig, err := ParseGlobalConfig(ctx, data) + if err != nil { + panic(err) + } + return globalConfigAndRaw{globalConfig, data} + } + + // No global config filename set, see if there is a test config set + return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} { + // Nope, return a config with preopting disabled + return globalConfigAndRaw{&GlobalConfig{ + DisablePreopt: true, + DisableGenerateProfile: true, + }, nil} + }) + }).(globalConfigAndRaw) +} + +// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig +// will return. It must be called before the first call to GetGlobalConfig for +// the config. +func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) { + config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} }) +} + +// ParseModuleConfig parses a per-module dexpreopt.config file into a +// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig +// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called +// from Make to read the module dexpreopt.config written in the Make config +// stage. +func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) { type ModuleJSONConfig struct { - ModuleConfig + *ModuleConfig // Copies of entries in ModuleConfig that are not constructable without extra parameters. They will be // used to construct the real value manually below. @@ -252,21 +300,63 @@ func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error return config.ModuleConfig, nil } -// CreateGlobalSoongConfig creates a GlobalSoongConfig from the current context. -// Should not be used in dexpreopt_gen. -func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig { - // Default to debug version to help find bugs. +// dex2oatModuleName returns the name of the module to use for the dex2oat host +// tool. It should be a binary module with public visibility that is compiled +// and installed for host. +func dex2oatModuleName(config android.Config) string { + // Default to the debug variant of dex2oat to help find bugs. // Set USE_DEX2OAT_DEBUG to false for only building non-debug versions. - var dex2oatBinary string - if ctx.Config().Getenv("USE_DEX2OAT_DEBUG") == "false" { - dex2oatBinary = "dex2oat" + if config.Getenv("USE_DEX2OAT_DEBUG") == "false" { + return "dex2oat" } else { - dex2oatBinary = "dex2oatd" + return "dex2oatd" + } +} + +var dex2oatDepTag = struct { + blueprint.BaseDependencyTag +}{} + +// RegisterToolDeps adds the necessary dependencies to binary modules for tools +// that are required later when Get(Cached)GlobalSoongConfig is called. It +// should be called from a mutator that's registered with +// android.RegistrationContext.FinalDepsMutators. +func RegisterToolDeps(ctx android.BottomUpMutatorContext) { + dex2oatBin := dex2oatModuleName(ctx.Config()) + v := ctx.Config().BuildOSTarget.Variations() + ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin) +} + +func dex2oatPathFromDep(ctx android.ModuleContext) android.Path { + dex2oatBin := dex2oatModuleName(ctx.Config()) + + dex2oatModule := ctx.GetDirectDepWithTag(dex2oatBin, dex2oatDepTag) + if dex2oatModule == nil { + // If this happens there's probably a missing call to AddToolDeps in DepsMutator. + panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin)) + } + + dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath() + if !dex2oatPath.Valid() { + panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule)) + } + + return dex2oatPath.Path() +} + +// createGlobalSoongConfig creates a GlobalSoongConfig from the current context. +// Should not be used in dexpreopt_gen. +func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig { + if ctx.Config().TestProductVariables != nil { + // If we're called in a test there'll be a confusing error from the path + // functions below that gets reported without a stack trace, so let's panic + // properly with a more helpful message. + panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.") } - return GlobalSoongConfig{ + return &GlobalSoongConfig{ Profman: ctx.Config().HostToolPath(ctx, "profman"), - Dex2oat: ctx.Config().HostToolPath(ctx, dex2oatBinary), + Dex2oat: dex2oatPathFromDep(ctx), Aapt: ctx.Config().HostToolPath(ctx, "aapt"), SoongZip: ctx.Config().HostToolPath(ctx, "soong_zip"), Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"), @@ -275,6 +365,47 @@ func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig { } } +// The main reason for this Once cache for GlobalSoongConfig is to make the +// dex2oat path available to singletons. In ordinary modules we get it through a +// dex2oatDepTag dependency, but in singletons there's no simple way to do the +// same thing and ensure the right variant is selected, hence this cache to make +// the resolved path available to singletons. This means we depend on there +// being at least one ordinary module with a dex2oatDepTag dependency. +// +// TODO(b/147613152): Implement a way to deal with dependencies from singletons, +// and then possibly remove this cache altogether (but the use in +// GlobalSoongConfigForTests also needs to be rethought). +var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig") + +// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called, +// and later returns the same cached instance. +func GetGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig { + globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} { + return createGlobalSoongConfig(ctx) + }).(*GlobalSoongConfig) + + // Always resolve the tool path from the dependency, to ensure that every + // module has the dependency added properly. + myDex2oat := dex2oatPathFromDep(ctx) + if myDex2oat != globalSoong.Dex2oat { + panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat)) + } + + return globalSoong +} + +// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an +// earlier GetGlobalSoongConfig call. This function works with any context +// compatible with a basic PathContext, since it doesn't try to create a +// GlobalSoongConfig with the proper paths (which requires a full +// ModuleContext). If there has been no prior call to GetGlobalSoongConfig, nil +// is returned. +func GetCachedGlobalSoongConfig(ctx android.PathContext) *GlobalSoongConfig { + return ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} { + return (*GlobalSoongConfig)(nil) + }).(*GlobalSoongConfig) +} + type globalJsonSoongConfig struct { Profman string Dex2oat string @@ -285,17 +416,18 @@ type globalJsonSoongConfig struct { ConstructContext string } -// LoadGlobalSoongConfig reads the dexpreopt_soong.config file into a -// GlobalSoongConfig struct. It is only used in dexpreopt_gen. -func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongConfig, error) { +// ParseGlobalSoongConfig parses the given data assumed to be read from the +// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is +// only used in dexpreopt_gen. +func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongConfig, error) { var jc globalJsonSoongConfig err := json.Unmarshal(data, &jc) if err != nil { - return GlobalSoongConfig{}, err + return &GlobalSoongConfig{}, err } - config := GlobalSoongConfig{ + config := &GlobalSoongConfig{ Profman: constructPath(ctx, jc.Profman), Dex2oat: constructPath(ctx, jc.Dex2oat), Aapt: constructPath(ctx, jc.Aapt), @@ -309,7 +441,17 @@ func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongCon } func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) { - config := CreateGlobalSoongConfig(ctx) + if GetGlobalConfig(ctx).DisablePreopt { + return + } + + config := GetCachedGlobalSoongConfig(ctx) + if config == nil { + // No module has enabled dexpreopting, so we assume there will be no calls + // to dexpreopt_gen. + return + } + jc := globalJsonSoongConfig{ Profman: config.Profman.String(), Dex2oat: config.Dex2oat.String(), @@ -336,7 +478,14 @@ func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonC } func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) { - config := CreateGlobalSoongConfig(ctx) + if GetGlobalConfig(ctx).DisablePreopt { + return + } + + config := GetCachedGlobalSoongConfig(ctx) + if config == nil { + return + } ctx.Strict("DEX2OAT", config.Dex2oat.String()) ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{ @@ -350,8 +499,8 @@ func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) { }, " ")) } -func GlobalConfigForTests(ctx android.PathContext) GlobalConfig { - return GlobalConfig{ +func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig { + return &GlobalConfig{ DisablePreopt: false, DisablePreoptModules: nil, OnlyPreoptBootImageAndSystemServer: false, @@ -389,7 +538,14 @@ func GlobalConfigForTests(ctx android.PathContext) GlobalConfig { BootFlags: "", Dex2oatImageXmx: "", Dex2oatImageXms: "", - SoongConfig: GlobalSoongConfig{ + } +} + +func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig { + // Install the test GlobalSoongConfig in the Once cache so that later calls to + // Get(Cached)GlobalSoongConfig returns it without trying to create a real one. + return config.Once(globalSoongConfigOnceKey, func() interface{} { + return &GlobalSoongConfig{ Profman: android.PathForTesting("profman"), Dex2oat: android.PathForTesting("dex2oat"), Aapt: android.PathForTesting("aapt"), @@ -397,6 +553,6 @@ func GlobalConfigForTests(ctx android.PathContext) GlobalConfig { Zip2zip: android.PathForTesting("zip2zip"), ManifestCheck: android.PathForTesting("manifest_check"), ConstructContext: android.PathForTesting("construct_context.sh"), - }, - } + } + }).(*GlobalSoongConfig) } diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index ac5b691c1..6cb987385 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -41,16 +41,25 @@ import ( "android/soong/android" + "github.com/google/blueprint" "github.com/google/blueprint/pathtools" ) const SystemPartition = "/system/" const SystemOtherPartition = "/system_other/" +type dependencyTag struct { + blueprint.BaseDependencyTag + name string +} + +var SystemServerDepTag = dependencyTag{name: "system-server-dep"} +var SystemServerForcedDepTag = dependencyTag{name: "system-server-forced-dep"} + // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a // ModuleConfig. The produced files and their install locations will be available through rule.Installs(). -func GenerateDexpreoptRule(ctx android.PathContext, - global GlobalConfig, module ModuleConfig) (rule *android.RuleBuilder, err error) { +func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConfig, + global *GlobalConfig, module *ModuleConfig) (rule *android.RuleBuilder, err error) { defer func() { if r := recover(); r != nil { @@ -72,13 +81,13 @@ func GenerateDexpreoptRule(ctx android.PathContext, var profile android.WritablePath if generateProfile { - profile = profileCommand(ctx, global, module, rule) + profile = profileCommand(ctx, globalSoong, global, module, rule) } if generateBootProfile { - bootProfileCommand(ctx, global, module, rule) + bootProfileCommand(ctx, globalSoong, global, module, rule) } - if !dexpreoptDisabled(global, module) { + if !dexpreoptDisabled(ctx, global, module) { // Don't preopt individual boot jars, they will be preopted together. if !contains(global.BootJars, module.Name) { appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) && @@ -87,7 +96,7 @@ func GenerateDexpreoptRule(ctx android.PathContext, generateDM := shouldGenerateDM(module, global) for archIdx, _ := range module.Archs { - dexpreoptCommand(ctx, global, module, rule, archIdx, profile, appImage, generateDM) + dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM) } } } @@ -95,14 +104,21 @@ func GenerateDexpreoptRule(ctx android.PathContext, return rule, nil } -func dexpreoptDisabled(global GlobalConfig, module ModuleConfig) bool { +func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool { if contains(global.DisablePreoptModules, module.Name) { return true } // Don't preopt system server jars that are updatable. for _, p := range global.UpdatableSystemServerJars { - if _, jar := SplitApexJarPair(p); jar == module.Name { + if _, jar := android.SplitApexJarPair(p); jar == module.Name { + return true + } + } + + // Don't preopt system server jars that are not Soong modules. + if android.InList(module.Name, NonUpdatableSystemServerJars(ctx, global)) { + if _, ok := ctx.(android.ModuleContext); !ok { return true } } @@ -119,8 +135,8 @@ func dexpreoptDisabled(global GlobalConfig, module ModuleConfig) bool { return false } -func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, - rule *android.RuleBuilder) android.WritablePath { +func profileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig, + module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath { profilePath := module.BuildPath.InSameDir(ctx, "profile.prof") profileInstalledPath := module.DexLocation + ".prof" @@ -131,7 +147,7 @@ func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleC cmd := rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). - Tool(global.SoongConfig.Profman) + Tool(globalSoong.Profman) if module.ProfileIsTextListing { // The profile is a test listing of classes (used for framework jars). @@ -158,8 +174,8 @@ func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleC return profilePath } -func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, - rule *android.RuleBuilder) android.WritablePath { +func bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig, + module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath { profilePath := module.BuildPath.InSameDir(ctx, "profile.bprof") profileInstalledPath := module.DexLocation + ".bprof" @@ -170,7 +186,7 @@ func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module Mod cmd := rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). - Tool(global.SoongConfig.Profman) + Tool(globalSoong.Profman) // The profile is a test listing of methods. // We need to generate the actual binary profile. @@ -190,8 +206,9 @@ func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module Mod return profilePath } -func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder, - archIdx int, profile android.WritablePath, appImage bool, generateDM bool) { +func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig, + module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath, + appImage bool, generateDM bool) { arch := module.Archs[archIdx] @@ -236,7 +253,8 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul var conditionalClassLoaderContextHost29 android.Paths var conditionalClassLoaderContextTarget29 []string - var classLoaderContextHostString string + var classLoaderContextHostString, classLoaderContextDeviceString string + var classLoaderDeps android.Paths if module.EnforceUsesLibraries { usesLibs := append(copyOf(module.UsesLibraries), module.PresentOptionalUsesLibraries...) @@ -282,6 +300,30 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul filepath.Join("/system/framework", hidlBase+".jar")) classLoaderContextHostString = strings.Join(classLoaderContextHost.Strings(), ":") + } else if android.InList(module.Name, NonUpdatableSystemServerJars(ctx, global)) { + // We expect that all dexpreopted system server jars are Soong modules. + mctx, isModule := ctx.(android.ModuleContext) + if !isModule { + panic("Cannot dexpreopt system server jar that is not a soong module.") + } + + // System server jars should be dexpreopted together: class loader context of each jar + // should include preceding jars (which can be found as dependencies of the current jar + // with a special tag). + var jarsOnHost android.Paths + var jarsOnDevice []string + mctx.VisitDirectDepsWithTag(SystemServerDepTag, func(dep android.Module) { + depName := mctx.OtherModuleName(dep) + if jar, ok := dep.(interface{ DexJar() android.Path }); ok { + jarsOnHost = append(jarsOnHost, jar.DexJar()) + jarsOnDevice = append(jarsOnDevice, "/system/framework/"+depName+".jar") + } else { + mctx.ModuleErrorf("module \"%s\" is not a jar", depName) + } + }) + classLoaderContextHostString = strings.Join(jarsOnHost.Strings(), ":") + classLoaderContextDeviceString = strings.Join(jarsOnDevice, ":") + classLoaderDeps = jarsOnHost } else { // Pass special class loader context to skip the classpath and collision check. // This will get removed once LOCAL_USES_LIBRARIES is enforced. @@ -293,20 +335,25 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String())) rule.Command().FlagWithOutput("rm -f ", odexPath) // Set values in the environment of the rule. These may be modified by construct_context.sh. - rule.Command().FlagWithArg("class_loader_context_arg=--class-loader-context=", classLoaderContextHostString) - rule.Command().Text(`stored_class_loader_context_arg=""`) + if classLoaderContextHostString == `\&` { + rule.Command().Text(`class_loader_context_arg=--class-loader-context=\&`) + rule.Command().Text(`stored_class_loader_context_arg=""`) + } else { + rule.Command().Text("class_loader_context_arg=--class-loader-context=PCL[" + classLoaderContextHostString + "]") + rule.Command().Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + classLoaderContextDeviceString + "]") + } if module.EnforceUsesLibraries { if module.ManifestPath != nil { rule.Command().Text(`target_sdk_version="$(`). - Tool(global.SoongConfig.ManifestCheck). + Tool(globalSoong.ManifestCheck). Flag("--extract-target-sdk-version"). Input(module.ManifestPath). Text(`)"`) } else { // No manifest to extract targetSdkVersion from, hope that DexJar is an APK rule.Command().Text(`target_sdk_version="$(`). - Tool(global.SoongConfig.Aapt). + Tool(globalSoong.Aapt). Flag("dump badging"). Input(module.DexPath). Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`). @@ -327,7 +374,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul Implicits(conditionalClassLoaderContextHost29) rule.Command().Textf(`conditional_target_libs_29="%s"`, strings.Join(conditionalClassLoaderContextTarget29, " ")) - rule.Command().Text("source").Tool(global.SoongConfig.ConstructContext).Input(module.DexPath) + rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath) } // Devices that do not have a product partition use a symlink from /product to /system/product. @@ -340,7 +387,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul cmd := rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). - Tool(global.SoongConfig.Dex2oat). + Tool(globalSoong.Dex2oat). Flag("--avoid-storing-invocation"). FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath). Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms). @@ -348,7 +395,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", module.PreoptBootClassPathDexFiles, ":"). Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":"). Flag("${class_loader_context_arg}"). - Flag("${stored_class_loader_context_arg}"). + Flag("${stored_class_loader_context_arg}").Implicits(classLoaderDeps). FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocations, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()). FlagWithInput("--dex-file=", module.DexPath). FlagWithArg("--dex-location=", dexLocationArg). @@ -379,7 +426,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul cmd.FlagWithArg("--copy-dex-files=", "false") } - if !anyHavePrefix(preoptFlags, "--compiler-filter=") { + if !android.PrefixInList(preoptFlags, "--compiler-filter=") { var compilerFilter string if contains(global.SystemServerJars, module.Name) { // Jars of system server, use the product option if it is set, speed otherwise. @@ -409,7 +456,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm") tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex") rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath) - rule.Command().Tool(global.SoongConfig.SoongZip). + rule.Command().Tool(globalSoong.SoongZip). FlagWithArg("-L", "9"). FlagWithOutput("-o", dmPath). Flag("-j"). @@ -474,14 +521,14 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul rule.Install(vdexPath, vdexInstallPath) } -func shouldGenerateDM(module ModuleConfig, global GlobalConfig) bool { +func shouldGenerateDM(module *ModuleConfig, global *GlobalConfig) bool { // Generating DM files only makes sense for verify, avoid doing for non verify compiler filter APKs. // No reason to use a dm file if the dex is already uncompressed. return global.GenerateDMFiles && !module.UncompressedDex && contains(module.PreoptFlags, "--compiler-filter=verify") } -func OdexOnSystemOtherByName(name string, dexLocation string, global GlobalConfig) bool { +func OdexOnSystemOtherByName(name string, dexLocation string, global *GlobalConfig) bool { if !global.HasSystemOther { return false } @@ -503,7 +550,7 @@ func OdexOnSystemOtherByName(name string, dexLocation string, global GlobalConfi return false } -func odexOnSystemOther(module ModuleConfig, global GlobalConfig) bool { +func odexOnSystemOther(module *ModuleConfig, global *GlobalConfig) bool { return OdexOnSystemOtherByName(module.Name, module.DexLocation, global) } @@ -516,7 +563,7 @@ func PathToLocation(path android.Path, arch android.ArchType) string { return filepath.Join(filepath.Dir(filepath.Dir(path.String())), filepath.Base(path.String())) } -func pathForLibrary(module ModuleConfig, lib string) android.Path { +func pathForLibrary(module *ModuleConfig, lib string) android.Path { path, ok := module.LibraryPaths[lib] if !ok { panic(fmt.Errorf("unknown library path for %q", lib)) @@ -537,56 +584,38 @@ func makefileMatch(pattern, s string) bool { } // Expected format for apexJarValue = <apex name>:<jar name> -func SplitApexJarPair(apexJarValue string) (string, string) { - var apexJarPair []string = strings.SplitN(apexJarValue, ":", 2) - if apexJarPair == nil || len(apexJarPair) != 2 { - panic(fmt.Errorf("malformed apexJarValue: %q, expected format: <apex>:<jar>", - apexJarValue)) - } - return apexJarPair[0], apexJarPair[1] -} - -// Expected format for apexJarValue = <apex name>:<jar name> func GetJarLocationFromApexJarPair(apexJarValue string) string { - apex, jar := SplitApexJarPair(apexJarValue) + apex, jar := android.SplitApexJarPair(apexJarValue) return filepath.Join("/apex", apex, "javalib", jar+".jar") } -func contains(l []string, s string) bool { - for _, e := range l { - if e == s { - return true - } +func GetJarsFromApexJarPairs(apexJarPairs []string) []string { + modules := make([]string, len(apexJarPairs)) + for i, p := range apexJarPairs { + _, jar := android.SplitApexJarPair(p) + modules[i] = jar } - return false + return modules } -// remove all elements in a from b, returning a new slice -func filterOut(a []string, b []string) []string { - var ret []string - for _, x := range b { - if !contains(a, x) { - ret = append(ret, x) - } - } - return ret -} +var nonUpdatableSystemServerJarsKey = android.NewOnceKey("nonUpdatableSystemServerJars") -func replace(l []string, from, to string) { - for i := range l { - if l[i] == from { - l[i] = to - } - } +// TODO: eliminate the superficial global config parameter by moving global config definition +// from java subpackage to dexpreopt. +func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string { + return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} { + return android.RemoveListFromList(global.SystemServerJars, + GetJarsFromApexJarPairs(global.UpdatableSystemServerJars)) + }).([]string) } -var copyOf = android.CopyOf - -func anyHavePrefix(l []string, prefix string) bool { - for _, x := range l { - if strings.HasPrefix(x, prefix) { +func contains(l []string, s string) bool { + for _, e := range l { + if e == s { return true } } return false } + +var copyOf = android.CopyOf diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go index e2818bb61..e89f04591 100644 --- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go +++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go @@ -80,13 +80,13 @@ func main() { globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath) if err != nil { - fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalSoongConfigPath, err) + fmt.Fprintf(os.Stderr, "error reading global Soong config %q: %s\n", *globalSoongConfigPath, err) os.Exit(2) } - globalSoongConfig, err := dexpreopt.LoadGlobalSoongConfig(ctx, globalSoongConfigData) + globalSoongConfig, err := dexpreopt.ParseGlobalSoongConfig(ctx, globalSoongConfigData) if err != nil { - fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalSoongConfigPath, err) + fmt.Fprintf(os.Stderr, "error parsing global Soong config %q: %s\n", *globalSoongConfigPath, err) os.Exit(2) } @@ -96,9 +96,9 @@ func main() { os.Exit(2) } - globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, globalConfigData, globalSoongConfig) + globalConfig, err := dexpreopt.ParseGlobalConfig(ctx, globalConfigData) if err != nil { - fmt.Fprintf(os.Stderr, "error parse global config %q: %s\n", *globalConfigPath, err) + fmt.Fprintf(os.Stderr, "error parsing global config %q: %s\n", *globalConfigPath, err) os.Exit(2) } @@ -108,9 +108,9 @@ func main() { os.Exit(2) } - moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, moduleConfigData) + moduleConfig, err := dexpreopt.ParseModuleConfig(ctx, moduleConfigData) if err != nil { - fmt.Fprintf(os.Stderr, "error loading module config %q: %s\n", *moduleConfigPath, err) + fmt.Fprintf(os.Stderr, "error parsing module config %q: %s\n", *moduleConfigPath, err) os.Exit(2) } @@ -130,12 +130,12 @@ func main() { } }() - writeScripts(ctx, globalConfig, moduleConfig, *dexpreoptScriptPath) + writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath) } -func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreopt.ModuleConfig, - dexpreoptScriptPath string) { - dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, module) +func writeScripts(ctx android.PathContext, globalSoong *dexpreopt.GlobalSoongConfig, + global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) { + dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module) if err != nil { panic(err) } @@ -150,7 +150,7 @@ func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String())) dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath) } - dexpreoptRule.Command().Tool(global.SoongConfig.SoongZip). + dexpreoptRule.Command().Tool(globalSoong.SoongZip). FlagWithArg("-o ", "$2"). FlagWithArg("-C ", installDir.String()). FlagWithArg("-D ", installDir.String()) diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go index a128dc009..d23999328 100644 --- a/dexpreopt/dexpreopt_test.go +++ b/dexpreopt/dexpreopt_test.go @@ -20,20 +20,20 @@ import ( "testing" ) -func testSystemModuleConfig(ctx android.PathContext, name string) ModuleConfig { +func testSystemModuleConfig(ctx android.PathContext, name string) *ModuleConfig { return testModuleConfig(ctx, name, "system") } -func testSystemProductModuleConfig(ctx android.PathContext, name string) ModuleConfig { +func testSystemProductModuleConfig(ctx android.PathContext, name string) *ModuleConfig { return testModuleConfig(ctx, name, "system/product") } -func testProductModuleConfig(ctx android.PathContext, name string) ModuleConfig { +func testProductModuleConfig(ctx android.PathContext, name string) *ModuleConfig { return testModuleConfig(ctx, name, "product") } -func testModuleConfig(ctx android.PathContext, name, partition string) ModuleConfig { - return ModuleConfig{ +func testModuleConfig(ctx android.PathContext, name, partition string) *ModuleConfig { + return &ModuleConfig{ Name: name, DexLocation: fmt.Sprintf("/%s/app/test/%s.apk", partition, name), BuildPath: android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)), @@ -61,10 +61,13 @@ func testModuleConfig(ctx android.PathContext, name, partition string) ModuleCon } func TestDexPreopt(t *testing.T) { - ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil)) - global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test") + config := android.TestConfig("out", nil, "", nil) + ctx := android.PathContextForTesting(config) + globalSoong := GlobalSoongConfigForTests(config) + global := GlobalConfigForTests(ctx) + module := testSystemModuleConfig(ctx, "test") - rule, err := GenerateDexpreoptRule(ctx, global, module) + rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module) if err != nil { t.Fatal(err) } @@ -80,7 +83,9 @@ func TestDexPreopt(t *testing.T) { } func TestDexPreoptSystemOther(t *testing.T) { - ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil)) + config := android.TestConfig("out", nil, "", nil) + ctx := android.PathContextForTesting(config) + globalSoong := GlobalSoongConfigForTests(config) global := GlobalConfigForTests(ctx) systemModule := testSystemModuleConfig(ctx, "Stest") systemProductModule := testSystemProductModuleConfig(ctx, "SPtest") @@ -89,7 +94,7 @@ func TestDexPreoptSystemOther(t *testing.T) { global.HasSystemOther = true type moduleTest struct { - module ModuleConfig + module *ModuleConfig expectedPartition string } tests := []struct { @@ -118,7 +123,7 @@ func TestDexPreoptSystemOther(t *testing.T) { for _, test := range tests { global.PatternsOnSystemOther = test.patterns for _, mt := range test.moduleTests { - rule, err := GenerateDexpreoptRule(ctx, global, mt.module) + rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module) if err != nil { t.Fatal(err) } @@ -138,12 +143,15 @@ func TestDexPreoptSystemOther(t *testing.T) { } func TestDexPreoptProfile(t *testing.T) { - ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil)) - global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test") + config := android.TestConfig("out", nil, "", nil) + ctx := android.PathContextForTesting(config) + globalSoong := GlobalSoongConfigForTests(config) + global := GlobalConfigForTests(ctx) + module := testSystemModuleConfig(ctx, "test") module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile")) - rule, err := GenerateDexpreoptRule(ctx, global, module) + rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module) if err != nil { t.Fatal(err) } diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go new file mode 100644 index 000000000..b572eb351 --- /dev/null +++ b/dexpreopt/testing.go @@ -0,0 +1,47 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dexpreopt + +import ( + "android/soong/android" +) + +type dummyToolBinary struct { + android.ModuleBase +} + +func (m *dummyToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {} + +func (m *dummyToolBinary) HostToolPath() android.OptionalPath { + return android.OptionalPathForPath(android.PathForTesting("dex2oat")) +} + +func dummyToolBinaryFactory() android.Module { + module := &dummyToolBinary{} + android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst) + return module +} + +func RegisterToolModulesForTest(ctx *android.TestContext) { + ctx.RegisterModuleType("dummy_tool_binary", dummyToolBinaryFactory) +} + +func BpToolModulesForTest() string { + return ` + dummy_tool_binary { + name: "dex2oatd", + } + ` +} |