Skip to content

Commit

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

This is my proposal (based on `Amanieu`'s idea) on how to fix #47048 and related issues.

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.

I don't know if it's worth to cache system libraries path. You can look for `cache: ` string during build Rust: https://pastebin.com/kGEQZGWP
I think there are enough calls to justify caching.

Fixes #47048
Fixes #49078
Fixes #53454
Fixes #60912
  • Loading branch information
bors committed Jan 31, 2020
2 parents b1cb3c0 + 229998b commit 3392cc9
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3436,6 +3436,7 @@ dependencies = [
"bitflags",
"cc",
"jobserver",
"lazy_static 1.4.0",
"libc",
"log",
"memmap",
Expand Down
15 changes: 13 additions & 2 deletions src/ci/azure-pipelines/try.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,19 @@ jobs:
strategy:
matrix:
dist-x86_64-linux: {}
dist-x86_64-linux-alt:
IMAGE: dist-x86_64-linux
- job: Windows
timeoutInMinutes: 600
pool:
vmImage: 'vs2017-win2016'
steps:
- template: steps/run.yml
strategy:
matrix:
dist-x86_64-mingw:
SCRIPT: python x.py dist
RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-full-tools --enable-profiler
CUSTOM_MINGW: 1
DIST_REQUIRE_ALL_TOOLS: 1

# The macOS and Windows builds here are currently disabled due to them not being
# overly necessary on `try` builds. We also don't actually have anything that
Expand Down
1 change: 1 addition & 0 deletions src/librustc_codegen_ssa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ bitflags = "1.2.1"
cc = "1.0.1"
num_cpus = "1.0"
memmap = "0.7"
lazy_static = "1"
log = "0.4.5"
libc = "0.2.44"
jobserver = "0.1.11"
Expand Down
80 changes: 80 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,81 @@ 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 often 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_some() {
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() {
*SYSTEM_LIBS.lock().unwrap() = Some(Some(probe_path.clone()));
return Some(probe_path);
};
let probe_path = path.join(&mingw_dir).join("sys-root/mingw/lib");
if probe_path.exists() {
*SYSTEM_LIBS.lock().unwrap() = Some(Some(probe_path.clone()));
return Some(probe_path);
};
};
};
*SYSTEM_LIBS.lock().unwrap() = Some(None);
None
}

use std::sync::Mutex;
lazy_static::lazy_static! {
static ref SYSTEM_LIBS: Mutex<Option<Option<PathBuf>>> = Mutex::new(None);
}

let system_libs = SYSTEM_LIBS.lock().unwrap().clone();
match system_libs {
Some(Some(compiler_libs_path)) => Some(compiler_libs_path),
Some(None) => None,
None => probe(sess),
}
}

pub fn get_file_path(sess: &Session, name: &str) -> PathBuf {
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 +1224,12 @@ fn link_args<'a, B: ArchiveBuilder<'a>>(
// target descriptor
let t = &sess.target.target;

if 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

0 comments on commit 3392cc9

Please sign in to comment.