From 9a419e28f523d8daadda15841089b22f31166a44 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Fri, 16 Aug 2024 17:14:21 +0900 Subject: [PATCH] apex: apex_available with prefix We now support prefix form (com.foo.*) in apex_available list. Bug: 360265761 Test: m --no-skip-soong-tests Change-Id: I50ab884651dd6321950cfd4563b59ef3ed0f07fd --- android/apex.go | 39 ++++++++++++++++++-- apex/apex.go | 15 +++++++- apex/apex_test.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 6 deletions(-) diff --git a/android/apex.go b/android/apex.go index ef4cf82cd..028be57ec 100644 --- a/android/apex.go +++ b/android/apex.go @@ -280,6 +280,7 @@ type ApexProperties struct { // // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. // "//apex_available:platform" refers to non-APEX partitions like "system.img". + // Prefix pattern (com.foo.*) can be used to match with any APEX name with the prefix(com.foo.). // Default is ["//apex_available:platform"]. Apex_available []string @@ -491,10 +492,27 @@ func CheckAvailableForApex(what string, apex_available []string) bool { if len(apex_available) == 0 { return what == AvailableToPlatform } - return InList(what, apex_available) || - (what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) || - (what == "com.google.mainline.primary.libs") || // TODO b/248601389 - (what == "com.google.mainline.go.primary.libs") // TODO b/248601389 + + // TODO b/248601389 + if what == "com.google.mainline.primary.libs" || what == "com.google.mainline.go.primary.libs" { + return true + } + + for _, apex_name := range apex_available { + // exact match. + if apex_name == what { + return true + } + // //apex_available:anyapex matches with any apex name, but not //apex_available:platform + if apex_name == AvailableToAnyApex && what != AvailableToPlatform { + return true + } + // prefix match. + if strings.HasSuffix(apex_name, ".*") && strings.HasPrefix(what, strings.TrimSuffix(apex_name, "*")) { + return true + } + } + return false } // Implements ApexModule @@ -523,6 +541,19 @@ func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { if n == AvailableToPlatform || n == AvailableToAnyApex { continue } + // Prefix pattern should end with .* and has at least two components. + if strings.Contains(n, "*") { + if !strings.HasSuffix(n, ".*") { + mctx.PropertyErrorf("apex_available", "Wildcard should end with .* like com.foo.*") + } + if strings.Count(n, ".") < 2 { + mctx.PropertyErrorf("apex_available", "Wildcard requires two or more components like com.foo.*") + } + if strings.Count(n, "*") != 1 { + mctx.PropertyErrorf("apex_available", "Wildcard is not allowed in the middle.") + } + continue + } if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() { mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n) } diff --git a/apex/apex.go b/apex/apex.go index dd1c0b52e..77ebf2624 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -2767,10 +2767,21 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) { return true } + + // Let's give some hint for apex_available + hint := fmt.Sprintf("%q", apexName) + + if strings.HasPrefix(apexName, "com.") && !strings.HasPrefix(apexName, "com.android.") && strings.Count(apexName, ".") >= 2 { + // In case of a partner APEX, prefix format might be an option. + components := strings.Split(apexName, ".") + components[len(components)-1] = "*" + hint += fmt.Sprintf(" or %q", strings.Join(components, ".")) + } + ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'."+ "\n\nDependency path:%s\n\n"+ - "Consider adding %q to 'apex_available' property of %q", - fromName, toName, ctx.GetPathString(true), apexName, toName) + "Consider adding %s to 'apex_available' property of %q", + fromName, toName, ctx.GetPathString(true), hint, toName) // Visit this module's dependencies to check and report any issues with their availability. return true }) diff --git a/apex/apex_test.go b/apex/apex_test.go index c37c7d0c7..6b9944d76 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -6722,6 +6722,99 @@ func TestApexAvailable_CreatedForApex(t *testing.T) { } } +func TestApexAvailable_PrefixMatch(t *testing.T) { + + for _, tc := range []struct { + name string + apexAvailable string + expectedError string + }{ + { + name: "prefix matches correctly", + apexAvailable: "com.foo.*", + }, + { + name: "prefix doesn't match", + apexAvailable: "com.bar.*", + expectedError: `Consider .* "com.foo\.\*"`, + }, + { + name: "short prefix", + apexAvailable: "com.*", + expectedError: "requires two or more components", + }, + { + name: "wildcard not in the end", + apexAvailable: "com.*.foo", + expectedError: "should end with .*", + }, + { + name: "wildcard in the middle", + apexAvailable: "com.foo*.*", + expectedError: "not allowed in the middle", + }, + { + name: "hint with prefix pattern", + apexAvailable: "//apex_available:platform", + expectedError: "Consider adding \"com.foo.bar\" or \"com.foo.*\"", + }, + } { + t.Run(tc.name, func(t *testing.T) { + errorHandler := android.FixtureExpectsNoErrors + if tc.expectedError != "" { + errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError) + } + context := android.GroupFixturePreparers( + prepareForApexTest, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/com.foo.bar-file_contexts": nil, + }), + ).ExtendWithErrorHandler(errorHandler) + + context.RunTestWithBp(t, ` + apex { + name: "com.foo.bar", + key: "myapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + apex_available: ["`+tc.apexAvailable+`"], + }`) + }) + } + testApexError(t, `Consider adding "com.foo" to`, ` + apex { + name: "com.foo", // too short for a partner apex + key: "myapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + } + `) +} + func TestOverrideApex(t *testing.T) { ctx := testApex(t, ` apex {