Align boot jar exclusion with apex_contributions
During trunk stable development, it is possible that a new boot jar is added to the source apex, but not available in prebuilt apex yet. Thefore we need to conditionally check the `contents` of the respective bootclasspath_fragment modules. https://r.android.com/2822393 did this using isActiveModule. This function relies on soong config namespaces to "hide" the source bootclasspath_fragment module when <module>.source_build is false. Soong config namespaces for mainline source vs prebuilt selection is no longer in use, so this CL replaces `isActiveModule`. The CL cleaves the implementation 1. For source builds, the check will run in the context of the bootclasspath_fragment module. `disableSourceApexVariant` will be used to skip the check if prebuilts are selected. 2. For prebuilt builds, the prebuilt bootclasspath_fragment module sets a provider, and the top-level apex will do the check. (2) is necessary to handle the edge case of multiple versioned mainline prebuilts and possible skew in apex boot jars across them. In case of prebuilt builds, the unselected mainline prebuilts will have HideFromMake called on them, so the check will only run on the apex that is actually used during the build. Bug: 328578801 Test: go test ./apex Change-Id: I6eec603397eea926f3a481b79ca938245064d809 Merged-In: I6eec603397eea926f3a481b79ca938245064d809
This commit is contained in:
parent
e8cb9178ec
commit
3d0d31a8c6
3 changed files with 166 additions and 2 deletions
|
@ -795,3 +795,127 @@ func TestNonBootJarInFragment(t *testing.T) {
|
|||
}
|
||||
`)
|
||||
}
|
||||
|
||||
// Source and prebuilt apex provide different set of boot jars
|
||||
func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) {
|
||||
bp := `
|
||||
apex {
|
||||
name: "myapex",
|
||||
key: "myapex.key",
|
||||
bootclasspath_fragments: ["apex-fragment"],
|
||||
updatable: false,
|
||||
}
|
||||
|
||||
apex_key {
|
||||
name: "myapex.key",
|
||||
public_key: "testkey.avbpubkey",
|
||||
private_key: "testkey.pem",
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "foo",
|
||||
srcs: ["b.java"],
|
||||
installable: true,
|
||||
apex_available: ["myapex"],
|
||||
permitted_packages: ["foo"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "bar",
|
||||
srcs: ["b.java"],
|
||||
installable: true,
|
||||
apex_available: ["myapex"],
|
||||
permitted_packages: ["bar"],
|
||||
}
|
||||
|
||||
bootclasspath_fragment {
|
||||
name: "apex-fragment",
|
||||
contents: ["foo", "bar"],
|
||||
apex_available:[ "myapex" ],
|
||||
hidden_api: {
|
||||
split_packages: ["*"],
|
||||
},
|
||||
}
|
||||
|
||||
prebuilt_apex {
|
||||
name: "com.google.android.myapex", // mainline prebuilt selection logic in soong relies on the naming convention com.google.android
|
||||
apex_name: "myapex",
|
||||
source_apex_name: "myapex",
|
||||
src: "myapex.apex",
|
||||
exported_bootclasspath_fragments: ["apex-fragment"],
|
||||
}
|
||||
|
||||
java_import {
|
||||
name: "foo",
|
||||
jars: ["foo.jar"],
|
||||
apex_available: ["myapex"],
|
||||
permitted_packages: ["foo"],
|
||||
}
|
||||
|
||||
prebuilt_bootclasspath_fragment {
|
||||
name: "apex-fragment",
|
||||
contents: ["foo"], // Unlike the source fragment, this is missing bar
|
||||
apex_available:[ "myapex" ],
|
||||
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_contributions {
|
||||
name: "my_apex_contributions",
|
||||
api_domain: "myapex",
|
||||
contents: [%v],
|
||||
}
|
||||
`
|
||||
testCases := []struct {
|
||||
desc string
|
||||
configuredBootJars []string
|
||||
apexContributionContents string
|
||||
errorExpected bool
|
||||
}{
|
||||
{
|
||||
desc: "Source apex is selected, and APEX_BOOT_JARS is correctly configured for source apex builds",
|
||||
configuredBootJars: []string{"myapex:foo", "myapex:bar"},
|
||||
},
|
||||
{
|
||||
desc: "Source apex is selected, and APEX_BOOT_JARS is missing bar",
|
||||
configuredBootJars: []string{"myapex:foo"},
|
||||
errorExpected: true,
|
||||
},
|
||||
{
|
||||
desc: "Prebuilt apex is selected, and APEX_BOOT_JARS is correctly configured for prebuilt apex build",
|
||||
configuredBootJars: []string{"myapex:foo"},
|
||||
apexContributionContents: `"prebuilt_com.google.android.myapex"`,
|
||||
},
|
||||
{
|
||||
desc: "Prebuilt apex is selected, and APEX_BOOT_JARS is missing foo",
|
||||
configuredBootJars: []string{"myapex:bar"},
|
||||
apexContributionContents: `"prebuilt_com.google.android.myapex"`,
|
||||
errorExpected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
fixture := android.GroupFixturePreparers(
|
||||
prepareForTestWithPlatformBootclasspath,
|
||||
PrepareForTestWithApexBuildComponents,
|
||||
prepareForTestWithMyapex,
|
||||
java.FixtureConfigureApexBootJars(tc.configuredBootJars...),
|
||||
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
|
||||
variables.BuildFlags = map[string]string{
|
||||
"RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions",
|
||||
}
|
||||
}),
|
||||
)
|
||||
if tc.errorExpected {
|
||||
fixture = fixture.ExtendWithErrorHandler(
|
||||
android.FixtureExpectsAtLeastOneErrorMatchingPattern(`in contents.*must also be declared in PRODUCT_APEX_BOOT_JARS`),
|
||||
)
|
||||
}
|
||||
fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.apexContributionContents))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -835,7 +835,21 @@ func (p *prebuiltCommon) providePrebuiltInfo(ctx android.ModuleContext) {
|
|||
android.SetProvider(ctx, android.PrebuiltInfoProvider, info)
|
||||
}
|
||||
|
||||
// Uses an object provided by its deps to validate that the contents of bcpf have been added to the global
|
||||
// PRODUCT_APEX_BOOT_JARS
|
||||
// This validation will only run on the apex which is active for this product/release_config
|
||||
func validateApexClasspathFragments(ctx android.ModuleContext) {
|
||||
ctx.VisitDirectDeps(func(m android.Module) {
|
||||
if info, exists := android.OtherModuleProvider(ctx, m, java.ClasspathFragmentValidationInfoProvider); exists {
|
||||
ctx.ModuleErrorf("%s in contents of %s must also be declared in PRODUCT_APEX_BOOT_JARS", info.UnknownJars, info.ClasspathFragmentModuleName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
// Validate contents of classpath fragments
|
||||
validateApexClasspathFragments(ctx)
|
||||
|
||||
p.apexKeysPath = writeApexKeys(ctx, p)
|
||||
// TODO(jungjw): Check the key validity.
|
||||
p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path()
|
||||
|
@ -1059,6 +1073,9 @@ func (a *ApexSet) ApexInfoMutator(mctx android.TopDownMutatorContext) {
|
|||
}
|
||||
|
||||
func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
// Validate contents of classpath fragments
|
||||
validateApexClasspathFragments(ctx)
|
||||
|
||||
a.apexKeysPath = writeApexKeys(ctx, a)
|
||||
a.installFilename = a.InstallFilename()
|
||||
if !strings.HasSuffix(a.installFilename, imageApexSuffix) && !strings.HasSuffix(a.installFilename, imageCapexSuffix) {
|
||||
|
|
|
@ -590,13 +590,36 @@ func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext)
|
|||
// So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS.
|
||||
// TODO(b/202896428): Add better way to handle this.
|
||||
_, unknown = android.RemoveFromList("android.car-module", unknown)
|
||||
if isActiveModule(ctx, ctx.Module()) && len(unknown) > 0 {
|
||||
if isApexVariant(ctx) && len(unknown) > 0 {
|
||||
if android.IsModulePrebuilt(ctx.Module()) {
|
||||
// prebuilt bcpf. the validation of this will be done at the top-level apex
|
||||
providerClasspathFragmentValidationInfoProvider(ctx, unknown)
|
||||
} else if !disableSourceApexVariant(ctx) {
|
||||
// source bcpf, and prebuilt apex are not selected.
|
||||
ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
return jars
|
||||
}
|
||||
|
||||
var ClasspathFragmentValidationInfoProvider = blueprint.NewProvider[ClasspathFragmentValidationInfo]()
|
||||
|
||||
type ClasspathFragmentValidationInfo struct {
|
||||
ClasspathFragmentModuleName string
|
||||
UnknownJars []string
|
||||
}
|
||||
|
||||
// Set a provider with the list of jars that have not been added to PRODUCT_APEX_BOOT_JARS
|
||||
// The validation will be done in the ctx of the top-level _selected_ apex
|
||||
func providerClasspathFragmentValidationInfoProvider(ctx android.ModuleContext, unknown []string) {
|
||||
info := ClasspathFragmentValidationInfo{
|
||||
ClasspathFragmentModuleName: ctx.ModuleName(),
|
||||
UnknownJars: unknown,
|
||||
}
|
||||
android.SetProvider(ctx, ClasspathFragmentValidationInfoProvider, info)
|
||||
}
|
||||
|
||||
// generateHiddenAPIBuildActions generates all the hidden API related build rules.
|
||||
func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput {
|
||||
|
||||
|
|
Loading…
Reference in a new issue