diff --git a/android/util.go b/android/util.go index 97bec102f..e02cca1df 100644 --- a/android/util.go +++ b/android/util.go @@ -199,6 +199,13 @@ func LastUniqueStrings(list []string) []string { return list[totalSkip:] } +// SortedUniqueStrings returns what the name says +func SortedUniqueStrings(list []string) []string { + unique := FirstUniqueStrings(list) + sort.Strings(unique) + return unique +} + // checkCalledFromInit panics if a Go package's init function is not on the // call stack. func checkCalledFromInit() { diff --git a/apex/apex.go b/apex/apex.go index 441911b3a..5ca9d7d81 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -46,6 +46,14 @@ var ( Description: "fs_config ${out}", }, "ro_paths", "exec_paths") + injectApexDependency = pctx.StaticRule("injectApexDependency", blueprint.RuleParams{ + Command: `rm -f $out && ${jsonmodify} $in ` + + `-a provideNativeLibs ${provideNativeLibs} ` + + `-a requireNativeLibs ${requireNativeLibs} -o $out`, + CommandDeps: []string{"${jsonmodify}"}, + Description: "Inject dependency into ${out}", + }, "provideNativeLibs", "requireNativeLibs") + // TODO(b/113233103): make sure that file_contexts is sane, i.e., validate // against the binary policy using sefcontext_compiler -p . @@ -139,6 +147,7 @@ func init() { pctx.HostBinToolVariable("soong_zip", "soong_zip") pctx.HostBinToolVariable("zip2zip", "zip2zip") pctx.HostBinToolVariable("zipalign", "zipalign") + pctx.HostBinToolVariable("jsonmodify", "jsonmodify") android.RegisterModuleType("apex", apexBundleFactory) android.RegisterModuleType("apex_test", testApexBundleFactory) @@ -427,6 +436,9 @@ type apexBundle struct { flattened bool testApex bool + + // intermediate path for apex_manifest.json + manifestOut android.WritablePath } func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, @@ -751,6 +763,10 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case) + // native lib dependencies + var provideNativeLibs []string + var requireNativeLibs []string + // Check if "uses" requirements are met with dependent apexBundles var providedNativeSharedLibs []string useVendor := proptools.Bool(a.properties.Use_vendor) @@ -783,6 +799,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { switch depTag { case sharedLibTag: if cc, ok := child.(*cc.Module); ok { + if cc.HasStubsVariants() { + provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base()) + } fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs) filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil}) return true @@ -894,6 +913,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.externalDeps) { a.externalDeps = append(a.externalDeps, cc.Name()) } + requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base()) // Don't track further return false } @@ -954,6 +974,21 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.installDir = android.PathForModuleInstall(ctx, "apex") a.filesInfo = filesInfo + a.manifestOut = android.PathForModuleOut(ctx, "apex_manifest.json") + // put dependency({provide|require}NativeLibs) in apex_manifest.json + manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json")) + provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs) + requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs)) + ctx.Build(pctx, android.BuildParams{ + Rule: injectApexDependency, + Input: manifestSrc, + Output: a.manifestOut, + Args: map[string]string{ + "provideNativeLibs": strings.Join(provideNativeLibs, " "), + "requireNativeLibs": strings.Join(requireNativeLibs, " "), + }, + }) + if a.apexTypes.zip() { a.buildUnflattenedApex(ctx, zipApex) } @@ -1001,8 +1036,6 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap a.container_private_key_file = key } - manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json")) - var abis []string for _, target := range ctx.MultiTargets() { if len(target.Arch.Abi) > 0 { @@ -1032,7 +1065,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap } } implicitInputs := append(android.Paths(nil), filesToCopy...) - implicitInputs = append(implicitInputs, manifest) + implicitInputs = append(implicitInputs, a.manifestOut) outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String() prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin") @@ -1127,7 +1160,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, "image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(), "copy_commands": strings.Join(copyCommands, " && "), - "manifest": manifest.String(), + "manifest": a.manifestOut.String(), "file_contexts": fileContexts.String(), "canned_fs_config": cannedFsConfig.String(), "key": a.private_key_file.String(), @@ -1165,7 +1198,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType ap "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir, "image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(), "copy_commands": strings.Join(copyCommands, " && "), - "manifest": manifest.String(), + "manifest": a.manifestOut.String(), }, }) } @@ -1196,16 +1229,7 @@ func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) { if a.installable() { // For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along // with other ordinary files. - manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json")) - - // rename to apex_manifest.json - copiedManifest := android.PathForModuleOut(ctx, "apex_manifest.json") - ctx.Build(pctx, android.BuildParams{ - Rule: android.Cp, - Input: manifest, - Output: copiedManifest, - }) - a.filesInfo = append(a.filesInfo, apexFile{copiedManifest, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil}) + a.filesInfo = append(a.filesInfo, apexFile{a.manifestOut, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil}) // rename to apex_pubkey copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey") diff --git a/apex/apex_test.go b/apex/apex_test.go index cecdaaf39..38d2bf2f7 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -270,6 +270,13 @@ func ensureListNotContains(t *testing.T, result []string, notExpected string) { } } +func ensureListEmpty(t *testing.T, result []string) { + t.Helper() + if len(result) > 0 { + t.Errorf("%q is expected to be empty", result) + } +} + // Minimal test func TestBasicApex(t *testing.T) { ctx, _ := testApex(t, ` @@ -1060,6 +1067,109 @@ func TestHeaderLibsDependency(t *testing.T) { ensureContains(t, cFlags, "-Imy_include") } +func TestDependenciesInApexManifest(t *testing.T) { + ctx, _ := testApex(t, ` + apex { + name: "myapex_nodep", + key: "myapex.key", + native_shared_libs: ["lib_nodep"], + compile_multilib: "both", + file_contexts: "myapex", + } + + apex { + name: "myapex_dep", + key: "myapex.key", + native_shared_libs: ["lib_dep"], + compile_multilib: "both", + file_contexts: "myapex", + } + + apex { + name: "myapex_provider", + key: "myapex.key", + native_shared_libs: ["libfoo"], + compile_multilib: "both", + file_contexts: "myapex", + } + + apex { + name: "myapex_selfcontained", + key: "myapex.key", + native_shared_libs: ["lib_dep", "libfoo"], + compile_multilib: "both", + file_contexts: "myapex", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "lib_nodep", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + } + + cc_library { + name: "lib_dep", + srcs: ["mylib.cpp"], + shared_libs: ["libfoo"], + system_shared_libs: [], + stl: "none", + } + + cc_library { + name: "libfoo", + srcs: ["mytest.cpp"], + stubs: { + versions: ["1"], + }, + system_shared_libs: [], + stl: "none", + } + `) + + names := func(s string) (ns []string) { + for _, n := range strings.Split(s, " ") { + if len(n) > 0 { + ns = append(ns, n) + } + } + return + } + + var injectRule android.TestingBuildParams + var provideNativeLibs, requireNativeLibs []string + + injectRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("injectApexDependency") + provideNativeLibs = names(injectRule.Args["provideNativeLibs"]) + requireNativeLibs = names(injectRule.Args["requireNativeLibs"]) + ensureListEmpty(t, provideNativeLibs) + ensureListEmpty(t, requireNativeLibs) + + injectRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("injectApexDependency") + provideNativeLibs = names(injectRule.Args["provideNativeLibs"]) + requireNativeLibs = names(injectRule.Args["requireNativeLibs"]) + ensureListEmpty(t, provideNativeLibs) + ensureListContains(t, requireNativeLibs, "libfoo.so") + + injectRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("injectApexDependency") + provideNativeLibs = names(injectRule.Args["provideNativeLibs"]) + requireNativeLibs = names(injectRule.Args["requireNativeLibs"]) + ensureListContains(t, provideNativeLibs, "libfoo.so") + ensureListEmpty(t, requireNativeLibs) + + injectRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("injectApexDependency") + provideNativeLibs = names(injectRule.Args["provideNativeLibs"]) + requireNativeLibs = names(injectRule.Args["requireNativeLibs"]) + ensureListContains(t, provideNativeLibs, "libfoo.so") + ensureListEmpty(t, requireNativeLibs) +} + func TestNonTestApex(t *testing.T) { ctx, _ := testApex(t, ` apex {