Skip to content

Commit

Permalink
Auto merge of #67429 - mati865:mingw-ultimate-fix, r=alexcrichton
Browse files Browse the repository at this point in the history
windows-gnu: prefer system crt libraries if they are available

The origin of the issue is the fact Rust ships mingw-w64 libraries but no headers and prefers own libraries over the system ones.
This leads to situation when headers aren't compatible with libraries (mingw-w64 doesn't provide any forward compatibility and AFAIK backwards compatibility is guaranteed only within major release series).

It's easier to understand how this PR works when looking at the linker invocation before and with this PR: https://www.diffchecker.com/GEuYFmzo
It adds system libraries path before Rust libraries so the linker will prefer them.
It has potential issue when system has files with the same names as Rust but that could be avoided by moving Rust shipped mingw-w64 libraries from `lib/rustlib/x86_64-pc-windows-gnu/lib` to say `lib/rustlib/x86_64-pc-windows-gnu/lib/mingw`. Then adding linker paths in this order: Rust libraries, system libraries, Rust shipped mingw-w64 libraries.

Fixes #47048
Fixes #49078
Fixes #53454
Fixes #60912
  • Loading branch information
bors committed Feb 5, 2020
2 parents eda1a7a + 1fad337 commit 58b8343
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
78 changes: 78 additions & 0 deletions src/librustc_codegen_ssa/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf> {
fn find_exe_in_path<P>(exe_name: P) -> Option<PathBuf>
where
P: AsRef<Path>,
{
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<PathBuf> {
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() {
Expand Down Expand Up @@ -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()) {
Expand Down
5 changes: 5 additions & 0 deletions src/librustc_session/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FxHashMap<Span, Span>>,

/// 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<RefCell<Option<Option<PathBuf>>>>,
}

pub struct PerfStats {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 58b8343

Please sign in to comment.