diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index 52b1689ba..ce12f4635 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -20,6 +20,7 @@ import ( "android/soong/android" "android/soong/java" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) // Contains tests for platform_bootclasspath logic from java/platform_bootclasspath.go that requires @@ -174,6 +175,141 @@ func TestPlatformBootclasspathDependencies(t *testing.T) { }) } +// TestPlatformBootclasspath_AlwaysUsePrebuiltSdks verifies that the build does not fail when +// AlwaysUsePrebuiltSdk() returns true. The structure of the modules in this test matches what +// currently exists in some places in the Android build but it is not the intended structure. It is +// in fact an invalid structure that should cause build failures. However, fixing that structure +// will take too long so in the meantime this tests the workarounds to avoid build breakages. +// +// The main issues with this structure are: +// 1. There is no prebuilt_bootclasspath_fragment referencing the "foo" java_sdk_library_import. +// 2. There is no prebuilt_apex/apex_set which makes the dex implementation jar available to the +// prebuilt_bootclasspath_fragment and the "foo" java_sdk_library_import. +// +// Together these cause the following symptoms: +// 1. The "foo" java_sdk_library_import does not have a dex implementation jar. +// 2. The "foo" java_sdk_library_import does not have a myapex variant. +// +// TODO(b/179354495): Fix the structure in this test once the main Android build has been fixed. +func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + prepareForTestWithMyapex, + // Configure two libraries, the first is a java_sdk_library whose prebuilt will be used because + // of AlwaysUsePrebuiltsSdk() but does not have an appropriate apex variant and does not provide + // a boot dex jar. The second is a normal library that is unaffected. The order matters because + // if the dependency on myapex:foo is filtered out because of either of those conditions then + // the dependencies resolved by the platform_bootclasspath will not match the configured list + // and so will fail the test. + java.FixtureConfigureUpdatableBootJars("myapex:foo", "myapex:bar"), + java.PrepareForTestWithJavaSdkLibraryFiles, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) + }), + java.FixtureWithPrebuiltApis(map[string][]string{ + "current": {}, + "30": {"foo"}, + }), + ).RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspath-fragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: ["myapex"], + permitted_packages: ["bar"], + } + + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: { + enabled: true, + }, + apex_available: ["myapex"], + permitted_packages: ["foo"], + } + + // A prebuilt java_sdk_library_import that is not preferred by default but will be preferred + // because AlwaysUsePrebuiltSdks() is true. + java_sdk_library_import { + name: "foo", + prefer: false, + shared_library: false, + public: { + jars: ["sdk_library/public/foo-stubs.jar"], + stub_srcs: ["sdk_library/public/foo_stub_sources"], + current_api: "sdk_library/public/foo.txt", + removed_api: "sdk_library/public/foo-removed.txt", + sdk_version: "current", + }, + apex_available: ["myapex"], + } + + // This always depends on the source foo module, its dependencies are not affected by the + // AlwaysUsePrebuiltSdks(). + bootclasspath_fragment { + name: "mybootclasspath-fragment", + apex_available: [ + "myapex", + ], + contents: [ + "foo", "bar", + ], + } + + platform_bootclasspath { + name: "myplatform-bootclasspath", + } +`, + ) + + java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{ + // The configured contents of BootJars. + "platform:prebuilt_foo", // Note: This is the platform not myapex variant. + "myapex:bar", + }) + + // Make sure that the myplatform-bootclasspath has the correct dependencies. + CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{ + // The following are stubs. + "platform:prebuilt_sdk_public_current_android", + "platform:prebuilt_sdk_system_current_android", + "platform:prebuilt_sdk_test_current_android", + + // Not a prebuilt as no prebuilt existed when it was added. + "platform:legacy.core.platform.api.stubs", + + // Needed for generating the boot image. + `platform:dex2oatd`, + + // The platform_bootclasspath intentionally adds dependencies on both source and prebuilt + // modules when available as it does not know which one will be preferred. + // + // The source module has an APEX variant but the prebuilt does not. + "myapex:foo", + "platform:prebuilt_foo", + + // Only a source module exists. + "myapex:bar", + }) +} + // CheckModuleDependencies checks the dependencies of the selected module against the expected list. // // The expected list must be a list of strings of the form ":", where is the diff --git a/java/bootclasspath.go b/java/bootclasspath.go index 02833ab66..634959a7e 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -29,7 +29,7 @@ func init() { func registerBootclasspathBuildComponents(ctx android.RegistrationContext) { ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator) + ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator).Parallel() }) } @@ -95,6 +95,15 @@ func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex st if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) { ctx.AddVariationDependencies(variations, tag, prebuiltName) addedDep = true + } else if ctx.Config().AlwaysUsePrebuiltSdks() && len(variations) > 0 { + // TODO(b/179354495): Remove this code path once the Android build has been fully migrated to + // use bootclasspath_fragment properly. + // Some prebuilt java_sdk_library modules do not yet have an APEX variations so try and add a + // dependency on the non-APEX variant. + if ctx.OtherModuleDependencyVariantExists(nil, prebuiltName) { + ctx.AddVariationDependencies(nil, tag, prebuiltName) + addedDep = true + } } // If no appropriate variant existing for this, so no dependency could be added, then it is an diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index e9f7c44d2..e1a36507a 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -766,7 +766,7 @@ func generateUpdatableBcpPackagesRule(ctx android.ModuleContext, image *bootImag if len(pp) > 0 { updatablePackages = append(updatablePackages, pp...) } else { - ctx.ModuleErrorf("Missing permitted_packages") + ctx.OtherModuleErrorf(module, "Missing permitted_packages") } } } diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 2dceb6535..f5afe5d5b 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "strings" "android/soong/android" @@ -560,7 +561,25 @@ func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents for _, module := range contents { bootDexJar := module.bootDexJar() if bootDexJar == nil { - ctx.ModuleErrorf("module %s does not provide a dex jar", module) + if ctx.Config().AlwaysUsePrebuiltSdks() { + // TODO(b/179354495): Remove this work around when it is unnecessary. + // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So, + // create a fake one that will cause a build error only if it is used. + fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name()) + + // Create an error rule that pretends to create the output file but will actually fail if it + // is run. + ctx.Build(pctx, android.BuildParams{ + Rule: android.ErrorRule, + Output: fake, + Args: map[string]string{ + "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module), + }, + }) + bootDexJars = append(bootDexJars, fake) + } else { + ctx.ModuleErrorf("module %s does not provide a dex jar", module) + } } else { bootDexJars = append(bootDexJars, bootDexJar) } diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index c8fafede6..6ebeb6b89 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -242,8 +242,15 @@ func (b *platformBootclasspathModule) checkUpdatableModules(ctx android.ModuleCo } else { name := ctx.OtherModuleName(m) if apexInfo.IsForPlatform() { - // error: this jar is part of the platform - ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name) + // If AlwaysUsePrebuiltSdks() returns true then it is possible that the updatable list will + // include platform variants of a prebuilt module due to workarounds elsewhere. In that case + // do not treat this as an error. + // TODO(b/179354495): Always treat this as an error when migration to bootclasspath_fragment + // modules is complete. + if !ctx.Config().AlwaysUsePrebuiltSdks() { + // error: this jar is part of the platform + ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name) + } } else { // TODO(b/177892522): Treat this as an error. // Cannot do that at the moment because framework-wifi and framework-tethering are in the