diff --git a/apex/apex.go b/apex/apex.go index ade8fa909..c89704249 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -89,6 +89,9 @@ type apexBundleProperties struct { Multilib apexMultilibProperties + // List of boot images that are embedded inside this APEX bundle. + Boot_images []string + // List of java libraries that are embedded inside this APEX bundle. Java_libs []string @@ -544,6 +547,7 @@ var ( certificateTag = dependencyTag{name: "certificate"} executableTag = dependencyTag{name: "executable", payload: true} fsTag = dependencyTag{name: "filesystem", payload: true} + bootImageTag = dependencyTag{name: "bootImage", payload: true} javaLibTag = dependencyTag{name: "javaLib", payload: true} jniLibTag = dependencyTag{name: "jniLib", payload: true} keyTag = dependencyTag{name: "key"} @@ -721,6 +725,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { // Common-arch dependencies come next commonVariation := ctx.Config().AndroidCommonTarget.Variations() + ctx.AddFarVariationDependencies(commonVariation, bootImageTag, a.properties.Boot_images...) ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...) ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...) ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...) @@ -730,10 +735,6 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") { ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent") } - // The ART boot image depends on dex2oat to compile it. - if !java.SkipDexpreoptBootJars(ctx) { - dexpreopt.RegisterToolDeps(ctx) - } } // Dependencies for signing @@ -1648,6 +1649,23 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { } else { ctx.PropertyErrorf("binaries", "%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName) } + case bootImageTag: + { + if _, ok := child.(*java.BootImageModule); !ok { + ctx.PropertyErrorf("boot_images", "%q is not a boot_image module", depName) + return false + } + bootImageInfo := ctx.OtherModuleProvider(child, java.BootImageInfoProvider).(java.BootImageInfo) + for arch, files := range bootImageInfo.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 boot image module here instead of nil + af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil) + filesInfo = append(filesInfo, af) + } + } + } case javaLibTag: switch child.(type) { case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import: @@ -1862,25 +1880,6 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } - if a.artApex { - // Specific to the ART apex: dexpreopt artifacts for libcore Java libraries. Build rules are - // generated by the dexpreopt singleton, and here we access build artifacts via the global - // boot image config. - for arch, files := range java.DexpreoptedArtApexJars(ctx) { - dirInApex := filepath.Join("javalib", arch.String()) - for _, f := range files { - localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String()) - af := newApexFile(ctx, f, localModule, dirInApex, etc, nil) - filesInfo = append(filesInfo, af) - } - } - // Call GetGlobalSoongConfig to initialize it, which may be necessary if dexpreopt is - // disabled for libraries/apps, but boot images are still needed. - if !java.SkipDexpreoptBootJars(ctx) { - dexpreopt.GetGlobalSoongConfig(ctx) - } - } - // Remove duplicates in filesInfo removeDup := func(filesInfo []apexFile) []apexFile { encountered := make(map[string]apexFile) diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go index 07feb0358..c13ea2ebf 100644 --- a/apex/boot_image_test.go +++ b/apex/boot_image_test.go @@ -15,6 +15,8 @@ package apex import ( + "reflect" + "strings" "testing" "android/soong/android" @@ -83,13 +85,39 @@ func TestBootImages(t *testing.T) { ) // Make sure that the framework-boot-image is using the correct configuration. - checkBootImage(t, ctx, "framework-boot-image", "platform:foo,platform:bar") + checkBootImage(t, ctx, "framework-boot-image", "platform:foo,platform:bar", ` +test_device/dex_bootjars/android/system/framework/arm/boot-foo.art +test_device/dex_bootjars/android/system/framework/arm/boot-foo.oat +test_device/dex_bootjars/android/system/framework/arm/boot-foo.vdex +test_device/dex_bootjars/android/system/framework/arm/boot-bar.art +test_device/dex_bootjars/android/system/framework/arm/boot-bar.oat +test_device/dex_bootjars/android/system/framework/arm/boot-bar.vdex +test_device/dex_bootjars/android/system/framework/arm64/boot-foo.art +test_device/dex_bootjars/android/system/framework/arm64/boot-foo.oat +test_device/dex_bootjars/android/system/framework/arm64/boot-foo.vdex +test_device/dex_bootjars/android/system/framework/arm64/boot-bar.art +test_device/dex_bootjars/android/system/framework/arm64/boot-bar.oat +test_device/dex_bootjars/android/system/framework/arm64/boot-bar.vdex +`) // Make sure that the art-boot-image is using the correct configuration. - checkBootImage(t, ctx, "art-boot-image", "com.android.art:baz,com.android.art:quuz") + checkBootImage(t, ctx, "art-boot-image", "com.android.art:baz,com.android.art:quuz", ` +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.art +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.oat +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.vdex +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.art +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.oat +test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.vdex +`) } -func checkBootImage(t *testing.T, ctx *android.TestContext, moduleName string, expectedConfiguredModules string) { +func checkBootImage(t *testing.T, ctx *android.TestContext, moduleName string, expectedConfiguredModules string, expectedBootImageFiles string) { t.Helper() bootImage := ctx.ModuleForTests(moduleName, "android_common").Module().(*java.BootImageModule) @@ -99,6 +127,20 @@ func checkBootImage(t *testing.T, ctx *android.TestContext, moduleName string, e if actual := modules.String(); actual != expectedConfiguredModules { t.Errorf("invalid modules for %s: expected %q, actual %q", moduleName, expectedConfiguredModules, actual) } + + // Get a list of all the paths in the boot image sorted by arch type. + allPaths := []string{} + bootImageFilesByArchType := bootImageInfo.AndroidBootImageFilesByArchType() + for _, archType := range android.ArchTypeList() { + if paths, ok := bootImageFilesByArchType[archType]; ok { + for _, path := range paths { + allPaths = append(allPaths, android.NormalizePathForTesting(path)) + } + } + } + if expected, actual := strings.TrimSpace(expectedBootImageFiles), strings.TrimSpace(strings.Join(allPaths, "\n")); !reflect.DeepEqual(expected, actual) { + t.Errorf("invalid paths for %s: expected \n%s, actual \n%s", moduleName, expected, actual) + } } func modifyDexpreoptConfig(configModifier func(dexpreoptConfig *dexpreopt.GlobalConfig)) func(fs map[string][]byte, config android.Config) { @@ -126,3 +168,61 @@ func withFrameworkBootImageJars(bootJars ...string) func(fs map[string][]byte, c dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList(bootJars) }) } + +func TestBootImageInApex(t *testing.T) { + ctx, _ := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + boot_images: [ + "mybootimage", + ], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "foo", + srcs: ["b.java"], + installable: true, + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + } + + boot_image { + name: "mybootimage", + image_name: "boot", + apex_available: [ + "myapex", + ], + } +`, + // Configure some libraries in the framework boot image. + withFrameworkBootImageJars("platform:foo", "platform:bar"), + ) + + ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ + "javalib/arm/boot-bar.art", + "javalib/arm/boot-bar.oat", + "javalib/arm/boot-bar.vdex", + "javalib/arm/boot-foo.art", + "javalib/arm/boot-foo.oat", + "javalib/arm/boot-foo.vdex", + "javalib/arm64/boot-bar.art", + "javalib/arm64/boot-bar.oat", + "javalib/arm64/boot-bar.vdex", + "javalib/arm64/boot-foo.art", + "javalib/arm64/boot-foo.oat", + "javalib/arm64/boot-foo.vdex", + }) +} + +// TODO(b/177892522) - add test for host apex. diff --git a/java/boot_image.go b/java/boot_image.go index 07ef0d841..d17b5cbfa 100644 --- a/java/boot_image.go +++ b/java/boot_image.go @@ -15,9 +15,11 @@ package java import ( + "fmt" "strings" "android/soong/android" + "android/soong/dexpreopt" "github.com/google/blueprint" ) @@ -38,6 +40,7 @@ type bootImageProperties struct { type BootImageModule struct { android.ModuleBase + android.ApexModuleBase properties bootImageProperties } @@ -46,6 +49,7 @@ func bootImageFactory() android.Module { m := &BootImageModule{} m.AddProperties(&m.properties) android.InitAndroidArchModule(m, android.HostAndDeviceDefault, android.MultilibCommon) + android.InitApexModule(m) return m } @@ -53,6 +57,9 @@ var BootImageInfoProvider = blueprint.NewProvider(BootImageInfo{}) type BootImageInfo struct { // The image config, internal to this module (and the dex_bootjars singleton). + // + // Will be nil if the BootImageInfo has not been provided for a specific module. That can occur + // when SkipDexpreoptBootJars(ctx) returns true. imageConfig *bootImageConfig } @@ -60,12 +67,56 @@ func (i BootImageInfo) Modules() android.ConfiguredJarList { return i.imageConfig.modules } +// Get a map from ArchType to the associated boot image's contents for Android. +// +// Extension boot images only return their own files, not the files of the boot images they extend. +func (i BootImageInfo) AndroidBootImageFilesByArchType() map[android.ArchType]android.OutputPaths { + files := map[android.ArchType]android.OutputPaths{} + if i.imageConfig != nil { + for _, variant := range i.imageConfig.variants { + // We also generate boot images for host (for testing), but we don't need those in the apex. + // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device + if variant.target.Os == android.Android { + files[variant.target.Arch.ArchType] = variant.imagesDeps + } + } + } + return files +} + +func (b *BootImageModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { + tag := ctx.OtherModuleDependencyTag(dep) + if tag == dexpreopt.Dex2oatDepTag { + // The dex2oat tool is only needed for building and is not required in the apex. + return false + } + panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag))) +} + +func (b *BootImageModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { + return nil +} + +func (b *BootImageModule) DepsMutator(ctx android.BottomUpMutatorContext) { + if SkipDexpreoptBootJars(ctx) { + return + } + + // Add a dependency onto the dex2oat tool which is needed for creating the boot image. The + // path is retrieved from the dependency by GetGlobalSoongConfig(ctx). + dexpreopt.RegisterToolDeps(ctx) +} + func (b *BootImageModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Nothing to do if skipping the dexpreopt of boot image jars. if SkipDexpreoptBootJars(ctx) { return } + // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars + // GenerateSingletonBuildActions method as it cannot create it for itself. + dexpreopt.GetGlobalSoongConfig(ctx) + // Get a map of the image configs that are supported. imageConfigs := genBootImageConfigs(ctx) diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 36d0d3017..2a7eb42dc 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -392,22 +392,6 @@ type dexpreoptBootJars struct { dexpreoptConfigForMake android.WritablePath } -// Accessor function for the apex package. Returns nil if dexpreopt is disabled. -func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]android.OutputPaths { - if SkipDexpreoptBootJars(ctx) { - return nil - } - // Include dexpreopt files for the primary boot image. - files := map[android.ArchType]android.OutputPaths{} - for _, variant := range artBootImageConfig(ctx).variants { - // We also generate boot images for host (for testing), but we don't need those in the apex. - if variant.target.Os == android.Android { - files[variant.target.Arch.ArchType] = variant.imagesDeps - } - } - return files -} - // Provide paths to boot images for use by modules that depend upon them. // // The build rules are created in GenerateSingletonBuildActions().