diff --git a/apex/apex.go b/apex/apex.go index 635ff30a3..8668a7820 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -1765,13 +1765,17 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { } case bcpfTag: { - if _, ok := child.(*java.BootclasspathFragmentModule); !ok { + bcpfModule, ok := child.(*java.BootclasspathFragmentModule) + if !ok { ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName) return false } filesToAdd := apexBootclasspathFragmentFiles(ctx, child) filesInfo = append(filesInfo, filesToAdd...) + for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() { + a.requiredDeps = append(a.requiredDeps, makeModuleName) + } return true } case sscpfTag: @@ -2175,13 +2179,15 @@ func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint. var filesToAdd []apexFile // Add the boot image files, e.g. .art, .oat and .vdex files. - for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() { - dirInApex := filepath.Join("javalib", arch.String()) - for _, f := range files { - androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String()) - // TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil - af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil) - filesToAdd = append(filesToAdd, af) + if bootclasspathFragmentInfo.ShouldInstallBootImageInApex() { + for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() { + dirInApex := filepath.Join("javalib", arch.String()) + for _, f := range files { + androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String()) + // TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil + af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil) + filesToAdd = append(filesToAdd, af) + } } } diff --git a/apex/apex_test.go b/apex/apex_test.go index 727a1f2ec..59545c2eb 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -8739,6 +8739,22 @@ func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) { }) } +// Verifies that the APEX depends on all the Make modules in the list. +func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) { + a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle) + for _, dep := range deps { + android.AssertStringListContains(t, "", a.requiredDeps, dep) + } +} + +// Verifies that the APEX does not depend on any of the Make modules in the list. +func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) { + a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle) + for _, dep := range deps { + android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep) + } +} + func TestMain(m *testing.M) { os.Exit(m.Run()) } diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index ce828e1af..8f44fc537 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -410,6 +410,7 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { // bootclasspath_fragment's contents property. java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"), addSource("foo", "bar"), + java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"), ).RunTest(t) ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ @@ -437,12 +438,62 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { `mybootclasspathfragment`, }) + // The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules. + ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + "mybootclasspathfragment-dexpreopt-arm64-boot.art", + "mybootclasspathfragment-dexpreopt-arm64-boot.oat", + "mybootclasspathfragment-dexpreopt-arm64-boot.vdex", + "mybootclasspathfragment-dexpreopt-arm64-boot-bar.art", + "mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat", + "mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex", + "mybootclasspathfragment-dexpreopt-arm-boot.art", + "mybootclasspathfragment-dexpreopt-arm-boot.oat", + "mybootclasspathfragment-dexpreopt-arm-boot.vdex", + "mybootclasspathfragment-dexpreopt-arm-boot-bar.art", + "mybootclasspathfragment-dexpreopt-arm-boot-bar.oat", + "mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex", + }) + // Make sure that the source bootclasspath_fragment copies its dex files to the predefined // locations for the art image. module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000") checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo") }) + t.Run("boot image files from source no boot image in apex", func(t *testing.T) { + result := android.GroupFixturePreparers( + commonPreparer, + + // Configure some libraries in the art bootclasspath_fragment that match the source + // bootclasspath_fragment's contents property. + java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"), + addSource("foo", "bar"), + java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"), + ).RunTest(t) + + ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + "etc/boot-image.prof", + "etc/classpaths/bootclasspath.pb", + "javalib/bar.jar", + "javalib/foo.jar", + }) + + ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + "mybootclasspathfragment-dexpreopt-arm64-boot.art", + "mybootclasspathfragment-dexpreopt-arm64-boot.oat", + "mybootclasspathfragment-dexpreopt-arm64-boot.vdex", + "mybootclasspathfragment-dexpreopt-arm64-boot-bar.art", + "mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat", + "mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex", + "mybootclasspathfragment-dexpreopt-arm-boot.art", + "mybootclasspathfragment-dexpreopt-arm-boot.oat", + "mybootclasspathfragment-dexpreopt-arm-boot.vdex", + "mybootclasspathfragment-dexpreopt-arm-boot-bar.art", + "mybootclasspathfragment-dexpreopt-arm-boot-bar.oat", + "mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex", + }) + }) + t.Run("boot image disable generate profile", func(t *testing.T) { result := android.GroupFixturePreparers( commonPreparer, @@ -472,6 +523,8 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { // Make sure that a preferred prebuilt with consistent contents doesn't affect the apex. addPrebuilt(true, "foo", "bar"), + + java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"), ).RunTest(t) ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index bfe895c17..fee51d72b 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -219,6 +219,11 @@ type BootclasspathFragmentModule struct { // Collect the module directory for IDE info in java/jdeps.go. modulePaths []string + + // Installs for on-device boot image files. This list has entries only if the installs should be + // handled by Make (e.g., the boot image should be installed on the system partition, rather than + // in the APEX). + bootImageDeviceInstalls []dexpreopterInstall } // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt @@ -387,6 +392,9 @@ type BootclasspathFragmentApexContentInfo struct { // Map from arch type to the boot image files. bootImageFilesByArch bootImageFilesByArch + // True if the boot image should be installed in the APEX. + shouldInstallBootImageInApex bool + // Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the // hidden API encoded dex jar path. contentModuleDexJarPaths bootDexJarByModule @@ -410,6 +418,11 @@ func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() return i.bootImageFilesByArch } +// Return true if the boot image should be installed in the APEX. +func (i *BootclasspathFragmentApexContentInfo) ShouldInstallBootImageInApex() bool { + return i.shouldInstallBootImageInApex +} + // DexBootJarPathForContentModule returns the path to the dex boot jar for specified module. // // The dex boot jar is one which has had hidden API encoding performed on it. @@ -550,6 +563,24 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // Copy the dex jars of this fragment's content modules to their predefined locations. copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule) } + + for _, variant := range imageConfig.apexVariants() { + arch := variant.target.Arch.ArchType.String() + for _, install := range variant.deviceInstalls { + // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT. + installDir := strings.TrimPrefix(filepath.Dir(install.To), "/") + installBase := filepath.Base(install.To) + installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir) + + b.bootImageDeviceInstalls = append(b.bootImageDeviceInstalls, dexpreopterInstall{ + name: arch + "-" + installBase, + moduleName: b.Name(), + outputPathOnHost: install.From, + installDirOnDevice: installPath, + installFileOnDevice: installBase, + }) + } + } } // A prebuilt fragment cannot contribute to an apex. @@ -599,6 +630,8 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC info.profilePathOnHost = imageConfig.profilePathOnHost info.profileInstallPathInApex = imageConfig.profileInstallPathInApex } + + info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex() } info.bootImageFilesByArch = bootImageFilesByArch @@ -813,6 +846,23 @@ func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android. return androidBootImageFilesByArch } +func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries { + var entriesList []android.AndroidMkEntries + for _, install := range b.bootImageDeviceInstalls { + entriesList = append(entriesList, install.ToMakeEntries()) + } + return entriesList +} + +// Returns the names of all Make modules that handle the installation of the boot image. +func (b *BootclasspathFragmentModule) BootImageDeviceInstallMakeModules() []string { + var makeModules []string + for _, install := range b.bootImageDeviceInstalls { + makeModules = append(makeModules, install.FullModuleName()) + } + return makeModules +} + // Collect information for opening IDE project files in java/jdeps.go. func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) { dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...) diff --git a/java/dexpreopt.go b/java/dexpreopt.go index e9bc51878..7c5f055e6 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -57,6 +57,25 @@ func (install *dexpreopterInstall) SubModuleName() string { return "-dexpreopt-" + install.name } +// Returns Make entries for installing the file. +// +// This function uses a value receiver rather than a pointer receiver to ensure that the object is +// safe to use in `android.AndroidMkExtraEntriesFunc`. +func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries { + return android.AndroidMkEntries{ + Class: "ETC", + SubName: install.SubModuleName(), + OutputFile: android.OptionalPathForPath(install.outputPathOnHost), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice) + entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false") + }, + }, + } +} + type dexpreopter struct { dexpreoptProperties DexpreoptProperties @@ -383,19 +402,7 @@ func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall { func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries { var entries []android.AndroidMkEntries for _, install := range d.builtInstalledForApex { - install := install - entries = append(entries, android.AndroidMkEntries{ - Class: "ETC", - SubName: install.SubModuleName(), - OutputFile: android.OptionalPathForPath(install.outputPathOnHost), - ExtraEntries: []android.AndroidMkExtraEntriesFunc{ - func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String()) - entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice) - entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false") - }, - }, - }) + entries = append(entries, install.ToMakeEntries()) } return entries } diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index c599c4da0..cad9c332a 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -313,10 +313,13 @@ type bootImageVariant struct { // This is only set for a variant of an image that extends another image. primaryImagesDeps android.Paths - // Rules which should be used in make to install the outputs. + // Rules which should be used in make to install the outputs on host. installs android.RuleBuilderInstalls vdexInstalls android.RuleBuilderInstalls unstrippedInstalls android.RuleBuilderInstalls + + // Rules which should be used in make to install the outputs on device. + deviceInstalls android.RuleBuilderInstalls } // Get target-specific boot image variant for the given boot image config and target. @@ -388,6 +391,11 @@ func (image *bootImageConfig) apexVariants() []*bootImageVariant { return variants } +// Returns true if the boot image should be installed in the APEX. +func (image *bootImageConfig) shouldInstallInApex() bool { + return strings.HasPrefix(image.installDirOnDevice, "apex/") +} + // Return boot image locations (as a list of symbolic paths). // // The image "location" is a symbolic path that, with multiarchitecture support, doesn't really @@ -710,6 +718,7 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p var vdexInstalls android.RuleBuilderInstalls var unstrippedInstalls android.RuleBuilderInstalls + var deviceInstalls android.RuleBuilderInstalls for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") { cmd.ImplicitOutput(artOrOat) @@ -735,12 +744,21 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())}) } + if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() { + installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String()) + for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") { + deviceInstalls = append(deviceInstalls, + android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())}) + } + } + rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String()) // save output and installed files for makevars image.installs = rule.Installs() image.vdexInstalls = vdexInstalls image.unstrippedInstalls = unstrippedInstalls + image.deviceInstalls = deviceInstalls } const failureMessage = `ERROR: Dex2oat failed to compile a boot image. diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 26c110544..df8d8c80a 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -41,17 +41,14 @@ func dexpreoptTargets(ctx android.PathContext) []android.Target { var ( bootImageConfigKey = android.NewOnceKey("bootImageConfig") + bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw") artBootImageName = "art" frameworkBootImageName = "boot" ) -// Construct the global boot image configs. -func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { - return ctx.Config().Once(bootImageConfigKey, func() interface{} { - +func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig { + return ctx.Config().Once(bootImageConfigRawKey, func() interface{} { global := dexpreopt.GetGlobalConfig(ctx) - targets := dexpreoptTargets(ctx) - deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName()) artModules := global.ArtApexJars frameworkModules := global.BootJars.RemoveList(artModules) @@ -79,10 +76,22 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { modules: frameworkModules, } - configs := map[string]*bootImageConfig{ + return map[string]*bootImageConfig{ artBootImageName: &artCfg, frameworkBootImageName: &frameworkCfg, } + }).(map[string]*bootImageConfig) +} + +// Construct the global boot image configs. +func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { + return ctx.Config().Once(bootImageConfigKey, func() interface{} { + targets := dexpreoptTargets(ctx) + deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName()) + + configs := genBootImageConfigRaw(ctx) + artCfg := configs[artBootImageName] + frameworkCfg := configs[frameworkBootImageName] // common to all configs for _, c := range configs { diff --git a/java/testing.go b/java/testing.go index 7441e4497..6c49bc866 100644 --- a/java/testing.go +++ b/java/testing.go @@ -506,3 +506,19 @@ func fakeApexMutator(mctx android.BottomUpMutatorContext) { } } } + +// Applies the given modifier on the boot image config with the given name. +func FixtureModifyBootImageConfig(name string, configModifier func(*bootImageConfig)) android.FixturePreparer { + return android.FixtureModifyConfig(func(androidConfig android.Config) { + pathCtx := android.PathContextForTesting(androidConfig) + config := genBootImageConfigRaw(pathCtx) + configModifier(config[name]) + }) +} + +// Sets the value of `installDirOnDevice` of the boot image config with the given name. +func FixtureSetBootImageInstallDirOnDevice(name string, installDir string) android.FixturePreparer { + return FixtureModifyBootImageConfig(name, func(config *bootImageConfig) { + config.installDirOnDevice = installDir + }) +}