libclang_rt_prebuilt_library_shared mixed builds
Also fix nil deref in setting $LOCAL_SOONG_UNSTRIPPED_BINARY
Bug: 201802518
Test: request_type_test.go
Test: prebuilt_test.go:TestPrebuiltLibrarySharedWithBazel
Test: mixed_{libc,droid}.sh
Change-Id: I22afb56c4b42d3412c2b2e1f079f1bcf8f3129a7
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 41f9886..4dc0e4c 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -14,6 +14,7 @@
type CcInfo struct {
OutputFiles []string
CcObjectFiles []string
+ CcSharedLibraryFiles []string
CcStaticLibraryFiles []string
Includes []string
SystemIncludes []string
@@ -128,28 +129,43 @@
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 @@
// 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 d3bcb45..606e285 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -63,6 +63,8 @@
}
func TestGetCcInfoParseResults(t *testing.T) {
+ const expectedSplits = 10
+ noResult := strings.Repeat("|", expectedSplits-1)
testCases := []struct {
description string
input string
@@ -71,10 +73,11 @@
}{
{
description: "no result",
- input: "||||||||",
+ input: noResult,
expectedOutput: CcInfo{
OutputFiles: []string{},
CcObjectFiles: []string{},
+ CcSharedLibraryFiles: []string{},
CcStaticLibraryFiles: []string{},
Includes: []string{},
SystemIncludes: []string{},
@@ -86,10 +89,11 @@
},
{
description: "only output",
- input: "test||||||||",
+ input: "test" + noResult,
expectedOutput: CcInfo{
OutputFiles: []string{"test"},
CcObjectFiles: []string{},
+ CcSharedLibraryFiles: []string{},
CcStaticLibraryFiles: []string{},
Includes: []string{},
SystemIncludes: []string{},
@@ -100,11 +104,37 @@
},
},
{
+ 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 @@
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 800b58f..f6fe6f7 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -241,7 +241,7 @@
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 feae812..c928ed9 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -298,6 +298,7 @@
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 @@
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 c8f8103..94f75fe 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -19,7 +19,7 @@
"testing"
"android/soong/android"
-
+ "android/soong/bazel/cquery"
"github.com/google/blueprint"
)
@@ -378,3 +378,74 @@
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())
+}