diff --git a/src/main/starlark/builtins_bzl/common/cc/experimental_cc_shared_library.bzl b/src/main/starlark/builtins_bzl/common/cc/experimental_cc_shared_library.bzl index 32483582fbfb09..9ef8152159c79b 100644 --- a/src/main/starlark/builtins_bzl/common/cc/experimental_cc_shared_library.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/experimental_cc_shared_library.bzl @@ -33,6 +33,15 @@ cc_common = _builtins.toplevel.cc_common # used sparingly after making sure it's safe to use. LINKABLE_MORE_THAN_ONCE = "LINKABLE_MORE_THAN_ONCE" +# Add this as a tag to any static lib target that doesn't export any symbols, +# thus can be statically linked more than once. This is useful in some cases, +# for example, a static lib has a constructor that needs to be run during +# loading time of the shared lib that has it linked into, which is how the +# code gets called by the OS. This static lib might need to be linked as a +# whole archive dep for multiple shared libs, otherwise this static lib will +# be dropped by the linker since there are no incoming symbol references. +NO_EXPORTING = "NO_EXPORTING" + CcSharedLibraryPermissionsInfo = provider( "Permissions for a cc shared library.", fields = { @@ -45,6 +54,7 @@ GraphNodeInfo = provider( "children": "Other GraphNodeInfo from dependencies of this target", "label": "Label of the target visited", "linkable_more_than_once": "Linkable into more than a single cc_shared_library", + "no_exporting": "The static lib doesn't export any symbols so don't export it", }, ) CcSharedLibraryInfo = provider( @@ -532,7 +542,12 @@ def _cc_shared_library_impl(ctx): runfiles = runfiles.merge(ctx.runfiles(files = precompiled_only_dynamic_libraries_runfiles)) for export in ctx.attr.roots: - exports[str(export.label)] = True + export_label = str(export.label) + if GraphNodeInfo in export and export[GraphNodeInfo].no_exporting: + if export_label in link_once_static_libs: + link_once_static_libs.remove(export_label) + continue + exports[export_label] = True debug_files = [] exports_debug_file = ctx.actions.declare_file(ctx.label.name + "_exports.txt") @@ -595,15 +610,19 @@ def _graph_structure_aspect_impl(target, ctx): # TODO(bazel-team): Add flag to Bazel that can toggle the initialization of # linkable_more_than_once. linkable_more_than_once = False + no_exporting = False if hasattr(ctx.rule.attr, "tags"): for tag in ctx.rule.attr.tags: if tag == LINKABLE_MORE_THAN_ONCE: linkable_more_than_once = True + elif tag == NO_EXPORTING: + no_exporting = True return [GraphNodeInfo( label = ctx.label, children = children, linkable_more_than_once = linkable_more_than_once, + no_exporting = no_exporting, )] def _cc_shared_library_permissions_impl(ctx): diff --git a/src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/BUILD.builtin_test b/src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/BUILD.builtin_test index 39dcd2053cbfd7..2449c9148cf6e2 100644 --- a/src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/BUILD.builtin_test +++ b/src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/BUILD.builtin_test @@ -1,4 +1,14 @@ -load(":starlark_tests.bzl", "additional_inputs_test", "build_failure_test", "debug_files_test", "interface_library_output_group_test", "linking_suffix_test", "paths_test", "runfiles_test") +load( + ":starlark_tests.bzl", + "additional_inputs_test", + "build_failure_test", + "debug_files_test", + "interface_library_output_group_test", + "linking_suffix_test", + "paths_test", + "runfiles_test", + "no_exporting_static_lib_test", +) LINKABLE_MORE_THAN_ONCE = "LINKABLE_MORE_THAN_ONCE" @@ -391,6 +401,51 @@ cc_shared_library_permissions( ], ) +cc_library( + name = "static_lib_no_exporting", + srcs = [ + "bar.cc", + "bar.h", + ], + tags = ["NO_EXPORTING"], +) + +cc_library( + name = "static_lib_exporting", + srcs = [ + "bar2.cc", + "bar2.h", + ], +) + +cc_shared_library( + name = "lib_with_no_exporting_roots_1", + roots = [":static_lib_no_exporting"], +) + +cc_shared_library( + name = "lib_with_no_exporting_roots_2", + roots = [":static_lib_no_exporting"], + dynamic_deps = [":lib_with_no_exporting_roots_3"], +) + +cc_shared_library( + name = "lib_with_no_exporting_roots_3", + roots = [":static_lib_no_exporting"], +) + +cc_shared_library( + name = "lib_with_no_exporting_roots", + roots = [ + ":static_lib_no_exporting", + ":static_lib_exporting", + ], + dynamic_deps = [ + ":lib_with_no_exporting_roots_1", + ":lib_with_no_exporting_roots_2", + ], +) + build_failure_test( name = "two_dynamic_deps_same_export_in_so_test", message = "Two shared libraries in dependencies export the same symbols", @@ -439,3 +494,8 @@ build_failure_test( }), target_under_test = "//src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/failing_targets:unaccounted_for_libs_so", ) + +no_exporting_static_lib_test( + name = "no_exporting_static_lib_test", + target_under_test = ":lib_with_no_exporting_roots", +) diff --git a/src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/starlark_tests.bzl b/src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/starlark_tests.bzl index 121bb1d41ad6fd..53725efb9695fb 100644 --- a/src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/starlark_tests.bzl +++ b/src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library/starlark_tests.bzl @@ -211,3 +211,21 @@ interface_library_output_group_test = analysistest.make( "is_windows": attr.bool(), }, ) + +def _no_exporting_static_lib_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + + # There should be only one exported file + actual_file = target_under_test[CcSharedLibraryInfo].exports[0] + + # Sometimes "@" is prefixed in some test environments + expected = "//src/main/starlark/tests/builtins_bzl/cc/cc_shared_library/test_cc_shared_library:static_lib_exporting" + asserts.true(env, actual_file.endswith(expected)) + + return analysistest.end(env) + +no_exporting_static_lib_test = analysistest.make( + _no_exporting_static_lib_test_impl, +)