extern crate bindgen; extern crate cc; #[cfg(windows)] extern crate flate2; extern crate glob; extern crate napi_build; #[cfg(windows)] extern crate reqwest; extern crate semver; #[cfg(windows)] extern crate tar; use glob::glob; use std::env; use std::path::PathBuf; use std::process::Command; // https://stackoverflow.com/questions/37498864/finding-executable-in-path-with-rust const NODE_PRINT_EXEC_PATH: &'static str = "console.log(process.execPath)"; fn main() { napi_build::setup(); let node_full_version = String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout).unwrap(); let node_version = semver::Version::parse(node_full_version.as_str().get(1..).unwrap()).unwrap(); let node_major_version = node_version.major; println!("cargo:rerun-if-env-changed=NODE_INCLUDE_PATH"); for entry in glob("./src/sys/**/*.*").unwrap() { println!( "cargo:rerun-if-changed={}", entry.unwrap().to_str().unwrap() ); } env::set_var("CARGO_RUSTC_FLAGS", "-Clink-args=-export_dynamic"); if node_major_version < 10 { panic!("node version is too low") } let node_include_path = find_node_include_path(node_full_version.trim_end()); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let mut sys_bindigs_path = PathBuf::from("src"); sys_bindigs_path.push("sys"); sys_bindigs_path.push("bindings.h"); bindgen::Builder::default() .header(sys_bindigs_path.to_str().unwrap().to_owned()) .clang_arg(String::from("-I") + node_include_path.to_str().unwrap()) .rustified_enum("(napi_|uv_).+") .whitelist_function("(napi_|uv_|extras_).+") .whitelist_type("(napi_|uv_|extras_).+") .generate() .expect("Unable to generate napi bindings") .write_to_file(out_path.join("bindings.rs")) .expect("Unable to write napi bindings"); let mut bindings_path = PathBuf::from("src"); bindings_path.push("sys"); bindings_path.push("bindings.cc"); let mut cc_builder = cc::Build::new(); cc_builder .cpp(true) .include(&node_include_path) .file(bindings_path); if !cfg!(windows) { cc_builder.flag("-Wno-unused-parameter"); }; if cfg!(target_os = "macos") { cc_builder.flag("-std=c++0x"); } else if cfg!(linux) || cfg!(target_env = "gnu") { cc_builder.flag("-std=c++14"); } cc_builder .cargo_metadata(true) .out_dir(&out_path) .compile("napi-bindings"); } #[cfg(target_os = "windows")] fn find_node_include_path(node_full_version: &str) -> PathBuf { let mut node_exec_path = PathBuf::from( String::from_utf8( Command::new("node") .arg("-e") .arg(NODE_PRINT_EXEC_PATH) .output() .unwrap() .stdout, ) .expect("can not find executable node"), ) .parent() .unwrap() .to_path_buf(); node_exec_path.push(format!("node-headers-{}.tar.gz", node_full_version)); let mut header_dist_path = PathBuf::from(&PathBuf::from(&node_exec_path).parent().unwrap()); let unpack_path = PathBuf::from(&header_dist_path); header_dist_path.push(format!("node-{}", node_full_version)); header_dist_path.push("include"); header_dist_path.push("node"); if !header_dist_path.exists() { let header_file_download_url = String::from_utf8( Command::new("node") .args(vec!["-e", "console.log(process.release.headersUrl)"]) .output() .unwrap() .stdout, ) .unwrap(); let resp = reqwest::blocking::get(&header_file_download_url).expect("request failed"); tar::Archive::new(flate2::read::GzDecoder::new(resp)) .unpack(&unpack_path) .expect("Unpack headers file failed"); }; header_dist_path } #[cfg(not(target_os = "windows"))] fn find_node_include_path(_node_full_version: &str) -> PathBuf { let node_exec_path = String::from_utf8( Command::new("node") .arg("-e") .arg(NODE_PRINT_EXEC_PATH) .output() .unwrap() .stdout, ) .unwrap(); PathBuf::from(node_exec_path) .parent() .unwrap() .parent() .unwrap() .join("include/node") }