use std::{env, path::PathBuf, process::Command};
fn ecl_config(flag: &str) -> String {
let output = Command::new("ecl-config")
.arg(flag)
.output()
.expect("Failed to run ecl-config");
assert!(output.status.success(), "ecl-config {flag} failed");
String::from_utf8(output.stdout).unwrap()
}
fn main() {
println!("cargo:rerun-if-changed=wrapper.h");
println!("cargo:rerun-if-env-changed=ECLISP_INCLUDE_DIRS");
let cflags = ecl_config("--cflags");
let mut cc_build = cc::Build::new();
cc_build.file("shim.c");
let mut builder = bindgen::Builder::default()
.header("wrapper.h")
.clang_args(cflags.split_whitespace());
for name in ["bdw-gc", "gmp"] {
if let Ok(lib) = pkg_config::Config::new().cargo_metadata(false).probe(name) {
for path in lib.include_paths {
builder = builder.clang_arg(format!("-I{}", path.display()));
cc_build.include(&path);
}
}
}
if let Some(path) = find_gc_include() {
builder = builder.clang_arg(format!("-I{}", path.display()));
cc_build.include(&path);
}
let bindings = builder
.opaque_type("ecl_clfloat")
.opaque_type("ecl_ffi_values")
.opaque_type("cl_lispunion")
.allowlist_function("eclisp_.*")
.allowlist_function("cl_boot")
.allowlist_function("cl_shutdown")
.allowlist_function("eclisp_.*")
.allowlist_function("cl_.*")
.allowlist_function("si_string_to_object")
.allowlist_function("ecl_make_simple_base_string")
.allowlist_function("ecl_null_terminated_base_string")
.allowlist_function("ecl_base_string_pointer_safe")
.allowlist_type("eclisp_root")
.allowlist_type("cl_object")
.allowlist_var("ECL_.*")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Failed to generate bindings");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_dir.join("bindings.rs"))
.expect("Failed to write bindings");
let libs = ecl_config("--libs");
for flag in shlex::split(&libs).expect("Invalid ECL linker flags") {
if let Some(path) = flag.strip_prefix("-L") {
if PathBuf::from(path).is_dir() {
println!("cargo:rustc-link-search=native={path}");
}
} else if let Some(lib) = flag.strip_prefix("-l") {
println!("cargo:rustc-link-lib={lib}");
}
}
for flag in shlex::split(&cflags).unwrap() {
cc_build.flag(&flag);
}
cc_build.compile("eclisp_shim");
println!("cargo:rerun-if-changed=shim.c");
}
fn find_gc_include() -> Option<PathBuf> {
if let Ok(lib) = pkg_config::Config::new()
.cargo_metadata(false)
.probe("bdw-gc")
{
return lib.include_paths.into_iter().next();
}
if cfg!(target_os = "macos") {
let output = Command::new("brew")
.args(["--prefix", "bdw-gc"])
.output()
.ok()?;
if output.status.success() {
let prefix = String::from_utf8(output.stdout).ok()?;
return Some(PathBuf::from(prefix.trim()).join("include"));
}
}
None
}