use std::env; use std::path::{Path, PathBuf}; use anyhow::{anyhow, bail, Context, Result}; fn find_hyperscan() -> Result { cargo_emit::rerun_if_env_changed!("HYPERSCAN_ROOT"); let link_kind = if cfg!(feature = "static") { "static" } else { "dylib" }; let static_libstd = cfg!(feature = "contained"); if let Ok(prefix) = env::var("HYPERSCAN_ROOT") { let prefix = Path::new(&prefix); let inc_path = prefix.join("include/hs"); let link_path = prefix.join("lib"); if cfg!(feature = "tracing") { cargo_emit::warning!("use HYPERSCAN_ROOT = {}", prefix.display()); } if !prefix.exists() || !prefix.is_dir() { bail!("HYPERSCAN_ROOT should point to a directory that exists."); } if link_path.exists() && link_path.is_dir() { cargo_emit::rustc_link_search!(link_path.to_string_lossy() => "native"); } else { bail!("`$HYPERSCAN_ROOT/lib` subdirectory not found."); } cargo_emit::rustc_link_search!(link_path.to_string_lossy() => "native"); if cfg!(feature = "static") { let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); let std_link = if target_os == "macos" { "c++" } else { "stdc++" }; if static_libstd { cargo_emit::rustc_link_lib!(std_link => "static:-bundle"); } else { cargo_emit::rustc_link_lib!(std_link); } } if !cfg!(feature = "compile") && cfg!(feature = "runtime") { cargo_emit::rustc_link_lib!("hs_runtime" => link_kind); } else { cargo_emit::rustc_link_lib!("hs" => link_kind); } if cfg!(feature = "chimera") { cargo_emit::rustc_link_lib!("chimera" => "static"); cargo_emit::rustc_link_lib!("pcre" => "static"); } if cfg!(feature = "tracing") { cargo_emit::warning!( "building with Hyperscan with {} library @ {:?}, link_paths=[{:?}], include_paths=[{:?}]", link_kind, prefix, link_path, inc_path ); } Ok(inc_path) } else { let libhs = pkg_config::Config::new() .statik(cfg!(feature = "static")) .cargo_metadata(true) .env_metadata(true) .probe("libhs")?; if cfg!(feature = "tracing") { cargo_emit::warning!( "building with Hyperscan {} with {} library, libs={:?}, link_paths={:?}, include_paths={:?}", libhs.version, link_kind, libhs.libs, libhs.link_paths, libhs.include_paths ); } if cfg!(feature = "chimera") { let libch = pkg_config::Config::new() .statik(cfg!(feature = "static")) .cargo_metadata(true) .env_metadata(true) .probe("libch")?; if cfg!(feature = "tracing") { cargo_emit::warning!( "building with Chimera {} with {} library, libs={:?}, link_paths={:?}, include_paths={:?}", libch.version, link_kind, libch.libs, libch.link_paths, libch.include_paths ); } } libhs .include_paths .first() .cloned() .ok_or_else(|| anyhow!("missing include path")) } } #[cfg(any(feature = "gen", not(target_pointer_width = "64")))] fn generate_binding(inc_dir: &Path, out_dir: &Path) -> Result<()> { let out_file = out_dir.join("hyperscan.rs"); let inc_file = inc_dir.join("hs.h"); let inc_file = inc_file.to_str().expect("header file"); if cfg!(feature = "tracing") { cargo_emit::warning!("generating raw Hyperscan binding file @ {}", out_file.display()); } cargo_emit::rerun_if_changed!(inc_file); bindgen::builder() .header(inc_file) .use_core() .ctypes_prefix("::libc") .clang_args(&["-x", "c++", "-std=c++11"]) .allowlist_var("^HS_.*") .allowlist_type("^hs_.*") .allowlist_function("^hs_.*") .blocklist_type("^__darwin_.*") .size_t_is_usize(true) .derive_copy(true) .derive_debug(true) .derive_default(true) .derive_partialeq(true) .derive_eq(true) .generate() .map_err(|_| anyhow::Error::msg("generate binding files"))? .write_to_file(out_file) .with_context(|| "write wrapper") } #[cfg(all(not(feature = "gen"), target_pointer_width = "64"))] fn generate_binding(_: &Path, out_dir: &Path) -> Result<()> { std::fs::copy("src/hyperscan.rs", out_dir.join("hyperscan.rs")) .map(|_| ()) .with_context(|| "copy binding file") } #[cfg(all(feature = "chimera", any(feature = "gen", not(target_pointer_width = "64"))))] fn generate_chimera_binding(inc_dir: &Path, out_dir: &Path) -> Result<()> { let out_file = out_dir.join("chimera.rs"); let inc_file = inc_dir.join("ch.h"); let inc_file = inc_file.to_str().expect("header file"); if cfg!(feature = "tracing") { cargo_emit::warning!("generating raw Chimera binding file @ {}", out_file.display()); } cargo_emit::rerun_if_changed!(inc_file); bindgen::builder() .header(inc_file) .use_core() .ctypes_prefix("::libc") .clang_args(&["-x", "c++", "-std=c++11"]) .allowlist_var("^CH_.*") .allowlist_type("^ch_.*") .allowlist_function("^ch_.*") .blocklist_type("^__darwin_.*") .size_t_is_usize(true) .derive_copy(true) .derive_debug(true) .derive_default(true) .derive_partialeq(true) .derive_eq(true) .generate() .map_err(|_| anyhow::Error::msg("generate binding files"))? .write_to_file(out_file) .with_context(|| "write wrapper") } #[cfg(all(not(feature = "gen"), feature = "chimera", target_pointer_width = "64"))] fn generate_chimera_binding(_: &Path, out_dir: &Path) -> Result<()> { std::fs::copy("src/chimera.rs", out_dir.join("chimera.rs")) .map(|_| ()) .with_context(|| "copy binding file") } #[cfg(all(not(feature = "chimera"), target_pointer_width = "64"))] fn generate_chimera_binding(_: &Path, _: &Path) -> Result<()> { Ok(()) } fn main() -> Result<()> { let inc_dir = if std::env::var("DOCS_RS").is_ok() { PathBuf::new() } else { find_hyperscan().with_context(|| "please download and install hyperscan from https://www.hyperscan.io/")? }; let out_dir = env::var("OUT_DIR")?; let out_dir = Path::new(&out_dir); generate_binding(&inc_dir, out_dir)?; if cfg!(feature = "chimera") { generate_chimera_binding(&inc_dir, out_dir)?; } Ok(()) }