use std::{
env,
path::{Path, PathBuf},
};
const CMAKE_INCLUDE: &str = "include";
const CMAKE_LIB: &str = "lib";
const LIB_BASE: &str = "open62541";
const LIB_EXT: &str = "open62541-ext";
fn main() {
let src = env::current_dir().unwrap();
let src_open62541 = src.join("open62541");
let src_wrapper_c = src.join("wrapper.c");
let src_wrapper_h = src.join("wrapper.h");
println!("cargo:rerun-if-changed={}", src_open62541.display());
println!("cargo:rerun-if-changed={}", src_wrapper_c.display());
println!("cargo:rerun-if-changed={}", src_wrapper_h.display());
let mut cmake = cmake::Config::new(src_open62541);
cmake
.define("CMAKE_INSTALL_INCLUDEDIR", CMAKE_INCLUDE)
.define("CMAKE_INSTALL_LIBDIR", CMAKE_LIB)
.define("C_STANDARD", "99")
.env("PYTHONDONTWRITEBYTECODE", "1");
if matches!(env::var("CARGO_CFG_TARGET_ENV"), Ok(env) if env == "musl") {
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
cmake
.cflag("-idirafter/usr/include")
.cflag(format!("-idirafter/usr/include/{arch}-linux-gnu"));
}
let dst = cmake.build();
let dst_include = dst.join(CMAKE_INCLUDE);
let dst_lib = dst.join(CMAKE_LIB);
if matches!(env::var("CARGO_CFG_TARGET_OS"), Ok(os) if os == "windows") {
println!("cargo:rustc-link-lib=Iphlpapi");
}
println!("cargo:rustc-link-search={}", dst_lib.display());
println!("cargo:rustc-link-lib={LIB_BASE}");
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
let out_bindings_rs = out.join("bindings.rs");
let out_extern_c = out.join("extern.c");
let builder = bindgen::Builder::default()
.allowlist_function("(__)?RS_.*")
.allowlist_function("(__)?UA_.*")
.allowlist_type("(__)?RS_.*")
.allowlist_type("(__)?UA_.*")
.allowlist_var("(__)?RS_.*")
.allowlist_var("(__)?UA_.*")
.clang_arg("-std=c99")
.clang_arg(format!("-I{}", dst_include.display()))
.default_enum_style(bindgen::EnumVariation::NewType {
is_bitfield: false,
is_global: false,
})
.rust_target(bindgen::RustTarget::Stable_1_71)
.derive_copy(false)
.derive_default(true)
.generate_comments(false)
.header(src_wrapper_h.to_str().unwrap())
.parse_callbacks(Box::new(CustomCallbacks { dst }))
.use_core()
.wrap_static_fns(true)
.wrap_static_fns_path(out_extern_c.to_str().unwrap());
let bindings = builder
.generate()
.expect("should generate `Bindings` instance");
bindings
.write_to_file(out_bindings_rs)
.expect("should write `bindings.rs`");
cc::Build::new()
.file(out_extern_c)
.file(src_wrapper_c)
.include(dst_include)
.warnings(false)
.flag_if_supported("-Wno-deprecated-declarations")
.flag_if_supported("-Wno-deprecated")
.compile(LIB_EXT);
}
#[derive(Debug)]
struct CustomCallbacks {
dst: PathBuf,
}
impl CustomCallbacks {
fn inside_dst(&self, filename: &str) -> bool {
Path::new(filename).starts_with(&self.dst)
}
}
impl bindgen::callbacks::ParseCallbacks for CustomCallbacks {
fn header_file(&self, filename: &str) {
if !self.inside_dst(filename) {
println!("cargo:rerun-if-changed={filename}");
}
}
fn include_file(&self, filename: &str) {
if !self.inside_dst(filename) {
println!("cargo:rerun-if-changed={filename}");
}
}
fn read_env_var(&self, key: &str) {
println!("cargo:rerun-if-env-changed={key}");
}
fn item_name(&self, original_item_name: &str) -> Option<String> {
original_item_name.strip_prefix("RS_").map(str::to_owned)
}
}