fn main() {
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if target_os != "linux" {
return;
}
build_linux();
}
fn copy_dir_recursive(src: &std::path::Path, dst: &std::path::Path) {
use std::fs;
fs::create_dir_all(dst).unwrap();
for entry in fs::read_dir(src).unwrap() {
let entry = entry.unwrap();
let ty = entry.file_type().unwrap();
let src_path = entry.path();
let dst_path = dst.join(entry.file_name());
if ty.is_dir() {
copy_dir_recursive(&src_path, &dst_path);
} else if ty.is_file() {
fs::copy(&src_path, &dst_path).unwrap();
}
}
}
fn build_linux() {
use std::{env, fs, path::PathBuf, process::Command};
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let liburing_src = manifest_dir.join("liburing");
let libdir_path = out_dir.join("liburing");
if libdir_path.exists() {
fs::remove_dir_all(&libdir_path).unwrap();
}
copy_dir_recursive(&liburing_src, &libdir_path);
let headers_path = manifest_dir.join("include/liburing_wrapper.h");
println!("cargo:rerun-if-changed={}", liburing_src.display());
println!("cargo:rerun-if-changed={}", headers_path.display());
let result = Command::new("./configure")
.current_dir(&libdir_path)
.output()
.expect("configure script failed");
if !result.status.success() {
panic!(
"failed to configure: stdout:\n{}\n\nstderr:\n{}",
String::from_utf8_lossy(&result.stdout),
String::from_utf8_lossy(&result.stderr)
);
}
let src_dir = libdir_path.join("src");
let sources = ["setup.c", "queue.c", "syscall.c", "register.c"];
let mut obj_files = Vec::new();
for source in &sources {
let src_file = src_dir.join(source);
let obj_file = out_dir.join(source.replace(".c", ".o"));
let status = Command::new("clang")
.arg("-c")
.arg("-o")
.arg(&obj_file)
.arg(&src_file)
.arg(format!("-I{}", src_dir.join("include").display()))
.arg("-D_GNU_SOURCE")
.arg("-Wno-unused-parameter")
.status()
.expect("Failed to compile liburing source file");
if !status.success() {
panic!("Failed to compile {}", source);
}
obj_files.push(obj_file);
}
let lib_path = out_dir.join("liburing.a");
let status = Command::new("ar")
.arg("rcs")
.arg(&lib_path)
.args(&obj_files)
.status()
.expect("Failed to create static library");
if !status.success() {
panic!("Failed to create liburing.a");
}
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=uring");
let bindings = bindgen::Builder::default()
.header(headers_path.to_str().unwrap())
.clang_arg(format!("-I{}", src_dir.join("include").display()))
.clang_arg("-D_GNU_SOURCE")
.wrap_static_fns(true)
.wrap_static_fns_path(out_dir.join("liburing_wrapper"))
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Unable to generate bindings");
let out_path =
PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
bindings.write_to_file(out_path).expect("Couldn't write bindings!");
let wrapper_c = out_dir.join("liburing_wrapper.c");
if wrapper_c.exists() {
let wrapper_obj = out_dir.join("liburing_wrapper.o");
let status = Command::new("clang")
.arg("-c")
.arg("-o")
.arg(&wrapper_obj)
.arg(&wrapper_c)
.arg(format!("-I{}", manifest_dir.display()))
.arg(format!("-I{}", src_dir.join("include").display()))
.arg("-D_GNU_SOURCE")
.arg("-Wno-unused-parameter")
.status()
.expect("Failed to compile wrapper file");
if !status.success() {
panic!("Failed to compile liburing_wrapper.c");
}
let status = Command::new("ar")
.arg("r")
.arg(&lib_path)
.arg(&wrapper_obj)
.status()
.expect("Failed to add wrapper to library");
if !status.success() {
panic!("Failed to add wrapper to liburing.a");
}
}
}