diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go index 41f98862c..4dc0e4cd1 100644 --- a/bazel/cquery/request_type.go +++ b/bazel/cquery/request_type.go @@ -14,6 +14,7 @@ var ( type CcInfo struct { OutputFiles []string CcObjectFiles []string + CcSharedLibraryFiles []string CcStaticLibraryFiles []string Includes []string SystemIncludes []string @@ -128,28 +129,43 @@ else: if linker_input.owner == target.label: rootStaticArchives.append(library.static_library.path) -rootDynamicLibraries = [] +sharedLibraries = [] +rootSharedLibraries = [] shared_info_tag = "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" if shared_info_tag in providers(target): shared_info = providers(target)[shared_info_tag] for lib in shared_info.linker_input.libraries: - rootDynamicLibraries += [lib.dynamic_library.path] + path = lib.dynamic_library.path + rootSharedLibraries += [path] + sharedLibraries.append(path) +else: + for linker_input in linker_inputs: + for library in linker_input.libraries: + if library.dynamic_library: + path = library.dynamic_library.path + sharedLibraries.append(path) + if linker_input.owner == target.label: + rootSharedLibraries.append(path) toc_file = "" toc_file_tag = "//build/bazel/rules:generate_toc.bzl%CcTocInfo" if toc_file_tag in providers(target): toc_file = providers(target)[toc_file_tag].toc.path +else: + # NOTE: It's OK if there's no ToC, as Soong just uses it for optimization + pass returns = [ outputFiles, - staticLibraries, ccObjectFiles, + sharedLibraries, + staticLibraries, includes, system_includes, headers, rootStaticArchives, - rootDynamicLibraries, + rootSharedLibraries, [toc_file] ] @@ -160,28 +176,35 @@ return "|".join([", ".join(r) for r in returns])` // The given rawString must correspond to the string output which was created by evaluating the // Starlark given in StarlarkFunctionBody. func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { - var outputFiles []string - var ccObjects []string - + const expectedLen = 10 splitString := strings.Split(rawString, "|") - if expectedLen := 9; len(splitString) != expectedLen { + if len(splitString) != expectedLen { return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString) } outputFilesString := splitString[0] - ccStaticLibrariesString := splitString[1] - ccObjectsString := splitString[2] - outputFiles = splitOrEmpty(outputFilesString, ", ") + ccObjectsString := splitString[1] + ccSharedLibrariesString := splitString[2] + ccStaticLibrariesString := splitString[3] + includesString := splitString[4] + systemIncludesString := splitString[5] + headersString := splitString[6] + rootStaticArchivesString := splitString[7] + rootDynamicLibrariesString := splitString[8] + tocFile := splitString[9] // NOTE: Will be the empty string if there wasn't + + outputFiles := splitOrEmpty(outputFilesString, ", ") + ccObjects := splitOrEmpty(ccObjectsString, ", ") + ccSharedLibraries := splitOrEmpty(ccSharedLibrariesString, ", ") ccStaticLibraries := splitOrEmpty(ccStaticLibrariesString, ", ") - ccObjects = splitOrEmpty(ccObjectsString, ", ") - includes := splitOrEmpty(splitString[3], ", ") - systemIncludes := splitOrEmpty(splitString[4], ", ") - headers := splitOrEmpty(splitString[5], ", ") - rootStaticArchives := splitOrEmpty(splitString[6], ", ") - rootDynamicLibraries := splitOrEmpty(splitString[7], ", ") - tocFile := splitString[8] // NOTE: Will be the empty string if there wasn't + includes := splitOrEmpty(includesString, ", ") + systemIncludes := splitOrEmpty(systemIncludesString, ", ") + headers := splitOrEmpty(headersString, ", ") + rootStaticArchives := splitOrEmpty(rootStaticArchivesString, ", ") + rootDynamicLibraries := splitOrEmpty(rootDynamicLibrariesString, ", ") return CcInfo{ OutputFiles: outputFiles, CcObjectFiles: ccObjects, + CcSharedLibraryFiles: ccSharedLibraries, CcStaticLibraryFiles: ccStaticLibraries, Includes: includes, SystemIncludes: systemIncludes, diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go index d3bcb45f4..606e285bb 100644 --- a/bazel/cquery/request_type_test.go +++ b/bazel/cquery/request_type_test.go @@ -63,6 +63,8 @@ func TestGetPythonBinaryParseResults(t *testing.T) { } func TestGetCcInfoParseResults(t *testing.T) { + const expectedSplits = 10 + noResult := strings.Repeat("|", expectedSplits-1) testCases := []struct { description string input string @@ -71,10 +73,11 @@ func TestGetCcInfoParseResults(t *testing.T) { }{ { description: "no result", - input: "||||||||", + input: noResult, expectedOutput: CcInfo{ OutputFiles: []string{}, CcObjectFiles: []string{}, + CcSharedLibraryFiles: []string{}, CcStaticLibraryFiles: []string{}, Includes: []string{}, SystemIncludes: []string{}, @@ -86,10 +89,11 @@ func TestGetCcInfoParseResults(t *testing.T) { }, { description: "only output", - input: "test||||||||", + input: "test" + noResult, expectedOutput: CcInfo{ OutputFiles: []string{"test"}, CcObjectFiles: []string{}, + CcSharedLibraryFiles: []string{}, CcStaticLibraryFiles: []string{}, Includes: []string{}, SystemIncludes: []string{}, @@ -99,12 +103,38 @@ func TestGetCcInfoParseResults(t *testing.T) { TocFile: "", }, }, + { + description: "only ToC", + input: noResult + "test", + expectedOutput: CcInfo{ + OutputFiles: []string{}, + CcObjectFiles: []string{}, + CcSharedLibraryFiles: []string{}, + CcStaticLibraryFiles: []string{}, + Includes: []string{}, + SystemIncludes: []string{}, + Headers: []string{}, + RootStaticArchives: []string{}, + RootDynamicLibraries: []string{}, + TocFile: "test", + }, + }, { description: "all items set", - input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|dir/subdir/hdr.h|rootstaticarchive1|rootdynamiclibrary1|lib.so.toc", + input: "out1, out2" + + "|object1, object2" + + "|shared_lib1, shared_lib2" + + "|static_lib1, static_lib2" + + "|., dir/subdir" + + "|system/dir, system/other/dir" + + "|dir/subdir/hdr.h" + + "|rootstaticarchive1" + + "|rootdynamiclibrary1" + + "|lib.so.toc", expectedOutput: CcInfo{ OutputFiles: []string{"out1", "out2"}, CcObjectFiles: []string{"object1", "object2"}, + CcSharedLibraryFiles: []string{"shared_lib1", "shared_lib2"}, CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"}, Includes: []string{".", "dir/subdir"}, SystemIncludes: []string{"system/dir", "system/other/dir"}, @@ -118,22 +148,22 @@ func TestGetCcInfoParseResults(t *testing.T) { description: "too few result splits", input: "|", expectedOutput: CcInfo{}, - expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 9, []string{"", ""}), + expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, []string{"", ""}), }, { description: "too many result splits", - input: strings.Repeat("|", 50), + input: strings.Repeat("|", expectedSplits+1), // 2 too many expectedOutput: CcInfo{}, - expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 9, make([]string, 51)), + expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)), }, } for _, tc := range testCases { actualOutput, err := GetCcInfo.ParseResult(tc.input) if (err == nil && tc.expectedErrorMessage != "") || (err != nil && err.Error() != tc.expectedErrorMessage) { - t.Errorf("%q: expected Error %s, got %s", tc.description, tc.expectedErrorMessage, err) + t.Errorf("%q:\nexpected Error %s\n, got %s", tc.description, tc.expectedErrorMessage, err) } else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) { - t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput) + t.Errorf("%q:\n expected %#v\n!= actual %#v", tc.description, tc.expectedOutput, actualOutput) } } } diff --git a/cc/androidmk.go b/cc/androidmk.go index 800b58f33..f6fe6f7bd 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -241,7 +241,7 @@ func (library *libraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries entries.Class = "SHARED_LIBRARIES" entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetString("LOCAL_SOONG_TOC", library.toc().String()) - if !library.buildStubs() { + if !library.buildStubs() && library.unstrippedOutputFile != nil { entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", library.unstrippedOutputFile.String()) } if len(library.Properties.Overrides) > 0 { diff --git a/cc/prebuilt.go b/cc/prebuilt.go index feae81266..c928ed960 100644 --- a/cc/prebuilt.go +++ b/cc/prebuilt.go @@ -298,6 +298,7 @@ func NewPrebuiltSharedLibrary(hod android.HostOrDeviceSupported) (*Module, *libr module, library := NewPrebuiltLibrary(hod, "srcs") library.BuildOnlyShared() module.bazelable = true + module.bazelHandler = &prebuiltSharedLibraryBazelHandler{module: module, library: library} // Prebuilt shared libraries can be included in APEXes android.InitApexModule(module) @@ -426,6 +427,69 @@ func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx androi return true } +type prebuiltSharedLibraryBazelHandler struct { + android.BazelHandler + + module *Module + library *libraryDecorator +} + +func (h *prebuiltSharedLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool { + bazelCtx := ctx.Config().BazelContext + ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx)) + if err != nil { + ctx.ModuleErrorf("Error getting Bazel CcInfo for %s: %s", label, err) + } + if !ok { + return false + } + sharedLibs := ccInfo.CcSharedLibraryFiles + if len(sharedLibs) != 1 { + ctx.ModuleErrorf("expected 1 shared library from bazel target %s, got %q", label, sharedLibs) + return false + } + + // TODO(b/184543518): cc_prebuilt_library_shared may have properties for re-exporting flags + + // TODO(eakammer):Add stub-related flags if this library is a stub library. + // h.library.exportVersioningMacroIfNeeded(ctx) + + // Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise + // validation will fail. For now, set this to an empty list. + // TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation. + h.library.collectedSnapshotHeaders = android.Paths{} + + if len(sharedLibs) == 0 { + h.module.outputFile = android.OptionalPath{} + return true + } + + out := android.PathForBazelOut(ctx, sharedLibs[0]) + h.module.outputFile = android.OptionalPathForPath(out) + + // FIXME(b/214600441): We don't yet strip prebuilt shared libraries + h.library.unstrippedOutputFile = out + + var toc android.Path + if len(ccInfo.TocFile) > 0 { + toc = android.PathForBazelOut(ctx, ccInfo.TocFile) + } else { + toc = out // Just reuse `out` so ninja still gets an input but won't matter + } + + info := SharedLibraryInfo{ + SharedLibrary: out, + TableOfContents: android.OptionalPathForPath(toc), + Target: ctx.Target(), + } + ctx.SetProvider(SharedLibraryInfoProvider, info) + + h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo) + h.module.maybeUnhideFromMake() + + return true +} + func (p *prebuiltObjectLinker) prebuilt() *android.Prebuilt { return &p.Prebuilt } diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go index c8f8103b0..94f75fe07 100644 --- a/cc/prebuilt_test.go +++ b/cc/prebuilt_test.go @@ -19,7 +19,7 @@ import ( "testing" "android/soong/android" - + "android/soong/bazel/cquery" "github.com/google/blueprint" ) @@ -378,3 +378,74 @@ func TestPrebuiltLibrarySanitized(t *testing.T) { static2 = ctx.ModuleForTests("libtest_static", "android_arm64_armv8-a_static_hwasan").Module().(*Module) assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a") } + +func TestPrebuiltLibrarySharedWithBazelWithoutToc(t *testing.T) { + const bp = ` +cc_prebuilt_library_shared { + name: "foo", + srcs: ["foo.so"], + bazel_module: { label: "//foo/bar:bar" }, +}` + outBaseDir := "outputbase" + config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) + config.BazelContext = android.MockBazelContext{ + OutputBaseDir: outBaseDir, + LabelToCcInfo: map[string]cquery.CcInfo{ + "//foo/bar:bar": cquery.CcInfo{ + CcSharedLibraryFiles: []string{"foo.so"}, + }, + }, + } + ctx := testCcWithConfig(t, config) + sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module() + pathPrefix := outBaseDir + "/execroot/__main__/" + + info := ctx.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo) + android.AssertPathRelativeToTopEquals(t, "prebuilt shared library", + pathPrefix+"foo.so", info.SharedLibrary) + android.AssertPathRelativeToTopEquals(t, "prebuilt's 'nullary' ToC", + pathPrefix+"foo.so", info.TableOfContents.Path()) + + outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("") + if err != nil { + t.Errorf("Unexpected error getting cc_object outputfiles %s", err) + } + expectedOutputFiles := []string{pathPrefix + "foo.so"} + android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings()) +} + +func TestPrebuiltLibrarySharedWithBazelWithToc(t *testing.T) { + const bp = ` +cc_prebuilt_library_shared { + name: "foo", + srcs: ["foo.so"], + bazel_module: { label: "//foo/bar:bar" }, +}` + outBaseDir := "outputbase" + config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) + config.BazelContext = android.MockBazelContext{ + OutputBaseDir: outBaseDir, + LabelToCcInfo: map[string]cquery.CcInfo{ + "//foo/bar:bar": cquery.CcInfo{ + CcSharedLibraryFiles: []string{"foo.so"}, + TocFile: "toc", + }, + }, + } + ctx := testCcWithConfig(t, config) + sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module() + pathPrefix := outBaseDir + "/execroot/__main__/" + + info := ctx.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo) + android.AssertPathRelativeToTopEquals(t, "prebuilt shared library's ToC", + pathPrefix+"toc", info.TableOfContents.Path()) + android.AssertPathRelativeToTopEquals(t, "prebuilt shared library", + pathPrefix+"foo.so", info.SharedLibrary) + + outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("") + if err != nil { + t.Errorf("Unexpected error getting cc_object outputfiles %s", err) + } + expectedOutputFiles := []string{pathPrefix + "foo.so"} + android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings()) +}