From b7f5731cccc60780fa5150f4726d3924357ba83c Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 23 Jun 2023 13:24:50 -0700 Subject: [PATCH] rust: Override the default MSVCRT when linking Rust and !rust together --- mesonbuild/backend/ninjabackend.py | 39 +++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 47a564ad7eb8..837d03c5ec98 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -37,6 +37,7 @@ from .. import compilers from ..arglist import CompilerArgs from ..compilers import Compiler +from ..compilers.mixins.visualstudio import VisualStudioLikeCompiler from ..linkers import ArLinker, RSPFileSyntax from ..mesonlib import ( File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine, @@ -1944,6 +1945,30 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: args += output linkdirs = mesonlib.OrderedSet() external_deps = target.external_deps.copy() + + # Have we already injected msvc-crt args? + # On non-windows targets treat this as if we've already injected them + # Also, if we don't have A C, C++, or Fortran compiler treat this as if we've already injected them + # + # We handle this here rather than in the rust compiler because in + # general we don't want to link rust targets to a non-default crt. + # However, because of the way that MSCRTs work you can only link to one + # per target, so if A links to the debug one, and B links to the normal + # one you can't link A and B. Rust is hardcoded to the default one, + # so if we compile C/C++ code and link against a non-default MSCRT then + # linking will fail. We can work around this by injecting MSCRT link + # arguments early in the rustc command line + # https://github.com/rust-lang/rust/issues/39016#issuecomment-853964918 + comp = mesonlib.first((self.environment.coredata.compilers[target.for_machine].get(l) for l in ['c', 'cpp', 'fortran']), + lambda x: x is not None and isinstance(x, VisualStudioLikeCompiler)) + crt_args_injected = not (self.environment.machines[target.for_machine].is_windows() and comp is not None) + + buildtype = self.environment.coredata.options[OptionKey('buildtype')].value + try: + crt = self.environment.coredata.options[OptionKey('b_scrt')].value + except KeyError: + crt = 'fake' + # TODO: we likely need to use verbatim to handle name_prefix and name_suffix for d in target.link_targets: linkdirs.add(d.subdir) @@ -1957,7 +1982,14 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: d_name = self._get_rust_dependency_name(target, d) args += ['--extern', '{}={}'.format(d_name, os.path.join(d.subdir, d.filename))] project_deps.append(RustDep(d_name, self.rust_crates[d.name].order)) - elif isinstance(d, build.StaticLibrary): + continue + + if not crt_args_injected and not {'c', 'cpp', 'fortran'}.isdisjoint(d.compilers): + for larg in comp.get_crt_link_args(crt, buildtype): + args += ['-C', f'link-arg={larg}'] + crt_args_injected = True + + if isinstance(d, build.StaticLibrary): # Rustc doesn't follow Meson's convention that static libraries # are called .a, and import libraries are .lib, so we have to # manually handle that. @@ -1997,6 +2029,11 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: 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 not crt_args_injected and not {'c', 'cpp', 'fortran'}.isdisjoint(d.compilers): + for larg in comp.get_crt_link_args(crt, buildtype): + args += ['-C', f'link-arg={larg}'] + crt_args_injected = True + if rustc.linker.id in {'link', 'lld-link'}: if verbatim: # If we can use the verbatim modifier, then everything is great