use bindgen::callbacks::ItemInfo;
use bindgen::callbacks::ItemKind;
use bindgen::callbacks::ParseCallbacks;
use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::sync::OnceLock;
static CARGO_MANIFEST_DIR: OnceLock<PathBuf> = OnceLock::new();
static CARGO_WORKSPACE_DIR: OnceLock<PathBuf> = OnceLock::new();
fn get_cargo_manifest_dir() -> &'static PathBuf {
CARGO_MANIFEST_DIR.get_or_init(|| PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()))
}
fn get_cargo_workspace_dir() -> &'static PathBuf {
CARGO_WORKSPACE_DIR.get_or_init(|| {
let manifest_dir = get_cargo_manifest_dir();
manifest_dir
.ancestors()
.skip(2) .take(2) .find(|dir| dir.join("Cargo.toml").exists())
.unwrap_or_else(|| panic!("Could not find parent directory with Cargo.toml"))
.to_path_buf()
})
}
#[derive(Debug)]
struct RenameFunctions;
impl ParseCallbacks for RenameFunctions {
fn item_name(&self, original_name: ItemInfo<'_>) -> Option<String> {
original_name.name.strip_prefix("wrap_").map(String::from)
}
fn generated_link_name_override(&self, item_info: ItemInfo<'_>) -> Option<String> {
match item_info.kind {
ItemKind::Function => {
if item_info.name.starts_with("wrap_") {
return Some(String::from(item_info.name));
}
None
}
_ => None,
}
}
}
fn build_libfabric(install_dir: &PathBuf) {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let libfabric_rsync_dir = out_dir.join("vendored");
let cargo_manifest_dir = get_cargo_manifest_dir();
let cargo_workspace_dir = get_cargo_workspace_dir();
println!(
"cargo:warning=cargo_manifest_dir {}",
cargo_manifest_dir.display()
);
println!(
"cargo:warning=cargo_workspace_dir {}",
cargo_workspace_dir.display()
);
println!(
"cargo:warning=libfabric_rsync_dir {}",
libfabric_rsync_dir.display()
);
println!("cargo:warning=install_dir {}", install_dir.display());
println!("cargo:warning=out_dir {}", out_dir.display());
if !libfabric_rsync_dir.exists() {
println!("cargo:warning=Running rsync as part of libfabric compilation.");
Command::new("rsync")
.arg("-av")
.arg("--exclude=vendored")
.arg(format!("{}/", cargo_workspace_dir.display()))
.arg(&libfabric_rsync_dir)
.status()
.expect("Failed to copy vendor directory");
}
println!("cargo:warning=Running autogen.sh as part of libfabric compilation.");
assert!(
Command::new("sh")
.current_dir(&libfabric_rsync_dir)
.arg("autogen.sh")
.status()
.unwrap()
.success()
);
println!("cargo:warning=Running configure as part of libfabric compilation.");
assert!(
Command::new("sh")
.current_dir(&libfabric_rsync_dir)
.arg("configure")
.arg(format!("--prefix={}", install_dir.display()))
.status()
.unwrap()
.success()
);
println!("cargo:warning=Running make install as part of libfabric compilation.");
assert!(
Command::new("make")
.current_dir(&libfabric_rsync_dir)
.arg("-j")
.arg("install")
.status()
.unwrap()
.success()
);
println!("cargo:warning=Libfabric successfully compiled.");
}
fn main() {
#[cfg(not(target_os = "linux"))]
compile_error!("This binding is only compatible with Linux.");
let asan = cfg!(feature = "asan");
if asan {
println!("cargo:rustc-link-lib=asan");
}
println!("cargo:rustc-link-lib=fabric");
let vendored = cfg!(feature = "vendored");
println!(
"cargo:warning=Building the binding with vendored: {}",
vendored
);
let (lib_path, include_paths) = match vendored {
true => {
let install_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("install");
build_libfabric(&install_dir);
let libfabric_dir = get_cargo_workspace_dir();
(
install_dir.join("lib"),
vec![
libfabric_dir.clone(),
libfabric_dir.join("include"),
libfabric_dir.join("include").join("rdma"),
libfabric_dir.join("include").join("rdma").join("providers"),
],
)
}
false => {
let lib = pkg_config::Config::new().probe("libfabric").unwrap();
assert_eq!(1, lib.include_paths.len());
assert_eq!(1, lib.link_paths.len());
(
lib.link_paths[0].clone(),
vec![
lib.include_paths[0].clone(),
lib.include_paths[0].join("rdma"),
lib.include_paths[0].join("rdma").join("providers"),
],
)
}
};
println!("cargo:rustc-link-search=native={}", lib_path.display());
println!("cargo:warning=Library link path: {}", lib_path.display());
include_paths
.iter()
.enumerate()
.for_each(|(i, x)| println!("cargo:warning=include_paths[{}]: {}", i, x.display()));
let mut builder = cc::Build::new();
let cargo_manifest_dir = get_cargo_manifest_dir().display();
builder.file(format!("{cargo_manifest_dir}/wrapper.c"));
for path in &include_paths {
builder.include(format!("{}", path.display()));
}
if asan {
builder.flag("-fsanitize=address");
builder.flag("-fsanitize-recover=address");
}
builder.compile("wrapper");
let builder = bindgen::Builder::default().header("wrapper.h").clang_args(
include_paths
.iter()
.map(|dir| format!("-I{}", dir.display())),
);
let bindings = builder
.clang_arg("-fno-inline-functions")
.clang_arg("-Wno-error=implicit-function-declaration")
.clang_arg("-Wno-error=int-conversion")
.parse_callbacks(Box::new(RenameFunctions))
.generate_inline_functions(false)
.wrap_static_fns(false)
.derive_default(true)
.derive_debug(true)
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}