diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 491162bb0204..b48008a77e21 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,31 @@ 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? + # + # If we don't have A C, C++, or Fortran compiler that is + # VisualStudioLike 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 = comp is None + + buildtype = self.environment.coredata.options[OptionKey('buildtype')].value + try: + crt = self.environment.coredata.options[OptionKey('b_vscrt')].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 +1983,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 +2030,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