aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpandan Das <spandandas@google.com>2024-05-03 21:36:48 +0000
committerSpandan Das <spandandas@google.com>2024-05-16 00:35:28 +0000
commit3d0d31a8c6ac8e308f1a258d2fab3b3cefb59d1e (patch)
tree0bfc5af33bfd2c3b6ee19ff2f2bc572c26efb151
parente8cb9178ec8b4faf24ffd3c0f123efa7f7f9fe65 (diff)
downloadsoong-3d0d31a8c6ac8e308f1a258d2fab3b3cefb59d1e.tar.gz
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
-rw-r--r--apex/platform_bootclasspath_test.go124
-rw-r--r--apex/prebuilt.go17
-rw-r--r--java/bootclasspath_fragment.go27
3 files changed, 166 insertions, 2 deletions
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 2be9c10e1..9f1e1e16f 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -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))
+ }
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 72a9e5219..b2afa3933 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -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) {
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 82a34ca9d..4d3d794d8 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -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 {
- ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
+ 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 {