use anyhow::Context; use build_target::Os; use std::{ borrow::BorrowMut, path::{Path, PathBuf}, process::Command, }; type Result = anyhow::Result; const OSDP_EXPORT_CONTENT: &str = "/* Auto generated from build.rs */ #ifndef OSDP_EXPORT_H_ #define OSDP_EXPORT_H_ #define OSDP_EXPORT #define OSDP_NO_EXPORT #define OSDP_DEPRECATED_EXPORT #endif /* OSDP_EXPORT_H_ */ "; fn path_join(root: &str, path: &str) -> String { Path::new(root) .join(path) .into_os_string() .into_string() .unwrap() } fn configure_file(path: &str, transforms: Vec<(&str, &str)>) -> Result<()> { let mut contents = std::fs::read_to_string(path)?; for (from, to) in transforms { if let Some(start) = contents.find(format!("@{from}@").as_str()) { let range = start..start + from.len() + 2; contents.replace_range(range, to); } } std::fs::write(path, contents)?; Ok(()) } fn exec_cmd(cmd: Vec<&str>) -> Result { let mut c = Command::new(cmd[0]); let mut c = c.borrow_mut(); for arg in &cmd[1..] { c = c.arg(*arg); } let stdout = String::from_utf8_lossy(&c.output()?.stdout).into_owned(); Ok(stdout.trim().to_owned()) } struct GitInfo { branch: String, tag: String, diff: String, rev: String, root: String, } impl GitInfo { pub fn new() -> Result { let diff = match exec_cmd(vec!["git", "diff", "--quiet", "--exit-code"]) { Ok(_) => "", Err(_) => "+", }; Ok(GitInfo { branch: exec_cmd(vec!["git", "rev-parse", "--abbrev-ref", "HEAD"])?, tag: exec_cmd(vec!["git", "describe", "--exact-match", "--tags"]) .unwrap_or("".to_owned()), diff: diff.to_owned(), rev: exec_cmd(vec!["git", "log", "--pretty=format:'%h'", "-n", "1"])?, root: exec_cmd(vec!["git", "rev-parse", "--show-toplevel"])?, }) } } fn generate_osdp_build_headers(out_dir: &str) -> Result<()> { /* generate osdp_export.h */ std::fs::write(path_join(out_dir, "osdp_export.h"), OSDP_EXPORT_CONTENT) .context("Failed to create osdp_export.h")?; /* generate osdp_config.h */ let git = GitInfo::new()?; let src = "vendor/src/osdp_config.h.in"; let dest = path_join(out_dir, "osdp_config.h"); std::fs::copy(src, &dest).context(format!("Failed: copy {src} -> {dest}"))?; configure_file( &dest, vec![ ("PROJECT_VERSION", env!("CARGO_PKG_VERSION")), ( "PROJECT_NAME", format!("{}-rust", env!("CARGO_PKG_NAME")).as_str(), ), ("GIT_BRANCH", git.branch.as_str()), ("GIT_REV", git.rev.as_ref()), ("GIT_TAG", git.tag.as_ref()), ("GIT_DIFF", git.diff.as_ref()), ("REPO_ROOT", git.root.as_ref()), ], ) } fn main() -> Result<()> { let out_dir = std::env::var("OUT_DIR").unwrap(); generate_osdp_build_headers(&out_dir)?; /* build LibOSDP */ let mut build = cc::Build::new(); let mut build = build .include("vendor/src") .include("vendor/include") .include("vendor/utils/include") .warnings(true) .include(&out_dir); if std::env::var("WIN_WERROR").is_err() && Os::target().unwrap() != Os::Windows { // TODO: Windows builds warn about various things which are legitimate // in other platforms. Over time, we need to assess each case and // handle it the way Windows likes us to do them and then remove this. build = build.warnings_into_errors(true) } let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); if target_os.is_empty() || target_os == "none" { println!("cargo:warning=Building for bare metal target"); build = build.define("__BARE_METAL__", "1") } let source_files = vec![ "vendor/utils/src/list.c", "vendor/utils/src/queue.c", "vendor/utils/src/slab.c", "vendor/utils/src/utils.c", "vendor/utils/src/logger.c", "vendor/utils/src/disjoint_set.c", "vendor/src/osdp_common.c", "vendor/src/osdp_phy.c", "vendor/src/osdp_sc.c", "vendor/src/osdp_file.c", "vendor/src/osdp_pd.c", "vendor/src/osdp_cp.c", "vendor/src/crypto/tinyaes_src.c", "vendor/src/crypto/tinyaes.c", ]; for file in source_files { build = build.file(file); } if cfg!(feature = "skip_mark_byte") { build = build.define("CONFIG_OSDP_SKIP_MARK_BYTE", "1"); } if cfg!(feature = "packet_trace") { build = build .define("CONFIG_OSDP_PACKET_TRACE", "1") .file("vendor/utils/src/pcap_gen.c") .file("vendor/src/osdp_diag.c"); } if cfg!(feature = "data_trace") { build = build .define("CONFIG_OSDP_DATA_TRACE", "1") .file("vendor/utils/src/pcap_gen.c") .file("vendor/src/osdp_diag.c"); } let short_enums = build.get_compiler().is_like_gnu() || build.get_compiler().is_like_clang(); if short_enums { build.flag("-fshort-enums"); } build.compile("libosdp.a"); /* generate bindings */ let mut args = vec![format!("-I{}", &out_dir)]; if short_enums { args.push("-fshort-enums".to_owned()); } else { args.push("-fno-short-enums".to_owned()); } let bindings = bindgen::Builder::default() .use_core() .header("vendor/include/osdp.h") .clang_args(args) .generate() .context("Unable to generate bindings")?; let out_path = PathBuf::from(out_dir); bindings .write_to_file(out_path.join("bindings.rs")) .context("Couldn't write bindings!") }