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
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.
func (a *allApexContributions) GenerateAndroidBuildActions(ctx ModuleContext) {
}

View File

@@ -2370,6 +2370,24 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.buildApex(ctx)
a.buildApexDependencyInfo(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

View File

@@ -252,3 +252,153 @@ func TestDexpreoptBootZip(t *testing.T) {
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)
}
// 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) {
p.apexKeysPath = writeApexKeys(ctx, p)
// TODO(jungjw): Check the key validity.
@@ -793,6 +812,9 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// dexpreopt any system server jars if present
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.
p.initApexFilesForAndroidMk(ctx)
@@ -1012,6 +1034,9 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// dexpreopt any system server jars if present
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.
a.initApexFilesForAndroidMk(ctx)

View File

@@ -534,7 +534,7 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC
if profile != nil {
info.profilePathOnHost = profile
info.profileInstallPathInApex = profileInstallPathInApex
info.profileInstallPathInApex = ProfileInstallPathInApex
}
// 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 di.PrebuiltExportPath(profileInstallPathInApex)
return di.PrebuiltExportPath(ProfileInstallPathInApex)
}
func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path {
@@ -1094,7 +1094,7 @@ var _ commonBootclasspathFragment = (*PrebuiltBootclasspathFragmentModule)(nil)
func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
for _, apex := range module.ApexProperties.Apex_available {
if isProfileProviderApex(ctx, apex) {
return []string{profileInstallPathInApex}
return []string{ProfileInstallPathInApex}
}
}
return nil

View File

@@ -21,6 +21,7 @@ import (
"android/soong/android"
"android/soong/dexpreopt"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -226,6 +227,7 @@ var artApexNames = []string{
var (
dexpreoptBootJarDepTag = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"}
dexBootJarsFragmentsKey = android.NewOnceKey("dexBootJarsFragments")
apexContributionsMetadataDepTag = dependencyTag{name: "all_apex_contributions"}
)
func init() {
@@ -502,6 +504,11 @@ type dexpreoptBootJars struct {
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) {
if _, ok := ctx.Module().(*dexpreoptBootJars); !ok {
return
@@ -520,6 +527,14 @@ func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) {
}
// For accessing the boot jars.
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") {
@@ -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 {
return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
fragments := make(map[string]android.Module)
@@ -823,6 +860,27 @@ type bootImageVariantOutputs struct {
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.
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")
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).
Flag("--avoid-storing-invocation").
FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
@@ -877,16 +942,7 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p
}
for _, apex := range image.profileImports {
fragment := getBootclasspathFragmentByApex(ctx, apex)
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()
importedProfile := getProfilePathForApex(ctx, apex, apexNameToBcpInfoMap)
if importedProfile == nil {
ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
"doesn't provide a profile",

View File

@@ -45,7 +45,7 @@ var (
frameworkBootImageName = "boot"
mainlineBootImageName = "mainline"
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

View File

@@ -950,6 +950,7 @@ func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
})
CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
`all_apex_contributions`,
`prebuilt_sdklib.stubs`,
`sdklib.impl`,
// 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{
`all_apex_contributions`,
`dex2oatd`,
`prebuilt_sdklib.stubs`,
`prebuilt_sdklib.stubs.source`,
@@ -1085,9 +1087,6 @@ func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) {
"prebuilt_sdklib.source_preferred_using_legacy_flags",
],
}
all_apex_contributions {
name: "all_apex_contributions",
}
java_sdk_library {
name: "sdklib.prebuilt_preferred_using_legacy_flags",
srcs: ["a.java"],
@@ -1169,9 +1168,6 @@ func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) {
prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
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) {
variables.BuildFlags = map[string]string{
"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",

View File

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