Merge "Use the correct prof file when multiple prebuilt apexes exist" into main

This commit is contained in:
Spandan Das
2023-12-20 00:02:20 +00:00
committed by Gerrit Code Review
10 changed files with 297 additions and 22 deletions

View File

@@ -954,3 +954,15 @@ type ApexTestInterface interface {
// Return true if the apex bundle is an apex_test // Return true if the apex bundle is an apex_test
IsTestApex() bool IsTestApex() bool
} }
var ApexExportsInfoProvider = blueprint.NewProvider[ApexExportsInfo]()
// ApexExportsInfo contains information about the artifacts provided by apexes to dexpreopt and hiddenapi
type ApexExportsInfo struct {
// Canonical name of this APEX. Used to determine the path to the activated APEX on
// device (/apex/<apex_name>)
ApexName string
// Path to the image profile file on host (or empty, if profile is not generated).
ProfilePathOnHost Path
}

View File

@@ -164,6 +164,18 @@ func (p *PrebuiltSelectionInfoMap) IsSelected(baseModuleName, name string) bool
} }
} }
// Return the list of soong modules selected for this api domain
// In the case of apexes, it is the canonical name of the apex on device (/apex/<apex_name>)
func (p *PrebuiltSelectionInfoMap) GetSelectedModulesForApiDomain(apiDomain string) []string {
selected := []string{}
for _, entry := range *p {
if entry.apiDomain == apiDomain {
selected = append(selected, entry.selectedModuleName)
}
}
return selected
}
// This module type does not have any build actions. // This module type does not have any build actions.
func (a *allApexContributions) GenerateAndroidBuildActions(ctx ModuleContext) { func (a *allApexContributions) GenerateAndroidBuildActions(ctx ModuleContext) {
} }

View File

@@ -2370,6 +2370,24 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.buildApex(ctx) a.buildApex(ctx)
a.buildApexDependencyInfo(ctx) a.buildApexDependencyInfo(ctx)
a.buildLintReports(ctx) a.buildLintReports(ctx)
// Set a provider for dexpreopt of bootjars
a.provideApexExportsInfo(ctx)
}
// Set a provider containing information about the jars and .prof provided by the apex
// Apexes built from source retrieve this information by visiting `bootclasspath_fragments`
// Used by dex_bootjars to generate the boot image
func (a *apexBundle) provideApexExportsInfo(ctx android.ModuleContext) {
ctx.VisitDirectDepsWithTag(bcpfTag, func(child android.Module) {
if info, ok := android.OtherModuleProvider(ctx, child, java.BootclasspathFragmentApexContentInfoProvider); ok {
exports := android.ApexExportsInfo{
ApexName: a.ApexVariationName(),
ProfilePathOnHost: info.ProfilePathOnHost(),
}
ctx.SetProvider(android.ApexExportsInfoProvider, exports)
}
})
} }
// apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that // apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that

View File

@@ -252,3 +252,153 @@ func TestDexpreoptBootZip(t *testing.T) {
testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, false) testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, false)
} }
// Multiple ART apexes might exist in the tree.
// The profile should correspond to the apex selected using release build flags
func TestDexpreoptProfileWithMultiplePrebuiltArtApexes(t *testing.T) {
ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art"
bp := `
// Platform.
platform_bootclasspath {
name: "platform-bootclasspath",
fragments: [
{
apex: "com.android.art",
module: "art-bootclasspath-fragment",
},
],
}
// Source ART APEX.
java_library {
name: "core-oj",
srcs: ["core-oj.java"],
installable: true,
apex_available: [
"com.android.art",
],
}
bootclasspath_fragment {
name: "art-bootclasspath-fragment",
image_name: "art",
contents: ["core-oj"],
apex_available: [
"com.android.art",
],
hidden_api: {
split_packages: ["*"],
},
}
apex_key {
name: "com.android.art.key",
public_key: "com.android.art.avbpubkey",
private_key: "com.android.art.pem",
}
apex {
name: "com.android.art",
key: "com.android.art.key",
bootclasspath_fragments: ["art-bootclasspath-fragment"],
updatable: false,
}
// Prebuilt ART APEX.
prebuilt_bootclasspath_fragment {
name: "art-bootclasspath-fragment",
image_name: "art",
hidden_api: {
annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
metadata: "my-bootclasspath-fragment/metadata.csv",
index: "my-bootclasspath-fragment/index.csv",
stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
all_flags: "my-bootclasspath-fragment/all-flags.csv",
},
apex_available: [
"com.android.art",
],
}
prebuilt_apex {
name: "com.android.art",
apex_name: "com.android.art",
src: "com.android.art-arm.apex",
exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
}
// Another Prebuilt ART APEX
prebuilt_apex {
name: "com.android.art.v2",
apex_name: "com.android.art", // Used to determine the API domain
src: "com.android.art-arm.apex",
exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
}
// APEX contribution modules
apex_contributions {
name: "art.source.contributions",
api_domain: "com.android.art",
contents: ["com.android.art"],
}
apex_contributions {
name: "art.prebuilt.contributions",
api_domain: "com.android.art",
contents: ["prebuilt_com.android.art"],
}
apex_contributions {
name: "art.prebuilt.v2.contributions",
api_domain: "com.android.art",
contents: ["com.android.art.v2"], // prebuilt_ prefix is missing because of prebuilt_rename mutator
}
`
testCases := []struct {
desc string
selectedArtApexContributions string
expectedProfile string
}{
{
desc: "Source apex com.android.art is selected, profile should come from source java library",
selectedArtApexContributions: "art.source.contributions",
expectedProfile: "out/soong/.intermediates/art-bootclasspath-fragment/android_common_apex10000/art-bootclasspath-fragment/boot.prof",
},
{
desc: "Prebuilt apex prebuilt_com.android.art is selected, profile should come from .prof deapexed from the prebuilt",
selectedArtApexContributions: "art.prebuilt.contributions",
expectedProfile: "out/soong/.intermediates/com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
},
{
desc: "Prebuilt apex prebuilt_com.android.art.v2 is selected, profile should come from .prof deapexed from the prebuilt",
selectedArtApexContributions: "art.prebuilt.v2.contributions",
expectedProfile: "out/soong/.intermediates/com.android.art.v2.deapexer/android_common/deapexer/etc/boot-image.prof",
},
}
for _, tc := range testCases {
result := android.GroupFixturePreparers(
java.PrepareForTestWithDexpreopt,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureConfigureBootJars("com.android.art:core-oj"),
PrepareForTestWithApexBuildComponents,
prepareForTestWithArtApex,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.BuildFlags = map[string]string{
"RELEASE_APEX_CONTRIBUTIONS_ART": tc.selectedArtApexContributions,
}
}),
).RunTestWithBp(t, bp)
dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
rule := dexBootJars.Output(ruleFile)
inputs := rule.Implicits.Strings()
android.AssertStringListContains(t, tc.desc, inputs, tc.expectedProfile)
}
}

View File

@@ -769,6 +769,25 @@ func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
p.apexInfoMutator(mctx) p.apexInfoMutator(mctx)
} }
// Set a provider containing information about the jars and .prof provided by the apex
// Apexes built from prebuilts retrieve this information by visiting its internal deapexer module
// Used by dex_bootjars to generate the boot image
func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext) {
if !p.hasExportedDeps() {
// nothing to do
return
}
if di, err := android.FindDeapexerProviderForModule(ctx); err == nil {
exports := android.ApexExportsInfo{
ApexName: p.ApexVariationName(),
ProfilePathOnHost: di.PrebuiltExportPath(java.ProfileInstallPathInApex),
}
ctx.SetProvider(android.ApexExportsInfoProvider, exports)
} else {
ctx.ModuleErrorf(err.Error())
}
}
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.apexKeysPath = writeApexKeys(ctx, p) p.apexKeysPath = writeApexKeys(ctx, p)
// TODO(jungjw): Check the key validity. // TODO(jungjw): Check the key validity.
@@ -793,6 +812,9 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// dexpreopt any system server jars if present // dexpreopt any system server jars if present
p.dexpreoptSystemServerJars(ctx) p.dexpreoptSystemServerJars(ctx)
// provide info used for generating the boot image
p.provideApexExportsInfo(ctx)
// Save the files that need to be made available to Make. // Save the files that need to be made available to Make.
p.initApexFilesForAndroidMk(ctx) p.initApexFilesForAndroidMk(ctx)
@@ -1012,6 +1034,9 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// dexpreopt any system server jars if present // dexpreopt any system server jars if present
a.dexpreoptSystemServerJars(ctx) a.dexpreoptSystemServerJars(ctx)
// provide info used for generating the boot image
a.provideApexExportsInfo(ctx)
// Save the files that need to be made available to Make. // Save the files that need to be made available to Make.
a.initApexFilesForAndroidMk(ctx) a.initApexFilesForAndroidMk(ctx)

View File

@@ -534,7 +534,7 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC
if profile != nil { if profile != nil {
info.profilePathOnHost = profile info.profilePathOnHost = profile
info.profileInstallPathInApex = profileInstallPathInApex info.profileInstallPathInApex = ProfileInstallPathInApex
} }
// Make the apex content info available for other modules. // Make the apex content info available for other modules.
@@ -1074,7 +1074,7 @@ func (module *PrebuiltBootclasspathFragmentModule) produceBootImageProfile(ctx a
return nil // An error has been reported by FindDeapexerProviderForModule. return nil // An error has been reported by FindDeapexerProviderForModule.
} }
return di.PrebuiltExportPath(profileInstallPathInApex) return di.PrebuiltExportPath(ProfileInstallPathInApex)
} }
func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path { func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path {
@@ -1094,7 +1094,7 @@ var _ commonBootclasspathFragment = (*PrebuiltBootclasspathFragmentModule)(nil)
func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
for _, apex := range module.ApexProperties.Apex_available { for _, apex := range module.ApexProperties.Apex_available {
if isProfileProviderApex(ctx, apex) { if isProfileProviderApex(ctx, apex) {
return []string{profileInstallPathInApex} return []string{ProfileInstallPathInApex}
} }
} }
return nil return nil

View File

@@ -21,6 +21,7 @@ import (
"android/soong/android" "android/soong/android"
"android/soong/dexpreopt" "android/soong/dexpreopt"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools" "github.com/google/blueprint/proptools"
) )
@@ -226,6 +227,7 @@ var artApexNames = []string{
var ( var (
dexpreoptBootJarDepTag = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"} dexpreoptBootJarDepTag = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"}
dexBootJarsFragmentsKey = android.NewOnceKey("dexBootJarsFragments") dexBootJarsFragmentsKey = android.NewOnceKey("dexBootJarsFragments")
apexContributionsMetadataDepTag = dependencyTag{name: "all_apex_contributions"}
) )
func init() { func init() {
@@ -502,6 +504,11 @@ type dexpreoptBootJars struct {
dexpreoptConfigForMake android.WritablePath dexpreoptConfigForMake android.WritablePath
} }
func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) {
// Create a dependency on all_apex_contributions to determine the selected mainline module
ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions")
}
func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) { func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) {
if _, ok := ctx.Module().(*dexpreoptBootJars); !ok { if _, ok := ctx.Module().(*dexpreoptBootJars); !ok {
return return
@@ -520,6 +527,14 @@ func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) {
} }
// For accessing the boot jars. // For accessing the boot jars.
addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJarDepTag) addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJarDepTag)
// Create a dependency on the apex selected using RELEASE_APEX_CONTRIBUTIONS_*
// TODO: b/308174306 - Remove the direct depedendency edge to the java_library (source/prebuilt) once all mainline modules
// have been flagged using RELEASE_APEX_CONTRIBUTIONS_*
apexes := []string{}
for i := 0; i < config.modules.Len(); i++ {
apexes = append(apexes, config.modules.Apex(i))
}
addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...)
} }
if ctx.OtherModuleExists("platform-bootclasspath") { if ctx.OtherModuleExists("platform-bootclasspath") {
@@ -532,6 +547,28 @@ func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) {
} }
} }
// Create a dependency from dex_bootjars to the specific apexes selected using all_apex_contributions
// This dependency will be used to get the path to the deapexed dex boot jars and profile (via a provider)
func addDependenciesOntoSelectedBootImageApexes(ctx android.BottomUpMutatorContext, apexes ...string) {
psi := android.PrebuiltSelectionInfoMap{}
ctx.VisitDirectDepsWithTag(apexContributionsMetadataDepTag, func(am android.Module) {
if ctx.OtherModuleHasProvider(am, android.PrebuiltSelectionInfoProvider) {
psi = ctx.OtherModuleProvider(am, android.PrebuiltSelectionInfoProvider).(android.PrebuiltSelectionInfoMap)
}
})
for _, apex := range apexes {
for _, selected := range psi.GetSelectedModulesForApiDomain(apex) {
// We need to add a dep on only the apex listed in `contents` of the selected apex_contributions module
// This is not available in a structured format in `apex_contributions`, so this hack adds a dep on all `contents`
// (some modules like art.module.public.api do not have an apex variation since it is a pure stub module that does not get installed)
apexVariationOfSelected := append(ctx.Target().Variations(), blueprint.Variation{Mutator: "apex", Variation: apex})
if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, selected) {
ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, selected)
}
}
}
}
func gatherBootclasspathFragments(ctx android.ModuleContext) map[string]android.Module { func gatherBootclasspathFragments(ctx android.ModuleContext) map[string]android.Module {
return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} { return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
fragments := make(map[string]android.Module) fragments := make(map[string]android.Module)
@@ -823,6 +860,27 @@ type bootImageVariantOutputs struct {
config *bootImageVariant config *bootImageVariant
} }
// Returns the profile file for an apex
// This information can come from two mechanisms
// 1. New: Direct deps to _selected_ apexes. The apexes return a BootclasspathFragmentApexContentInfo
// 2. Legacy: An edge to bootclasspath_fragment module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes
// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2)
func getProfilePathForApex(ctx android.ModuleContext, apexName string, apexNameToBcpInfoMap map[string]android.ApexExportsInfo) android.Path {
if info, exists := apexNameToBcpInfoMap[apexName]; exists {
return info.ProfilePathOnHost
}
// TODO: b/308174306 - Remove the legacy mechanism
fragment := getBootclasspathFragmentByApex(ctx, apexName)
if fragment == nil {
ctx.ModuleErrorf("Boot image config imports profile from '%[2]s', but a "+
"bootclasspath_fragment for APEX '%[2]s' doesn't exist or is not added as a "+
"dependency of dex_bootjars",
apexName)
return nil
}
return fragment.(commonBootclasspathFragment).getProfilePath()
}
// Generate boot image build rules for a specific target. // Generate boot image build rules for a specific target.
func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs { func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs {
@@ -865,6 +923,13 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p
invocationPath := outputPath.ReplaceExtension(ctx, "invocation") invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
apexNameToBcpInfoMap := map[string]android.ApexExportsInfo{}
ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(am android.Module) {
if info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider); exists {
apexNameToBcpInfoMap[info.ApexName] = info
}
})
cmd.Tool(globalSoong.Dex2oat). cmd.Tool(globalSoong.Dex2oat).
Flag("--avoid-storing-invocation"). Flag("--avoid-storing-invocation").
FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath). FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
@@ -877,16 +942,7 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p
} }
for _, apex := range image.profileImports { for _, apex := range image.profileImports {
fragment := getBootclasspathFragmentByApex(ctx, apex) importedProfile := getProfilePathForApex(ctx, apex, apexNameToBcpInfoMap)
if fragment == nil {
ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but a "+
"bootclasspath_fragment for APEX '%[2]s' doesn't exist or is not added as a "+
"dependency of dex_bootjars",
image.name,
apex)
return bootImageVariantOutputs{}
}
importedProfile := fragment.(commonBootclasspathFragment).getProfilePath()
if importedProfile == nil { if importedProfile == nil {
ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+ ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
"doesn't provide a profile", "doesn't provide a profile",

View File

@@ -45,7 +45,7 @@ var (
frameworkBootImageName = "boot" frameworkBootImageName = "boot"
mainlineBootImageName = "mainline" mainlineBootImageName = "mainline"
bootImageStem = "boot" bootImageStem = "boot"
profileInstallPathInApex = "etc/boot-image.prof" ProfileInstallPathInApex = "etc/boot-image.prof"
) )
// getImageNames returns an ordered list of image names. The order doesn't matter but needs to be // getImageNames returns an ordered list of image names. The order doesn't matter but needs to be

View File

@@ -950,6 +950,7 @@ func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
}) })
CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{ CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
`all_apex_contributions`,
`prebuilt_sdklib.stubs`, `prebuilt_sdklib.stubs`,
`sdklib.impl`, `sdklib.impl`,
// This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the // This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the
@@ -1022,6 +1023,7 @@ func testJavaSdkLibraryImport_Preferred(t *testing.T, prefer string, preparer an
}) })
CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{ CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
`all_apex_contributions`,
`dex2oatd`, `dex2oatd`,
`prebuilt_sdklib.stubs`, `prebuilt_sdklib.stubs`,
`prebuilt_sdklib.stubs.source`, `prebuilt_sdklib.stubs.source`,
@@ -1085,9 +1087,6 @@ func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) {
"prebuilt_sdklib.source_preferred_using_legacy_flags", "prebuilt_sdklib.source_preferred_using_legacy_flags",
], ],
} }
all_apex_contributions {
name: "all_apex_contributions",
}
java_sdk_library { java_sdk_library {
name: "sdklib.prebuilt_preferred_using_legacy_flags", name: "sdklib.prebuilt_preferred_using_legacy_flags",
srcs: ["a.java"], srcs: ["a.java"],
@@ -1169,9 +1168,6 @@ func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) {
prepareForJavaTest, prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles, PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("sdklib.source_preferred_using_legacy_flags", "sdklib.prebuilt_preferred_using_legacy_flags"), FixtureWithLastReleaseApis("sdklib.source_preferred_using_legacy_flags", "sdklib.prebuilt_preferred_using_legacy_flags"),
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
android.RegisterApexContributionsBuildComponents(ctx)
}),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.BuildFlags = map[string]string{ variables.BuildFlags = map[string]string{
"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions", "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",

View File

@@ -383,6 +383,7 @@ func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
RegisterSystemModulesBuildComponents(ctx) RegisterSystemModulesBuildComponents(ctx)
registerSystemserverClasspathBuildComponents(ctx) registerSystemserverClasspathBuildComponents(ctx)
registerLintBuildComponents(ctx) registerLintBuildComponents(ctx)
android.RegisterApexContributionsBuildComponents(ctx)
} }
// gatherRequiredDepsForTest gathers the module definitions used by // gatherRequiredDepsForTest gathers the module definitions used by
@@ -570,6 +571,11 @@ func gatherRequiredDepsForTest() string {
} }
` `
bp += `
all_apex_contributions {
name: "all_apex_contributions",
}
`
return bp return bp
} }