Separate dexpreopt.GlobalSoongConfig to allow independent caching of

it.

Introduce a Once cache for GlobalSoongConfig to allow it to get binary
tool paths from ordinary module dependencies (coming in a future CL)
that are then reused in singletons.

Bug: 145934348
Test: m
Change-Id: I440a09dba7d337965a196527566b0966a18e3653
This commit is contained in:
Martin Stjernholm 2020-01-10 20:32:59 +00:00
parent f0f747c949
commit be9d0d21d1
8 changed files with 104 additions and 61 deletions

View File

@ -22,8 +22,7 @@ import (
)
// 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
@ -82,8 +81,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,
@ -180,11 +177,9 @@ func constructWritablePath(ctx android.PathContext, path string) android.Writabl
}
// 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) {
// struct. 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) (GlobalConfig, error) {
type GlobalJSONConfig struct {
GlobalConfig
@ -204,10 +199,6 @@ 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
}
@ -253,9 +244,16 @@ func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error
return config.ModuleConfig, nil
}
// CreateGlobalSoongConfig creates a GlobalSoongConfig from the current context.
// createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
// Should not be used in dexpreopt_gen.
func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig {
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.")
}
// Default to debug version to help find bugs.
// Set USE_DEX2OAT_DEBUG to false for only building non-debug versions.
var dex2oatBinary string
@ -276,6 +274,26 @@ func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig {
}
}
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)
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 (which requires a full ModuleContext). It will panic if
// called before the first GetGlobalSoongConfig call.
func GetCachedGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig {
return ctx.Config().Get(globalSoongConfigOnceKey).(GlobalSoongConfig)
}
type globalJsonSoongConfig struct {
Profman string
Dex2oat string
@ -310,7 +328,7 @@ func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongCon
}
func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
config := CreateGlobalSoongConfig(ctx)
config := GetCachedGlobalSoongConfig(ctx)
jc := globalJsonSoongConfig{
Profman: config.Profman.String(),
Dex2oat: config.Dex2oat.String(),
@ -337,7 +355,7 @@ func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonC
}
func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
config := CreateGlobalSoongConfig(ctx)
config := GetCachedGlobalSoongConfig(ctx)
ctx.Strict("DEX2OAT", config.Dex2oat.String())
ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{
@ -390,7 +408,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"),
@ -398,6 +423,6 @@ func GlobalConfigForTests(ctx android.PathContext) GlobalConfig {
Zip2zip: android.PathForTesting("zip2zip"),
ManifestCheck: android.PathForTesting("manifest_check"),
ConstructContext: android.PathForTesting("construct_context.sh"),
},
}
}
}).(GlobalSoongConfig)
}

View File

@ -49,7 +49,7 @@ const SystemOtherPartition = "/system_other/"
// 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,
func GenerateDexpreoptRule(ctx android.PathContext, globalSoong GlobalSoongConfig,
global GlobalConfig, module ModuleConfig) (rule *android.RuleBuilder, err error) {
defer func() {
@ -72,10 +72,10 @@ 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) {
@ -87,7 +87,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)
}
}
}
@ -119,8 +119,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 +131,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 +158,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 +170,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 +190,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]
@ -299,14 +300,14 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul
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 +328,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 +341,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).
@ -409,7 +410,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").

View File

@ -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)
if err != nil {
fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalSoongConfigPath, err)
fmt.Fprintf(os.Stderr, "error loading 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.LoadGlobalConfig(ctx, globalConfigData)
if err != nil {
fmt.Fprintf(os.Stderr, "error parse global config %q: %s\n", *globalConfigPath, err)
fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalConfigPath, 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())

View File

@ -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")
@ -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)
}

View File

@ -104,6 +104,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo
return dexJarFile
}
globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
global := dexpreoptGlobalConfig(ctx)
bootImage := defaultBootImageConfig(ctx)
if global.UseApexImage {
@ -189,7 +190,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo
PresignedPrebuilt: d.isPresignedPrebuilt,
}
dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, dexpreoptConfig)
dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
if err != nil {
ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
return dexJarFile

View File

@ -304,6 +304,7 @@ func buildBootImage(ctx android.SingletonContext, config bootImageConfig) *bootI
func buildBootImageRuleForArch(ctx android.SingletonContext, image *bootImage,
arch android.ArchType, profile android.Path, missingDeps []string) android.WritablePaths {
globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
global := dexpreoptGlobalConfig(ctx)
symbolsDir := image.symbolsDir.Join(ctx, image.installSubdir, arch.String())
@ -339,7 +340,7 @@ func buildBootImageRuleForArch(ctx android.SingletonContext, image *bootImage,
invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
cmd.Tool(global.SoongConfig.Dex2oat).
cmd.Tool(globalSoong.Dex2oat).
Flag("--avoid-storing-invocation").
FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
@ -442,6 +443,7 @@ It is likely that the boot classpath is inconsistent.
Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
global := dexpreoptGlobalConfig(ctx)
if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
@ -473,7 +475,7 @@ func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missin
rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
Tool(global.SoongConfig.Profman).
Tool(globalSoong.Profman).
FlagWithInput("--create-profile-from=", bootImageProfile).
FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
FlagForEachArg("--dex-location=", image.dexLocationsDeps).
@ -496,6 +498,7 @@ func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missin
var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule")
func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
global := dexpreoptGlobalConfig(ctx)
if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
@ -522,7 +525,7 @@ func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImage, mi
rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
Tool(global.SoongConfig.Profman).
Tool(globalSoong.Profman).
Flag("--generate-boot-profile").
FlagWithInput("--create-profile-from=", bootFrameworkProfile).
FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).

View File

@ -39,8 +39,7 @@ func dexpreoptGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
panic(err)
} else if data != nil {
soongConfig := dexpreopt.CreateGlobalSoongConfig(ctx)
globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, data, soongConfig)
globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, data)
if err != nil {
panic(err)
}

View File

@ -57,7 +57,13 @@ func TestMain(m *testing.M) {
}
func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
return TestConfig(buildDir, env, bp, fs)
config := TestConfig(buildDir, env, bp, fs)
// Set up the global Once cache used for dexpreopt.GlobalSoongConfig, so that
// it doesn't create a real one, which would fail.
_ = dexpreopt.GlobalSoongConfigForTests(config)
return config
}
func testContext() *android.TestContext {