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
diff --git a/android/base_module_context.go b/android/base_module_context.go
index e24ce9d..7fa5876 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -17,6 +17,7 @@
import (
"fmt"
"regexp"
+ "slices"
"strings"
"github.com/google/blueprint"
@@ -562,7 +563,7 @@
}
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 0e2f564..870ca7e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -7,6 +7,7 @@
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 80af9c5..587f63f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -20,10 +20,12 @@
"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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 (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 988c1ce..54c1fac 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -20,6 +20,7 @@
"path/filepath"
"reflect"
"regexp"
+ "slices"
"sort"
"strconv"
"strings"
@@ -28,6 +29,7 @@
"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 @@
"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 @@
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 f8e8899..f367174 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -45,10 +45,7 @@
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"),
diff --git a/cc/cc.go b/cc/cc.go
index 4cda633..d187259 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -213,6 +213,9 @@
// 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 @@
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 @@
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 @@
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 1f21614..7dffa72 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1194,6 +1194,7 @@
SharedLibrary: unstrippedOutputFile,
TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering,
Target: ctx.Target(),
+ IsStubs: library.buildStubs(),
})
addStubDependencyProviders(ctx)
diff --git a/cc/linkable.go b/cc/linkable.go
index cd33e28..ef204eb 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -317,7 +317,9 @@
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 @@
}
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 7c87297..ba4c662 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -221,6 +221,7 @@
Target: ctx.Target(),
TableOfContents: p.tocFile,
+ IsStubs: p.buildStubs(),
})
return outputFile
@@ -232,6 +233,7 @@
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 e7dff40..4a2adf0 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -166,6 +166,7 @@
Target: ctx.Target(),
TableOfContents: p.tocFile,
+ IsStubs: false,
})
p.libraryDecorator.flagExporter.setProvider(ctx)
diff --git a/java/app.go b/java/app.go
index fed971a..15b1114 100644
--- a/java/app.go
+++ b/java/app.go
@@ -67,6 +67,9 @@
// 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 @@
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 @@
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 @@
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 85024e1..ac4f496 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1621,6 +1621,8 @@
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 @@
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 20cd2af..bd3359b 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -617,6 +617,9 @@
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 6b91ccb..0b26f92 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -454,6 +454,9 @@
// 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 @@
}
+ 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 @@
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 @@
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 @@
// 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)