aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2023-01-05 10:26:08 -0800
committerDylan Baker <dylan@pnwbakers.com>2023-02-22 15:35:50 -0800
commitc0db0b73dac284fe0144929b6aca9c386399c004 (patch)
tree8b6d845c73e3552a3a9e43f53c866ae783939c27
parent81a8c488f007e842e83aa16ba941877250430a15 (diff)
downloadmeson-c0db0b73dac284fe0144929b6aca9c386399c004.zip
meson-c0db0b73dac284fe0144929b6aca9c386399c004.tar.gz
meson-c0db0b73dac284fe0144929b6aca9c386399c004.tar.bz2
Implement rustc controlled whole-archive linking
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, rustc makes some assumptions about library names (specifically static names), that meson does not keep. This can be fixed with rustc 1.67, where a new +verbatim modifier has been added. We can then force rustc to use the name we give it. Before that, we can sneak through `/WHOELARCHIVE:` in cases of dynamic linking (into a dll or exe), but we can't force the archiver to do what we want (rustc considers the archiver to be an implementation detail). The only solution I can come up with is to copy the library to the format that rustc expects. I've run into some issues with that as well, so we warn in that case. The decisions to leave static into static broken on MSVC for 1.61–1.66 was made because: 1) The work around is non-trivial, and we would have to support that workaround for a long time 2) The number of users of Rust in Meson is small 3) The number of users of Rust in Meson on Windows, with MSVC is tiny 4) Using rustup to update rustc on windows is trivial, and solves the problem completely Fixes: #10723 Fixes: #11247 Co-authored-by: Nirbheek Chauhan <nirbheek@centricular.com>
-rw-r--r--mesonbuild/backend/ninjabackend.py52
-rw-r--r--test cases/rust/2 sharedlib/meson.build5
-rw-r--r--test cases/rust/2 sharedlib/stuff.rs10
-rw-r--r--test cases/rust/2 sharedlib/value.c3
-rw-r--r--test cases/rust/3 staticlib/meson.build6
-rw-r--r--test cases/rust/3 staticlib/other.rs5
-rw-r--r--test cases/rust/3 staticlib/prog.rs4
-rw-r--r--test cases/rust/3 staticlib/stuff.rs13
-rw-r--r--test cases/rust/3 staticlib/value.c5
-rw-r--r--test cases/rust/5 polyglot static/clib.c12
-rw-r--r--test cases/rust/5 polyglot static/meson.build3
-rw-r--r--test cases/rust/5 polyglot static/prog.c5
-rw-r--r--test cases/rust/5 polyglot static/stuff.rs2
-rw-r--r--test cases/rust/5 polyglot static/test.json2
14 files changed, 113 insertions, 14 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 21dd87b..3d6bfb9 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1901,7 +1901,8 @@ class NinjaBackend(backends.Backend):
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 @@ class NinjaBackend(backends.Backend):
# 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 02b8cf7..295fa04 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 8cabc62..e7c0521 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 0000000..d17b6de
--- /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 6769564..cf8e103 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 0000000..037be33
--- /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 fbf3181..eee2653 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 8cabc62..7cfcdff 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 0000000..b71c8060
--- /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 0000000..366dbe5
--- /dev/null
+++ b/test cases/rust/5 polyglot static/clib.c
@@ -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();
+}
diff --git a/test cases/rust/5 polyglot static/meson.build b/test cases/rust/5 polyglot static/meson.build
index b2a44da..bed7977 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 dbbd880..0a8e0d1 100644
--- a/test cases/rust/5 polyglot static/prog.c
+++ b/test cases/rust/5 polyglot static/prog.c
@@ -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();
}
diff --git a/test cases/rust/5 polyglot static/stuff.rs b/test cases/rust/5 polyglot static/stuff.rs
index ecf623c..3777ae8 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 1d4eff4..cc0d2da 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"}
]
}