Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Windows static library detection and usage #154

Merged
merged 3 commits into from Jan 17, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 50 additions & 27 deletions src/lib.rs
Expand Up @@ -776,34 +776,38 @@ impl Library {
}

let prefix = "lib";
if target.contains("msvc") {
// According to link.exe documentation:
// https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
//
// LINK doesn't use file extensions to make assumptions about the contents of a file.
// Instead, LINK examines each input file to determine what kind of file it is.
//
// However, rustc appends `.lib` to the string it receives from the -l command line argument,
// which it receives from Cargo via cargo:rustc-link-lib:
// https://github.com/rust-lang/rust/blob/657f246812ab2684e3c3954b1c77f98fd59e0b21/compiler/rustc_codegen_ssa/src/back/linker.rs#L828
// https://github.com/rust-lang/rust/blob/657f246812ab2684e3c3954b1c77f98fd59e0b21/compiler/rustc_codegen_ssa/src/back/linker.rs#L843
// So the only file extension that works for MSVC targets is `.lib`
return test_suffixes(filename, &[".lib"]);
} else if target.contains("windows") && target.contains("gnu") {
// GNU targets for Windows, including gnullvm, use `LinkerFlavor::Gcc` internally in rustc,
// which tells rustc to use the GNU linker. rustc does not prepend/append to the string it
// receives via the -l command line argument before passing it to the linker:
// https://github.com/rust-lang/rust/blob/657f246812ab2684e3c3954b1c77f98fd59e0b21/compiler/rustc_codegen_ssa/src/back/linker.rs#L446
// https://github.com/rust-lang/rust/blob/657f246812ab2684e3c3954b1c77f98fd59e0b21/compiler/rustc_codegen_ssa/src/back/linker.rs#L457
// GNU ld can work with more types of files than just the .lib files that MSVC's link.exe needs.
// GNU ld will prepend the `lib` prefix to the filename if necessary, so it is okay to remove
// the `lib` prefix from the filename. The `.a` suffix *requires* the `lib` prefix.
// https://sourceware.org/binutils/docs-2.39/ld.html#index-direct-linking-to-a-dll
if filename.starts_with(prefix) {
if target.contains("windows") {
if target.contains("gnu") && filename.starts_with(prefix) {
// GNU targets for Windows, including gnullvm, use `LinkerFlavor::Gcc` internally in rustc,
// which tells rustc to use the GNU linker. rustc does not prepend/append to the string it
// receives via the -l command line argument before passing it to the linker:
// https://github.com/rust-lang/rust/blob/657f246812ab2684e3c3954b1c77f98fd59e0b21/compiler/rustc_codegen_ssa/src/back/linker.rs#L446
// https://github.com/rust-lang/rust/blob/657f246812ab2684e3c3954b1c77f98fd59e0b21/compiler/rustc_codegen_ssa/src/back/linker.rs#L457
// GNU ld can work with more types of files than just the .lib files that MSVC's link.exe needs.
// GNU ld will prepend the `lib` prefix to the filename if necessary, so it is okay to remove
// the `lib` prefix from the filename. The `.a` suffix *requires* the `lib` prefix.
// https://sourceware.org/binutils/docs-2.39/ld.html#index-direct-linking-to-a-dll
let filename = &filename[prefix.len()..];
return test_suffixes(filename, &[".dll.a", ".dll", ".lib", ".a"]);
} else {
return test_suffixes(filename, &[".dll.a", ".dll", ".lib"]);
// According to link.exe documentation:
// https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
//
// LINK doesn't use file extensions to make assumptions about the contents of a file.
// Instead, LINK examines each input file to determine what kind of file it is.
//
// However, rustc appends `.lib` to the string it receives from the -l command line argument,
// which it receives from Cargo via cargo:rustc-link-lib:
// https://github.com/rust-lang/rust/blob/657f246812ab2684e3c3954b1c77f98fd59e0b21/compiler/rustc_codegen_ssa/src/back/linker.rs#L828
// https://github.com/rust-lang/rust/blob/657f246812ab2684e3c3954b1c77f98fd59e0b21/compiler/rustc_codegen_ssa/src/back/linker.rs#L843
// So the only file extension that works for MSVC targets is `.lib`
// However, for externally created libraries, there's no
// guarantee that the extension is ".lib" so we need to
// consider all options.
// See:
// https://github.com/mesonbuild/meson/issues/8153
// https://github.com/rust-lang/rust/issues/114013
return test_suffixes(filename, &[".dll.a", ".dll", ".lib", ".a"]);
}
} else if target.contains("apple") {
if filename.starts_with(prefix) {
Expand Down Expand Up @@ -901,6 +905,10 @@ impl Library {
iter.next().map(|s| s.to_owned()),
);
}
"-u" => {
let meta = format!("rustc-link-arg=-Wl,-u,{}", val);
config.print_metadata(&meta);
}
_ => {}
}
}
Expand All @@ -927,6 +935,12 @@ impl Library {
self.include_paths.push(PathBuf::from(inc));
}
}
"-undefined" | "--undefined" => {
if let Some(symbol) = iter.next() {
let meta = format!("rustc-link-arg=-Wl,{},{}", part, symbol);
config.print_metadata(&meta);
}
}
_ => {
let path = std::path::Path::new(part);
if path.is_file() {
Expand Down Expand Up @@ -1000,10 +1014,19 @@ fn envify(name: &str) -> String {

/// System libraries should only be linked dynamically
fn is_static_available(name: &str, system_roots: &[PathBuf], dirs: &[PathBuf]) -> bool {
let libname = format!("lib{}.a", name);
let libnames = {
let mut names = vec![format!("lib{}.a", name)];

if cfg!(target_os = "windows") {
names.push(format!("{}.lib", name));
}

names
};

dirs.iter().any(|dir| {
!system_roots.iter().any(|sys| dir.starts_with(sys)) && dir.join(&libname).exists()
let library_exists = libnames.iter().any(|libname| dir.join(&libname).exists());
library_exists && !system_roots.iter().any(|sys| dir.starts_with(sys))
})
}

Expand Down