From 6decef916c4a40855b506582620351a729ad76ea Mon Sep 17 00:00:00 2001 From: Jiakai Zhang Date: Wed, 12 Jan 2022 17:56:19 +0000 Subject: [PATCH] Allow installing boot images outside of APEX. After this change, `bootImageConfig.installDirOnDevice` can be set to a path outside of the APEX, in which case, the boot image will not be installed in the APEX. Instead, it will be installed to the given path by Make. This is a no-op change. Current behavior is not affected. Bug: 211973309 Test: m nothing Test: - 1. m com.android.art 2. See the boot image still being installed in the ART APEX. Test: - 1. Change `installDirOnDevice` of the ART boot image config to `system/framework`. 2. See the boot image being installed in `/system/framework/`. Change-Id: Ib13b17cc9e94dc5754c9b51b04df3307323b8783 --- apex/apex.go | 22 +++++++----- apex/apex_test.go | 16 +++++++++ apex/bootclasspath_fragment_test.go | 53 +++++++++++++++++++++++++++++ java/bootclasspath_fragment.go | 50 +++++++++++++++++++++++++++ java/dexpreopt.go | 33 +++++++++++------- java/dexpreopt_bootjars.go | 20 ++++++++++- java/dexpreopt_config.go | 23 +++++++++---- java/testing.go | 16 +++++++++ 8 files changed, 204 insertions(+), 29 deletions(-) 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 + }) +}