summaryrefslogtreecommitdiff
path: root/dexpreopt/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'dexpreopt/config.go')
-rw-r--r--dexpreopt/config.go240
1 files changed, 198 insertions, 42 deletions
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)
}