Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 20fce2d3 authored by Spandan Das's avatar Spandan Das
Browse files

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
parent 39b6cc53
Loading
Loading
Loading
Loading
+108 −12
Original line number Diff line number Diff line
@@ -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)
		}
	}
}
+54 −0
Original line number Diff line number Diff line
@@ -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() {