use std::env;
use std::path::{Path, PathBuf};
struct Womp {
flag: &'static str,
link: Vec<String>,
}
struct Compiler {
is_msvc: bool,
is_clang: bool,
is_gcc: bool,
is_apple_clang: bool,
search_paths: Vec<PathBuf>,
}
fn main() {
let wants_static = cfg!(feature = "static") || env::var_os("OPENMP_STATIC").is_some();
println!("cargo:rerun-if-env-changed=CC");
println!("cargo:rerun-if-env-changed=OPENMP_STATIC");
println!("cargo:rerun-if-changed=src/ffi.rs");
println!("cargo:rerun-if-changed=src/lib.rs");
let comp = probe_compiler();
let womp = find_openmp(wants_static, comp);
println!("cargo:flag={}", womp.flag);
if let Ok(s) = env::join_paths(&womp.link) {
if let Some(s) = s.to_str() {
println!("cargo:cargo_link_instructions={}", s);
}
}
for l in &womp.link {
println!("cargo:{}", l);
}
}
fn probe_compiler() -> Compiler {
let mut compiler_libs = Vec::new();
env::set_var("LANG", "C");
let comp = cc::Build::new()
.flag("-v")
.flag("-print-search-dirs")
.get_compiler();
let mut cmd = comp.to_command();
let (out, err) = match cmd.output() {
Ok(out) => (String::from_utf8(out.stdout).unwrap(), String::from_utf8(out.stderr).unwrap()),
Err(err) => {
println!("cargo:warning=Error when setting up OpenMP via openmp-sys crate. Your C compiler doesn't seem to work. The command:\n\
cargo:warning={:?}\n\
cargo:warning=Failed because: {}\n\
cargo:warning=(the PATH is: {:?}; CC is {:?})", cmd, err, env::var("PATH"), env::var("CC"));
("".to_string(), "".to_string())
}
};
if let Ok(library_path) = env::var("LIBRARY_PATH") {
for lib_dir in env::split_paths(&library_path) {
compiler_libs.push(Path::new(&lib_dir).to_path_buf());
}
}
for line in out.split('\n').filter_map(|l| l.strip_prefix("libraries: =")) {
compiler_libs.extend(env::split_paths(line));
}
let is_clang = err.contains("clang") || comp.is_like_clang();
let is_apple_clang = is_clang && err.starts_with("Apple");
if is_clang && err.contains("apple-darwin") {
for lib_dir in &["/opt/local/lib", "/usr/local/lib"] {
let lib_dir = Path::new(lib_dir);
if lib_dir.exists() {
compiler_libs.push(lib_dir.to_path_buf());
}
}
}
Compiler {
is_msvc: comp.is_like_msvc(),
is_gcc: comp.is_like_gnu() && !is_clang,
is_clang,
is_apple_clang,
search_paths: compiler_libs,
}
}
fn find_openmp(wants_static: bool, comp: Compiler) -> Womp {
if comp.is_msvc {
if wants_static {
println!("cargo:warning=Visual Studio doesn't support static OpenMP. Ship vcomp.dll");
}
return Womp {
flag: "/openmp",
link: vec!["rustc-link-lib=vcomp".into()],
}
}
let flag = if comp.is_apple_clang {
"-Xpreprocessor -fopenmp"
} else {
"-fopenmp"
};
let mut out_libs = vec![];
if wants_static && comp.is_gcc {
find_and_link(&["gcc_eh"], true, &comp.search_paths, &mut out_libs);
let target = std::env::var("TARGET").unwrap();
if target == "aarch64-apple-darwin" {
find_and_link(&["gcc"], true, &comp.search_paths, &mut out_libs);
}
}
let lib_names = if comp.is_clang {&["omp", "iomp", "gomp"][..]} else {&["gomp"]};
find_and_link(lib_names, wants_static, &comp.search_paths, &mut out_libs);
Womp {
flag,
link: out_libs,
}
}
fn find_and_link(lib_names: &[&str], statik: bool, in_paths: &[PathBuf], out: &mut Vec<String>) {
let names = lib_names.iter().copied().map(|lib_name| if statik {
(lib_name, format!("lib{}.a", lib_name))
} else {
(lib_name, format!("{}{}{}", env::consts::DLL_PREFIX, lib_name, env::consts::DLL_SUFFIX))
}).collect::<Vec<_>>();
for path in in_paths {
for (name, file) in &names {
if path.join(file).exists() {
out.push(format!("rustc-link-search=native={}", path.display()));
out.push(format!("rustc-link-lib{}={}", if statik {"=static"} else {""}, name));
return;
}
}
}
let cc = env::var("CC").unwrap_or_else(|_| "cc".to_owned());
println!("cargo:warning=openmp-sys is unable to find library {} for {} in {:?}", names[0].1, cc, in_paths);
}