vectorscan 0.1.2

A Rust wrapper for Vectorscan
use std::path::{Path, PathBuf};

/// Get the environment variable with the given name, panicking if it is not set.
fn env(name: &str) -> String {
    std::env::var(name).unwrap_or_else(|_| panic!("`{}` should be set in the environment", name))
}

fn main() {
    let out_dir = PathBuf::from(env("OUT_DIR"));

    let include_dir = out_dir
        .join("include")
        .into_os_string()
        .into_string()
        .unwrap();

    // Choose appropriate C++ runtime library
    {
        let compiler_version_out = String::from_utf8(
            std::process::Command::new("c++")
                .args(["-v"])
                .output()
                .expect("Failed to get C++ compiler version")
                .stderr,
        )
        .unwrap();

        if compiler_version_out.contains("gcc") {
            println!("cargo:rustc-link-lib=stdc++");
        } else if compiler_version_out.contains("clang") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            panic!("No compatible compiler found: either clang or gcc is needed");
        }
    }

    // Run cmake for vectorscan
    {
        let vectorscan_src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("vectorscan-sys");
        if !vectorscan_src_dir.exists() {
            panic!("Vectorscan-sys source directory is missing");
        }

        let profile = {
            // See https://doc.rust-lang.org/cargo/reference/profiles.html#opt-level for possible values
            match env("OPT_LEVEL").as_str() {
                "0" => "Debug",
                "s" | "z" => "MinSizeRel",
                _ => "Release",
            }
        };

        let fat_runtime = {
            let arch = env("CARGO_CFG_TARGET_ARCH");
            let vendor = env("CARGO_CFG_TARGET_VENDOR");
            if arch == "x86_64" && vendor != "apple" {
                // NOTE: The fat runtime might also work on macOS isntead of just Linux.
                //       But this would need at minimum the vectorscan/cmake/build_wrapper.sh
                //       script overhauled to get working.
                //
                //       For now just do not use the fat runtime for macOS.
                "ON"
            } else {
                "OFF"
            }
        };

        let dst = cmake::Config::new(&vectorscan_src_dir)
            .profile(profile)
            .define("CMAKE_INSTALL_INCLUDEDIR", &include_dir)
            .define("FAT_RUNTIME", fat_runtime)
            .define("BUILD_AVX512", "OFF") // could enable for x86_64?
            .define("BUILD_EXAMPLES", "OFF")
            .define("BUILD_BENCHMARKS", "OFF")
            .define("BUILD_UNITTESTS", "OFF")
            .define("BUILD_DOCS", "OFF")
            .define("BUILD_TOOLS", "OFF")
            .build();

        println!("cargo:rustc-link-lib=static=hs");
        println!(
            "cargo:rustc-link-search={}",
            dst.join("lib").to_str().unwrap()
        );
        println!(
            "cargo:rustc-link-search={}",
            dst.join("lib64").to_str().unwrap()
        );
    }

    // Run bindgen if needed, or else use the pre-generated bindings
    #[cfg(feature = "bindgen")]
    {
        let config = bindgen::Builder::default()
            .allowlist_function("hs_.*")
            .allowlist_type("hs_.*")
            .allowlist_var("HS_.*")
            .header("wrapper.h")
            .clang_arg(format!("-I{}", &include_dir));
        config
            .generate()
            .expect("Unable to generate bindings")
            .write_to_file(out_dir.join("bindings.rs"))
            .expect("Failed to write Rust bindings to Vectorscan");
    }
    #[cfg(not(feature = "bindgen"))]
    {
        std::fs::copy("src/bindings.rs", out_dir.join("bindings.rs"))
            .expect("Failed to write Rust bindings to Vectorscan");
    }
}