Skip to content

Commit

Permalink
Implement rustc controlled whole-archive linking
Browse files Browse the repository at this point in the history
Rustc as of version 1.61.0 has support for controlling when
whole-archive linking takes place, previous to this it tried to make a
good guess about what you wanted, which worked most of the time. This is
now implemented. Additionally, because meson breaks some rustc
assumption about library naming on windows, we have to manually pass
whole-archive libraries, bypassing rustc's interface and talking
directly to the linker for MSVC (and those imitating it), which we were
doing incorrectly. This has been fixed.

Fixes: mesonbuild#10723
Fixes: mesonbuild#11247

Co-authored-by: Nirbheek Chauhan <nirbheek@centricular.com>
  • Loading branch information
dcbaker and nirbheek committed Feb 10, 2023
1 parent a846fa3 commit 365ab8b
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 11 deletions.
32 changes: 31 additions & 1 deletion mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1905,7 +1905,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
args += output
linkdirs = mesonlib.OrderedSet()
external_deps = target.external_deps.copy()
for d in itertools.chain(target.link_targets, target.link_whole_targets):
for d in target.link_targets:
linkdirs.add(d.subdir)
if d.uses_rust():
# specify `extern CRATE_NAME=OUTPUT_FILE` for each Rust
Expand All @@ -1926,6 +1926,36 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
# Rust uses -l for non rust dependencies, but we still need to
# add dylib=foo
args += ['-l', f'dylib={d.name}']

# Since 1.61.0 Rust has a special modifier for whole-archive linking,
# before that it would treat linking two static libraries as
# whole-archive linking.
#
# We have to disable bundling When whole-archvie is enabled
# See: https://github.com/rust-lang/rust/issues/99429
if mesonlib.version_compare(rustc.version, '>= 1.61.0'):
whole_archive = ':+whole-archive,-bundle'
else:
whole_archive = ''

for d in target.link_whole_targets:
linkdirs.add(d.subdir)
if d.uses_rust():
# specify `extern CRATE_NAME=OUTPUT_FILE` for each Rust
# dependency, so that collisions with libraries in rustc's
# sysroot don't cause ambiguity
args += ['--extern', '{}={}'.format(d.name, os.path.join(d.subdir, d.filename))]
project_deps.append(RustDep(d.name, self.rust_crates[d.name].order))
else:
# Rustc doesn't follow Meson's convention that static libraries
# are called .a, and import libraries are .lib, so we have to
# manually handle that.
if rustc.linker.id in {'link', 'lld-link'}:
for link_whole_arg in rustc.linker.get_link_whole_for([self.get_target_filename_for_linking(d)]):
args += ['-C', f'link-arg={link_whole_arg}']
else:
args += ['-l', f'static{whole_archive}={d.name}']
external_deps.extend(d.external_deps)
for e in external_deps:
for a in e.get_link_args():
if a.endswith(('.dll', '.so', '.dylib')):
Expand Down
6 changes: 4 additions & 2 deletions test cases/rust/3 staticlib/meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
project('rust static library', 'rust')
project('rust static library', 'rust', 'c')

l = static_library('stuff', 'stuff.rs', install : true)
o = static_library('other', 'other.rs')
v = static_library('value', 'value.c')
l = static_library('stuff', 'stuff.rs', link_whole : [o, v], install : true)
e = executable('prog', 'prog.rs', link_with : l, install : true)
test('linktest', e)
5 changes: 5 additions & 0 deletions test cases/rust/3 staticlib/other.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub fn explore(
value: i32,
) -> String {
format!("library{}string", value)
}
4 changes: 3 additions & 1 deletion test cases/rust/3 staticlib/prog.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
extern crate stuff;

fn main() { println!("printing: {}", stuff::explore()); }
fn main() {
println!("printing: {}", stuff::explore());
}
13 changes: 12 additions & 1 deletion test cases/rust/3 staticlib/stuff.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
#![crate_name = "stuff"]

pub fn explore() -> &'static str { "librarystring" }
extern crate other;

extern "C" {
fn c_explore_value() -> i32;
}

pub fn explore(
) -> String {
unsafe {
other::explore(c_explore_value())
}
}
5 changes: 5 additions & 0 deletions test cases/rust/3 staticlib/value.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
int
c_explore_value (void)
{
return 42;
}
12 changes: 12 additions & 0 deletions test cases/rust/5 polyglot static/clib.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <stdio.h>

void hello_from_rust(void);

static void hello_from_c(void) {
printf("Hello from C!\n");
}

void hello_from_both(void) {
hello_from_c();
hello_from_rust();
}
3 changes: 2 additions & 1 deletion test cases/rust/5 polyglot static/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ deps = [

extra_winlibs = meson.get_compiler('c').get_id() in ['msvc', 'clang-cl'] ? ['userenv.lib', 'ws2_32.lib', 'bcrypt.lib'] : []

l = static_library('stuff', 'stuff.rs', rust_crate_type : 'staticlib', install : true)
r = static_library('stuff', 'stuff.rs', rust_crate_type : 'staticlib')
l = static_library('clib', 'clib.c', link_with : r, install : true)
e = executable('prog', 'prog.c',
dependencies: deps,
link_with : l,
Expand Down
5 changes: 2 additions & 3 deletions test cases/rust/5 polyglot static/prog.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include <stdio.h>

void f();
void hello_from_both();

int main(void) {
printf("Hello from C!\n");
f();
hello_from_both();
}
2 changes: 1 addition & 1 deletion test cases/rust/5 polyglot static/stuff.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![crate_name = "stuff"]

#[no_mangle]
pub extern fn f() {
pub extern "C" fn hello_from_rust() {
println!("Hello from Rust!");
}
2 changes: 1 addition & 1 deletion test cases/rust/5 polyglot static/test.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"installed": [
{"type": "exe", "file": "usr/bin/prog"},
{"type": "pdb", "file": "usr/bin/prog"},
{"type": "file", "file": "usr/lib/libstuff.a"}
{"type": "file", "file": "usr/lib/libclib.a"}
]
}

0 comments on commit 365ab8b

Please sign in to comment.