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())