diff --git a/android/bazel.go b/android/bazel.go index b9d707090..992d8aa27 100644 --- a/android/bazel.go +++ b/android/bazel.go @@ -218,11 +218,7 @@ var ( // Per-module denylist to opt modules out of mixed builds. Such modules will // still be generated via bp2build. - mixedBuildsDisabledList = []string{ - "libc", // b/190211183, missing libbionic_Slibdl_Sliblibdl_Ubp2build_Ucc_Ulibrary_Ushared.so - "libdl", // b/190211183, missing libbionic_Slinker_Slibld-android_Ubp2build_Ucc_Ulibrary_Ushared.so - "libdl_android", // b/190211183, missing libbionic_Slinker_Slibld-android_Ubp2build_Ucc_Ulibrary_Ushared.so - } + mixedBuildsDisabledList = []string{} // Used for quicker lookups bp2buildModuleDoNotConvert = map[string]bool{} diff --git a/android/bazel_handler.go b/android/bazel_handler.go index e4bbe64ff..b11b4743e 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -542,10 +542,13 @@ def get_arch(target): platform_name = build_options(target)["//command_line_option:platforms"][0].name if platform_name == "host": return "HOST" - elif not platform_name.startswith("android_"): - fail("expected platform name of the form 'android_', but was " + str(platforms)) + elif platform_name.startswith("android_"): + return platform_name[len("android_"):] + elif platform_name.startswith("linux_"): + return platform_name[len("linux_"):] + else: + fail("expected platform name of the form 'android_' or 'linux_', but was " + str(platforms)) return "UNKNOWN" - return platform_name[len("android_"):] def format(target): id_string = str(target.label) + "|" + get_arch(target) @@ -742,8 +745,17 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) { } rule := NewRuleBuilder(pctx, ctx) cmd := rule.Command() - cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s", - ctx.Config().BazelContext.OutputBase(), buildStatement.Command)) + + // cd into Bazel's execution root, which is the action cwd. + cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && ", ctx.Config().BazelContext.OutputBase())) + + for _, pair := range buildStatement.Env { + // Set per-action env variables, if any. + cmd.Flag(pair.Key + "=" + pair.Value) + } + + // The actual Bazel action. + cmd.Text(" " + buildStatement.Command) for _, outputPath := range buildStatement.OutputPaths { cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath)) diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go index 92135c64a..52c6c2fd9 100644 --- a/bazel/cquery/request_type.go +++ b/bazel/cquery/request_type.go @@ -20,6 +20,10 @@ type CcInfo struct { // be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles, // but general cc_library will also have dynamic libraries in output files). RootStaticArchives []string + // Dynamic libraries (.so files) created by the current target. These will + // be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles, + // but general cc_library will also have dynamic libraries in output files). + RootDynamicLibraries []string } type getOutputFilesRequestType struct{} @@ -86,13 +90,21 @@ for linker_input in linker_inputs: if linker_input.owner == target.label: rootStaticArchives.append(library.static_library.path) +rootDynamicLibraries = [] + +if "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" in providers(target): + shared_info = providers(target)["@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"] + for lib in shared_info.linker_input.libraries: + rootDynamicLibraries += [lib.dynamic_library.path] + returns = [ outputFiles, staticLibraries, ccObjectFiles, includes, system_includes, - rootStaticArchives + rootStaticArchives, + rootDynamicLibraries ] return "|".join([", ".join(r) for r in returns])` @@ -106,7 +118,7 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { var ccObjects []string splitString := strings.Split(rawString, "|") - if expectedLen := 6; len(splitString) != expectedLen { + if expectedLen := 7; len(splitString) != expectedLen { return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString) } outputFilesString := splitString[0] @@ -118,6 +130,7 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { includes := splitOrEmpty(splitString[3], ", ") systemIncludes := splitOrEmpty(splitString[4], ", ") rootStaticArchives := splitOrEmpty(splitString[5], ", ") + rootDynamicLibraries := splitOrEmpty(splitString[6], ", ") return CcInfo{ OutputFiles: outputFiles, CcObjectFiles: ccObjects, @@ -125,6 +138,7 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { Includes: includes, SystemIncludes: systemIncludes, RootStaticArchives: rootStaticArchives, + RootDynamicLibraries: rootDynamicLibraries, }, nil } diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go index 602849e7f..035544e9f 100644 --- a/bazel/cquery/request_type_test.go +++ b/bazel/cquery/request_type_test.go @@ -46,7 +46,7 @@ func TestGetCcInfoParseResults(t *testing.T) { }{ { description: "no result", - input: "|||||", + input: "||||||", expectedOutput: CcInfo{ OutputFiles: []string{}, CcObjectFiles: []string{}, @@ -54,11 +54,12 @@ func TestGetCcInfoParseResults(t *testing.T) { Includes: []string{}, SystemIncludes: []string{}, RootStaticArchives: []string{}, + RootDynamicLibraries: []string{}, }, }, { description: "only output", - input: "test|||||", + input: "test||||||", expectedOutput: CcInfo{ OutputFiles: []string{"test"}, CcObjectFiles: []string{}, @@ -66,11 +67,12 @@ func TestGetCcInfoParseResults(t *testing.T) { Includes: []string{}, SystemIncludes: []string{}, RootStaticArchives: []string{}, + RootDynamicLibraries: []string{}, }, }, { description: "all items set", - input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1", + input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1", expectedOutput: CcInfo{ OutputFiles: []string{"out1", "out2"}, CcObjectFiles: []string{"object1", "object2"}, @@ -78,19 +80,20 @@ func TestGetCcInfoParseResults(t *testing.T) { Includes: []string{".", "dir/subdir"}, SystemIncludes: []string{"system/dir", "system/other/dir"}, RootStaticArchives: []string{"rootstaticarchive1"}, + RootDynamicLibraries: []string{"rootdynamiclibrary1"}, }, }, { description: "too few result splits", input: "|", expectedOutput: CcInfo{}, - expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, []string{"", ""}), + expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, []string{"", ""}), }, { description: "too many result splits", input: strings.Repeat("|", 8), expectedOutput: CcInfo{}, - expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, make([]string, 9)), + expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, make([]string, 9)), }, } for _, tc := range testCases { diff --git a/cc/cc.go b/cc/cc.go index 555cb6ca1..ea6686f54 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -1673,10 +1673,6 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { c.makeLinkType = GetMakeLinkType(actx, c) - if c.maybeGenerateBazelActions(actx) { - return - } - ctx := &moduleContext{ ModuleContext: actx, moduleContextImpl: moduleContextImpl{ @@ -1685,6 +1681,11 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } ctx.ctx = ctx + if c.maybeGenerateBazelActions(actx) { + c.maybeInstall(ctx, apexInfo) + return + } + deps := c.depsToPaths(ctx) if ctx.Failed() { return @@ -1772,19 +1773,7 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } c.outputFile = android.OptionalPathForPath(outputFile) - // If a lib is directly included in any of the APEXes or is not available to the - // platform (which is often the case when the stub is provided as a prebuilt), - // unhide the stubs variant having the latest version gets visible to make. In - // addition, the non-stubs variant is renamed to .bootstrap. This is to - // force anything in the make world to link against the stubs library. (unless it - // is explicitly referenced via .bootstrap suffix or the module is marked with - // 'bootstrap: true'). - if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() && - !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() && - c.IsStubs() && !c.InVendorRamdisk() { - c.Properties.HideFromMake = false // unhide - // Note: this is still non-installable - } + c.maybeUnhideFromMake() // glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or // RECOVERY_SNAPSHOT_VERSION is current. @@ -1795,6 +1784,26 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } } + c.maybeInstall(ctx, apexInfo) +} + +func (c *Module) maybeUnhideFromMake() { + // If a lib is directly included in any of the APEXes or is not available to the + // platform (which is often the case when the stub is provided as a prebuilt), + // unhide the stubs variant having the latest version gets visible to make. In + // addition, the non-stubs variant is renamed to .bootstrap. This is to + // force anything in the make world to link against the stubs library. (unless it + // is explicitly referenced via .bootstrap suffix or the module is marked with + // 'bootstrap: true'). + if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() && + !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() && + c.IsStubs() && !c.InVendorRamdisk() { + c.Properties.HideFromMake = false // unhide + // Note: this is still non-installable + } +} + +func (c *Module) maybeInstall(ctx ModuleContext, apexInfo android.ApexInfo) { if !proptools.BoolDefault(c.Properties.Installable, true) { // If the module has been specifically configure to not be installed then // hide from make as otherwise it will break when running inside make diff --git a/cc/library.go b/cc/library.go index 28605f5c4..3061be4c9 100644 --- a/cc/library.go +++ b/cc/library.go @@ -28,6 +28,7 @@ import ( "android/soong/android" "android/soong/bazel" + "android/soong/bazel/cquery" "android/soong/cc/config" ) @@ -579,22 +580,10 @@ type ccLibraryBazelHandler struct { module *Module } -func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool { - if !handler.module.static() { - // TODO(cparsons): Support shared libraries. - return false - } - bazelCtx := ctx.Config().BazelContext - ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType) - if err != nil { - ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err) - return false - } - if !ok { - return ok - } +// generateStaticBazelBuildActions constructs the StaticLibraryInfo Soong +// provider from a Bazel shared library's CcInfo provider. +func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool { rootStaticArchives := ccInfo.RootStaticArchives - objPaths := ccInfo.CcObjectFiles if len(rootStaticArchives) != 1 { ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives) return false @@ -602,6 +591,7 @@ func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.Modu outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0]) handler.module.outputFile = android.OptionalPathForPath(outputFilePath) + objPaths := ccInfo.CcObjectFiles objFiles := make(android.Paths, len(objPaths)) for i, objPath := range objPaths { objFiles[i] = android.PathForBazelOut(ctx, objPath) @@ -615,26 +605,110 @@ func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.Modu ReuseObjects: objects, Objects: objects, - // TODO(cparsons): Include transitive static libraries in this provider to support + // TODO(b/190524881): Include transitive static libraries in this provider to support // static libraries with deps. TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL). Direct(outputFilePath). Build(), }) - ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfoFromCcInfo(ctx, ccInfo)) + return true +} + +// generateSharedBazelBuildActions constructs the SharedLibraryInfo Soong +// provider from a Bazel shared library's CcInfo provider. +func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool { + rootDynamicLibraries := ccInfo.RootDynamicLibraries + + if len(rootDynamicLibraries) != 1 { + ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries) + return false + } + outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0]) + handler.module.outputFile = android.OptionalPathForPath(outputFilePath) + + handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath + + tocFile := getTocFile(ctx, label, ccInfo.OutputFiles) + handler.module.linker.(*libraryDecorator).tocFile = tocFile + + ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{ + TableOfContents: tocFile, + SharedLibrary: outputFilePath, + // TODO(b/190524881): Include transitive static libraries in this provider to support + // static libraries with deps. + //TransitiveStaticLibrariesForOrdering + Target: ctx.Target(), + }) + return true +} + +// getTocFile looks for the .so.toc file in the target's output files, if any. The .so.toc file +// contains the table of contents of all symbols of a shared object. +func getTocFile(ctx android.ModuleContext, label string, outputFiles []string) android.OptionalPath { + var tocFile string + for _, file := range outputFiles { + if strings.HasSuffix(file, ".so.toc") { + if tocFile != "" { + ctx.ModuleErrorf("The %s target cannot produce more than 1 .toc file.", label) + } + tocFile = file + // Don't break to validate that there are no multiple .toc files per .so. + } + } + if tocFile == "" { + return android.OptionalPath{} + } + return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile)) +} + +func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool { + bazelCtx := ctx.Config().BazelContext + ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType) + if err != nil { + ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err) + return false + } + if !ok { + return ok + } + + if handler.module.static() { + if ok := handler.generateStaticBazelBuildActions(ctx, label, ccInfo); !ok { + return false + } + } else if handler.module.Shared() { + if ok := handler.generateSharedBazelBuildActions(ctx, label, ccInfo); !ok { + return false + } + } else { + return false + } + + handler.module.linker.(*libraryDecorator).setFlagExporterInfoFromCcInfo(ctx, ccInfo) + handler.module.maybeUnhideFromMake() + if i, ok := handler.module.linker.(snapshotLibraryInterface); ok { // 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 + // TODO(b/190533363): More closely mirror the collectHeadersForSnapshot // implementation. i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{} } - return ok } +func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) { + flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo) + // flag exporters consolidates properties like includes, flags, dependencies that should be + // exported from this module to other modules + ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo) + // Store flag info to be passed along to androidmk + // TODO(b/184387147): Androidmk should be done in Bazel, not Soong. + library.flagExporterInfo = &flagExporterInfo +} + func GlobHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths { ret := android.Paths{} diff --git a/cc/library_headers.go b/cc/library_headers.go index 20659292d..d6b45290e 100644 --- a/cc/library_headers.go +++ b/cc/library_headers.go @@ -73,13 +73,7 @@ func (h *libraryHeaderBazelHander) generateBazelBuildActions(ctx android.ModuleC // HeaderLibraryInfo is an empty struct to indicate to dependencies that this is a header library ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{}) - flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo) - // Store flag info to be passed along to androimk - // TODO(b/184387147): Androidmk should be done in Bazel, not Soong. - h.library.flagExporterInfo = &flagExporterInfo - // flag exporters consolidates properties like includes, flags, dependencies that should be - // exported from this module to other modules - ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo) + h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo) // Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise // validation will fail. For now, set this to an empty list. diff --git a/cc/library_test.go b/cc/library_test.go index 797527547..ba372a8e2 100644 --- a/cc/library_test.go +++ b/cc/library_test.go @@ -19,6 +19,7 @@ import ( "testing" "android/soong/android" + "android/soong/bazel/cquery" ) func TestLibraryReuse(t *testing.T) { @@ -240,3 +241,48 @@ func TestStubsVersions_ParseError(t *testing.T) { testCcError(t, `"libfoo" .*: versions: "X" could not be parsed as an integer and is not a recognized codename`, bp) } + +func TestCcLibraryWithBazel(t *testing.T) { + bp := ` +cc_library { + name: "foo", + srcs: ["foo.cc"], + bazel_module: { label: "//foo/bar:bar" }, +}` + config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) + config.BazelContext = android.MockBazelContext{ + OutputBaseDir: "outputbase", + LabelToCcInfo: map[string]cquery.CcInfo{ + "//foo/bar:bar": cquery.CcInfo{ + CcObjectFiles: []string{"foo.o"}, + Includes: []string{"include"}, + SystemIncludes: []string{"system_include"}, + RootStaticArchives: []string{"foo.a"}, + RootDynamicLibraries: []string{"foo.so"}, + }, + }, + } + ctx := testCcWithConfig(t, config) + + staticFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module() + outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("") + if err != nil { + t.Errorf("Unexpected error getting cc_object outputfiles %s", err) + } + + expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.a"} + android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings()) + + sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module() + outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("") + if err != nil { + t.Errorf("Unexpected error getting cc_object outputfiles %s", err) + } + expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"} + android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings()) + + entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0] + expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"} + gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"] + android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags) +}