Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions cc/private/toolchain/unix_cc_configure.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -493,24 +493,32 @@ 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
bin_search_flags = ["-B" + escape_string(str(cc_path.dirname))]
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:
Expand Down
59 changes: 38 additions & 21 deletions tests/builtins_bzl/cc/cc_shared_library/test/starlark_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -66,51 +66,68 @@ 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([
"-lprivate_lib_so",
])

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)
for arg in args
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)
Expand Down
5 changes: 5 additions & 0 deletions tests/integration/cc_integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/cc_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion tests/integration/lto_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand Down Expand Up @@ -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")
Expand Down
6 changes: 6 additions & 0 deletions tests/test_utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
}