Skip to content

Commit

Permalink
rust: Override the default MSVCRT when linking Rust and !rust together
Browse files Browse the repository at this point in the history
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
  • Loading branch information
dcbaker authored and Dudemanguy committed Aug 7, 2023
1 parent a29acf6 commit 5985294
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
55 changes: 54 additions & 1 deletion mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1948,6 +1948,49 @@ 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
crt_args_injected = not any(x is not None and x.get_argument_syntax() == 'msvc' for x in
(self.environment.coredata.compilers[target.for_machine].get(l)
for l in ['c', 'cpp', 'fortran']))

crt_link_args: T.List[str] = []
try:
buildtype = self.environment.coredata.options[OptionKey('buildtype')].value
crt = self.environment.coredata.options[OptionKey('b_vscrt')].value
is_debug = buildtype == 'debug'

if crt == 'from_buildtype':
crt = 'mdd' if is_debug else 'md'
elif crt == 'static_from_buildtype':
crt = 'mtd' if is_debug else 'mt'

if crt == 'mdd':
crt_link_args = ['-l', 'static=msvcrtd']
elif crt == 'md':
# this is the default, no need to inject anything
crt_args_injected = True
elif crt == 'mtd':
crt_link_args = ['-l', 'static=libcmtd']
elif crt == 'mt':
crt_link_args = ['-l', 'static=libcmt']

except KeyError:
crt_args_injected = True

# TODO: we likely need to use verbatim to handle name_prefix and name_suffix
for d in target.link_targets:
linkdirs.add(d.subdir)
Expand All @@ -1961,7 +2004,13 @@ 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):
args += crt_link_args
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.
Expand Down Expand Up @@ -2001,6 +2050,10 @@ 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):
crt_args_injected = True
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
Expand Down
15 changes: 15 additions & 0 deletions test cases/rust/20 rust and cpp/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"matrix": {
"options": {
"b_vscrt": [
{ "val": "none" },
{ "val": "mdd" },
{ "val": "md" },
{ "val": "mtd" },
{ "val": "mt" },
{ "val": "from_buildtype" },
{ "val": "static_from_buildtype" }
]
}
}
}

0 comments on commit 5985294

Please sign in to comment.