use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs};
fn main() {
println!("cargo:rerun-if-env-changed=DOCS_RS");
if env::var("DOCS_RS").is_ok() {
return;
}
unsafe { std::env::set_var("RUSTC_BOOTSTRAP", "ezffi") };
activate_git_hooks();
let crate_name = env::var("CARGO_PKG_NAME").unwrap();
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let out_dir = env::var("OUT_DIR").unwrap();
println!("cargo:rerun-if-changed=cbindgen.toml");
println!("cargo:rerun-if-changed=src/");
let target_dir = Path::new(&out_dir)
.ancestors()
.nth(3) .expect("Failed to find target dir");
let include_dir = target_dir.join("include").join(&crate_name);
fs::create_dir_all(&include_dir).unwrap();
let config_path = Path::new(&crate_dir).join("cbindgen.toml");
let base_config =
cbindgen::Config::from_file(&config_path).expect("Failed to read cbindgen.toml");
let profile = match env::var("PROFILE").as_deref() {
Ok("release") => cbindgen::Profile::Release,
_ => cbindgen::Profile::Debug,
};
const EZFFI_TRAILER: &str = "\n\
#define EZFFI_OPTION_UNWRAP(Type, opt_ptr) ({ Type __ez_v; ezffi_option_unwrap((opt_ptr), &__ez_v); __ez_v; })\n\
#define EZFFI_RESULT_UNWRAP(Type, res_ptr) ({ Type __ez_v; ezffi_result_unwrap((res_ptr), &__ez_v); __ez_v; })\n";
const STRING_TRAILER: &str = "\n\
#define EZFFI_STR(lit) ((EzffiStr){ .ptr = (uint8_t*)(lit), .len = sizeof(lit) - 1 })\n\
#define EZFFI_CSTR(cstr) ((EzffiStr){ .ptr = (uint8_t*)(cstr), .len = strlen(cstr) })\n";
const SLICE_TRAILER: &str = "\n\
#define EZFFI_SLICE(p, n) ((EzffiSlice){ .ptr = (void*)(p), .len = (n) })\n\
#define EZFFI_SLICE_FROM_ARRAY(arr) ((EzffiSlice){ .ptr = (void*)(arr), .len = sizeof(arr) / sizeof((arr)[0]) })\n";
#[rustfmt::skip]
const VARIANTS: &[(&str, &str, Option<&str>)] = &[
("ezffi.h", "_option_result", Some(EZFFI_TRAILER)),
("string.h", "_string", Some(STRING_TRAILER)),
("slice.h", "_slice", Some(SLICE_TRAILER)),
("std/arc.h", "_arc", None),
("std/btreemap.h", "_btreemap", None),
("std/btreeset.h", "_btreeset", None),
("std/hashmap.h", "_hashmap", None),
("std/hashset.h", "_hashset", None),
("std/rc.h", "_rc", None),
("std/vec.h", "_vec", None),
("std/vecdeque.h", "_vecdeque", None),
];
for &(path, feature, variant_trailer) in VARIANTS {
let mut config = base_config.clone();
config.trailer = variant_trailer.map(String::from);
if feature == "_slice" {
config.export.include.push("EzffiSlice".to_string());
}
let out_path = include_dir.join(path);
if let Some(parent) = out_path.parent() {
fs::create_dir_all(parent).unwrap();
}
cbindgen::Builder::new()
.with_crate(&crate_dir)
.with_config(config)
.with_parse_expand_profile(profile)
.with_parse_expand_default_features(false)
.with_parse_expand_features(&[feature.to_string()])
.generate()
.expect("Unable to generate bindings")
.write_to_file(&out_path);
}
}
fn activate_git_hooks() {
let Some(manifest_dir) = env::var("CARGO_MANIFEST_DIR").ok() else {
return;
};
let mut dir = PathBuf::from(manifest_dir);
let workspace_root = loop {
if dir.join(".git").exists() {
break dir;
}
if !dir.pop() {
return;
}
};
let hooks_dir = workspace_root.join(".githooks");
if !hooks_dir.is_dir() {
return;
}
let _ = Command::new("git")
.args(["config", "core.hooksPath", ".githooks"])
.current_dir(&workspace_root)
.status()
.ok();
}