Skip to content

Commit c6e6ade

Browse files
committed
rust: Do not bundle static libraries into internal libraries
When a Rust rlib/staticlib links to a C static library, it should not bundle its object files, unless it gets installed. This solves cases of multiple symbol definition when there is a diamond dependency graph. The only case we want to bundle is when a Rust staticlib link-whole another static library. In non-Rust case we achieve that by extracting objects, but rustc can do it for us. As extra bonus, rustc can bundle static libraries built with a CustomTarget.
1 parent 3271c4f commit c6e6ade

File tree

10 files changed

+91
-12
lines changed

10 files changed

+91
-12
lines changed

mesonbuild/backend/ninjabackend.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,7 +1969,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
19691969
# have to hope that the default cases of +whole-archive are sufficient.
19701970
# See: https://github.com/rust-lang/rust/issues/99429
19711971
if mesonlib.version_compare(rustc.version, '>= 1.63.0'):
1972-
whole_archive = '+whole-archive,-bundle'
1972+
whole_archive = '+whole-archive'
19731973
else:
19741974
whole_archive = ''
19751975

@@ -1979,6 +1979,15 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
19791979
else:
19801980
verbatim = ''
19811981

1982+
def _link_library(libname: str, static: bool, modifiers: T.Optional[T.List[str]] = None, bundle: bool = False):
1983+
_type = 'static' if static else 'dylib'
1984+
modifiers = modifiers or []
1985+
if not bundle and static:
1986+
modifiers.append('-bundle')
1987+
if modifiers:
1988+
_type += ':' + ','.join(modifiers)
1989+
args.extend(['-l', f'{_type}={libname}'])
1990+
19821991
linkdirs = mesonlib.OrderedSet()
19831992
external_deps = target.external_deps.copy()
19841993
target_deps = target.get_dependencies()
@@ -2035,11 +2044,15 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
20352044
need_link = False
20362045

20372046
if need_link:
2038-
_type = 'static' if isinstance(d, build.StaticLibrary) else 'dylib'
2039-
if modifiers:
2040-
_type += ':' + ','.join(modifiers)
2047+
static = isinstance(d, build.StaticLibrary)
2048+
# rlib does not support bundling. That probably could cause
2049+
# unusable installed rlib if they link to uninstalled static
2050+
# libraries. Installing rlib is not something we generally
2051+
# support anyway.
2052+
# https://github.com/rust-lang/rust/issues/108081
2053+
bundle = cratetype == 'staticlib' and link_whole
20412054
libname = os.path.basename(lib) if verbatim else d.name
2042-
args += ['-l', f'{_type}={libname}']
2055+
_link_library(libname, static, modifiers, bundle)
20432056

20442057
for e in external_deps:
20452058
for a in e.get_link_args():
@@ -2052,13 +2065,12 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
20522065
lib, ext = os.path.splitext(lib)
20532066
if lib.startswith('lib'):
20542067
lib = lib[3:]
2055-
_type = 'static' if a.endswith(('.a', '.lib')) else 'dylib'
2056-
args.extend(['-l', f'{_type}={lib}'])
2068+
static = a.endswith(('.a', '.lib'))
2069+
_link_library(lib, static)
20572070
elif a.startswith('-L'):
20582071
args.append(a)
20592072
elif a.startswith('-l'):
2060-
_type = 'static' if e.static else 'dylib'
2061-
args.extend(['-l', f'{_type}={a[2:]}'])
2073+
_link_library(a[2:], e.static)
20622074
for d in linkdirs:
20632075
if d == '':
20642076
d = '.'

mesonbuild/build.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,7 @@ def link_whole(self, targets, promoted: bool = False):
14341434
msg += "Use the 'pic' option to static_library to build with PIC."
14351435
raise InvalidArguments(msg)
14361436
self.check_can_link_together(t)
1437-
if isinstance(self, StaticLibrary) and not self.uses_rust():
1437+
if isinstance(self, StaticLibrary):
14381438
# When we're a static library and we link_whole: to another static
14391439
# library, we need to add that target's objects to ourselves.
14401440
self._bundle_static_library(t, promoted)
@@ -1461,7 +1461,10 @@ def get_internal_static_libraries_recurse(self, result: OrderedSet[Target]) -> N
14611461
t.get_internal_static_libraries_recurse(result)
14621462

14631463
def _bundle_static_library(self, t: T.Union[Target, CustomTargetIndex], promoted: bool = False) -> None:
1464-
if isinstance(t, (CustomTarget, CustomTargetIndex)) or t.uses_rust():
1464+
if self.uses_rust():
1465+
# Rustc can bundle static libraries, no need to extract objects.
1466+
self.link_whole_targets.append(t)
1467+
elif isinstance(t, (CustomTarget, CustomTargetIndex)) or t.uses_rust():
14651468
# To extract objects from a custom target we would have to extract
14661469
# the archive, WIP implementation can be found in
14671470
# https://github.com/mesonbuild/meson/pull/9218.
@@ -1476,7 +1479,8 @@ def _bundle_static_library(self, t: T.Union[Target, CustomTargetIndex], promoted
14761479
m += (f' Meson had to promote link to link_whole because {self.name!r} is installed but not {t.name!r},'
14771480
f' and thus has to include objects from {t.name!r} to be usable.')
14781481
raise InvalidArguments(m)
1479-
self.objects.append(t.extract_all_objects())
1482+
else:
1483+
self.objects.append(t.extract_all_objects())
14801484

14811485
def check_can_link_together(self, t: BuildTargetTypes) -> None:
14821486
links_with_rust_abi = isinstance(t, BuildTarget) and t.uses_rust_abi()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
int c_func(void);
2+
int c_func(void) {
3+
return 123;
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
int r3(void);
2+
3+
int main_func(void) {
4+
return r3() == 246 ? 0 : 1;
5+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Regression test for a diamond dependency graph:
2+
# ┌►R1┐
3+
# main-►R3─┤ ├─►C1
4+
# └►R2┘
5+
# Both libr1.rlib and libr2.rlib used to contain func.c.o. That was causing
6+
# libr3.rlib to have duplicated func.c.o and then libmain.so failed to link:
7+
# multiple definition of `c_func'.
8+
9+
libc1 = static_library('c1', 'func.c')
10+
libr1 = static_library('r1', 'r1.rs', link_with: libc1)
11+
libr2 = static_library('r2', 'r2.rs', link_with: libc1)
12+
libr3 = static_library('r3', 'r3.rs',
13+
link_with: [libr1, libr2],
14+
rust_abi: 'c',
15+
)
16+
shared_library('main', 'main.c', link_whole: [libr3])
17+
18+
# Same dependency graph, but r3 is now installed. Since c1, r1 and r2 are
19+
# not installed, r3 must contain them.
20+
libr3 = static_library('r3-installed', 'r3.rs',
21+
link_with: [libr1, libr2],
22+
rust_abi: 'c',
23+
install: true,
24+
)
25+
shared_library('main-installed', 'main.c', link_with: [libr3])
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
extern "C" {
2+
fn c_func() -> i32;
3+
}
4+
5+
pub fn r1() -> i32 {
6+
unsafe {
7+
c_func()
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
extern "C" {
2+
fn c_func() -> i32;
3+
}
4+
5+
pub fn r2() -> i32 {
6+
unsafe {
7+
c_func()
8+
}
9+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[no_mangle]
2+
pub fn r3() -> i32 {
3+
r1::r1() + r2::r2()
4+
}

test cases/rust/20 transitive dependencies/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ exe = executable('footest', 'foo.c',
2525
link_with: foo,
2626
)
2727
test('footest', exe)
28+
29+
subdir('diamond')
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"installed": [
3+
{"type": "file", "file": "usr/lib/libr3-installed.a"}
4+
]
5+
}

0 commit comments

Comments
 (0)