Skip to content

Commit f51081d

Browse files
dcbakerVolker-Weissmann
authored andcommitted
rust: Override the default MSVCRT when linking Rust and !rust together
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.
1 parent 4ad637e commit f51081d

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

mesonbuild/backend/ninjabackend.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1948,6 +1948,49 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
19481948
args += output
19491949
linkdirs = mesonlib.OrderedSet()
19501950
external_deps = target.external_deps.copy()
1951+
1952+
# Have we already injected msvc-crt args?
1953+
#
1954+
# If we don't have A C, C++, or Fortran compiler that is
1955+
# VisualStudioLike treat this as if we've already injected them
1956+
#
1957+
# We handle this here rather than in the rust compiler because in
1958+
# general we don't want to link rust targets to a non-default crt.
1959+
# However, because of the way that MSCRTs work you can only link to one
1960+
# per target, so if A links to the debug one, and B links to the normal
1961+
# one you can't link A and B. Rust is hardcoded to the default one,
1962+
# so if we compile C/C++ code and link against a non-default MSCRT then
1963+
# linking will fail. We can work around this by injecting MSCRT link
1964+
# arguments early in the rustc command line
1965+
# https://github.com/rust-lang/rust/issues/39016
1966+
crt_args_injected = not any(x is not None and x.get_argument_syntax() == 'msvc' for x in
1967+
(self.environment.coredata.compilers[target.for_machine].get(l)
1968+
for l in ['c', 'cpp', 'fortran']))
1969+
1970+
crt_link_args: T.List[str] = []
1971+
try:
1972+
buildtype = self.environment.coredata.options[OptionKey('buildtype')].value
1973+
crt = self.environment.coredata.options[OptionKey('b_vscrt')].value
1974+
is_debug = buildtype == 'debug'
1975+
1976+
if crt == 'from_buildtype':
1977+
crt = 'mdd' if is_debug else 'md'
1978+
elif crt == 'static_from_buildtype':
1979+
crt = 'mtd' if is_debug else 'mt'
1980+
1981+
if crt == 'mdd':
1982+
crt_link_args = ['-l', 'static=msvcrtd']
1983+
elif crt == 'md':
1984+
# this is the default, no need to inject anything
1985+
crt_args_injected = True
1986+
elif crt == 'mtd':
1987+
crt_link_args = ['-l', 'static=libcmtd']
1988+
elif crt == 'mt':
1989+
crt_link_args = ['-l', 'static=libcmt']
1990+
1991+
except KeyError:
1992+
crt_args_injected = True
1993+
19511994
# TODO: we likely need to use verbatim to handle name_prefix and name_suffix
19521995
for d in target.link_targets:
19531996
linkdirs.add(d.subdir)
@@ -1961,7 +2004,13 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
19612004
d_name = self._get_rust_dependency_name(target, d)
19622005
args += ['--extern', '{}={}'.format(d_name, os.path.join(d.subdir, d.filename))]
19632006
project_deps.append(RustDep(d_name, self.rust_crates[d.name].order))
1964-
elif isinstance(d, build.StaticLibrary):
2007+
continue
2008+
2009+
if not crt_args_injected and not {'c', 'cpp', 'fortran'}.isdisjoint(d.compilers):
2010+
args += crt_link_args
2011+
crt_args_injected = True
2012+
2013+
if isinstance(d, build.StaticLibrary):
19652014
# Rustc doesn't follow Meson's convention that static libraries
19662015
# are called .a, and import libraries are .lib, so we have to
19672016
# manually handle that.
@@ -2001,6 +2050,10 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
20012050
args += ['--extern', '{}={}'.format(d_name, os.path.join(d.subdir, d.filename))]
20022051
project_deps.append(RustDep(d_name, self.rust_crates[d.name].order))
20032052
else:
2053+
if not crt_args_injected and not {'c', 'cpp', 'fortran'}.isdisjoint(d.compilers):
2054+
crt_args_injected = True
2055+
crt_args_injected = True
2056+
20042057
if rustc.linker.id in {'link', 'lld-link'}:
20052058
if verbatim:
20062059
# If we can use the verbatim modifier, then everything is great
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"matrix": {
3+
"options": {
4+
"b_vscrt": [
5+
{ "val": "none" },
6+
{ "val": "mdd" },
7+
{ "val": "md" },
8+
{ "val": "mtd" },
9+
{ "val": "mt" },
10+
{ "val": "from_buildtype" },
11+
{ "val": "static_from_buildtype" }
12+
]
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)