diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go index bf610ef6a..ec62eb3d8 100644 --- a/dexpreopt/class_loader_context.go +++ b/dexpreopt/class_loader_context.go @@ -534,3 +534,26 @@ func fromJsonClassLoaderContextRec(ctx android.PathContext, jClcs map[string]*js } return clcs } + +// Convert Soong CLC map to JSON representation for Make. +func toJsonClassLoaderContext(clcMap ClassLoaderContextMap) jsonClassLoaderContextMap { + jClcMap := make(jsonClassLoaderContextMap) + for sdkVer, clcs := range clcMap { + sdkVerStr := fmt.Sprintf("%d", sdkVer) + jClcMap[sdkVerStr] = toJsonClassLoaderContextRec(clcs) + } + return jClcMap +} + +// Recursive helper for toJsonClassLoaderContext. +func toJsonClassLoaderContextRec(clcs []*ClassLoaderContext) map[string]*jsonClassLoaderContext { + jClcs := make(map[string]*jsonClassLoaderContext, len(clcs)) + for _, clc := range clcs { + jClcs[clc.Name] = &jsonClassLoaderContext{ + Host: clc.Host.String(), + Device: clc.Device, + Subcontexts: toJsonClassLoaderContextRec(clc.Subcontexts), + } + } + return jClcs +} diff --git a/dexpreopt/config.go b/dexpreopt/config.go index 867ece62b..d55204b09 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -114,6 +114,7 @@ type ModuleConfig struct { ProfileBootListing android.OptionalPath EnforceUsesLibraries bool + ProvidesUsesLibrary string // the name of the (usually the same as its module) ClassLoaderContexts ClassLoaderContextMap Archs []android.ArchType @@ -290,6 +291,42 @@ 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) { + 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, "", " ") + if err != nil { + ctx.ModuleErrorf("failed to JSON marshal module dexpreopt.config: %v", err) + return + } + + android.WriteFileRule(ctx, path, string(data)) +} + // 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. diff --git a/java/androidmk.go b/java/androidmk.go index cc454b03d..21f3012a4 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -125,6 +125,10 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("LOCAL_MODULE_STEM", library.Stem()) entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", library.linter.reports) + + if library.dexpreopter.configPath != nil { + entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath) + } }, }, }) diff --git a/java/app.go b/java/app.go index e6c9a2d98..ce89e9bb6 100755 --- a/java/app.go +++ b/java/app.go @@ -455,6 +455,7 @@ func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { a.dexpreopter.installPath = a.installPath(ctx) + a.dexpreopter.isApp = true if a.dexProperties.Uncompress_dex == nil { // If the value was not force-set by the user, use reasonable default based on the module. a.dexProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx)) diff --git a/java/app_import.go b/java/app_import.go index df940f1ff..6f21bfbbf 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -255,6 +255,7 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName()) } + a.dexpreopter.isApp = true a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk") a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned) a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) diff --git a/java/dexpreopt.go b/java/dexpreopt.go index b5830c744..ac00592a7 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -30,6 +30,7 @@ type dexpreopter struct { installPath android.InstallPath uncompressedDex bool isSDKLibrary bool + isApp bool isTest bool isPresignedPrebuilt bool @@ -38,6 +39,11 @@ type dexpreopter struct { classLoaderContexts dexpreopt.ClassLoaderContextMap 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). + configPath android.WritablePath } type DexpreoptProperties struct { @@ -117,7 +123,40 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo // the dexpreopter struct hasn't been fully initialized before we're called, // e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively // disabled, even if installable is true. - if d.dexpreoptDisabled(ctx) || d.installPath.Base() == "." { + if d.installPath.Base() == "." { + return + } + + dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) + + buildPath := android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath + + providesUsesLib := ctx.ModuleName() + if ulib, ok := ctx.Module().(ProvidesUsesLib); ok { + name := ulib.ProvidesUsesLib() + if name != nil { + providesUsesLib = *name + } + } + + 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, + BuildPath: buildPath, + 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) { return } @@ -157,8 +196,6 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo // The image locations for all Android variants are identical. imageLocations := bootImage.getAnyAndroidVariant().imageLocations() - dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) - var profileClassListing android.OptionalPath var profileBootListing android.OptionalPath profileIsTextListing := false @@ -177,10 +214,11 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo } } + // Full dexpreopt config, used to create dexpreopt build rules. dexpreoptConfig := &dexpreopt.ModuleConfig{ Name: ctx.ModuleName(), DexLocation: dexLocation, - BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath, + BuildPath: buildPath, DexPath: dexJarFile, ManifestPath: d.manifestFile, UncompressedDex: d.uncompressedDex, @@ -192,6 +230,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo ProfileBootListing: profileBootListing, EnforceUsesLibraries: d.enforceUsesLibs, + ProvidesUsesLibrary: providesUsesLib, ClassLoaderContexts: d.classLoaderContexts, Archs: archs, diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go index 5550a4c17..a9e0773b7 100644 --- a/java/dexpreopt_test.go +++ b/java/dexpreopt_test.go @@ -148,7 +148,7 @@ func TestDexpreoptEnabled(t *testing.T) { t.Run(test.name, func(t *testing.T) { ctx, _ := testJava(t, test.bp) - dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeDescription("dexpreopt") + dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt") enabled := dexpreopt.Rule != nil if enabled != test.enabled {