embree3-sys 0.1.0

FFI bindings for Intel's Embree high-performance ray tracing library.
Documentation
use std::env;
use std::path::PathBuf;

// If any of these environment variables are set and are not "0", then we use
// some backup headers and the Embree lib is not linked. This is useful for
// providing documentation and running CI without requiring an Embree
// installation.
const USE_BACKUP_HEADERS_ENV_VARS: &[&str] = &["DOCS_RS", "CI"];

fn main() {
    // Based on https://rust-lang.github.io/rust-bindgen/tutorial-0.html

    // Determine if we should use the backup headers (if any of the environment
    // variables is set and not "0").
    let mut use_backup_headers = false;
    for env_var in USE_BACKUP_HEADERS_ENV_VARS {
        println!("cargo:rerun-if-env-changed={}", env_var);
        use_backup_headers =
            use_backup_headers || env::var_os(env_var).filter(|v| v != "0").is_some();
    }

    // Select a wrapper header and link to the Embree lib.
    let wrapper_path;
    if !use_backup_headers {
        wrapper_path = "wrapper.h";

        // If EMBREE_DIR is set, add it as a linker search path.
        println!("cargo:rerun-if-env-changed=EMBREE_DIR");
        if let Ok(embree_dir_str) = env::var("EMBREE_DIR") {
            let embree_lib_dir = PathBuf::from(embree_dir_str).join("lib");
            println!(
                "cargo:rustc-link-search=native={}",
                embree_lib_dir.display()
            );
        }

        // Link to the Embree lib.
        println!("cargo:rustc-link-lib=embree3");
    } else {
        wrapper_path = "backup_embree_headers/wrapper.h";

        // We are using the backup headers, so don't attempt to link to the
        // Embree lib.
    }

    // If wrapper.h changes, rebuild the crate.
    println!("cargo:rerun-if-changed={}", wrapper_path);

    // Generate the bindings.
    let bindings = bindgen::Builder::default()
        .header(wrapper_path)
        // If headers included in wrapper.h change, rebuild the crate.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate_comments(false)
        .trust_clang_mangling(false)
        .allowlist_function("rtc.*")
        .allowlist_type("RTC.*")
        .allowlist_var("RTC.*")
        .rustified_enum("RTCFormat")
        .rustified_enum("RTCBuildQuality")
        .rustified_enum("RTCDeviceProperty")
        .rustified_enum("RTCError")
        .rustified_enum("RTCBufferType")
        .rustified_enum("RTCGeometryType")
        .rustified_enum("RTCSubdivisionMode")
        .bitfield_enum("RTC.*Flags")
        .generate()
        .expect("Unable to generate Embree bindings.");

    // Extract the bindings source code, then clean up some names and types.
    // Note: It'd be nice to replace all patterns at once rather than using
    //       several replace() calls, but there's no point in including another
    //       build dependency (eg. regex) to handle this. The runtime of the
    //       replace() calls is dwarfed by bindgen anyway.
    let bindings_source = bindings
        .to_string()
        .replace("RTC_FORMAT_", "")
        .replace("RTC_BUILD_QUALITY_", "")
        .replace("RTC_DEVICE_PROPERTY_", "")
        .replace("RTC_ERROR_", "")
        .replace("RTC_BUFFER_TYPE_", "")
        .replace("RTC_GEOMETRY_TYPE_", "")
        .replace("RTC_SUBDIVISION_MODE_", "")
        .replace("RTC_INTERSECT_CONTEXT_FLAG_", "")
        .replace("RTC_CURVE_FLAG_", "")
        .replace("RTC_SCENE_FLAG_", "")
        .replace("RTC_BUILD_FLAG_", "")
        .replace(
            "pub type size_t = ::std::os::raw::c_ulong",
            "pub type size_t = usize",
        )
        .replace(
            "pub type __ssize_t = ::std::os::raw::c_long",
            "pub type __ssize_t = isize",
        );

    // Write the processed bindings.
    let out_file = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
    std::fs::write(out_file, bindings_source).expect("Unable to save generated Embree bindings.");
}