diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 3a14fa5ae3196..4f05aac089867 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -968,7 +968,78 @@ pub fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary } } +// Because windows-gnu target is meant to be self-contained for pure Rust code it bundles +// own mingw-w64 libraries. These libraries are usually not compatible with mingw-w64 +// installed in the system. This breaks many cases where Rust is mixed with other languages +// (e.g. *-sys crates). +// We prefer system mingw-w64 libraries if they are available to avoid this issue. +fn get_crt_libs_path(sess: &Session) -> Option { + fn find_exe_in_path

(exe_name: P) -> Option + where + P: AsRef, + { + for dir in env::split_paths(&env::var_os("PATH")?) { + let full_path = dir.join(&exe_name); + if full_path.is_file() { + return Some(fix_windows_verbatim_for_gcc(&full_path)); + } + } + None + } + + fn probe(sess: &Session) -> Option { + if let (linker, LinkerFlavor::Gcc) = linker_and_flavor(&sess) { + let linker_path = if cfg!(windows) && linker.extension().is_none() { + linker.with_extension("exe") + } else { + linker + }; + if let Some(linker_path) = find_exe_in_path(linker_path) { + let mingw_arch = match &sess.target.target.arch { + x if x == "x86" => "i686", + x => x, + }; + let mingw_dir = format!("{}-w64-mingw32", mingw_arch); + // Here we have path/bin/gcc but we need path/ + let mut path = linker_path; + path.pop(); + path.pop(); + // Based on Clang MinGW driver + let probe_path = path.join(&mingw_dir).join("lib"); + if probe_path.exists() { + return Some(probe_path); + }; + let probe_path = path.join(&mingw_dir).join("sys-root/mingw/lib"); + if probe_path.exists() { + return Some(probe_path); + }; + }; + }; + None + } + + let mut system_library_path = sess.system_library_path.borrow_mut(); + match &*system_library_path { + Some(Some(compiler_libs_path)) => Some(compiler_libs_path.clone()), + Some(None) => None, + None => { + let path = probe(sess); + *system_library_path = Some(path.clone()); + path + } + } +} + pub fn get_file_path(sess: &Session, name: &str) -> PathBuf { + // prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details + if sess.target.target.llvm_target.contains("windows-gnu") { + if let Some(compiler_libs_path) = get_crt_libs_path(sess) { + let file_path = compiler_libs_path.join(name); + if file_path.exists() { + return file_path; + } + } + } let fs = sess.target_filesearch(PathKind::Native); let file_path = fs.get_lib_path().join(name); if file_path.exists() { @@ -1150,6 +1221,13 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( // target descriptor let t = &sess.target.target; + // prefer system mingw-w64 libs, see get_crt_libs_path comment for more details + if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") { + if let Some(compiler_libs_path) = get_crt_libs_path(sess) { + cmd.include_path(&compiler_libs_path); + } + } + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index f8cba58f9d904..70984917d7ca2 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -132,6 +132,10 @@ pub struct Session { /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: Lock>, + + /// Path for libraries that will take preference over libraries shipped by Rust. + /// Used by windows-gnu targets to priortize system mingw-w64 libraries. + pub system_library_path: OneThread>>>, } pub struct PerfStats { @@ -1068,6 +1072,7 @@ fn build_session_( driver_lint_caps, trait_methods_not_found: Lock::new(Default::default()), confused_type_with_std_module: Lock::new(Default::default()), + system_library_path: OneThread::new(RefCell::new(Default::default())), }; validate_commandline_args_with_session_available(&sess);