From c6246671ea80c1d5776bfff383489bae76d7737d Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Thu, 8 Apr 2021 00:00:19 +0900 Subject: [PATCH] Expose "full" dexpreopt.config 1. Instead of 'slim' config, use full config even for libs 2. Define moduleJSONConfig for fields which cannot be converted to JSON field directly(Path type field, ProfileBootListing, DexPreoptImagesDeps are added in this CL) and exclude fields which is convertible(DexPreoptImageLocations) Bug: 158843648 Test: m dist Change-Id: I3f9192ab5292bd079be1b686bb3b25735a836cbc --- dexpreopt/config.go | 95 ++++++++++++++++++++----------------- dexpreopt/dexpreopt_test.go | 17 +++++++ java/androidmk.go | 3 ++ java/dexpreopt.go | 40 ++++++++-------- 4 files changed, 91 insertions(+), 64 deletions(-) diff --git a/dexpreopt/config.go b/dexpreopt/config.go index 26ff5baab..151f032fb 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -130,7 +130,7 @@ type ModuleConfig struct { ClassLoaderContexts ClassLoaderContextMap Archs []android.ArchType - DexPreoptImages []android.Path + DexPreoptImages android.Paths DexPreoptImagesDeps []android.OutputPaths DexPreoptImageLocations []string @@ -259,29 +259,35 @@ func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) { config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} }) } +// This struct is required to convert ModuleConfig from/to JSON. +// The types of fields in ModuleConfig are not convertible, +// so moduleJSONConfig has those fields as a convertible type. +type moduleJSONConfig struct { + *ModuleConfig + + BuildPath string + DexPath string + ManifestPath string + + ProfileClassListing string + ProfileBootListing string + + EnforceUsesLibrariesStatusFile string + ClassLoaderContexts jsonClassLoaderContextMap + + DexPreoptImages []string + DexPreoptImagesDeps [][]string + + PreoptBootClassPathDexFiles []string +} + // 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 - - // Copies of entries in ModuleConfig that are not constructable without extra parameters. They will be - // used to construct the real value manually below. - BuildPath string - DexPath string - ManifestPath string - ProfileClassListing string - EnforceUsesLibrariesStatusFile string - ClassLoaderContexts jsonClassLoaderContextMap - DexPreoptImages []string - DexPreoptImageLocations []string - PreoptBootClassPathDexFiles []string - } - - config := ModuleJSONConfig{} + config := moduleJSONConfig{} err := json.Unmarshal(data, &config) if err != nil { @@ -296,7 +302,6 @@ func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, err config.ModuleConfig.EnforceUsesLibrariesStatusFile = constructPath(ctx, config.EnforceUsesLibrariesStatusFile) config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts) config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages) - config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles) // This needs to exist, but dependencies are already handled in Make, so we don't need to pass them through JSON. @@ -305,34 +310,38 @@ func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, err return config.ModuleConfig, nil } -// WriteSlimModuleConfigForMake serializes a subset of ModuleConfig into a per-module -// dexpreopt.config JSON file. It is a way to pass dexpreopt information about Soong modules to -// Make, which is needed when a Make module has a dependency on a Soong module. -func WriteSlimModuleConfigForMake(ctx android.ModuleContext, config *ModuleConfig, path android.WritablePath) { +func pathsListToStringLists(pathsList []android.OutputPaths) [][]string { + ret := make([][]string, 0, len(pathsList)) + for _, paths := range pathsList { + ret = append(ret, paths.Strings()) + } + return ret +} + +func moduleConfigToJSON(config *ModuleConfig) ([]byte, error) { + return json.MarshalIndent(&moduleJSONConfig{ + BuildPath: config.BuildPath.String(), + DexPath: config.DexPath.String(), + ManifestPath: config.ManifestPath.String(), + ProfileClassListing: config.ProfileClassListing.String(), + ProfileBootListing: config.ProfileBootListing.String(), + EnforceUsesLibrariesStatusFile: config.EnforceUsesLibrariesStatusFile.String(), + ClassLoaderContexts: toJsonClassLoaderContext(config.ClassLoaderContexts), + DexPreoptImages: config.DexPreoptImages.Strings(), + DexPreoptImagesDeps: pathsListToStringLists(config.DexPreoptImagesDeps), + PreoptBootClassPathDexFiles: config.PreoptBootClassPathDexFiles.Strings(), + ModuleConfig: config, + }, "", " ") +} + +// WriteModuleConfig serializes a ModuleConfig into a per-module dexpreopt.config JSON file. +// These config files are used for post-processing. +func WriteModuleConfig(ctx android.ModuleContext, config *ModuleConfig, path android.WritablePath) { if path == nil { return } - // JSON representation of the slim module dexpreopt.config. - type slimModuleJSONConfig struct { - Name string - DexLocation string - BuildPath string - EnforceUsesLibraries bool - ProvidesUsesLibrary string - ClassLoaderContexts jsonClassLoaderContextMap - } - - jsonConfig := &slimModuleJSONConfig{ - Name: config.Name, - DexLocation: config.DexLocation, - BuildPath: config.BuildPath.String(), - EnforceUsesLibraries: config.EnforceUsesLibraries, - ProvidesUsesLibrary: config.ProvidesUsesLibrary, - ClassLoaderContexts: toJsonClassLoaderContext(config.ClassLoaderContexts), - } - - data, err := json.MarshalIndent(jsonConfig, "", " ") + data, err := moduleConfigToJSON(config) if err != nil { ctx.ModuleErrorf("failed to JSON marshal module dexpreopt.config: %v", err) return diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go index 12df36b6f..cd7551c8d 100644 --- a/dexpreopt/dexpreopt_test.go +++ b/dexpreopt/dexpreopt_test.go @@ -166,3 +166,20 @@ func TestDexPreoptProfile(t *testing.T) { t.Errorf("\nwant installs:\n %v\ngot:\n %v", wantInstalls, rule.Installs()) } } + +func TestDexPreoptConfigToJson(t *testing.T) { + config := android.TestConfig("out", nil, "", nil) + ctx := android.BuilderContextForTesting(config) + module := testSystemModuleConfig(ctx, "test") + data, err := moduleConfigToJSON(module) + if err != nil { + t.Errorf("Failed to convert module config data to JSON, %v", err) + } + parsed, err := ParseModuleConfig(ctx, data) + if err != nil { + t.Errorf("Failed to parse JSON, %v", err) + } + before := fmt.Sprintf("%v", module) + after := fmt.Sprintf("%v", parsed) + android.AssertStringEquals(t, "The result must be the same as the original after marshalling and unmarshalling it.", before, after) +} diff --git a/java/androidmk.go b/java/androidmk.go index 4e594a2fa..015454464 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -398,6 +398,9 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { if len(app.dexpreopter.builtInstalled) > 0 { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", app.dexpreopter.builtInstalled) } + if app.dexpreopter.configPath != nil { + entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", app.dexpreopter.configPath) + } for _, extra := range app.extraOutputFiles { install := app.onDeviceDir + "/" + extra.Base() entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", extra.String()+":"+install) diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 3571590db..d00d74b8c 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -41,9 +41,12 @@ type dexpreopter struct { builtInstalled string - // A path to a dexpreopt.config file generated by Soong for libraries that may be used as a - // by Make modules. The path is passed to Make via LOCAL_SOONG_DEXPREOPT_CONFIG - // variable. If the path is nil, no config is generated (which is the case for apps and tests). + // The config is used for two purposes: + // - Passing dexpreopt information about libraries from Soong to Make. This is needed when + // a is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py). + // Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself. + // - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally + // dexpreopt another partition). configPath android.WritablePath } @@ -138,27 +141,13 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr } } - if !d.isApp && !d.isTest { - // Slim dexpreopt config is serialized to dexpreopt.config files and used by - // dex_preopt_config_merger.py to get information about dependencies. - // Note that it might be needed even if dexpreopt is disabled for this module. - slimDexpreoptConfig := &dexpreopt.ModuleConfig{ - Name: ctx.ModuleName(), - DexLocation: dexLocation, - EnforceUsesLibraries: d.enforceUsesLibs, - ProvidesUsesLibrary: providesUsesLib, - ClassLoaderContexts: d.classLoaderContexts, - // The rest of the fields are not needed. - } - d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config") - dexpreopt.WriteSlimModuleConfigForMake(ctx, slimDexpreoptConfig, d.configPath) - } - - if d.dexpreoptDisabled(ctx) { + // If it is neither app nor test, make config files regardless of its dexpreopt setting. + // The config files are required for apps defined in make which depend on the lib. + // TODO(b/158843648): The config for apps should be generated as well regardless of setting. + if (d.isApp || d.isTest) && d.dexpreoptDisabled(ctx) { return } - globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) global := dexpreopt.GetGlobalConfig(ctx) isSystemServerJar := inList(ctx.ModuleName(), global.SystemServerJars) @@ -251,6 +240,15 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr PresignedPrebuilt: d.isPresignedPrebuilt, } + d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config") + dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath) + + if d.dexpreoptDisabled(ctx) { + return + } + + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) + dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig) if err != nil { ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())