diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 21dd87b98f35..3d6bfb90658a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1901,7 +1901,8 @@ 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): + # TODO: we likely need to use verbatim to handle name_prefix and name_suffix + for d in target.link_targets: linkdirs.add(d.subdir) if d.uses_rust(): # specify `extern CRATE_NAME=OUTPUT_FILE` for each Rust @@ -1922,6 +1923,55 @@ 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. However, to make this work we have to disable + # bundling, which can't be done until 1.63.0… So for 1.61–1.62 we just + # have to hope that the default cases of +whole-archive are sufficent. + # See: https://github.com/rust-lang/rust/issues/99429 + if mesonlib.version_compare(rustc.version, '>= 1.63.0'): + whole_archive = ':+whole-archive,-bundle' + else: + whole_archive = '' + + if mesonlib.version_compare(rustc.version, '>= 1.67.0'): + verbatim = ',+verbatim' + else: + verbatim = '' + + 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: + if rustc.linker.id in {'link', 'lld-link'}: + if verbatim: + # If we can use the verbatim modifier, then everything is great + args += ['-l', f'static{whole_archive}{verbatim}={d.get_outputs()[0]}'] + elif isinstance(target, build.StaticLibrary): + # If we don't, for static libraries the only option is + # to make a copy, since we can't pass objects in, or + # directly affect the archiver. but we're not going to + # do that given how quickly rustc versions go out of + # support unless there's a compelling reason to do so. + # This only affects 1.61–1.66 + mlog.warning('Due to limitations in Rustc versions 1.61–1.66 and meson library naming', + 'whole-archive linking with MSVC may or may not work. Upgrade rustc to', + '>= 1.67. A best effort is being made, but likely won\'t work') + args += ['-l', f'static={d.name}'] + else: + # When doing dynamic linking (binaries and [c]dylibs), + # we can instead just proxy the correct arguments to the linker + 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')): diff --git a/test cases/rust/2 sharedlib/meson.build b/test cases/rust/2 sharedlib/meson.build index 02b8cf74150e..295fa049740a 100644 --- a/test cases/rust/2 sharedlib/meson.build +++ b/test cases/rust/2 sharedlib/meson.build @@ -1,10 +1,11 @@ -project('rust shared library', 'rust') +project('rust shared library', 'rust', 'c') if host_machine.system() == 'darwin' error('MESON_SKIP_TEST: does not work right on macos, please fix!') endif -l = shared_library('stuff', 'stuff.rs', install : true) +s = static_library('static', 'value.c') +l = shared_library('stuff', 'stuff.rs', link_whole : s, install : true) e = executable('prog', 'prog.rs', link_with : l, install : true) if build_machine.system() == 'windows' diff --git a/test cases/rust/2 sharedlib/stuff.rs b/test cases/rust/2 sharedlib/stuff.rs index 8cabc62572e3..e7c0521fe2fd 100644 --- a/test cases/rust/2 sharedlib/stuff.rs +++ b/test cases/rust/2 sharedlib/stuff.rs @@ -1,3 +1,11 @@ #![crate_name = "stuff"] -pub fn explore() -> &'static str { "librarystring" } +extern "C" { + fn c_value() -> i32; +} + +pub fn explore() -> String { + unsafe { + format!("library{}string", c_value()) + } +} diff --git a/test cases/rust/2 sharedlib/value.c b/test cases/rust/2 sharedlib/value.c new file mode 100644 index 000000000000..d17b6debe521 --- /dev/null +++ b/test cases/rust/2 sharedlib/value.c @@ -0,0 +1,3 @@ +int c_value(void) { + return 7; +} diff --git a/test cases/rust/3 staticlib/meson.build b/test cases/rust/3 staticlib/meson.build index 6769564637b8..cf8e1032e13b 100644 --- a/test cases/rust/3 staticlib/meson.build +++ b/test cases/rust/3 staticlib/meson.build @@ -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) diff --git a/test cases/rust/3 staticlib/other.rs b/test cases/rust/3 staticlib/other.rs new file mode 100644 index 000000000000..037be33eed4a --- /dev/null +++ b/test cases/rust/3 staticlib/other.rs @@ -0,0 +1,5 @@ +pub fn explore( + value: i32, +) -> String { + format!("library{}string", value) +} diff --git a/test cases/rust/3 staticlib/prog.rs b/test cases/rust/3 staticlib/prog.rs index fbf31815910f..eee26539b363 100644 --- a/test cases/rust/3 staticlib/prog.rs +++ b/test cases/rust/3 staticlib/prog.rs @@ -1,3 +1,5 @@ extern crate stuff; -fn main() { println!("printing: {}", stuff::explore()); } +fn main() { + println!("printing: {}", stuff::explore()); +} diff --git a/test cases/rust/3 staticlib/stuff.rs b/test cases/rust/3 staticlib/stuff.rs index 8cabc62572e3..7cfcdffff119 100644 --- a/test cases/rust/3 staticlib/stuff.rs +++ b/test cases/rust/3 staticlib/stuff.rs @@ -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()) + } +} diff --git a/test cases/rust/3 staticlib/value.c b/test cases/rust/3 staticlib/value.c new file mode 100644 index 000000000000..b71c8060aa22 --- /dev/null +++ b/test cases/rust/3 staticlib/value.c @@ -0,0 +1,5 @@ +int +c_explore_value (void) +{ + return 42; +} diff --git a/test cases/rust/5 polyglot static/clib.c b/test cases/rust/5 polyglot static/clib.c new file mode 100644 index 000000000000..366dbe592cb9 --- /dev/null +++ b/test cases/rust/5 polyglot static/clib.c @@ -0,0 +1,12 @@ +#include + +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(); +} diff --git a/test cases/rust/5 polyglot static/meson.build b/test cases/rust/5 polyglot static/meson.build index b2a44dad9790..bed7977abaea 100644 --- a/test cases/rust/5 polyglot static/meson.build +++ b/test cases/rust/5 polyglot static/meson.build @@ -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, diff --git a/test cases/rust/5 polyglot static/prog.c b/test cases/rust/5 polyglot static/prog.c index dbbd8800aa5d..0a8e0d1baa4b 100644 --- a/test cases/rust/5 polyglot static/prog.c +++ b/test cases/rust/5 polyglot static/prog.c @@ -1,8 +1,7 @@ #include -void f(); +void hello_from_both(); int main(void) { - printf("Hello from C!\n"); - f(); + hello_from_both(); } diff --git a/test cases/rust/5 polyglot static/stuff.rs b/test cases/rust/5 polyglot static/stuff.rs index ecf623c644e1..3777ae85a68e 100644 --- a/test cases/rust/5 polyglot static/stuff.rs +++ b/test cases/rust/5 polyglot static/stuff.rs @@ -1,6 +1,6 @@ #![crate_name = "stuff"] #[no_mangle] -pub extern fn f() { +pub extern "C" fn hello_from_rust() { println!("Hello from Rust!"); } diff --git a/test cases/rust/5 polyglot static/test.json b/test cases/rust/5 polyglot static/test.json index 1d4eff4be6b2..cc0d2da7eefc 100644 --- a/test cases/rust/5 polyglot static/test.json +++ b/test cases/rust/5 polyglot static/test.json @@ -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"} ] }