diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index 1971dcd63..8b6e87653 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -16,6 +16,7 @@ package apex import ( "fmt" + "sort" "strings" "testing" @@ -359,6 +360,19 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { addPrebuilt := func(prefer bool, contents ...string) android.FixturePreparer { text := fmt.Sprintf(` + prebuilt_apex { + name: "com.android.art", + arch: { + arm64: { + src: "com.android.art-arm64.apex", + }, + arm: { + src: "com.android.art-arm.apex", + }, + }, + exported_bootclasspath_fragments: ["mybootclasspathfragment"], + } + prebuilt_bootclasspath_fragment { name: "mybootclasspathfragment", image_name: "art", @@ -372,7 +386,47 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text) } - t.Run("boot image files", func(t *testing.T) { + t.Run("boot image files from source", 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"), + ).RunTest(t) + + ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + "etc/classpaths/bootclasspath.pb", + "javalib/arm/boot.art", + "javalib/arm/boot.oat", + "javalib/arm/boot.vdex", + "javalib/arm/boot-bar.art", + "javalib/arm/boot-bar.oat", + "javalib/arm/boot-bar.vdex", + "javalib/arm64/boot.art", + "javalib/arm64/boot.oat", + "javalib/arm64/boot.vdex", + "javalib/arm64/boot-bar.art", + "javalib/arm64/boot-bar.oat", + "javalib/arm64/boot-bar.vdex", + "javalib/bar.jar", + "javalib/foo.jar", + }) + + java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{ + `bar`, + `com.android.art.key`, + `mybootclasspathfragment`, + }) + + // 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 with preferred prebuilt", func(t *testing.T) { result := android.GroupFixturePreparers( commonPreparer, @@ -407,7 +461,13 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { `bar`, `com.android.art.key`, `mybootclasspathfragment`, + `prebuilt_com.android.art`, }) + + // Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined + // locations for the art image. + module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art") + checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo") }) t.Run("source with inconsistency between config and contents", func(t *testing.T) { @@ -528,11 +588,36 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { `prebuilt_mybootclasspathfragment`, }) - java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common", []string{ + java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{ `dex2oatd`, `prebuilt_bar`, `prebuilt_foo`, }) + + module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art") + checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo") +} + +// checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the +// predefined locations of boot dex jars used as inputs for the ART boot image. +func checkCopiesToPredefinedLocationForArt(t *testing.T, config android.Config, module android.TestingModule, modules ...string) { + t.Helper() + bootJarLocations := []string{} + for _, output := range module.AllOutputs() { + output = android.StringRelativeToTop(config, output) + if strings.HasPrefix(output, "out/soong/test_device/dex_artjars_input/") { + bootJarLocations = append(bootJarLocations, output) + } + } + + sort.Strings(bootJarLocations) + expected := []string{} + for _, m := range modules { + expected = append(expected, fmt.Sprintf("out/soong/test_device/dex_artjars_input/%s.jar", m)) + } + sort.Strings(expected) + + android.AssertArrayString(t, "copies to predefined locations for art", expected, bootJarLocations) } func TestBootclasspathFragmentContentsNoName(t *testing.T) { diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index c47ab3854..48743951e 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -409,6 +409,13 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // Perform hidden API processing. hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments) + if imageConfig != nil { + if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) { + // Copy the dex jars of this fragment's content modules to their predefined locations. + copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule) + } + } + // A prebuilt fragment cannot contribute to an apex. if !android.IsModulePrebuilt(ctx.Module()) { // Provide the apex content info. @@ -417,6 +424,29 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo } } +// shouldCopyBootFilesToPredefinedLocations determines whether the current module should copy boot +// files, e.g. boot dex jars or boot image files, to the predefined location expected by the rest +// of the build. +// +// This ensures that only a single module will copy its files to the image configuration. +func shouldCopyBootFilesToPredefinedLocations(ctx android.ModuleContext, imageConfig *bootImageConfig) bool { + // Bootclasspath fragment modules that are for the platform do not produce boot related files. + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + if apexInfo.IsForPlatform() { + return false + } + + // If the image configuration has no modules specified then it means that the build has been + // configured to build something other than a boot image, e.g. an sdk, so do not try and copy the + // files. + if imageConfig.modules.Len() == 0 { + return false + } + + // Only copy files from the module that is preferred. + return isActiveModule(ctx.Module()) +} + // provideApexContentInfo creates, initializes and stores the apex content info for use by other // modules. func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIOutput *HiddenAPIOutput) { @@ -635,10 +665,6 @@ func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android. return false } - // Copy the dex jars of this fragment's content modules to their predefined locations. - bootDexJarByModule := extractEncodedDexJarsFromModules(ctx, contents) - copyBootJarsToPredefinedLocations(ctx, bootDexJarByModule, imageConfig.dexPathsByModule) - // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) buildBootImage(ctx, imageConfig, profile)