fn main() {
let (major, minor) = *llvm_sys::LLVM_VERSION_FROM_FEATURES;
println!("cargo:rustc-env=LLVM_VERSION_MAJOR={}{}", major, minor);
if llvm_sys::LLVM_CONFIG_PATH.is_none() {
println!("cargo:rustc-cfg=LLVM_NOT_FOUND");
return;
}
let includedir = llvm_sys::llvm_config("--includedir");
let mut build = cc::Build::new();
build
.cpp(true)
.include(includedir.trim())
.file("cpp/ffi.cc");
#[cfg(target_env = "msvc")]
build.flag_if_supported("/std:c++17");
#[cfg(not(target_env = "msvc"))]
build.flag_if_supported("-std=c++17");
let has_rtti = llvm_sys::llvm_config("--has-rtti");
if has_rtti.trim() == "YES" {
#[cfg(target_env = "msvc")]
build.flag_if_supported("/GR-");
#[cfg(not(target_env = "msvc"))]
build.flag_if_supported("-fno-rtti");
}
build.define("LLVM_VERSION_MAJOR", major.to_string().as_str());
build.define("LLVM_VERSION_MINOR", minor.to_string().as_str());
build.warnings(false);
build.compile("llvm-plugin-cpp");
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
let libdir = llvm_sys::llvm_config("--libdir");
println!("cargo:rustc-link-search=native={}", libdir.trim());
println!("cargo:rustc-link-lib=dylib=LLVM");
}
#[cfg(target_os = "windows")]
{
let libdir = llvm_sys::llvm_config("--libdir");
println!("cargo:rustc-link-search=native={}", libdir.trim());
println!("cargo:rustc-link-lib=dylib=LLVM-C");
#[cfg(feature = "win-link-opt")]
println!("cargo:rustc-link-lib=dylib=opt");
#[cfg(feature = "win-link-lld")]
println!("cargo:rustc-link-lib=dylib=lld");
}
println!("cargo:rerun-if-changed=cpp");
println!("cargo:rerun-if-env-changed={}", &*llvm_sys::ENV_LLVM_PREFIX);
}
mod llvm_sys {
use lazy_static::lazy_static;
use regex::Regex;
use semver::Version;
use std::env;
use std::ffi::OsStr;
use std::io::{self, ErrorKind};
use std::path::PathBuf;
use std::process::Command;
lazy_static! {
pub static ref LLVM_CONFIG_PATH: Option<PathBuf> = locate_llvm_config();
pub static ref LLVM_VERSION_FROM_FEATURES: (u32, u32) = llvm_version_from_features();
pub static ref ENV_LLVM_PREFIX: String = {
let (major, minor) = *LLVM_VERSION_FROM_FEATURES;
format!("LLVM_SYS_{}{}_PREFIX", major, minor)
};
}
fn locate_llvm_config() -> Option<PathBuf> {
let prefix = env::var_os(&*ENV_LLVM_PREFIX)
.map(|p| PathBuf::from(p).join("bin"))
.unwrap_or_default();
for binary_name in llvm_config_binary_names() {
let binary_name = prefix.join(binary_name);
match llvm_version(&binary_name) {
Ok(version) if LLVM_VERSION_FROM_FEATURES.0 as u64 == version.major => {
return Some(binary_name);
}
Ok(_) => continue,
Err(ref e) if e.kind() == ErrorKind::NotFound => {
}
Err(e) => panic!("Failed to search PATH for llvm-config: {}", e),
}
}
None
}
fn llvm_config_binary_names() -> std::vec::IntoIter<String> {
let (major, minor) = *LLVM_VERSION_FROM_FEATURES;
let mut base_names = vec![
"llvm-config".into(),
format!("llvm-config-{}", major),
format!("llvm{}-config", major),
format!("llvm-config-{}.{}", major, minor),
format!("llvm-config{}{}", major, minor),
];
if cfg!(windows) {
let mut exe_names = base_names.clone();
for name in exe_names.iter_mut() {
name.push_str(".exe");
}
base_names.extend(exe_names);
}
base_names.into_iter()
}
pub fn llvm_config(arg: &str) -> String {
let path = LLVM_CONFIG_PATH.as_ref().unwrap();
llvm_config_ex(path, arg).expect("Surprising failure from llvm-config")
}
fn llvm_config_ex<S: AsRef<OsStr>>(binary: S, arg: &str) -> io::Result<String> {
Command::new(binary).arg(arg).output().and_then(|output| {
if output.stdout.is_empty() {
Err(io::Error::new(
io::ErrorKind::NotFound,
"llvm-config returned empty output",
))
} else {
Ok(String::from_utf8(output.stdout)
.expect("Output from llvm-config was not valid UTF-8"))
}
})
}
fn llvm_version<S: AsRef<OsStr>>(binary: &S) -> io::Result<Version> {
let version_str = llvm_config_ex(binary.as_ref(), "--version")?;
let re = Regex::new(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))??").unwrap();
let c = match re.captures(&version_str) {
Some(c) => c,
None => {
panic!(
"Could not determine LLVM version from llvm-config. Version string: {}",
version_str
);
}
};
let s = match c.name("patch") {
None => format!("{}.0", &c[0]),
Some(_) => c[0].to_string(),
};
Ok(Version::parse(&s).unwrap())
}
fn llvm_version_from_features() -> (u32, u32) {
if cfg!(feature = "llvm10-0") {
(10, 0)
} else if cfg!(feature = "llvm11-0") {
(11, 0)
} else if cfg!(feature = "llvm12-0") {
(12, 0)
} else if cfg!(feature = "llvm13-0") {
(13, 0)
} else if cfg!(feature = "llvm14-0") {
(14, 0)
} else if cfg!(feature = "llvm15-0") {
(15, 0)
} else if cfg!(feature = "llvm16-0") {
(16, 0)
} else if cfg!(feature = "llvm17-0") {
(17, 0)
} else {
panic!("Missing llvm* feature")
}
}
}