diff options
author | 2024-10-11 12:52:21 -0700 | |
---|---|---|
committer | 2024-11-05 14:31:14 -0800 | |
commit | b614cd441b355e48e59d1f5cd61a800103404151 (patch) | |
tree | 52631bc0f41419e8e6e251d9be1653599349c133 | |
parent | 7ceb14aa4bb17638e3521ca11bd5c671e32adc50 (diff) |
Verify that libraries in apexes don't link to implementations outside the apex
As part of removing some of the complexity in Soong around using
stub vs. implementations for shared library dependencies a syntax
will be added to Soong to allow explicitly selecting stubs vs.
implementation. To avoid incorrect use, add a verification pass
on apexes that ensure that all transitive implementation libraries
used to link native libraries or binaries in the apex are
themselves in the apex.
Bug: 372543712
Test: TestApexVerifyNativeImplementationLibs
Flag: EXEMPT host only
Change-Id: I4aeaca00a359ce97e8f9efd2d8bffb8f9d2dc0df
-rw-r--r-- | android/base_module_context.go | 3 | ||||
-rw-r--r-- | apex/Android.bp | 1 | ||||
-rw-r--r-- | apex/apex.go | 126 | ||||
-rw-r--r-- | apex/apex_test.go | 401 | ||||
-rw-r--r-- | apex/classpath_element_test.go | 5 | ||||
-rw-r--r-- | cc/cc.go | 17 | ||||
-rw-r--r-- | cc/library.go | 1 | ||||
-rw-r--r-- | cc/linkable.go | 10 | ||||
-rw-r--r-- | cc/prebuilt.go | 2 | ||||
-rw-r--r-- | cc/vndk_prebuilt.go | 1 | ||||
-rw-r--r-- | java/app.go | 33 | ||||
-rw-r--r-- | java/java.go | 11 | ||||
-rw-r--r-- | rust/library.go | 3 | ||||
-rw-r--r-- | rust/rust.go | 24 |
14 files changed, 627 insertions, 11 deletions
diff --git a/android/base_module_context.go b/android/base_module_context.go index e24ce9d2b..7fa58766f 100644 --- a/android/base_module_context.go +++ b/android/base_module_context.go @@ -17,6 +17,7 @@ package android import ( "fmt" "regexp" + "slices" "strings" "github.com/google/blueprint" @@ -562,7 +563,7 @@ func (b *baseModuleContext) WalkDepsProxy(visit func(ModuleProxy, ModuleProxy) b } func (b *baseModuleContext) GetWalkPath() []Module { - return b.walkPath + return slices.Clone(b.walkPath) } func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { diff --git a/apex/Android.bp b/apex/Android.bp index 0e2f564b9..870ca7e1e 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -7,6 +7,7 @@ bootstrap_go_package { pkgPath: "android/soong/apex", deps: [ "blueprint", + "blueprint-bpmodify", "soong", "soong-aconfig", "soong-aconfig-codegen", diff --git a/apex/apex.go b/apex/apex.go index 80af9c5b3..587f63fe1 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -20,10 +20,12 @@ import ( "fmt" "path/filepath" "regexp" + "slices" "sort" "strings" "github.com/google/blueprint" + "github.com/google/blueprint/depset" "github.com/google/blueprint/proptools" "android/soong/android" @@ -464,6 +466,12 @@ type apexBundle struct { // GenerateAndroidBuildActions. filesInfo []apexFile + // List of files that were excluded by the unwanted_transitive_deps property. + unwantedTransitiveFilesInfo []apexFile + + // List of files that were excluded due to conflicts with other variants of the same module. + duplicateTransitiveFilesInfo []apexFile + // List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES). makeModulesToInstall []string @@ -1877,6 +1885,14 @@ type visitorContext struct { // visitor skips these from this list of module names unwantedTransitiveDeps []string + + // unwantedTransitiveFilesInfo contains files that would have been in the apex + // except that they were listed in unwantedTransitiveDeps. + unwantedTransitiveFilesInfo []apexFile + + // duplicateTransitiveFilesInfo contains files that would ahve been in the apex + // except that another variant of the same module was already in the apex. + duplicateTransitiveFilesInfo []apexFile } func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { @@ -1887,6 +1903,7 @@ func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { // Needs additional verification for the resulting APEX to ensure that skipped artifacts don't make problems. // For example, DT_NEEDED modules should be found within the APEX unless they are marked in `requiredNativeLibs`. if f.transitiveDep && f.module != nil && android.InList(mctx.OtherModuleName(f.module), vctx.unwantedTransitiveDeps) { + vctx.unwantedTransitiveFilesInfo = append(vctx.unwantedTransitiveFilesInfo, f) continue } dest := filepath.Join(f.installDir, f.builtFile.Base()) @@ -1897,6 +1914,8 @@ func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { mctx.ModuleErrorf("apex file %v is provided by two different files %v and %v", dest, e.builtFile, f.builtFile) return + } else { + vctx.duplicateTransitiveFilesInfo = append(vctx.duplicateTransitiveFilesInfo, f) } // If a module is directly included and also transitively depended on // consider it as directly included. @@ -1911,6 +1930,7 @@ func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { for _, v := range encountered { vctx.filesInfo = append(vctx.filesInfo, v) } + sort.Slice(vctx.filesInfo, func(i, j int) bool { // Sort by destination path so as to ensure consistent ordering even if the source of the files // changes. @@ -2341,6 +2361,8 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // 3) some fields in apexBundle struct are configured a.installDir = android.PathForModuleInstall(ctx, "apex") a.filesInfo = vctx.filesInfo + a.unwantedTransitiveFilesInfo = vctx.unwantedTransitiveFilesInfo + a.duplicateTransitiveFilesInfo = vctx.duplicateTransitiveFilesInfo a.setPayloadFsType(ctx) a.setSystemLibLink(ctx) @@ -2367,6 +2389,8 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.setOutputFiles(ctx) a.enforcePartitionTagOnApexSystemServerJar(ctx) + + a.verifyNativeImplementationLibs(ctx) } // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file @@ -2920,3 +2944,105 @@ func rBcpPackages() map[string][]string { func (a *apexBundle) IsTestApex() bool { return a.testApex } + +// verifyNativeImplementationLibs compares the list of transitive implementation libraries used to link native +// libraries in the apex against the list of implementation libraries in the apex, ensuring that none of the +// libraries in the apex have references to private APIs from outside the apex. +func (a *apexBundle) verifyNativeImplementationLibs(ctx android.ModuleContext) { + var directImplementationLibs android.Paths + var transitiveImplementationLibs []depset.DepSet[android.Path] + + if a.properties.IsCoverageVariant { + return + } + + if a.testApex { + return + } + + if a.UsePlatformApis() { + return + } + + checkApexTag := func(tag blueprint.DependencyTag) bool { + switch tag { + case sharedLibTag, jniLibTag, executableTag, androidAppTag: + return true + default: + return false + } + } + + checkTransitiveTag := func(tag blueprint.DependencyTag) bool { + switch { + case cc.IsSharedDepTag(tag), java.IsJniDepTag(tag), rust.IsRlibDepTag(tag), rust.IsDylibDepTag(tag), checkApexTag(tag): + return true + default: + return false + } + } + + var appEmbeddedJNILibs android.Paths + ctx.VisitDirectDeps(func(dep android.Module) { + tag := ctx.OtherModuleDependencyTag(dep) + if !checkApexTag(tag) { + return + } + if tag == sharedLibTag || tag == jniLibTag { + outputFile := android.OutputFileForModule(ctx, dep, "") + directImplementationLibs = append(directImplementationLibs, outputFile) + } + if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { + transitiveImplementationLibs = append(transitiveImplementationLibs, info.ImplementationDeps) + } + if info, ok := android.OtherModuleProvider(ctx, dep, java.AppInfoProvider); ok { + appEmbeddedJNILibs = append(appEmbeddedJNILibs, info.EmbeddedJNILibs...) + } + }) + + depSet := depset.New(depset.PREORDER, directImplementationLibs, transitiveImplementationLibs) + allImplementationLibs := depSet.ToList() + + allFileInfos := slices.Concat(a.filesInfo, a.unwantedTransitiveFilesInfo, a.duplicateTransitiveFilesInfo) + + for _, lib := range allImplementationLibs { + inApex := slices.ContainsFunc(allFileInfos, func(fi apexFile) bool { + return fi.builtFile == lib + }) + inApkInApex := slices.Contains(appEmbeddedJNILibs, lib) + + if !inApex && !inApkInApex { + ctx.ModuleErrorf("library in apex transitively linked against implementation library %q not in apex", lib) + var depPath []android.Module + ctx.WalkDeps(func(child, parent android.Module) bool { + if depPath != nil { + return false + } + + tag := ctx.OtherModuleDependencyTag(child) + + if parent == ctx.Module() { + if !checkApexTag(tag) { + return false + } + } + + if checkTransitiveTag(tag) { + if android.OutputFileForModule(ctx, child, "") == lib { + depPath = ctx.GetWalkPath() + } + return true + } + + return false + }) + if depPath != nil { + ctx.ModuleErrorf("dependency path:") + for _, m := range depPath { + ctx.ModuleErrorf(" %s", ctx.OtherModuleName(m)) + } + return + } + } + } +} diff --git a/apex/apex_test.go b/apex/apex_test.go index 988c1ce7e..54c1facd5 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -20,6 +20,7 @@ import ( "path/filepath" "reflect" "regexp" + "slices" "sort" "strconv" "strings" @@ -28,6 +29,7 @@ import ( "android/soong/aconfig/codegen" "github.com/google/blueprint" + "github.com/google/blueprint/bpmodify" "github.com/google/blueprint/proptools" "android/soong/android" @@ -225,6 +227,10 @@ var prepareForTestWithMyapex = android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/myapex-file_contexts": nil, }) +var prepareForTestWithOtherapex = android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/otherapex-file_contexts": nil, +}) + // ensure that 'result' equals 'expected' func ensureEquals(t *testing.T, result string, expected string) { t.Helper() @@ -12114,3 +12120,398 @@ func TestFilesystemWithApexDeps(t *testing.T) { fileList := android.ContentFromFileRuleForTests(t, result, partition.Output("fileList")) android.AssertDeepEquals(t, "filesystem with apex", "apex/myapex.apex\n", fileList) } + +func TestApexVerifyNativeImplementationLibs(t *testing.T) { + t.Parallel() + + extractDepenencyPathFromErrors := func(errs []error) []string { + i := slices.IndexFunc(errs, func(err error) bool { + return strings.Contains(err.Error(), "dependency path:") + }) + if i < 0 { + return nil + } + var dependencyPath []string + for _, err := range errs[i+1:] { + s := err.Error() + lastSpace := strings.LastIndexByte(s, ' ') + if lastSpace >= 0 { + dependencyPath = append(dependencyPath, s[lastSpace+1:]) + } + } + return dependencyPath + } + + checkErrors := func(wantDependencyPath []string) func(t *testing.T, result *android.TestResult) { + return func(t *testing.T, result *android.TestResult) { + t.Helper() + if len(result.Errs) == 0 { + t.Fatalf("expected errors") + } + t.Log("found errors:") + for _, err := range result.Errs { + t.Log(err) + } + if g, w := result.Errs[0].Error(), "library in apex transitively linked against implementation library"; !strings.Contains(g, w) { + t.Fatalf("expected error %q, got %q", w, g) + } + dependencyPath := extractDepenencyPathFromErrors(result.Errs) + if g, w := dependencyPath, wantDependencyPath; !slices.Equal(g, w) { + t.Errorf("expected dependency path %q, got %q", w, g) + } + } + } + + addToSharedLibs := func(module, lib string) func(bp *bpmodify.Blueprint) { + return func(bp *bpmodify.Blueprint) { + m := bp.ModulesByName(module) + props, err := m.GetOrCreateProperty(bpmodify.List, "shared_libs") + if err != nil { + panic(err) + } + props.AddStringToList(lib) + } + } + + bpTemplate := ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib"], + rust_dyn_libs: ["libmyrust"], + binaries: ["mybin", "myrustbin"], + jni_libs: ["libjni"], + apps: ["myapp"], + updatable: false, + } + + apex { + name: "otherapex", + key: "myapex.key", + native_shared_libs: ["libotherapex"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["foo.cpp"], + apex_available: ["myapex"], + } + + cc_binary { + name: "mybin", + srcs: ["foo.cpp"], + apex_available: ["myapex"], + } + + rust_library { + name: "libmyrust", + crate_name: "myrust", + srcs: ["src/lib.rs"], + rustlibs: ["libmyrust_transitive_dylib"], + rlibs: ["libmyrust_transitive_rlib"], + apex_available: ["myapex"], + } + + rust_library{ + name: "libmyrust_transitive_dylib", + crate_name: "myrust_transitive_dylib", + srcs: ["src/lib.rs"], + apex_available: ["myapex"], + } + + rust_library { + name: "libmyrust_transitive_rlib", + crate_name: "myrust_transitive_rlib", + srcs: ["src/lib.rs"], + apex_available: ["myapex"], + } + + rust_binary { + name: "myrustbin", + srcs: ["src/main.rs"], + apex_available: ["myapex"], + } + + cc_library { + name: "libbar", + sdk_version: "current", + srcs: ["bar.cpp"], + apex_available: ["myapex"], + stl: "none", + } + + android_app { + name: "myapp", + jni_libs: ["libembeddedjni"], + use_embedded_native_libs: true, + sdk_version: "current", + apex_available: ["myapex"], + } + + cc_library { + name: "libembeddedjni", + sdk_version: "current", + srcs: ["bar.cpp"], + apex_available: ["myapex"], + stl: "none", + } + + cc_library { + name: "libjni", + sdk_version: "current", + srcs: ["bar.cpp"], + apex_available: ["myapex"], + stl: "none", + } + + cc_library { + name: "libotherapex", + sdk_version: "current", + srcs: ["otherapex.cpp"], + apex_available: ["otherapex"], + stubs: { + symbol_file: "libotherapex.map.txt", + versions: ["1", "2", "3"], + }, + stl: "none", + } + + cc_library { + name: "libplatform", + sdk_version: "current", + srcs: ["libplatform.cpp"], + stubs: { + symbol_file: "libplatform.map.txt", + versions: ["1", "2", "3"], + }, + stl: "none", + system_shared_libs: [], + } + ` + + testCases := []struct { + name string + bpModifier func(bp *bpmodify.Blueprint) + dependencyPath []string + }{ + { + name: "library dependency in other apex", + bpModifier: addToSharedLibs("mylib", "libotherapex#impl"), + dependencyPath: []string{"myapex", "mylib", "libotherapex"}, + }, + { + name: "transitive library dependency in other apex", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("mylib", "libbar")(bp) + addToSharedLibs("libbar", "libotherapex#impl")(bp) + }, + dependencyPath: []string{"myapex", "mylib", "libbar", "libotherapex"}, + }, + { + name: "library dependency in platform", + bpModifier: addToSharedLibs("mylib", "libplatform#impl"), + dependencyPath: []string{"myapex", "mylib", "libplatform"}, + }, + { + name: "jni library dependency in other apex", + bpModifier: addToSharedLibs("libjni", "libotherapex#impl"), + dependencyPath: []string{"myapex", "libjni", "libotherapex"}, + }, + { + name: "transitive jni library dependency in other apex", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("libjni", "libbar")(bp) + addToSharedLibs("libbar", "libotherapex#impl")(bp) + }, + dependencyPath: []string{"myapex", "libjni", "libbar", "libotherapex"}, + }, + { + name: "jni library dependency in platform", + bpModifier: addToSharedLibs("libjni", "libplatform#impl"), + dependencyPath: []string{"myapex", "libjni", "libplatform"}, + }, + { + name: "transitive jni library dependency in platform", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("libjni", "libbar")(bp) + addToSharedLibs("libbar", "libplatform#impl")(bp) + }, + dependencyPath: []string{"myapex", "libjni", "libbar", "libplatform"}, + }, + // TODO: embedded JNI in apps should be checked too, but Soong currently just packages the transitive + // JNI libraries even if they came from another apex. + //{ + // name: "app jni library dependency in other apex", + // bpModifier: addToSharedLibs("libembeddedjni", "libotherapex#impl"), + // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libotherapex"}, + //}, + //{ + // name: "transitive app jni library dependency in other apex", + // bpModifier: func(bp *bpmodify.Blueprint) { + // addToSharedLibs("libembeddedjni", "libbar")(bp) + // addToSharedLibs("libbar", "libotherapex#impl")(bp) + // }, + // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libotherapex"}, + //}, + //{ + // name: "app jni library dependency in platform", + // bpModifier: addToSharedLibs("libembeddedjni", "libplatform#impl"), + // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libplatform"}, + //}, + //{ + // name: "transitive app jni library dependency in platform", + // bpModifier: func(bp *bpmodify.Blueprint) { + // addToSharedLibs("libembeddedjni", "libbar")(bp) + // addToSharedLibs("libbar", "libplatform#impl")(bp) + // }, + // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libplatform"}, + //}, + { + name: "binary dependency in other apex", + bpModifier: addToSharedLibs("mybin", "libotherapex#impl"), + dependencyPath: []string{"myapex", "mybin", "libotherapex"}, + }, + { + name: "transitive binary dependency in other apex", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("mybin", "libbar")(bp) + addToSharedLibs("libbar", "libotherapex#impl")(bp) + }, + dependencyPath: []string{"myapex", "mybin", "libbar", "libotherapex"}, + }, + { + name: "binary dependency in platform", + bpModifier: addToSharedLibs("mybin", "libplatform#impl"), + dependencyPath: []string{"myapex", "mybin", "libplatform"}, + }, + { + name: "transitive binary dependency in platform", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("mybin", "libbar")(bp) + addToSharedLibs("libbar", "libplatform#impl")(bp) + }, + dependencyPath: []string{"myapex", "mybin", "libbar", "libplatform"}, + }, + + { + name: "rust library dependency in other apex", + bpModifier: addToSharedLibs("libmyrust", "libotherapex#impl"), + dependencyPath: []string{"myapex", "libmyrust", "libotherapex"}, + }, + { + name: "transitive rust library dependency in other apex", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("libmyrust", "libbar")(bp) + addToSharedLibs("libbar", "libotherapex#impl")(bp) + }, + dependencyPath: []string{"myapex", "libmyrust", "libbar", "libotherapex"}, + }, + { + name: "rust library dependency in platform", + bpModifier: addToSharedLibs("libmyrust", "libplatform#impl"), + dependencyPath: []string{"myapex", "libmyrust", "libplatform"}, + }, + { + name: "transitive rust library dependency in platform", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("libmyrust", "libbar")(bp) + addToSharedLibs("libbar", "libplatform#impl")(bp) + }, + dependencyPath: []string{"myapex", "libmyrust", "libbar", "libplatform"}, + }, + { + name: "transitive rust library dylib dependency in other apex", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("libmyrust_transitive_dylib", "libotherapex#impl")(bp) + }, + dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libotherapex"}, + }, + { + name: "transitive rust library dylib dependency in platform", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("libmyrust_transitive_dylib", "libplatform#impl")(bp) + }, + dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libplatform"}, + }, + { + name: "transitive rust library rlib dependency in other apex", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("libmyrust_transitive_rlib", "libotherapex#impl")(bp) + }, + dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libotherapex"}, + }, + { + name: "transitive rust library rlib dependency in platform", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("libmyrust_transitive_rlib", "libplatform#impl")(bp) + }, + dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libplatform"}, + }, + { + name: "rust binary dependency in other apex", + bpModifier: addToSharedLibs("myrustbin", "libotherapex#impl"), + dependencyPath: []string{"myapex", "myrustbin", "libotherapex"}, + }, + { + name: "transitive rust binary dependency in other apex", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("myrustbin", "libbar")(bp) + addToSharedLibs("libbar", "libotherapex#impl")(bp) + }, + dependencyPath: []string{"myapex", "myrustbin", "libbar", "libotherapex"}, + }, + { + name: "rust binary dependency in platform", + bpModifier: addToSharedLibs("myrustbin", "libplatform#impl"), + dependencyPath: []string{"myapex", "myrustbin", "libplatform"}, + }, + { + name: "transitive rust binary dependency in platform", + bpModifier: func(bp *bpmodify.Blueprint) { + addToSharedLibs("myrustbin", "libbar")(bp) + addToSharedLibs("libbar", "libplatform#impl")(bp) + }, + dependencyPath: []string{"myapex", "myrustbin", "libbar", "libplatform"}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + bp, err := bpmodify.NewBlueprint("", []byte(bpTemplate)) + if err != nil { + t.Fatal(err) + } + if testCase.bpModifier != nil { + func() { + defer func() { + if r := recover(); r != nil { + t.Fatalf("panic in bpModifier: %v", r) + } + }() + testCase.bpModifier(bp) + }() + } + android.GroupFixturePreparers( + android.PrepareForTestWithAndroidBuildComponents, + cc.PrepareForTestWithCcBuildComponents, + java.PrepareForTestWithDexpreopt, + rust.PrepareForTestWithRustDefaultModules, + PrepareForTestWithApexBuildComponents, + prepareForTestWithMyapex, + prepareForTestWithOtherapex, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildId = proptools.StringPtr("TEST.BUILD_ID") + }), + ).ExtendWithErrorHandler(android.FixtureCustomErrorHandler(checkErrors(testCase.dependencyPath))). + RunTestWithBp(t, bp.String()) + }) + } +} diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go index f8e889983..f3671743a 100644 --- a/apex/classpath_element_test.go +++ b/apex/classpath_element_test.go @@ -45,10 +45,7 @@ func TestCreateClasspathElements(t *testing.T) { prepareForTestWithPlatformBootclasspath, prepareForTestWithArtApex, prepareForTestWithMyapex, - // For otherapex. - android.FixtureMergeMockFs(android.MockFS{ - "system/sepolicy/apex/otherapex-file_contexts": nil, - }), + prepareForTestWithOtherapex, java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("foo", "othersdklibrary"), java.FixtureConfigureApexBootJars("myapex:bar"), @@ -213,6 +213,9 @@ type PathDeps struct { // LLNDK headers for the ABI checker to check LLNDK implementation library. LlndkIncludeDirs android.Paths LlndkSystemIncludeDirs android.Paths + + directImplementationDeps android.Paths + transitiveImplementationDeps []depset.DepSet[android.Path] } // LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module @@ -2036,6 +2039,10 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { c.outputFile = android.OptionalPathForPath(outputFile) c.maybeUnhideFromMake() + + android.SetProvider(ctx, ImplementationDepInfoProvider, &ImplementationDepInfo{ + ImplementationDeps: depset.New(depset.PREORDER, deps.directImplementationDeps, deps.transitiveImplementationDeps), + }) } android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()}) @@ -2342,6 +2349,9 @@ func AddSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext, mo if version != "" && canBeOrLinkAgainstVersionVariants(mod) { // Version is explicitly specified. i.e. libFoo#30 + if version == "impl" { + version = "" + } variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version}) if tag, ok := depTag.(libraryDependencyTag); ok { tag.explicitlyVersioned = true @@ -3040,6 +3050,13 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { linkFile = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary) depFile = sharedLibraryInfo.TableOfContents + if !sharedLibraryInfo.IsStubs { + depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, "")) + if info, ok := android.OtherModuleProvider(ctx, dep, ImplementationDepInfoProvider); ok { + depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps) + } + } + ptr = &depPaths.SharedLibs switch libDepTag.Order { case earlyLibraryDependency: diff --git a/cc/library.go b/cc/library.go index 1f2161494..7dffa7266 100644 --- a/cc/library.go +++ b/cc/library.go @@ -1194,6 +1194,7 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext, SharedLibrary: unstrippedOutputFile, TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering, Target: ctx.Target(), + IsStubs: library.buildStubs(), }) addStubDependencyProviders(ctx) diff --git a/cc/linkable.go b/cc/linkable.go index cd33e2855..ef204eb91 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -317,7 +317,9 @@ type SharedLibraryInfo struct { SharedLibrary android.Path Target android.Target - TableOfContents android.OptionalPath + TableOfContents android.OptionalPath + IsStubs bool + ImplementationDeps depset.DepSet[string] // should be obtained from static analogue TransitiveStaticLibrariesForOrdering depset.DepSet[android.Path] @@ -386,3 +388,9 @@ type FlagExporterInfo struct { } var FlagExporterInfoProvider = blueprint.NewProvider[FlagExporterInfo]() + +var ImplementationDepInfoProvider = blueprint.NewProvider[*ImplementationDepInfo]() + +type ImplementationDepInfo struct { + ImplementationDeps depset.DepSet[android.Path] +} diff --git a/cc/prebuilt.go b/cc/prebuilt.go index 7c8729709..ba4c662c6 100644 --- a/cc/prebuilt.go +++ b/cc/prebuilt.go @@ -221,6 +221,7 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext, Target: ctx.Target(), TableOfContents: p.tocFile, + IsStubs: p.buildStubs(), }) return outputFile @@ -232,6 +233,7 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext, android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{ SharedLibrary: latestStub, Target: ctx.Target(), + IsStubs: true, }) return latestStub diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go index e7dff4012..4a2adf00a 100644 --- a/cc/vndk_prebuilt.go +++ b/cc/vndk_prebuilt.go @@ -166,6 +166,7 @@ func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext, Target: ctx.Target(), TableOfContents: p.tocFile, + IsStubs: false, }) p.libraryDecorator.flagExporter.setProvider(ctx) diff --git a/java/app.go b/java/app.go index fed971a55..15b1114a7 100644 --- a/java/app.go +++ b/java/app.go @@ -67,6 +67,9 @@ type AppInfo struct { // TestHelperApp is true if the module is a android_test_helper_app TestHelperApp bool + + // EmbeddedJNILibs is the list of paths to JNI libraries that were embedded in the APK. + EmbeddedJNILibs android.Paths } var AppInfoProvider = blueprint.NewProvider[*AppInfo]() @@ -405,9 +408,18 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.checkEmbedJnis(ctx) a.generateAndroidBuildActions(ctx) a.generateJavaUsedByApex(ctx) + + var embeddedJniLibs []android.Path + + if a.embeddedJniLibs { + for _, jni := range a.jniLibs { + embeddedJniLibs = append(embeddedJniLibs, jni.path) + } + } android.SetProvider(ctx, AppInfoProvider, &AppInfo{ - Updatable: Bool(a.appProperties.Updatable), - TestHelperApp: false, + Updatable: Bool(a.appProperties.Updatable), + TestHelperApp: false, + EmbeddedJNILibs: embeddedJniLibs, }) } @@ -1070,12 +1082,12 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx) } jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps, - checkNativeSdkVersion, func(dep cc.LinkableInterface) bool { - return !dep.IsNdk(ctx.Config()) && !dep.IsStubs() - }) + checkNativeSdkVersion, func(dep cc.LinkableInterface) bool { return !dep.IsNdk(ctx.Config()) && !dep.IsStubs() }) var certificates []Certificate + var directImplementationDeps android.Paths + var transitiveImplementationDeps []depset.DepSet[android.Path] ctx.VisitDirectDeps(func(module android.Module) { otherName := ctx.OtherModuleName(module) tag := ctx.OtherModuleDependencyTag(module) @@ -1087,7 +1099,18 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName) } } + + if IsJniDepTag(tag) { + directImplementationDeps = append(directImplementationDeps, android.OutputFileForModule(ctx, module, "")) + if info, ok := android.OtherModuleProvider(ctx, module, cc.ImplementationDepInfoProvider); ok { + transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps) + } + } }) + android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{ + ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps), + }) + return jniLib, prebuiltJniPackages, certificates } diff --git a/java/java.go b/java/java.go index 85024e199..ac4f49619 100644 --- a/java/java.go +++ b/java/java.go @@ -1621,6 +1621,8 @@ func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, j.data = append(j.data, android.OutputFileForModule(ctx, dep, "")) }) + var directImplementationDeps android.Paths + var transitiveImplementationDeps []depset.DepSet[android.Path] ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) { sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider) if sharedLibInfo.SharedLibrary != nil { @@ -1639,11 +1641,20 @@ func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, Output: relocatedLib, }) j.data = append(j.data, relocatedLib) + + directImplementationDeps = append(directImplementationDeps, android.OutputFileForModule(ctx, dep, "")) + if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { + transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps) + } } else { ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep)) } }) + android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{ + ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps), + }) + j.Library.GenerateAndroidBuildActions(ctx) } diff --git a/rust/library.go b/rust/library.go index 20cd2af7d..bd3359b6a 100644 --- a/rust/library.go +++ b/rust/library.go @@ -617,6 +617,9 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa TableOfContents: android.OptionalPathForPath(tocFile), SharedLibrary: outputFile, Target: ctx.Target(), + // TODO: when rust supports stubs uses the stubs state rather than inferring it from + // apex_exclude. + IsStubs: Bool(library.Properties.Apex_exclude), }) } diff --git a/rust/rust.go b/rust/rust.go index 6b91ccb25..0b26f92a4 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -454,6 +454,9 @@ type PathDeps struct { // Paths to generated source files SrcDeps android.Paths srcProviderFiles android.Paths + + directImplementationDeps android.Paths + transitiveImplementationDeps []depset.DepSet[android.Path] } type RustLibraries []RustLibrary @@ -990,6 +993,10 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } + android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{ + ImplementationDeps: depset.New(depset.PREORDER, deps.directImplementationDeps, deps.transitiveImplementationDeps), + }) + ctx.Phony("rust", ctx.RustModule().OutputFile().Path()) } @@ -1243,6 +1250,11 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName) mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName)) + depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, "")) + if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { + depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps) + } + case depTag == rlibDepTag: rlib, ok := rustDep.compiler.(libraryInterface) if !ok || !rlib.rlib() { @@ -1258,6 +1270,11 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...) depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path())) + // rlibs are not installed, so don't add the output file to directImplementationDeps + if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { + depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps) + } + case depTag == procMacroDepTag: directProcMacroDeps = append(directProcMacroDeps, rustDep) mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName) @@ -1390,6 +1407,13 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { // dependency crosses the APEX boundaries). sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep) + if !sharedLibraryInfo.IsStubs { + depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, "")) + if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { + depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps) + } + } + // Re-get linkObject as ChooseStubOrImpl actually tells us which // object (either from stub or non-stub) to use. linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary) |