binaryen_capi-sys 0.117.0

Rust Raw Bindings for binaryen
Documentation
use std::{path::{Path, PathBuf}, process::Command};

const BINARYEN_URL: &str = "https://github.com/WebAssembly/binaryen.git";
const CMAKE_ARGS: [&str;2] = ["-DBUILD_TESTS=OFF", "-DBUILD_TOOLS=OFF"];

fn main() {
    if std::env::var("DOCS_RS").is_err() {
        println!("cargo:rerun-if-changed=build.rs");
        // println!("cargo:rerun-if-changed=Cargo.toml");
    
        let out_dir = std::env::var("OUT_DIR").unwrap();
    
        let blib_path: PathBuf;
        let binclude_path: PathBuf;

        if let Ok(bpath) = std::env::var("BINARYEN_PATH") {
            blib_path = Path::new(&bpath).join("lib");
            binclude_path = Path::new(&bpath).join("include");
        } else if let Some(bpath) = serch_binaryen() {
            blib_path = bpath.join("lib");
            binclude_path = bpath.join("include");
        } else {
            let (lib_path, include_path) = build_binaryen(&out_dir);
            blib_path = lib_path;
            binclude_path = include_path;
        }

        println!("cargo:rustc-link-search={}", blib_path.to_str().unwrap());
        println!("cargo:rustc-link-lib=binaryen");
        println!("cargo:rustc-link-lib=stdc++");

        // bindgen
        let bindings = bindgen::Builder::default()
            .header(
                binclude_path.join("binaryen-c.h").to_str().unwrap()
            )
            .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
            .generate()
            .expect("Unable to generate bindings");

        bindings.write_to_file(Path::new(&out_dir).join("bindings.rs"))
            .expect("Couldn't write bindings!");
    } else {
        let binding = bindgen::Builder::default()
            .header("libs/include/binaryen-c.h")
            .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
            .generate()
            .expect("Unable to generate bindings");
        
        binding.write_to_file(Path::new(&std::env::var("OUT_DIR").unwrap()).join("bindings.rs"))
            .expect("Couldn't write bindings!");
    }
}

fn build_binaryen(out_dir: &String) -> (PathBuf, PathBuf){
    let _ = Command::new("rm")
        .args(["-rf", Path::new(out_dir).join("binaryen").to_str().unwrap()])
        .output();

    // git clone
    let clone = Command::new("git")
        .args(["clone", BINARYEN_URL])
        .current_dir(out_dir)
        .output();

    if let Err(err) = clone {
        panic!("Failed to clone binaryen: {}\n(need git)", err);
    }

    let check_ninja = Command::new("ninja")
        .arg("--version")
        .output();

    // build binaryen
    // ninja check
    if let Ok(_) = check_ninja {
        // cmake
        let cmake = Command::new("cmake")
            .args(CMAKE_ARGS)
            .args(["-G", "Ninja"])
            .arg(".")
            .current_dir(Path::new(out_dir).join("binaryen"))
            .output();

        if let Err(err) = cmake {
            panic!("Failed to cmake binaryen: {}\n(need cmake)", err);
        }

        // ninja
        let ninja = Command::new("ninja")
            .current_dir(Path::new(out_dir).join("binaryen"))
            .output();

        if let Err(err) = ninja {
            panic!("Failed to ninja binaryen: {}", err);
        }
    } else {
        // cmake
        let cmake = Command::new("cmake")
            .args(CMAKE_ARGS)
            .arg(".")
            .current_dir(Path::new(out_dir).join("binaryen"))
            .output();

        if let Err(err) = cmake {
            panic!("Failed to cmake binaryen: {}\n(need cmake)", err);
        }

        // make
        let make = Command::new("make")
            .current_dir(Path::new(out_dir).join("binaryen"))
            .output();

        if let Err(err) = make {
            panic!("Failed to make binaryen: {}\n(need make or ninja)", err);
        }
    }

    (Path::new(out_dir).join("binaryen").join("lib"), Path::new(out_dir).join("binaryen").join("src"))
}

fn serch_binaryen() -> Option<PathBuf> {
    let os = std::env::var_os("CARGO_CFG_TARGET_OS");

    if let Some(os) = os {
        if os == "windows" {
            let where_ = Command::new("where.exe")
                .arg("wasm-opt")
                .output();
            match where_ {
                Err(_) => None,
                Ok(out) => {
                    if out.status.code().unwrap() != 0 {
                        return None;
                    }
                    
                    let w = std::str::from_utf8(&out.stdout).unwrap();
                    let bin_path = w.split("\n").nth(0).unwrap();
                    Some(Path::new(bin_path).ancestors().nth(2).unwrap().into())
                }
            }
        } else {
            let where_ = Command::new("which")
                .arg("wasm-opt")
                .output();
            match where_ {
                Err(_) => None,
                Ok(out) => {
                    if out.status.code().unwrap() != 0 {
                        return None;
                    }
                    
                    let w = std::str::from_utf8(&out.stdout).unwrap();
                    let bin_path = w.split("\n").nth(0).unwrap();
                    Some(Path::new(bin_path).ancestors().nth(2).unwrap().into())
                }
            }
        }
    } else {
        None
    }
}