From 8539fc76e1c8f871e3988b6e1d8b6c0134c75f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 12 Sep 2025 15:01:48 +0400 Subject: [PATCH 1/5] rust: add test case for issue #15020 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not link with link_whole/bundle, to show linking issue with Apple ld. Signed-off-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- test cases/rust/3 staticlib/meson.build | 3 +++ test cases/rust/3 staticlib/prog2.rs | 5 +++++ test cases/rust/3 staticlib/stuff2.rs | 14 ++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 test cases/rust/3 staticlib/prog2.rs create mode 100644 test cases/rust/3 staticlib/stuff2.rs diff --git a/test cases/rust/3 staticlib/meson.build b/test cases/rust/3 staticlib/meson.build index cf8e1032e13b..c577fab9535f 100644 --- a/test cases/rust/3 staticlib/meson.build +++ b/test cases/rust/3 staticlib/meson.build @@ -5,3 +5,6 @@ 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) + +l = static_library('stuff2', 'stuff2.rs', link_with : [o, v]) +e = executable('prog2', 'prog2.rs', link_with : l) diff --git a/test cases/rust/3 staticlib/prog2.rs b/test cases/rust/3 staticlib/prog2.rs new file mode 100644 index 000000000000..9c25c77d5160 --- /dev/null +++ b/test cases/rust/3 staticlib/prog2.rs @@ -0,0 +1,5 @@ +extern crate stuff2; + +fn main() { + println!("printing: {}", stuff2::explore()); +} diff --git a/test cases/rust/3 staticlib/stuff2.rs b/test cases/rust/3 staticlib/stuff2.rs new file mode 100644 index 000000000000..5e0167ace38a --- /dev/null +++ b/test cases/rust/3 staticlib/stuff2.rs @@ -0,0 +1,14 @@ +#![crate_name = "stuff2"] + +extern crate other; + +extern "C" { + fn c_explore_value() -> i32; +} + +pub fn explore( +) -> String { + unsafe { + other::explore(c_explore_value()) + } +} From 29595ced351d94f9ae06b532e21ad64d86e75173 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 12 Sep 2025 15:25:10 +0200 Subject: [PATCH 2/5] ninjabackend: use compilers.is_library Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index db061f286919..a46c4b4cb8c3 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -25,7 +25,7 @@ from .. import mlog from .. import compilers from ..arglist import CompilerArgs -from ..compilers import Compiler +from ..compilers import Compiler, is_library from ..linkers import ArLikeLinker, RSPFileSyntax from ..mesonlib import ( File, LibType, MachineChoice, MesonBugException, MesonException, OrderedSet, PerMachine, @@ -2110,7 +2110,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: elif a.startswith('-L'): args.append(a) continue - elif a.endswith(('.dll', '.so', '.dylib', '.a', '.lib')): + elif is_library(a): dir_, lib = os.path.split(a) linkdirs.add(dir_) From f3339460882e61c2dc90aa70bf742d6a83ce33de Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 12 Sep 2025 15:27:00 +0200 Subject: [PATCH 3/5] ninjabackend: unify conversion from path to -L/-l When Meson cannot use -Clink-arg=, it has to split the path into a -L option (to add to the search directory) and a -l option (to actually link). Furthermore, if rustc does not support verbatim, Meson needs to undo the addition of "lib" and the extension. Do both tasks in the same place and with the same logic, instead of treating internal and external libraries differently. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 35 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a46c4b4cb8c3..e4063c212e5c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2038,18 +2038,24 @@ def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Comp except (KeyError, AttributeError): pass - if mesonlib.version_compare(rustc.version, '>= 1.67.0'): - verbatim = '+verbatim' - else: - verbatim = '' + has_verbatim = mesonlib.version_compare(rustc.version, '>= 1.67.0') def _link_library(libname: str, static: bool, bundle: bool = False) -> None: type_ = 'static' if static else 'dylib' modifiers = [] + # Except with -Clink-arg, search is limited to the -L search paths + dir_, libname = os.path.split(libname) + linkdirs.add(dir_) if not bundle and static: modifiers.append('-bundle') - if verbatim: - modifiers.append(verbatim) + if has_verbatim: + modifiers.append('+verbatim') + else: + # undo the effects of -l without verbatim + libname, ext = os.path.splitext(libname) + if libname.startswith('lib'): + libname = libname[3:] + if modifiers: type_ += ':' + ','.join(modifiers) args.append(f'-l{type_}={libname}') @@ -2062,11 +2068,11 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: external_deps = target.external_deps.copy() target_deps = target.get_dependencies() for d in target_deps: - linkdirs.add(d.subdir) deps.append(self.get_dependency_filename(d)) if isinstance(d, build.StaticLibrary): external_deps.extend(d.external_deps) if d.uses_rust_abi(): + linkdirs.add(d.subdir) if d not in itertools.chain(target.link_targets, target.link_whole_targets): # Indirect Rust ABI dependency, we only need its path in linkdirs. continue @@ -2094,8 +2100,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: link_whole = d in target.link_whole_targets if isinstance(target, build.StaticLibrary) or (isinstance(target, build.Executable) and rustc.get_crt_static()): static = isinstance(d, build.StaticLibrary) - libname = os.path.basename(lib) if verbatim else d.name - _link_library(libname, static, bundle=link_whole) + _link_library(lib, static, bundle=link_whole) elif link_whole: link_whole_args = rustc.linker.get_link_whole_for([lib]) args += [f'-Clink-arg={a}' for a in link_whole_args] @@ -2111,18 +2116,14 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: args.append(a) continue elif is_library(a): - dir_, lib = os.path.split(a) - linkdirs.add(dir_) - if isinstance(target, build.StaticLibrary): - if not verbatim: - lib, ext = os.path.splitext(lib) - if lib.startswith('lib'): - lib = lib[3:] static = a.endswith(('.a', '.lib')) - _link_library(lib, static) + _link_library(a, static) continue + dir_, _ = os.path.split(lib) + linkdirs.add(dir_) + args.append(f'-Clink-arg={a}') for d in linkdirs: From ed7c08aedc57262e0db9720127c30f99ce6e840e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 12 Sep 2025 15:35:31 +0200 Subject: [PATCH 4/5] ninjabackend: rust: fail on unsupported library names When verbatim is not available, libraries that have custom name_prefix or name_suffix will not be found. Fail the build completely instead of ending up with a bad option. Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index e4063c212e5c..9980cf61bcd6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2041,6 +2041,7 @@ def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Comp has_verbatim = mesonlib.version_compare(rustc.version, '>= 1.67.0') def _link_library(libname: str, static: bool, bundle: bool = False) -> None: + orig_libname = libname type_ = 'static' if static else 'dylib' modifiers = [] # Except with -Clink-arg, search is limited to the -L search paths @@ -2052,9 +2053,14 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: modifiers.append('+verbatim') else: # undo the effects of -l without verbatim + badname = not is_library(libname) libname, ext = os.path.splitext(libname) if libname.startswith('lib'): libname = libname[3:] + else: + badname = True + if badname: + raise MesonException(f"rustc does not implement '-l{type_}:+verbatim'; cannot link to '{orig_libname}' due to nonstandard name") if modifiers: type_ += ':' + ','.join(modifiers) From ec593632303f81048da6f6d27eb5288a458afec5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 12 Sep 2025 14:51:21 +0200 Subject: [PATCH 5/5] rust: query linker in addition to compiler for verbatim support Signed-off-by: Paolo Bonzini --- mesonbuild/backend/ninjabackend.py | 4 +--- mesonbuild/compilers/rust.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 9980cf61bcd6..ba1bab15dea4 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2038,8 +2038,6 @@ def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Comp except (KeyError, AttributeError): pass - has_verbatim = mesonlib.version_compare(rustc.version, '>= 1.67.0') - def _link_library(libname: str, static: bool, bundle: bool = False) -> None: orig_libname = libname type_ = 'static' if static else 'dylib' @@ -2049,7 +2047,7 @@ def _link_library(libname: str, static: bool, bundle: bool = False) -> None: linkdirs.add(dir_) if not bundle and static: modifiers.append('-bundle') - if has_verbatim: + if rustc.has_verbatim(): modifiers.append('+verbatim') else: # undo the effects of -l without verbatim diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index d0f92668a00e..c0b01bb3fcef 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -11,7 +11,7 @@ import typing as T from .. import options -from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged +from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged, version_compare from ..options import OptionKey from .compilers import Compiler, CompileCheckMode, clike_debug_args @@ -194,6 +194,20 @@ def get_cfgs(self) -> T.List[str]: def get_crt_static(self) -> bool: return 'target_feature="crt-static"' in self.get_cfgs() + @functools.lru_cache(maxsize=None) + def has_verbatim(self) -> bool: + if version_compare(self.version, '< 1.67.0'): + return False + # GNU ld support '-l:PATH' + if 'ld.' in self.linker.id: + return True + # -l:+verbatim does not work (yet?) with MSVC link or Apple ld64 + # (https://github.com/rust-lang/rust/pull/138753). For ld64, it + # works together with -l:+whole_archive because -force_load (the macOS + # equivalent of --whole-archive), receives the full path to the library + # being linked. However, Meson uses "bundle", not "whole_archive". + return False + def get_debug_args(self, is_debug: bool) -> T.List[str]: return clike_debug_args[is_debug]