use std::env;
#[cfg(feature = "link_qt_object_files")]
use std::{
collections::HashSet,
path::PathBuf,
sync::{OnceLock, RwLock},
};
#[cfg(feature = "link_qt_object_files")]
static LINKED_OBJECT_FILES: OnceLock<RwLock<HashSet<PathBuf>>> = OnceLock::new();
fn extract_lib_from_filename<'a>(target: &str, filename: &'a str) -> Option<&'a str> {
fn test_suffixes<'b>(filename: &'b str, suffixes: &[&str]) -> Option<&'b str> {
for suffix in suffixes {
if let Some(lib_basename) = filename.strip_suffix(suffix) {
return Some(lib_basename);
}
}
None
}
let prefix = "lib";
#[allow(clippy::collapsible_else_if)]
if target.contains("msvc") {
test_suffixes(filename, &[".lib"])
} else if target.contains("windows") && target.contains("gnu") {
if let Some(filename) = filename.strip_prefix(prefix) {
test_suffixes(filename, &[".dll.a", ".dll", ".lib", ".a"])
} else {
test_suffixes(filename, &[".dll.a", ".dll", ".lib"])
}
} else if target.contains("apple") {
if let Some(filename) = filename.strip_prefix(prefix) {
test_suffixes(filename, &[".a", ".so", ".dylib"])
} else {
None
}
} else {
if let Some(filename) = filename.strip_prefix(prefix) {
test_suffixes(filename, &[".a", ".so"])
} else {
None
}
}
}
fn is_object_file(path: &std::path::Path) -> bool {
let Some(ext_os) = path.extension() else {
return false;
};
let Some(ext) = ext_os.to_str() else {
return false;
};
ext == "o" || ext == "obj"
}
fn split_flags(link_args: &[u8]) -> Vec<String> {
let mut word = Vec::new();
let mut words = Vec::new();
let mut escaped = false;
for &b in link_args {
match b {
_ if escaped => {
escaped = false;
word.push(b);
}
b'\\' => escaped = true,
b'\t' | b'\n' | b'\r' | b' ' => {
if !word.is_empty() {
words.push(String::from_utf8(word).unwrap());
word = Vec::new();
}
}
_ => word.push(b),
}
}
if !word.is_empty() {
words.push(String::from_utf8(word).unwrap());
}
words
}
pub(crate) fn parse_libs_cflags(link_args: &[u8], _builder: &mut cc::Build) {
let mut is_msvc = false;
let target = env::var("TARGET");
if let Ok(target) = &target {
if target.contains("msvc") {
is_msvc = true;
}
}
let words = split_flags(link_args);
let parts = words
.iter()
.filter(|l| l.len() > 2)
.map(|arg| (&arg[0..2], &arg[2..]));
for (flag, val) in parts {
match flag {
"-L" => {
println!("cargo::rustc-link-search=native={val}");
}
"-F" => {
println!("cargo::rustc-link-search=framework={val}");
}
"-I" => (),
"-l" => {
if is_msvc && ["m", "c", "pthread"].contains(&val) {
continue;
}
println!("cargo::rustc-link-lib={val}");
}
"-D" => (),
_ => {}
}
}
let mut iter = words.iter().flat_map(|arg| {
if let Some(arg) = arg.strip_prefix("-Wl,") {
arg.split(',').collect()
} else {
vec![arg.as_ref()]
}
});
while let Some(part) = iter.next() {
match part {
"-framework" => {
if let Some(lib) = iter.next() {
println!("cargo::rustc-link-lib=framework={lib}");
}
}
"-isystem" | "-iquote" | "-idirafter" => {}
_ => {
let path = std::path::Path::new(part);
if path.is_file() {
if let (Some(dir), Some(file_name), Ok(target)) =
(path.parent(), path.file_name(), &target)
{
if is_object_file(path) {
#[cfg(feature = "link_qt_object_files")]
{
let already_linked_object_files =
LINKED_OBJECT_FILES.get_or_init(|| RwLock::new(HashSet::new()));
if !already_linked_object_files
.read()
.expect("Lock poisoned")
.contains(path)
{
_builder.object(path);
already_linked_object_files
.write()
.expect("Lock poisoned!")
.insert(path.to_path_buf());
}
}
} else {
match extract_lib_from_filename(target, &file_name.to_string_lossy()) {
Some(lib_basename) => {
println!("cargo::rustc-link-search={}", dir.display());
println!("cargo::rustc-link-lib={lib_basename}");
}
None => {
println!("cargo::warning=File path {} found in .prl file, but could not extract library base name to pass to linker command line", path.display());
}
}
}
}
}
}
}
}
let linker_options = words.iter().filter(|arg| arg.starts_with("-Wl,"));
for option in linker_options {
let mut pop = false;
let mut ld_option = vec![];
for subopt in option[4..].split(',') {
if pop {
pop = false;
continue;
}
if subopt == "-framework" {
pop = true;
continue;
}
ld_option.push(subopt);
}
println!("cargo::rustc-link-arg=-Wl,{}", ld_option.join(","));
}
}