diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 717d5635f842..dc05b19fce43 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -9,10 +9,13 @@ import re import typing as T +from .. import arglist from .. import options +from ..linkers.linkers import GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged from ..options import OptionKey from .compilers import Compiler, clike_debug_args +from .mixins.clike import GROUP_FLAGS if T.TYPE_CHECKING: from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType @@ -62,6 +65,35 @@ def get_rustup_run_and_args(exelist: T.List[str]) -> T.Optional[T.Tuple[T.List[s except StopIteration: return None + +class RustcCompilerArgs(arglist.CompilerArgs): + def to_native_inplace(self): + # Check if we need to add --start/end-group for circular dependencies + # between static libraries, and for recursively searching for symbols + # needed by static libraries that are provided by object files or + # shared libraries. + # This covers all ld.bfd, ld.gold, ld.gold, and xild on Linux, which + # all act like (or are) gnu ld + if isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker)): + group_start = -1 + group_end = -1 + last_rustc_arg = -1 + for i, each in enumerate(self): + if each in {'--emit', '-o', '--extern'}: + last_rustc_arg = i + if last_rustc_arg == i - 1 or not GROUP_FLAGS.search(each): + continue + group_end = i + if group_start < 0: + # First occurrence of a library + group_start = i + # Only add groups if there are multiple libraries. + if group_end > group_start >= 0: + # Last occurrence of a library + self.insert(group_end + 1, '-Clink-arg=-Wl,--end-group') + self.insert(group_start, '-Clink-arg=-Wl,--start-group') + + class RustCompiler(Compiler): # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX @@ -100,6 +132,10 @@ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoic self.base_options.add(OptionKey('b_vscrt')) self.native_static_libs: T.List[str] = [] + def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> RustcCompilerArgs: + # This is correct, mypy just doesn't understand co-operative inheritance + return RustcCompilerArgs(self, args) + def needs_static_linker(self) -> bool: return False diff --git a/test cases/rust/27 staticlib group/lib1.c b/test cases/rust/27 staticlib group/lib1.c new file mode 100644 index 000000000000..8e759852e190 --- /dev/null +++ b/test cases/rust/27 staticlib group/lib1.c @@ -0,0 +1,11 @@ +#include +#include "lib1.h" +#include "lib2.h" + +void from_lib2(void) { + printf("hello world"); +} + +void c_func(void) { + from_lib1(); +} diff --git a/test cases/rust/27 staticlib group/lib1.h b/test cases/rust/27 staticlib group/lib1.h new file mode 100644 index 000000000000..8bb18d4bbe4e --- /dev/null +++ b/test cases/rust/27 staticlib group/lib1.h @@ -0,0 +1,4 @@ +#pragma once + +void from_lib2(void); +void c_func(void); diff --git a/test cases/rust/27 staticlib group/lib2.c b/test cases/rust/27 staticlib group/lib2.c new file mode 100644 index 000000000000..a61d5349f878 --- /dev/null +++ b/test cases/rust/27 staticlib group/lib2.c @@ -0,0 +1,8 @@ +#include +#include "lib1.h" +#include "lib2.h" + +void from_lib1(void) +{ + from_lib2(); +} diff --git a/test cases/rust/27 staticlib group/lib2.h b/test cases/rust/27 staticlib group/lib2.h new file mode 100644 index 000000000000..08c4cd30ad1e --- /dev/null +++ b/test cases/rust/27 staticlib group/lib2.h @@ -0,0 +1,3 @@ +#pragma once + +void from_lib1(void); diff --git a/test cases/rust/27 staticlib group/main.c b/test cases/rust/27 staticlib group/main.c new file mode 100644 index 000000000000..f96ba6687221 --- /dev/null +++ b/test cases/rust/27 staticlib group/main.c @@ -0,0 +1,6 @@ +#include "lib1.h" +#include "lib2.h" + +void main() { + c_func(); +} diff --git a/test cases/rust/27 staticlib group/main.rs b/test cases/rust/27 staticlib group/main.rs new file mode 100644 index 000000000000..538359943271 --- /dev/null +++ b/test cases/rust/27 staticlib group/main.rs @@ -0,0 +1,9 @@ +extern "C" { + fn c_func(); +} + +fn main() { + unsafe { + c_func(); + } +} diff --git a/test cases/rust/27 staticlib group/meson.build b/test cases/rust/27 staticlib group/meson.build new file mode 100644 index 000000000000..a83f0834c5fe --- /dev/null +++ b/test cases/rust/27 staticlib group/meson.build @@ -0,0 +1,13 @@ +project('staticlib group', 'c', 'rust') + +ld_bfd = find_program('ld.bfd') +if not ld_bfd.found() + error('MESON_SKIP_TEST ld.bfd not found') +endif + +lib1 = static_library('lib1', 'lib1.c') +lib2 = static_library('lib2', 'lib2.c') +executable('lib1first', 'main.rs', link_with : [lib1, lib2], + rust_args: ['-C', 'lto=n', '-C', 'linker-plugin-lto=n', '-Clink-arg=-fuse-ld=bfd']) +executable('lib2first', 'main.rs', link_with : [lib2, lib1], + rust_args: ['-C', 'lto=n', '-C', 'linker-plugin-lto=n', '-Clink-arg=-fuse-ld=bfd'])