rofisys 0.4.0

This system crate provides Rust language bindings (via the use of Bindgen) to the Rust-OFI library.
Documentation
extern crate bindgen;
extern crate subprocess;

use autotools;
use glob::glob;
use std::env;
use std::path::PathBuf;
use std::process::{exit, Command, Stdio};

fn build_rofi(
    out_path: &PathBuf,
    ofi_env: &PathBuf,
    ofi_lib_dir: &PathBuf,
    ofi_inc_dir: &PathBuf,
) -> PathBuf {
    let dest = out_path.clone().join("rofi_src");
    Command::new("cp")
        .args(&["-r", "rofi", &dest.to_string_lossy()])
        .status()
        .unwrap();

    #[cfg(feature = "shared")]
    let path = autotools::Config::new(dest)
        .reconf("-ivfWnone")
        .enable("shared", None)
        .disable("static", None)
        .with(format!("ofi={}", ofi_env.display()), None)
        .ldflag(format! {" -L{} -libverbs -pthread -ldl -lrdmacm -lrt",ofi_lib_dir.display()})
        .cflag(format! {"-O3  -I{}",ofi_inc_dir.display()})
        .cxxflag(format! {"-O3"})
        .build();
    #[cfg(not(feature = "shared"))]
    let path = autotools::Config::new(dest)
        .reconf("-ivfWnone")
        .disable("shared", None)
        .enable("static", None)
        .with(format!("ofi={}", ofi_env.display()), None)
        .ldflag(format! {"-L{} -libverbs -pthread -ldl -lrdmacm -lrt",ofi_lib_dir.display()})
        .cflag(format! {"-O3 -I{} ",ofi_inc_dir.display()})
        .cxxflag(format! {"-O3"})
        .build();
    path
}

fn check_lib_for_function(lib: PathBuf, func: &str) -> Option<()> {
    let nm_out = Command::new("nm")
        .args(&["-g", &lib.to_string_lossy()])
        .stdout(Stdio::piped())
        .spawn()
        .ok()?;
    let grep1 = Command::new("grep")
        .arg(func)
        .stdin(Stdio::from(nm_out.stdout?)) // Pipe through.
        .stdout(Stdio::piped())
        .spawn()
        .ok()?;
    let grep2 = Command::new("grep")
        .arg("T")
        .stdin(Stdio::from(grep1.stdout?)) // Pipe through.
        .output()
        .ok()?;
    if grep2.stdout.len() > 0 {
        return Some(());
    } else {
        return None;
    }
}

fn build_bindings() {
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());

    let ofi_env = match env::var("OFI_DIR") {
        Ok(val) => std::path::PathBuf::from(val),
        Err(_) => {
            let dest = out_path.clone().join("ofi_src");
            Command::new("cp")
                .args(&["-r", "libfabric", &dest.to_string_lossy()])
                .status()
                .unwrap();

            #[cfg(not(feature = "shared"))]
            let install_dest = autotools::Config::new(dest.clone())
                .reconf("-ivf")
                .disable("shared", None)
                .enable("only", None)
                .enable("verbs", None)
                .enable("atomics", None)
                .enable("rxm", None)
                .enable("xpmem", Some("no"))
                .cflag("-O3")
                .cxxflag("-O3")
                .build();
            #[cfg(feature = "shared")]
            let install_dest = autotools::Config::new(dest.clone())
                .reconf("-ivf")
                .enable("shared", None)
                .disable("static", None)
                .enable("only", None)
                .enable("verbs", None)
                .enable("atomics", None)
                .enable("rxm", None)
                .enable("xpmem", Some("no"))
                .cflag("-O3")
                .cxxflag("-O3")
                .build();
            std::path::PathBuf::from(install_dest)
        }
    };
    let ofi_lib_dir = ofi_env.join("lib");
    let ofi_inc_dir = ofi_env.join("include");

    let rofi_env = match env::var("ROFI_DIR") {
        Ok(val) => {
            let rofi_path = std::path::PathBuf::from(val);
            let rofi_lib_dir = rofi_path.join("lib");
            let rofi_a = rofi_lib_dir.join("librofi.a");
            let rofi_so = rofi_lib_dir.join("librofi.so");
            if check_lib_for_function(rofi_a, "rofi_transport_init").is_some() {
                rofi_path
            } else if check_lib_for_function(rofi_so, "rofi_transport_init").is_some() {
                rofi_path
            } else {
                println!("cargo:warning=unable to detect rofi version at {:?}. SUGGESTED: Rofisys includes a bundled version of Rofi it can build itself, simply unset the ROFI_DIR env variable to use the bundled version. ALTERNATIVE: update to version 0.3 of Rofi manually.",rofi_path);
                exit(1);
            }
        }
        Err(_) => build_rofi(&out_path, &ofi_env, &ofi_lib_dir, &ofi_inc_dir),
    };
    for entry in glob("rofi/**/*.c").expect("Failed to read glob pattern") {
        match entry {
            Ok(path) => println!("cargo:rerun-if-changed={}", path.display()),
            Err(_) => {}
        }
    }
    for entry in glob("rofi/**/*.h").expect("Failed to read glob pattern") {
        match entry {
            Ok(path) => println!("cargo:rerun-if-changed={}", path.display()),
            Err(_) => {}
        }
    }

    let rofi_inc_dir = rofi_env.join("include");
    let rofi_lib_dir = rofi_env.join("lib");

    println!("cargo:root={}", out_path.display());
    println!("cargo:rustc-link-search=native={}", rofi_lib_dir.display());
    println!("cargo:rustc-link-search=native={}", ofi_lib_dir.display());
    println!("cargo:rustc-link-arg=-Wl,-rpath,{}", rofi_lib_dir.display());
    println!("cargo:rustc-link-arg=-Wl,-rpath,{}", ofi_lib_dir.display());

    println!("cargo:rustc-link-search=native=/usr/lib64");
    println!("cargo:rustc-link-search=native=/usr/lib64/libibverbs");
    println!("cargo:rustc-link-lib=rofi");
    println!("cargo:rustc-link-lib=fabric");
    println!("cargo:rustc-link-lib=ibverbs");
    println!("cargo:rustc-link-lib=rdmacm");
    println!("cargo:rustc-link-lib=pmi_simple");

    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .clang_arg("-I/usr/include")
        .clang_arg(format! {"-I{}",ofi_inc_dir.display()})
        .clang_arg(format! {"-I{}/rdma",ofi_inc_dir.display()})
        .clang_arg(format!("-I{}", rofi_inc_dir.display()))
        .generate()
        .expect("Unable to generate bindings");

    // Write the bindings to src/bindings.rs file.
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

fn main() {
    build_bindings();
}