Skip to content

Commit 1cd0788

Browse files
authored
Apply get_lib_name correctly to the C++ runtime libraries (#1508)
#1500 added an additional `for_windows` parameter to `get_lib_name`. I missed the fact that we also pass that function to `map_each` here: https://github.com/bazelbuild/rules_rust/blob/main/rust/private/rustc.bzl#L1671 and as such, this code does not always work correctly (we don't get to pass the `for_windows` parameter, and internally at Google it ended up evaluating to `True` on Linux builds). I tried to avoid flattening the `cc_toolchain.dynamic_runtime_lib` and `cc_toolchain.static_runtime_lib` depsets by using a lambda: ``` args.add_all( cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration), map_each = lambda x: get_lib_name(x, for_windows = toolchain.os.startswith("windows)), format_each = "-ldylib=%s", ) ``` However it looks like such usage of lambdas is not allowed: ``` Error in add_all: to avoid unintended retention of analysis data structures, the map_each function (declared at ...) must be declared by a top-level def statement ``` So instead of `get_lib_name` we now have `get_lib_name_default` and `get_lib_name_for_windows`.
1 parent 90808f0 commit 1cd0788

File tree

3 files changed

+77
-36
lines changed

3 files changed

+77
-36
lines changed

rust/private/rustc.bzl

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ load(
2727
"expand_dict_value_locations",
2828
"expand_list_element_locations",
2929
"find_cc_toolchain",
30-
"get_lib_name",
30+
"get_lib_name_default",
31+
"get_lib_name_for_windows",
3132
"get_preferred_artifact",
3233
"is_exec_configuration",
3334
"make_static_lib_symlink",
@@ -425,7 +426,7 @@ def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib):
425426

426427
# Take the absolute value of hash() since it could be negative.
427428
path_hash = abs(hash(lib.path))
428-
lib_name = get_lib_name(lib, for_windows = toolchain.os.startswith("windows"))
429+
lib_name = get_lib_name_for_windows(lib) if toolchain.os.startswith("windows") else get_lib_name_default(lib)
429430

430431
prefix = "lib"
431432
extension = ".a"
@@ -488,7 +489,7 @@ def _disambiguate_libs(actions, toolchain, crate_info, dep_info, use_pic):
488489
if _is_dylib(lib):
489490
continue
490491
artifact = get_preferred_artifact(lib, use_pic)
491-
name = get_lib_name(artifact, for_windows = toolchain.os.startswith("windows"))
492+
name = get_lib_name_for_windows(artifact) if toolchain.os.startswith("windows") else get_lib_name_default(artifact)
492493

493494
# On Linux-like platforms, normally library base names start with
494495
# `lib`, following the pattern `lib[name].(a|lo)` and we pass
@@ -1523,7 +1524,7 @@ def _get_crate_dirname(crate):
15231524
"""
15241525
return crate.output.dirname
15251526

1526-
def _portable_link_flags(lib, use_pic, ambiguous_libs, for_windows = False):
1527+
def _portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name, for_windows):
15271528
artifact = get_preferred_artifact(lib, use_pic)
15281529
if ambiguous_libs and artifact.path in ambiguous_libs:
15291530
artifact = ambiguous_libs[artifact.path]
@@ -1561,14 +1562,14 @@ def _portable_link_flags(lib, use_pic, ambiguous_libs, for_windows = False):
15611562
artifact.basename.startswith("libtest-") or artifact.basename.startswith("libstd-") or
15621563
artifact.basename.startswith("test-") or artifact.basename.startswith("std-")
15631564
):
1564-
return ["-lstatic=%s" % get_lib_name(artifact, for_windows)]
1565+
return ["-lstatic=%s" % get_lib_name(artifact)]
15651566
return [
1566-
"-lstatic=%s" % get_lib_name(artifact, for_windows),
1567+
"-lstatic=%s" % get_lib_name(artifact),
15671568
"-Clink-arg=-l%s" % (get_lib_name(artifact) if not for_windows else artifact.basename),
15681569
]
15691570
elif _is_dylib(lib):
15701571
return [
1571-
"-ldylib=%s" % get_lib_name(artifact, for_windows),
1572+
"-ldylib=%s" % get_lib_name(artifact),
15721573
]
15731574

15741575
return []
@@ -1580,7 +1581,7 @@ def _make_link_flags_windows(linker_input_and_use_pic_and_ambiguous_libs):
15801581
if lib.alwayslink:
15811582
ret.extend(["-C", "link-arg=/WHOLEARCHIVE:%s" % get_preferred_artifact(lib, use_pic).path])
15821583
else:
1583-
ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, for_windows = True))
1584+
ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_for_windows, for_windows = True))
15841585
return ret
15851586

15861587
def _make_link_flags_darwin(linker_input_and_use_pic_and_ambiguous_libs):
@@ -1593,7 +1594,7 @@ def _make_link_flags_darwin(linker_input_and_use_pic_and_ambiguous_libs):
15931594
("link-arg=-Wl,-force_load,%s" % get_preferred_artifact(lib, use_pic).path),
15941595
])
15951596
else:
1596-
ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
1597+
ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_windows = False))
15971598
return ret
15981599

15991600
def _make_link_flags_default(linker_input_and_use_pic_and_ambiguous_libs):
@@ -1610,7 +1611,7 @@ def _make_link_flags_default(linker_input_and_use_pic_and_ambiguous_libs):
16101611
"link-arg=-Wl,--no-whole-archive",
16111612
])
16121613
else:
1613-
ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs))
1614+
ret.extend(_portable_link_flags(lib, use_pic, ambiguous_libs, get_lib_name_default, for_windows = False))
16141615
return ret
16151616

16161617
def _libraries_dirnames(linker_input_and_use_pic_and_ambiguous_libs):
@@ -1639,10 +1640,13 @@ def _add_native_link_flags(args, dep_info, linkstamp_outs, ambiguous_libs, crate
16391640

16401641
if toolchain.os == "windows":
16411642
make_link_flags = _make_link_flags_windows
1643+
get_lib_name = get_lib_name_for_windows
16421644
elif toolchain.os.startswith("mac") or toolchain.os.startswith("darwin"):
16431645
make_link_flags = _make_link_flags_darwin
1646+
get_lib_name = get_lib_name_default
16441647
else:
16451648
make_link_flags = _make_link_flags_default
1649+
get_lib_name = get_lib_name_default
16461650

16471651
# TODO(hlopko): Remove depset flattening by using lambdas once we are on >=Bazel 5.0
16481652
args_and_pic_and_ambiguous_libs = [(arg, use_pic, ambiguous_libs) for arg in dep_info.transitive_noncrates.to_list()]

rust/private/utils.bzl

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,11 @@ def _path_parts(path):
9797
path_parts = path.split("/")
9898
return [part for part in path_parts if part != "."]
9999

100-
def get_lib_name(lib, for_windows = False):
101-
"""Returns the name of a library artifact, eg. libabc.a -> abc
100+
def get_lib_name_default(lib):
101+
"""Returns the name of a library artifact.
102102
103103
Args:
104104
lib (File): A library file
105-
for_windows: Whether we're building on Windows.
106105
107106
Returns:
108107
str: The name of the library
@@ -126,11 +125,49 @@ def get_lib_name(lib, for_windows = False):
126125
# The library name is now everything minus the extension.
127126
libname = ".".join(comps[:-1])
128127

129-
if libname.startswith("lib") and not for_windows:
128+
if libname.startswith("lib"):
130129
return libname[3:]
131130
else:
132131
return libname
133132

133+
# TODO: Could we remove this function in favor of a "windows" parameter in the
134+
# above function? It looks like currently lambdas cannot accept local parameters
135+
# so the following doesn't work:
136+
# args.add_all(
137+
# cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
138+
# map_each = lambda x: get_lib_name(x, for_windows = toolchain.os.startswith("windows)),
139+
# format_each = "-ldylib=%s",
140+
# )
141+
def get_lib_name_for_windows(lib):
142+
"""Returns the name of a library artifact for Windows builds.
143+
144+
Args:
145+
lib (File): A library file
146+
147+
Returns:
148+
str: The name of the library
149+
"""
150+
# On macos and windows, dynamic/static libraries always end with the
151+
# extension and potential versions will be before the extension, and should
152+
# be part of the library name.
153+
# On linux, the version usually comes after the extension.
154+
# So regardless of the platform we want to find the extension and make
155+
# everything left to it the library name.
156+
157+
# Search for the extension - starting from the right - by removing any
158+
# trailing digit.
159+
comps = lib.basename.split(".")
160+
for comp in reversed(comps):
161+
if comp.isdigit():
162+
comps.pop()
163+
else:
164+
break
165+
166+
# The library name is now everything minus the extension.
167+
libname = ".".join(comps[:-1])
168+
169+
return libname
170+
134171
def abs(value):
135172
"""Returns the absolute value of a number.
136173

test/unit/versioned_libs/versioned_libs_unit_test.bzl

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,32 @@
33
load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
44

55
# buildifier: disable=bzl-visibility
6-
load("//rust/private:utils.bzl", "get_lib_name")
6+
load("//rust/private:utils.bzl", "get_lib_name_default", "get_lib_name_for_windows")
77

88
def _produced_expected_lib_name_test_impl(ctx):
99
env = unittest.begin(ctx)
1010

11-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.dylib")))
12-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.so")))
13-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.a")))
14-
asserts.equals(env, "python", get_lib_name(struct(basename = "python.dll")))
15-
asserts.equals(env, "python", get_lib_name(struct(basename = "python.lib")))
16-
17-
asserts.equals(env, "python3", get_lib_name(struct(basename = "libpython3.dylib")))
18-
asserts.equals(env, "python3.8", get_lib_name(struct(basename = "libpython3.8.dylib")))
19-
asserts.equals(env, "python3", get_lib_name(struct(basename = "libpython3.a")))
20-
asserts.equals(env, "python3.8", get_lib_name(struct(basename = "libpython3.8.a")))
21-
22-
asserts.equals(env, "python38", get_lib_name(struct(basename = "python38.dll")))
23-
asserts.equals(env, "python38m", get_lib_name(struct(basename = "python38m.dll")))
24-
25-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.so.3")))
26-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.so.3.8")))
27-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.so.3.8.0")))
28-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.a.3")))
29-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.a.3.8")))
30-
asserts.equals(env, "python", get_lib_name(struct(basename = "libpython.a.3.8.0")))
31-
asserts.equals(env, "python-3.8.0", get_lib_name(struct(basename = "libpython-3.8.0.so.3.8.0")))
11+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.dylib")))
12+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.so")))
13+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.a")))
14+
asserts.equals(env, "python", get_lib_name_for_windows(struct(basename = "python.dll")))
15+
asserts.equals(env, "python", get_lib_name_for_windows(struct(basename = "python.lib")))
16+
17+
asserts.equals(env, "python3", get_lib_name_default(struct(basename = "libpython3.dylib")))
18+
asserts.equals(env, "python3.8", get_lib_name_default(struct(basename = "libpython3.8.dylib")))
19+
asserts.equals(env, "python3", get_lib_name_default(struct(basename = "libpython3.a")))
20+
asserts.equals(env, "python3.8", get_lib_name_default(struct(basename = "libpython3.8.a")))
21+
22+
asserts.equals(env, "python38", get_lib_name_for_windows(struct(basename = "python38.dll")))
23+
asserts.equals(env, "python38m", get_lib_name_for_windows(struct(basename = "python38m.dll")))
24+
25+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.so.3")))
26+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.so.3.8")))
27+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.so.3.8.0")))
28+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.a.3")))
29+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.a.3.8")))
30+
asserts.equals(env, "python", get_lib_name_default(struct(basename = "libpython.a.3.8.0")))
31+
asserts.equals(env, "python-3.8.0", get_lib_name_default(struct(basename = "libpython-3.8.0.so.3.8.0")))
3232

3333
return unittest.end(env)
3434

0 commit comments

Comments
 (0)