Skip to content

Commit d4ed1a1

Browse files
committed
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 ab859a2 commit d4ed1a1

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
@@ -1943,6 +1943,49 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
19431943
args += output
19441944
linkdirs = mesonlib.OrderedSet()
19451945
external_deps = target.external_deps.copy()
1946+
1947+
# Have we already injected msvc-crt args?
1948+
#
1949+
# If we don't have A C, C++, or Fortran compiler that is
1950+
# VisualStudioLike treat this as if we've already injected them
1951+
#
1952+
# We handle this here rather than in the rust compiler because in
1953+
# general we don't want to link rust targets to a non-default crt.
1954+
# However, because of the way that MSCRTs work you can only link to one
1955+
# per target, so if A links to the debug one, and B links to the normal
1956+
# one you can't link A and B. Rust is hardcoded to the default one,
1957+
# so if we compile C/C++ code and link against a non-default MSCRT then
1958+
# linking will fail. We can work around this by injecting MSCRT link
1959+
# arguments early in the rustc command line
1960+
# https://github.com/rust-lang/rust/issues/39016
1961+
crt_args_injected = not any(x is not None and x.get_argument_syntax() == 'msvc' for x in
1962+
(self.environment.coredata.compilers[target.for_machine].get(l)
1963+
for l in ['c', 'cpp', 'fortran']))
1964+
1965+
crt_link_args: T.List[str] = []
1966+
try:
1967+
buildtype = self.environment.coredata.options[OptionKey('buildtype')].value
1968+
crt = self.environment.coredata.options[OptionKey('b_vscrt')].value
1969+
is_debug = buildtype == 'debug'
1970+
1971+
if crt == 'from_buildtype':
1972+
crt = 'mdd' if is_debug else 'md'
1973+
elif crt == 'static_from_buildtype':
1974+
crt = 'mtd' if is_debug else 'mt'
1975+
1976+
if crt == 'mdd':
1977+
crt_link_args = ['-l', 'static=msvcrtd']
1978+
elif crt == 'md':
1979+
# this is the default, no need to inject anything
1980+
crt_args_injected = True
1981+
elif crt == 'mtd':
1982+
crt_link_args = ['-l', 'static=libcmtd']
1983+
elif crt == 'mt':
1984+
crt_link_args = ['-l', 'static=libcmt']
1985+
1986+
except KeyError:
1987+
crt_args_injected = True
1988+
19461989
# TODO: we likely need to use verbatim to handle name_prefix and name_suffix
19471990
for d in target.link_targets:
19481991
linkdirs.add(d.subdir)
@@ -1956,7 +1999,13 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
19561999
d_name = self._get_rust_dependency_name(target, d)
19572000
args += ['--extern', '{}={}'.format(d_name, os.path.join(d.subdir, d.filename))]
19582001
project_deps.append(RustDep(d_name, self.rust_crates[d.name].order))
1959-
elif isinstance(d, build.StaticLibrary):
2002+
continue
2003+
2004+
if not crt_args_injected and not {'c', 'cpp', 'fortran'}.isdisjoint(d.compilers):
2005+
args += crt_link_args
2006+
crt_args_injected = True
2007+
2008+
if isinstance(d, build.StaticLibrary):
19602009
# Rustc doesn't follow Meson's convention that static libraries
19612010
# are called .a, and import libraries are .lib, so we have to
19622011
# manually handle that.
@@ -1996,6 +2045,10 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
19962045
args += ['--extern', '{}={}'.format(d_name, os.path.join(d.subdir, d.filename))]
19972046
project_deps.append(RustDep(d_name, self.rust_crates[d.name].order))
19982047
else:
2048+
if not crt_args_injected and not {'c', 'cpp', 'fortran'}.isdisjoint(d.compilers):
2049+
crt_args_injected = True
2050+
crt_args_injected = True
2051+
19992052
if rustc.linker.id in {'link', 'lld-link'}:
20002053
if verbatim:
20012054
# 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)