From b432df9cda56a19f05377628989ee4c7550e8678 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Mon, 22 Mar 2021 22:09:42 +0000 Subject: [PATCH] Add dependencies from platform_bootclasspath to contents Adds a FinalDeps mutator to add dependencies from the platform_bootclasspath to the configured boot jars which can be from either the platform or any apex. It adds dependencies for every configured boot jar, whether in ArtApexJars, BootJars or UpdatableBootJars. At the moment the dependencies are only used for testing purposes but following changes will make more use of them. Bug: 177892522 Test: m nothing Change-Id: I981305bf45bc20539a3d36987252f490e2b885cc --- apex/Android.bp | 1 + apex/platform_bootclasspath_test.go | 158 ++++++++++++++++++++++++++++ java/platform_bootclasspath.go | 93 ++++++++++++++++ java/platform_bootclasspath_test.go | 101 +++++++++++++++++- java/testing.go | 31 ++++++ 5 files changed, 381 insertions(+), 3 deletions(-) create mode 100644 apex/platform_bootclasspath_test.go diff --git a/apex/Android.bp b/apex/Android.bp index 1890b89c3..7b5240286 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -31,6 +31,7 @@ bootstrap_go_package { testSrcs: [ "apex_test.go", "boot_image_test.go", + "platform_bootclasspath_test.go", "vndk_test.go", ], pluginFor: ["soong_build"], diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go new file mode 100644 index 000000000..4aed9e00d --- /dev/null +++ b/apex/platform_bootclasspath_test.go @@ -0,0 +1,158 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apex + +import ( + "testing" + + "android/soong/android" + "android/soong/dexpreopt" + "android/soong/java" + "github.com/google/blueprint" +) + +// Contains tests for platform_bootclasspath logic from java/platform_bootclasspath.go that requires +// apexes. + +var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers( + java.PrepareForTestWithDexpreopt, + PrepareForTestWithApexBuildComponents, +) + +func TestPlatformBootclasspathDependencies(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + prepareForTestWithArtApex, + prepareForTestWithMyapex, + // Configure some libraries in the art and framework boot images. + dexpreopt.FixtureSetArtBootJars("com.android.art:baz", "com.android.art:quuz"), + dexpreopt.FixtureSetBootJars("platform:foo"), + dexpreopt.FixtureSetUpdatableBootJars("myapex:bar"), + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("foo"), + ).RunTestWithBp(t, ` + apex { + name: "com.android.art", + key: "com.android.art.key", + bootclasspath_fragments: [ + "art-bootclasspath-fragment", + ], + updatable: false, + } + + apex_key { + name: "com.android.art.key", + public_key: "com.android.art.avbpubkey", + private_key: "com.android.art.pem", + } + + bootclasspath_fragment { + name: "art-bootclasspath-fragment", + apex_available: [ + "com.android.art", + ], + contents: [ + "baz", + "quuz", + ], + } + + java_library { + name: "baz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + installable: true, + } + + // Add a java_import that is not preferred and so won't have an appropriate apex variant created + // for it to make sure that the platform_bootclasspath doesn't try and add a dependency onto it. + java_import { + name: "baz", + apex_available: [ + "com.android.art", + ], + jars: ["b.jar"], + } + + java_library { + name: "quuz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + installable: true, + } + + apex { + name: "myapex", + key: "myapex.key", + java_libs: [ + "bar", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_sdk_library { + name: "foo", + srcs: ["b.java"], + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: ["myapex"], + permitted_packages: ["bar"], + } + + platform_bootclasspath { + name: "myplatform-bootclasspath", + } +`, + ) + + // Make sure that the myplatform-bootclasspath has the correct dependencies. + CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{ + `platform:dex2oatd`, + `com.android.art:baz`, + `com.android.art:quuz`, + `platform:foo`, + `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 +// name of the apex, or platform is it is not part of an apex and is the module name. +func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) { + t.Helper() + module := ctx.ModuleForTests(name, variant).Module() + modules := []android.Module{} + ctx.VisitDirectDeps(module, func(m blueprint.Module) { + modules = append(modules, m.(android.Module)) + }) + + pairs := java.ApexNamePairsFromModules(ctx, modules) + android.AssertDeepEquals(t, "module dependencies", expected, pairs) +} diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 550707754..5272eaf77 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -17,6 +17,7 @@ package java import ( "android/soong/android" "android/soong/dexpreopt" + "github.com/google/blueprint" ) func init() { @@ -25,10 +26,38 @@ func init() { func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory) + + ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("platform_bootclasspath_deps", platformBootclasspathDepsMutator) + }) } +type platformBootclasspathDependencyTag struct { + blueprint.BaseDependencyTag + + name string +} + +// Avoid having to make platform bootclasspath content visible to the platform bootclasspath. +// +// This is a temporary workaround to make it easier to migrate to platform bootclasspath with proper +// dependencies. +// TODO(b/177892522): Remove this and add needed visibility. +func (t platformBootclasspathDependencyTag) ExcludeFromVisibilityEnforcement() { +} + +// The tag used for the dependency between the platform bootclasspath and any configured boot jars. +var platformBootclasspathModuleDepTag = platformBootclasspathDependencyTag{name: "module"} + +var _ android.ExcludeFromVisibilityEnforcementTag = platformBootclasspathDependencyTag{} + type platformBootclasspathModule struct { android.ModuleBase + + // The apex:module pairs obtained from the configured modules. + // + // Currently only for testing. + configuredModules []android.Module } func platformBootclasspathFactory() android.Module { @@ -47,7 +76,71 @@ func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorCon dexpreopt.RegisterToolDeps(ctx) } +func platformBootclasspathDepsMutator(ctx android.BottomUpMutatorContext) { + m := ctx.Module() + if p, ok := m.(*platformBootclasspathModule); ok { + // Add dependencies on all the modules configured in the "art" boot image. + artImageConfig := genBootImageConfigs(ctx)[artBootImageName] + addDependenciesOntoBootImageModules(ctx, artImageConfig.modules) + + // Add dependencies on all the modules configured in the "boot" boot image. That does not + // include modules configured in the "art" boot image. + bootImageConfig := p.getImageConfig(ctx) + addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules) + + // Add dependencies on all the updatable modules. + updatableModules := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars + addDependenciesOntoBootImageModules(ctx, updatableModules) + } +} + +func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tag blueprint.DependencyTag) { + var variations []blueprint.Variation + if apex != "platform" { + // Pick the correct apex variant. + variations = []blueprint.Variation{ + {Mutator: "apex", Variation: apex}, + } + } + + addedDep := false + if ctx.OtherModuleDependencyVariantExists(variations, name) { + ctx.AddFarVariationDependencies(variations, tag, name) + addedDep = true + } + + // Add a dependency on the prebuilt module if it exists. + prebuiltName := android.PrebuiltNameFromSource(name) + if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) { + ctx.AddVariationDependencies(variations, tag, prebuiltName) + addedDep = true + } + + // If no appropriate variant existing for this, so no dependency could be added, then it is an + // error, unless missing dependencies are allowed. The simplest way to handle that is to add a + // dependency that will not be satisfied and the default behavior will handle it. + if !addedDep { + ctx.AddFarVariationDependencies(variations, tag, name) + } +} + +func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList) { + for i := 0; i < modules.Len(); i++ { + apex := modules.Apex(i) + name := modules.Jar(i) + + addDependencyOntoApexModulePair(ctx, apex, name, platformBootclasspathModuleDepTag) + } +} + func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) { + tag := ctx.OtherModuleDependencyTag(module) + if tag == platformBootclasspathModuleDepTag { + b.configuredModules = append(b.configuredModules, module) + } + }) + // Nothing to do if skipping the dexpreopt of boot image jars. if SkipDexpreoptBootJars(ctx) { return diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go index 1c81cfdc2..ebbe3a5ee 100644 --- a/java/platform_bootclasspath_test.go +++ b/java/platform_bootclasspath_test.go @@ -29,10 +29,105 @@ var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers( ) func TestPlatformBootclasspath(t *testing.T) { - prepareForTestWithPlatformBootclasspath. - RunTestWithBp(t, ` + preparer := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"), + android.FixtureWithRootAndroidBp(` platform_bootclasspath { name: "platform-bootclasspath", } - `) + + java_library { + name: "bar", + srcs: ["a.java"], + system_modules: "none", + sdk_version: "none", + compile_dex: true, + } + `), + ) + + var addSourceBootclassPathModule = android.FixtureAddTextFile("source/Android.bp", ` + java_library { + name: "foo", + srcs: ["a.java"], + system_modules: "none", + sdk_version: "none", + compile_dex: true, + } + `) + + var addPrebuiltBootclassPathModule = android.FixtureAddTextFile("prebuilt/Android.bp", ` + java_import { + name: "foo", + jars: ["a.jar"], + compile_dex: true, + prefer: false, + } + `) + + var addPrebuiltPreferredBootclassPathModule = android.FixtureAddTextFile("prebuilt/Android.bp", ` + java_import { + name: "foo", + jars: ["a.jar"], + compile_dex: true, + prefer: true, + } + `) + + t.Run("missing", func(t *testing.T) { + preparer. + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"platform-bootclasspath" depends on undefined module "foo"`)). + RunTest(t) + }) + + t.Run("source", func(t *testing.T) { + result := android.GroupFixturePreparers( + preparer, + addSourceBootclassPathModule, + ).RunTest(t) + + CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{ + "platform:foo", + "platform:bar", + }) + }) + + t.Run("prebuilt", func(t *testing.T) { + result := android.GroupFixturePreparers( + preparer, + addPrebuiltBootclassPathModule, + ).RunTest(t) + + CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{ + "platform:prebuilt_foo", + "platform:bar", + }) + }) + + t.Run("source+prebuilt - source preferred", func(t *testing.T) { + result := android.GroupFixturePreparers( + preparer, + addSourceBootclassPathModule, + addPrebuiltBootclassPathModule, + ).RunTest(t) + + CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{ + "platform:foo", + "platform:bar", + }) + }) + + t.Run("source+prebuilt - prebuilt preferred", func(t *testing.T) { + result := android.GroupFixturePreparers( + preparer, + addSourceBootclassPathModule, + addPrebuiltPreferredBootclassPathModule, + ).RunTest(t) + + CheckPlatformBootclasspathModules(t, result, "platform-bootclasspath", []string{ + "platform:prebuilt_foo", + "platform:bar", + }) + }) } diff --git a/java/testing.go b/java/testing.go index 80c107d12..455cca9dd 100644 --- a/java/testing.go +++ b/java/testing.go @@ -300,6 +300,37 @@ func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, varia } } +// CheckPlatformBootclasspathModules returns the apex:module pair for the modules depended upon by +// the platform-bootclasspath module. +func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, name string, expected []string) { + t.Helper() + platformBootclasspath := result.Module(name, "android_common").(*platformBootclasspathModule) + pairs := ApexNamePairsFromModules(result.TestContext, platformBootclasspath.configuredModules) + android.AssertDeepEquals(t, fmt.Sprintf("%s modules", "platform-bootclasspath"), expected, pairs) +} + +// ApexNamePairsFromModules returns the apex:module pair for the supplied modules. +func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module) []string { + pairs := []string{} + for _, module := range modules { + pairs = append(pairs, apexNamePairFromModule(ctx, module)) + } + return pairs +} + +func apexNamePairFromModule(ctx *android.TestContext, module android.Module) string { + name := module.Name() + var apex string + apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) + if apexInfo.IsForPlatform() { + apex = "platform" + } else { + apex = apexInfo.InApexes[0] + } + + return fmt.Sprintf("%s:%s", apex, name) +} + func CheckHiddenAPIRuleInputs(t *testing.T, expected string, hiddenAPIRule android.TestingBuildParams) { t.Helper() actual := strings.TrimSpace(strings.Join(android.NormalizePathsForTesting(hiddenAPIRule.Implicits), "\n"))