From 127b40b316452b598d5800a7aed10b8a159833af Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Mon, 30 Sep 2019 16:04:35 +0900 Subject: [PATCH] Add apex_available to control the availablity of a module to APEXes apex_available property controls the availability of a module to APEXes. For example, `apex_available: ["myapex", "otherapex"]` makes the module available only to the two APEXes: myapex and otherapex, and nothing else, even to the platform. If the module is intended to be available to any APEX, then a pseudo name "//apex_available:anyapex" can be used. If the module is intended to be available to the platform, then another pseudo name "//apex_available:platform" is used. For now, if unspecified, this property defaults to ["//apex_available:platform", "//apex_available:anyapex"], which means the module is available to everybody. This will be reduced to ["//apex_available:platform"], when marking for apex_available for existing modules are finished. Bug: 139870423 Bug: 128708192 Test: m Change-Id: Id4b233c3056c7858f984cbf9427cfac4118b2682 --- android/apex.go | 46 +++++++++++++- apex/apex.go | 10 +++ apex/apex_test.go | 152 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 2 deletions(-) diff --git a/android/apex.go b/android/apex.go index 99b13ab72..557febfc9 100644 --- a/android/apex.go +++ b/android/apex.go @@ -78,12 +78,23 @@ type ApexModule interface { // Return the no_apex property NoApex() bool + + // Tests if this module is available for the specified APEX or ":platform" + AvailableFor(what string) bool } type ApexProperties struct { // Whether this module should not be part of any APEX. Default is false. + // TODO(b/128708192): remove this as this is equal to apex_available: [":platform"] No_apex *bool + // Availability of this module in APEXes. Only the listed APEXes can include this module. + // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. + // "//apex_available:platform" refers to non-APEX partitions like "system.img". + // Default is ["//apex_available:platform", "//apex_available:anyapex"]. + // TODO(b/128708192) change the default to ["//apex_available:platform"] + Apex_available []string + // Name of the apex variant that this module is mutated into ApexName string `blueprint:"mutated"` } @@ -136,15 +147,46 @@ func (m *ApexModuleBase) NoApex() bool { return proptools.Bool(m.ApexProperties.No_apex) } +const ( + availableToPlatform = "//apex_available:platform" + availableToAnyApex = "//apex_available:anyapex" +) + +func (m *ApexModuleBase) AvailableFor(what string) bool { + if len(m.ApexProperties.Apex_available) == 0 { + // apex_available defaults to ["//apex_available:platform", "//apex_available:anyapex"], + // which means 'available to everybody'. + return true + } + return InList(what, m.ApexProperties.Apex_available) || + (what != availableToPlatform && InList(availableToAnyApex, m.ApexProperties.Apex_available)) +} + +func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { + for _, n := range m.ApexProperties.Apex_available { + if n == availableToPlatform || n == availableToAnyApex { + continue + } + if !mctx.OtherModuleExists(n) { + mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n) + } + } +} + func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module { if len(m.apexVariations) > 0 { + m.checkApexAvailableProperty(mctx) sort.Strings(m.apexVariations) - variations := []string{""} // Original variation for platform + variations := []string{} + availableForPlatform := m.AvailableFor(availableToPlatform) + if availableForPlatform { + variations = append(variations, "") // Original variation for platform + } variations = append(variations, m.apexVariations...) modules := mctx.CreateVariations(variations...) for i, m := range modules { - if i == 0 { + if availableForPlatform && i == 0 { continue } m.(ApexModule).setApexName(variations[i]) diff --git a/apex/apex.go b/apex/apex.go index 220eb0043..aff8d50b6 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -1215,6 +1215,16 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } + // check apex_available requirements + for _, fi := range filesInfo { + if am, ok := fi.module.(android.ApexModule); ok { + if !am.AvailableFor(ctx.ModuleName()) { + ctx.ModuleErrorf("requires %q that is not available for the APEX", fi.module.Name()) + return + } + } + } + // prepend the name of this APEX to the module names. These names will be the names of // modules that will be defined if the APEX is flattened. for i := range filesInfo { diff --git a/apex/apex_test.go b/apex/apex_test.go index 114d89ff9..ecfa46fa5 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -2422,6 +2422,158 @@ func TestApexWithApps(t *testing.T) { } +func TestApexAvailable(t *testing.T) { + // libfoo is not available to myapex, but only to otherapex + testApexError(t, "requires \"libfoo\" that is not available for the APEX", ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + apex { + name: "otherapex", + key: "otherapex.key", + native_shared_libs: ["libfoo"], + } + + apex_key { + name: "otherapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + apex_available: ["otherapex"], + }`) + + // libbar is an indirect dep + testApexError(t, "requires \"libbar\" that is not available for the APEX", ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + apex { + name: "otherapex", + key: "otherapex.key", + native_shared_libs: ["libfoo"], + } + + apex_key { + name: "otherapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + shared_libs: ["libbar"], + system_shared_libs: [], + apex_available: ["myapex", "otherapex"], + } + + cc_library { + name: "libbar", + stl: "none", + system_shared_libs: [], + apex_available: ["otherapex"], + }`) + + testApexError(t, "\"otherapex\" is not a valid module name", ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + apex_available: ["otherapex"], + }`) + + ctx, _ := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["libfoo", "libbar"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + apex_available: ["myapex"], + } + + cc_library { + name: "libbar", + stl: "none", + system_shared_libs: [], + apex_available: ["//apex_available:anyapex"], + }`) + + // check that libfoo and libbar are created only for myapex, but not for the platform + ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared_myapex") + ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared") + ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_core_shared_myapex") + ensureListNotContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_core_shared") + + ctx, _ = testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + apex_available: ["//apex_available:platform"], + }`) + + // check that libfoo is created only for the platform + ensureListNotContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared_myapex") + ensureListContains(t, ctx.ModuleVariantsForTests("libfoo"), "android_arm64_armv8-a_core_shared") +} + func TestMain(m *testing.M) { run := func() int { setUp()