diff --git a/cc/private/toolchain/unix_cc_configure.bzl b/cc/private/toolchain/unix_cc_configure.bzl index 80a4f642b..841b7a7dc 100644 --- a/cc/private/toolchain/unix_cc_configure.bzl +++ b/cc/private/toolchain/unix_cc_configure.bzl @@ -493,10 +493,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): False, ), ":") - gold_or_lld_linker_path = ( - _find_linker_path(repository_ctx, cc, "lld", is_clang) or - _find_linker_path(repository_ctx, cc, "gold", is_clang) - ) + lld_linker_path = _find_linker_path(repository_ctx, cc, "lld", is_clang) cc_path = repository_ctx.path(cc) if not str(cc_path).startswith(str(repository_ctx.path(".")) + "/"): # cc is outside the repository, set -B @@ -504,13 +501,24 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overridden_tools): else: # cc is inside the repository, don't set -B. bin_search_flags = [] - if not gold_or_lld_linker_path: + if not lld_linker_path: ld_path = repository_ctx.path(tool_paths["ld"]) if ld_path.dirname != cc_path.dirname: bin_search_flags.append("-B" + str(ld_path.dirname)) force_linker_flags = [] - if gold_or_lld_linker_path: - force_linker_flags.append("-fuse-ld=" + gold_or_lld_linker_path) + if lld_linker_path: + force_linker_flags.append("-fuse-ld=" + lld_linker_path) + else: + # GNU ld rejects unresolved symbols from indirect shared library + # dependencies when linking an executable. Bazel supplies those + # dependencies at runtime. + force_linker_flags.extend(_add_linker_option_if_supported( + repository_ctx, + cc, + force_linker_flags, + "-Wl,--allow-shlib-undefined", + "--allow-shlib-undefined", + )) # TODO: It's unclear why these flags aren't added on macOS. if bin_search_flags and not darwin: diff --git a/tests/builtins_bzl/cc/cc_shared_library/test/starlark_tests.bzl b/tests/builtins_bzl/cc/cc_shared_library/test/starlark_tests.bzl index 82f227ba3..e8dcc6612 100644 --- a/tests/builtins_bzl/cc/cc_shared_library/test/starlark_tests.bzl +++ b/tests/builtins_bzl/cc/cc_shared_library/test/starlark_tests.bzl @@ -66,11 +66,27 @@ def _linking_order_test_impl(env, target): for arg in args if arg.endswith(".o") and env.ctx.label.package in arg ] + user_libs_are_objects = bool(user_libs) + if user_libs: + foo_lib = "foo.pic.o" + baz_lib = "baz.pic.o" + a_suffix_lib = "a_suffix.pic.o" + qux2_lib = "qux2.pic.o" + else: + user_libs = [ + paths.basename(arg) + for arg in args + if arg.endswith(".a") and env.ctx.label.package in arg + ] + foo_lib = "libfoo.a" + baz_lib = "libbaz.a" + a_suffix_lib = "liba_suffix.a" + qux2_lib = "libqux2.a" env.expect.that_collection(user_libs).contains_at_least_predicates([ - matching.contains("foo.pic.o"), - matching.contains("baz.pic.o"), - matching.contains("a_suffix.pic.o"), + matching.contains(foo_lib), + matching.contains(baz_lib), + matching.contains(a_suffix_lib), ]).in_order() env.expect.that_collection(args).contains_at_least([ @@ -78,8 +94,8 @@ def _linking_order_test_impl(env, target): ]) env.expect.where( - detail = "liba_suffix.pic.o should be the last user library linked", - ).that_str(user_libs[-1]).equals("a_suffix.pic.o") + detail = "a_suffix should be the last user library linked", + ).that_str(user_libs[-1]).equals(a_suffix_lib) all_objects = [ paths.basename(arg) @@ -87,30 +103,31 @@ def _linking_order_test_impl(env, target): if arg.endswith(".o") ] - env.expect.that_collection(all_objects).contains_at_least_predicates([ - matching.contains("a_suffix.pic.o"), - ]) - - # We expect a_suffix.pic.o to be the last object linked if and only if this is Bazel, - # as runtimes-on-demand is not enabled in Bazel. - if env.ctx.attr.is_bazel: - env.expect.where( - detail = "a_suffix.pic.o should be the last object linked", - ).that_str(all_objects[-1]).equals("a_suffix.pic.o") - else: - env.expect.where( - detail = "a_suffix.pic.o should not be the last object linked", - ).that_str(all_objects[-1]).not_equals("a_suffix.pic.o") + if user_libs_are_objects: + env.expect.that_collection(all_objects).contains_at_least_predicates([ + matching.contains("a_suffix.pic.o"), + ]) + + # We expect a_suffix.pic.o to be the last object linked if and only if this is Bazel, + # as runtimes-on-demand is not enabled in Bazel. + if env.ctx.attr.is_bazel: + env.expect.where( + detail = "a_suffix.pic.o should be the last object linked", + ).that_str(all_objects[-1]).equals("a_suffix.pic.o") + else: + env.expect.where( + detail = "a_suffix.pic.o should not be the last object linked", + ).that_str(all_objects[-1]).not_equals("a_suffix.pic.o") # qux2 is a LINKABLE_MORE_THAN_ONCE library which is enabled by semantics. # It might not be present but if it is we want to test it's in the right # place in the linking command line before libbar - if "qux2.pic.o" in user_libs: + if qux2_lib in user_libs: found_bar = False for arg in args: if "-lbar_so" in arg: found_bar = True - elif "qux2.pic.o" in arg: + elif qux2_lib in arg: env.expect.where( detail = "qux2 should come before bar in command line", ).that_bool(found_bar).equals(False) diff --git a/tests/integration/cc_integration_test.sh b/tests/integration/cc_integration_test.sh index 08aba64ca..3674248ea 100755 --- a/tests/integration/cc_integration_test.sh +++ b/tests/integration/cc_integration_test.sh @@ -2476,6 +2476,11 @@ EOF function test_external_repo_lto() { is_bazel || return 0 + + if ! linker_supports_start_end_lib clang; then + echo "Linker does not support --start-lib/--end-lib. Skipping test." + return 0 + fi REPO_PATH=$TEST_TMPDIR/repo mkdir -p "$REPO_PATH" diff --git a/tests/integration/cc_tests.sh b/tests/integration/cc_tests.sh index 5bd89d06b..6e8ab130a 100755 --- a/tests/integration/cc_tests.sh +++ b/tests/integration/cc_tests.sh @@ -307,9 +307,9 @@ EOF } function test_cc_rule_cc_with_tree_artifacts() { - if is_darwin; then - # The default linker on Mac does not support --start-lib --end-lib which this test requires - echo "Skipping incompatible test on Mac" + local -r cc="${CC:-gcc}" + if ! linker_supports_start_end_lib "$cc"; then + echo "Linker does not support --start-lib/--end-lib. Skipping test." return 0; fi mkdir -p foo diff --git a/tests/integration/lto_tests.sh b/tests/integration/lto_tests.sh index 0b26f1915..39ec50421 100755 --- a/tests/integration/lto_tests.sh +++ b/tests/integration/lto_tests.sh @@ -3,12 +3,20 @@ set -euo pipefail source "$(rlocation rules_cc/tests/test_utils.sh)" source "$(rlocation rules_cc/tests/unittest.bash)" +LTO_TESTS_ENABLED=1 + function set_up() { if is_bazel; then local -r clang="$(which clang || true)" if [[ ! -x "$clang" ]]; then echo "clang not installed. Skipping test." - return 1 + LTO_TESTS_ENABLED=0 + return + fi + if ! linker_supports_start_end_lib "$clang"; then + echo "Linker does not support --start-lib/--end-lib. Skipping test." + LTO_TESTS_ENABLED=0 + return fi add_to_bazelrc "common --repo_env=CC=$clang" else @@ -17,6 +25,7 @@ function set_up() { } function test_thin_lto() { + [[ "$LTO_TESTS_ENABLED" == 1 ]] || return 0 mkdir -p hello cat > hello/BUILD << EOF load("@rules_cc//cc:cc_binary.bzl", "cc_binary") @@ -56,6 +65,7 @@ EOF } function test_thin_lto_only_dep_has_source() { + [[ "$LTO_TESTS_ENABLED" == 1 ]] || return 0 mkdir -p hello cat > hello/BUILD << EOF load("@rules_cc//cc:cc_binary.bzl", "cc_binary") diff --git a/tests/test_utils.sh b/tests/test_utils.sh index 2b3357246..62ea4f89c 100644 --- a/tests/test_utils.sh +++ b/tests/test_utils.sh @@ -43,6 +43,12 @@ function is_bazel() { [ $TEST_WORKSPACE == "_main" ] } +function linker_supports_start_end_lib() { + echo "int main() {}" | + "$1" -x c++ - -o /dev/null \ + -Wl,--start-lib -Wl,--end-lib &>/dev/null +} + function add_to_bazelrc() { echo "$@" >> .bazelrc }