Enforce stub libraries should have a single apex_available

If a library contributes to an API surface, it will have only a single
copy on device. Therefore, we should disallow installation to muliple
apexes/platform.

There are some exceptions to this rule today, and they have been relaxed
using allowlists.

Bug: 277651159
Test: go test ./apex
Change-Id: Ice3023ecd28412a2610d8b98628cb727b58c5c3b
This commit is contained in:
Spandan Das
2023-04-12 17:21:39 +00:00
parent 39b6cc5336
commit 20fce2d340
2 changed files with 162 additions and 12 deletions

View File

@@ -3131,10 +3131,7 @@ func TestStaticLinking(t *testing.T) {
stubs: {
versions: ["1", "2", "3"],
},
apex_available: [
"//apex_available:platform",
"myapex",
],
apex_available: ["myapex"],
}
cc_binary {
@@ -4134,7 +4131,7 @@ func TestDependenciesInApexManifest(t *testing.T) {
apex {
name: "myapex_selfcontained",
key: "myapex.key",
native_shared_libs: ["lib_dep", "libfoo"],
native_shared_libs: ["lib_dep_on_bar", "libbar"],
compile_multilib: "both",
file_contexts: ":myapex-file_contexts",
updatable: false,
@@ -4167,6 +4164,18 @@ func TestDependenciesInApexManifest(t *testing.T) {
],
}
cc_library {
name: "lib_dep_on_bar",
srcs: ["mylib.cpp"],
shared_libs: ["libbar"],
system_shared_libs: [],
stl: "none",
apex_available: [
"myapex_selfcontained",
],
}
cc_library {
name: "libfoo",
srcs: ["mytest.cpp"],
@@ -4177,9 +4186,22 @@ func TestDependenciesInApexManifest(t *testing.T) {
stl: "none",
apex_available: [
"myapex_provider",
],
}
cc_library {
name: "libbar",
srcs: ["mytest.cpp"],
stubs: {
versions: ["1"],
},
system_shared_libs: [],
stl: "none",
apex_available: [
"myapex_selfcontained",
],
}
`)
var apexManifestRule android.TestingBuildParams
@@ -4206,7 +4228,7 @@ func TestDependenciesInApexManifest(t *testing.T) {
apexManifestRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained_image").Rule("apexManifestRule")
provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
ensureListContains(t, provideNativeLibs, "libfoo.so")
ensureListContains(t, provideNativeLibs, "libbar.so")
ensureListEmpty(t, requireNativeLibs)
}
@@ -8488,14 +8510,14 @@ func TestTestForForLibInOtherApex(t *testing.T) {
apex {
name: "com.android.art",
key: "myapex.key",
native_shared_libs: ["mylib"],
native_shared_libs: ["libnativebridge"],
updatable: false,
}
apex {
name: "com.android.art.debug",
key: "myapex.key",
native_shared_libs: ["mylib", "mytestlib"],
native_shared_libs: ["libnativebridge", "libnativebrdige_test"],
updatable: false,
}
@@ -8506,8 +8528,8 @@ func TestTestForForLibInOtherApex(t *testing.T) {
}
cc_library {
name: "mylib",
srcs: ["mylib.cpp"],
name: "libnativebridge",
srcs: ["libnativebridge.cpp"],
system_shared_libs: [],
stl: "none",
stubs: {
@@ -8517,10 +8539,10 @@ func TestTestForForLibInOtherApex(t *testing.T) {
}
cc_library {
name: "mytestlib",
name: "libnativebrdige_test",
srcs: ["mylib.cpp"],
system_shared_libs: [],
shared_libs: ["mylib"],
shared_libs: ["libnativebridge"],
stl: "none",
apex_available: ["com.android.art.debug"],
test_for: ["com.android.art"],
@@ -10192,3 +10214,77 @@ func TestCannedFsConfig_HasCustomConfig(t *testing.T) {
// Ensure that canned_fs_config has "cat my_config" at the end
ensureContains(t, cmd, `( echo '/ 1000 1000 0755'; echo '/apex_manifest.json 1000 1000 0644'; echo '/apex_manifest.pb 1000 1000 0644'; cat my_config ) >`)
}
func TestStubLibrariesMultipleApexViolation(t *testing.T) {
testCases := []struct {
desc string
hasStubs bool
apexAvailable string
expectedError string
}{
{
desc: "non-stub library can have multiple apex_available",
hasStubs: false,
apexAvailable: `["myapex", "otherapex"]`,
},
{
desc: "stub library should not be available to anyapex",
hasStubs: true,
apexAvailable: `["//apex_available:anyapex"]`,
expectedError: "Stub libraries should have a single apex_available.*anyapex",
},
{
desc: "stub library should not be available to multiple apexes",
hasStubs: true,
apexAvailable: `["myapex", "otherapex"]`,
expectedError: "Stub libraries should have a single apex_available.*myapex.*otherapex",
},
{
desc: "stub library can be available to a core apex and a test apex",
hasStubs: true,
apexAvailable: `["myapex", "test_myapex"]`,
},
}
bpTemplate := `
cc_library {
name: "libfoo",
%v
apex_available: %v,
}
apex {
name: "myapex",
key: "apex.key",
updatable: false,
native_shared_libs: ["libfoo"],
}
apex {
name: "otherapex",
key: "apex.key",
updatable: false,
}
apex_test {
name: "test_myapex",
key: "apex.key",
updatable: false,
native_shared_libs: ["libfoo"],
}
apex_key {
name: "apex.key",
}
`
for _, tc := range testCases {
stubs := ""
if tc.hasStubs {
stubs = `stubs: {symbol_file: "libfoo.map.txt"},`
}
bp := fmt.Sprintf(bpTemplate, stubs, tc.apexAvailable)
mockFsFixturePreparer := android.FixtureModifyMockFS(func(fs android.MockFS) {
fs["system/sepolicy/apex/test_myapex-file_contexts"] = nil
})
if tc.expectedError == "" {
testApex(t, bp, mockFsFixturePreparer)
} else {
testApexError(t, tc.expectedError, bp, mockFsFixturePreparer)
}
}
}

View File

@@ -1987,6 +1987,56 @@ func moduleContextFromAndroidModuleContext(actx android.ModuleContext, c *Module
return ctx
}
// TODO (b/277651159): Remove this allowlist
var (
skipStubLibraryMultipleApexViolation = map[string]bool{
"libclang_rt.asan": true,
"libclang_rt.hwasan": true,
// runtime apex
"libc": true,
"libc_hwasan": true,
"libdl_android": true,
"libm": true,
"libdl": true,
// art apex
"libandroidio": true,
"libdexfile": true,
"libnativebridge": true,
"libnativehelper": true,
"libnativeloader": true,
"libsigchain": true,
}
)
// Returns true if a stub library could be installed in multiple apexes
func (c *Module) stubLibraryMultipleApexViolation(ctx android.ModuleContext) bool {
// If this is not an apex variant, no check necessary
if !c.InAnyApex() {
return false
}
// If this is not a stub library, no check necessary
if !c.HasStubsVariants() {
return false
}
// Skip the allowlist
// Use BaseModuleName so that this matches prebuilts.
if _, exists := skipStubLibraryMultipleApexViolation[c.BaseModuleName()]; exists {
return false
}
_, aaWithoutTestApexes, _ := android.ListSetDifference(c.ApexAvailable(), c.TestApexes())
// Stub libraries should not have more than one apex_available
if len(aaWithoutTestApexes) > 1 {
return true
}
// Stub libraries should not use the wildcard
if aaWithoutTestApexes[0] == android.AvailableToAnyApex {
return true
}
// Default: no violation
return false
}
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
// Handle the case of a test module split by `test_per_src` mutator.
//
@@ -2013,6 +2063,10 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
return
}
if c.stubLibraryMultipleApexViolation(actx) {
actx.PropertyErrorf("apex_available",
"Stub libraries should have a single apex_available (test apexes excluded). Got %v", c.ApexAvailable())
}
if c.Properties.Clang != nil && *c.Properties.Clang == false {
ctx.PropertyErrorf("clang", "false (GCC) is no longer supported")
} else if c.Properties.Clang != nil && !ctx.DeviceConfig().BuildBrokenClangProperty() {