Skip to content

Commit b427ef9

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.
1 parent f469bd4 commit b427ef9

File tree

8 files changed

+74
-9
lines changed

8 files changed

+74
-9
lines changed

mesonbuild/backend/ninjabackend.py

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

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

1981+
def add_native_library(libname: str, static: bool, modifiers: T.Optional[T.List[str]] = None):
1982+
_type = 'static' if static else 'dylib'
1983+
modifiers = modifiers or []
1984+
if static and target.is_internal():
1985+
modifiers.append('-bundle')
1986+
if modifiers:
1987+
_type += ':' + ','.join(modifiers)
1988+
args.extend(['-l', f'{_type}={libname}'])
1989+
19811990
linkdirs = mesonlib.OrderedSet()
19821991
external_deps = target.external_deps.copy()
19831992
target_deps = target.get_dependencies()
@@ -2034,10 +2043,8 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
20342043
need_link = False
20352044

20362045
if need_link:
2037-
_type = 'static' if isinstance(d, build.StaticLibrary) else 'dylib'
2038-
if modifiers:
2039-
_type += ':' + ','.join(modifiers)
2040-
args += ['-l', f'{_type}={os.path.basename(lib)}']
2046+
static = isinstance(d, build.StaticLibrary)
2047+
add_native_library(os.path.basename(lib), static, modifiers)
20412048

20422049
for e in external_deps:
20432050
for a in e.get_link_args():
@@ -2050,13 +2057,13 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
20502057
lib, ext = os.path.splitext(lib)
20512058
if lib.startswith('lib'):
20522059
lib = lib[3:]
2053-
_type = 'static' if a.endswith(('.a', '.lib')) else 'dylib'
2054-
args.extend(['-l', f'{_type}={lib}'])
2060+
static = a.endswith(('.a', '.lib'))
2061+
add_native_library(lib, static)
20552062
elif a.startswith('-L'):
20562063
args.append(a)
20572064
elif a.startswith('-l'):
2058-
_type = 'static' if e.static else 'dylib'
2059-
args.extend(['-l', f'{_type}={a[2:]}'])
2065+
static = a.endswith(('.a', '.lib'))
2066+
add_native_library(a[2:], e.static)
20602067
for d in linkdirs:
20612068
if d == '':
20622069
d = '.'
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')

0 commit comments

Comments
 (0)