Workaround to make AlwaysUsePrebuiltSdks() work with platform_bootclasspath

The AlwaysUsePrebuiltSdks() causes all java_sdk_library_import modules
to be preferred over the source, i.e. as if they had prefer: true set.
That interacts badly with the work that is being done to integrate the
bootclasspath_fragment/platform_bootclasspath modules into the build.

It would work fine once that integration has been completed but in the
interim it causes problems. e.g. it does not cause a problem in AOSP
because those java_sdk_library_import modules that are affected have
already been integrated into the build properly.

Unfortunately, internally that is not the case because there are
java_sdk_library/java_sdk_library_import modules that still need to
be updated.

Before the java_sdk_library_import can be safely preferred each
java_sdk_library/java_sdk_library_import module that contributes to the
bootclasspath must:
* Be in the contents of matching bootclasspath_fragment and
  prebuilt_bootclasspath_fragment modules.
* Have an apex and one of a prebuilt_apex/apex_set that contains the
  dex implementation jar and lists the prebuilt_bootclasspath_fragment
  name in its exported_bootclasspath_fragments property.

Safely preferred in this context means that the whole build will
continue to work rather than the current situation which is that only
some of the build will work and some will fail if an attempt is
actually made to build it.

Unfortunately, many java_sdk_library_import modules are missing:
* The prebuilt_bootclasspath_fragment.
* The exported_bootclasspath_fragments property on the
  prebuilt_apex/apex_set that contains them.

Together these cause the following symptoms:
1. The java_sdk_library_import does not have a dex implementation jar.
2. The java_sdk_library_import does not have a myapex variant.

These workarounds will avoid Soong reporting build failures. However,
the build will still fail if an attempt is made to build anything
produced by the platform-bootclasspath, e.g. hidden API processing or
a system image.

Bug: 188505921
Bug: 179354495
Test: m TARGET_BUILD_APPS=Calendar
Change-Id: I3226e21cd6a7f9e4d6bbe94e54129ac5e1d4c679
This commit is contained in:
Paul Duffin 2021-05-19 09:36:09 +01:00
parent 9e06a2bddf
commit 59db6d4c5d
5 changed files with 176 additions and 5 deletions

View File

@ -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 "<apex>:<module>", where <apex> is the

View File

@ -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

View File

@ -761,7 +761,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")
}
}
}

View File

@ -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)
}

View File

@ -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