diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index db061f286919..ba1bab15dea4 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, @@ -2038,18 +2038,28 @@ 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 = '' - 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 + dir_, libname = os.path.split(libname) + linkdirs.add(dir_) if not bundle and static: modifiers.append('-bundle') - if verbatim: - modifiers.append(verbatim) + if rustc.has_verbatim(): + 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) args.append(f'-l{type_}={libname}') @@ -2062,11 +2072,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 +2104,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] @@ -2110,19 +2119,15 @@ 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')): - dir_, lib = os.path.split(a) - linkdirs.add(dir_) - + elif is_library(a): 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: 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] 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()) + } +}