From 5c998b9ff8a2571fb929e1e8764d4f10583660b1 Mon Sep 17 00:00:00 2001 From: Jooyung Han Date: Thu, 27 Jun 2019 11:30:33 +0900 Subject: [PATCH] Add "provide_cpp_shared_libs/uses" props to "apex" For APEXes to share C++ native libraries, we need a new kind of depedency between APEXes: "providing" APEXes and "using" APEXes. To reflect this dependency two new properties are added. provide_cpp_shared_libs: bool this indicates that the current APEX module provides the native C++ shared libs to other APEXes. uses: []string this indicates that the current APEX module uses the native C++ shared libraries from APEXes listed. With these two, "using" APEXes can omit shared libraries in its APEX bundle and use them from the "providing" APEXes. Note that without corresponding changes in ld.config.txt, this won't work.(The linker namespaces should be configured so that user APEX can access provided libs.) Bug: 136975105 Test: m nothing (this will trigger soong's test) Change-Id: Iec6f9f67bcbde01145acc383f862ba21c8197536 --- apex/apex.go | 42 +++++++++++ apex/apex_test.go | 178 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 215 insertions(+), 5 deletions(-) diff --git a/apex/apex.go b/apex/apex.go index f683f9693..41728d11c 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -111,6 +111,7 @@ var ( testTag = dependencyTag{name: "test"} keyTag = dependencyTag{name: "key"} certificateTag = dependencyTag{name: "certificate"} + usesTag = dependencyTag{name: "uses"} ) func init() { @@ -147,6 +148,7 @@ func init() { android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.TopDown("apex_deps", apexDepsMutator) ctx.BottomUp("apex", apexMutator).Parallel() + ctx.BottomUp("apex_uses", apexUsesMutator).Parallel() }) } @@ -187,6 +189,11 @@ func apexMutator(mctx android.BottomUpMutatorContext) { mctx.CreateVariations(apexBundleName) } } +func apexUsesMutator(mctx android.BottomUpMutatorContext) { + if ab, ok := mctx.Module().(*apexBundle); ok { + mctx.AddFarVariationDependencies(nil, usesTag, ab.properties.Uses...) + } +} type apexNativeDependencies struct { // List of native libraries @@ -272,6 +279,12 @@ type apexBundleProperties struct { // List of sanitizer names that this APEX is enabled for SanitizerNames []string `blueprint:"mutated"` + + // Indicates this APEX provides C++ shared libaries to other APEXes. Default: false. + Provide_cpp_shared_libs *bool + + // List of providing APEXes' names so that this APEX can depend on provided shared libraries. + Uses []string } type apexTargetBundleProperties struct { @@ -727,6 +740,30 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case) + // Check if "uses" requirements are met with dependent apexBundles + var providedNativeSharedLibs []string + useVendor := proptools.Bool(a.properties.Use_vendor) + ctx.VisitDirectDepsBlueprint(func(m blueprint.Module) { + if ctx.OtherModuleDependencyTag(m) != usesTag { + return + } + otherName := ctx.OtherModuleName(m) + other, ok := m.(*apexBundle) + if !ok { + ctx.PropertyErrorf("uses", "%q is not a provider", otherName) + return + } + if proptools.Bool(other.properties.Use_vendor) != useVendor { + ctx.PropertyErrorf("use_vendor", "%q has different value of use_vendor", otherName) + return + } + if !proptools.Bool(other.properties.Provide_cpp_shared_libs) { + ctx.PropertyErrorf("uses", "%q does not provide native_shared_libs", otherName) + return + } + providedNativeSharedLibs = append(providedNativeSharedLibs, other.properties.Native_shared_libs...) + }) + ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { if _, ok := parent.(*apexBundle); ok { // direct dependencies @@ -815,6 +852,11 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // indirect dependencies if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() { if cc, ok := child.(*cc.Module); ok { + if android.InList(cc.Name(), providedNativeSharedLibs) { + // If we're using a shared library which is provided from other APEX, + // don't include it in this APEX + return false + } if !a.Host() && (cc.IsStubs() || cc.HasStubsVariants()) { // If the dependency is a stubs lib, don't include it in this APEX, // but make sure that the lib is installed on the device. diff --git a/apex/apex_test.go b/apex/apex_test.go index b410425ff..94cf19d0a 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -29,7 +29,32 @@ import ( var buildDir string +func testApexError(t *testing.T, pattern, bp string) { + ctx, config := testApexContext(t, bp) + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + if len(errs) > 0 { + android.FailIfNoMatchingErrors(t, pattern, errs) + return + } + _, errs = ctx.PrepareBuildActions(config) + if len(errs) > 0 { + android.FailIfNoMatchingErrors(t, pattern, errs) + return + } + + t.Fatalf("missing expected error %q (0 errors are returned)", pattern) +} + func testApex(t *testing.T, bp string) *android.TestContext { + ctx, config := testApexContext(t, bp) + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + android.FailIfErrored(t, errs) + _, errs = ctx.PrepareBuildActions(config) + android.FailIfErrored(t, errs) + return ctx +} + +func testApexContext(t *testing.T, bp string) (*android.TestContext, android.Config) { config := android.TestArchConfig(buildDir, nil) config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current") config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test") @@ -48,6 +73,7 @@ func testApex(t *testing.T, bp string) *android.TestContext { ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.TopDown("apex_deps", apexDepsMutator) ctx.BottomUp("apex", apexMutator) + ctx.BottomUp("apex_uses", apexUsesMutator) ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel() ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel() }) @@ -168,8 +194,10 @@ func testApex(t *testing.T, bp string) *android.TestContext { "system/sepolicy/apex/myapex-file_contexts": nil, "system/sepolicy/apex/myapex_keytest-file_contexts": nil, "system/sepolicy/apex/otherapex-file_contexts": nil, + "system/sepolicy/apex/commonapex-file_contexts": nil, "mylib.cpp": nil, "mytest.cpp": nil, + "mylib_common.cpp": nil, "myprebuilt": nil, "my_include": nil, "vendor/foo/devkeys/test.x509.pem": nil, @@ -188,12 +216,8 @@ func testApex(t *testing.T, bp string) *android.TestContext { "myapex-arm.apex": nil, "frameworks/base/api/current.txt": nil, }) - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - android.FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - android.FailIfErrored(t, errs) - return ctx + return ctx, config } func setUp() { @@ -210,6 +234,7 @@ func tearDown() { // ensure that 'result' contains 'expected' func ensureContains(t *testing.T, result string, expected string) { + t.Helper() if !strings.Contains(result, expected) { t.Errorf("%q is not found in %q", expected, result) } @@ -217,18 +242,21 @@ func ensureContains(t *testing.T, result string, expected string) { // ensures that 'result' does not contain 'notExpected' func ensureNotContains(t *testing.T, result string, notExpected string) { + t.Helper() if strings.Contains(result, notExpected) { t.Errorf("%q is found in %q", notExpected, result) } } func ensureListContains(t *testing.T, result []string, expected string) { + t.Helper() if !android.InList(expected, result) { t.Errorf("%q is not found in %v", expected, result) } } func ensureListNotContains(t *testing.T, result []string, notExpected string) { + t.Helper() if android.InList(notExpected, result) { t.Errorf("%q is found in %v", notExpected, result) } @@ -789,6 +817,30 @@ func TestUseVendor(t *testing.T) { ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib2.so") } +func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) { + testApexError(t, `dependency "mylib" of "myapex" missing variant:\n.*image:vendor`, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib"], + use_vendor: true, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + } + `) +} + func TestStaticLinking(t *testing.T) { ctx := testApex(t, ` apex { @@ -1321,6 +1373,122 @@ func TestApexWithTests(t *testing.T) { ensureContains(t, copyCmds, "image.apex/bin/test/mytest") } +func TestApexUsesOtherApex(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib"], + uses: ["commonapex"], + } + + apex { + name: "commonapex", + key: "myapex.key", + native_shared_libs: ["libcommon"], + provide_cpp_shared_libs: true, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["libcommon"], + system_shared_libs: [], + stl: "none", + } + + cc_library { + name: "libcommon", + srcs: ["mylib_common.cpp"], + system_shared_libs: [], + stl: "none", + } + `) + + module1 := ctx.ModuleForTests("myapex", "android_common_myapex") + apexRule1 := module1.Rule("apexRule") + copyCmds1 := apexRule1.Args["copy_commands"] + + module2 := ctx.ModuleForTests("commonapex", "android_common_commonapex") + apexRule2 := module2.Rule("apexRule") + copyCmds2 := apexRule2.Args["copy_commands"] + + ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex") + ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_core_shared_commonapex") + ensureContains(t, copyCmds1, "image.apex/lib64/mylib.so") + ensureContains(t, copyCmds2, "image.apex/lib64/libcommon.so") + ensureNotContains(t, copyCmds1, "image.apex/lib64/libcommon.so") +} + +func TestApexUsesFailsIfNotProvided(t *testing.T) { + testApexError(t, `uses: "commonapex" does not provide native_shared_libs`, ` + apex { + name: "myapex", + key: "myapex.key", + uses: ["commonapex"], + } + + apex { + name: "commonapex", + key: "myapex.key", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) + testApexError(t, `uses: "commonapex" is not a provider`, ` + apex { + name: "myapex", + key: "myapex.key", + uses: ["commonapex"], + } + + cc_library { + name: "commonapex", + system_shared_libs: [], + stl: "none", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) +} + +func TestApexUsesFailsIfUseVenderMismatch(t *testing.T) { + testApexError(t, `use_vendor: "commonapex" has different value of use_vendor`, ` + apex { + name: "myapex", + key: "myapex.key", + use_vendor: true, + uses: ["commonapex"], + } + + apex { + name: "commonapex", + key: "myapex.key", + provide_cpp_shared_libs: true, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) +} + func TestMain(m *testing.M) { run := func() int { setUp()