diff --git a/.github/workflows/linux-musl.yaml b/.github/workflows/linux-musl.yaml index b6acf585..4ad5fe9b 100644 --- a/.github/workflows/linux-musl.yaml +++ b/.github/workflows/linux-musl.yaml @@ -33,7 +33,7 @@ jobs: - name: Run tests run: | - docker run --rm -v $(pwd)/.cargo:/root/.cargo -v $(pwd):/napi-rs -w /napi-rs builder cargo test -p napi-rs --lib -- --nocapture + docker run --rm -v $(pwd)/.cargo:/root/.cargo -v $(pwd):/napi-rs -w /napi-rs builder cargo test -p napi-sys --lib -- --nocapture - name: Unit test run: | diff --git a/.github/workflows/linux.yaml b/.github/workflows/linux.yaml index c2891f2f..d1603e6a 100644 --- a/.github/workflows/linux.yaml +++ b/.github/workflows/linux.yaml @@ -58,7 +58,7 @@ jobs: timeout-minutes: 5 with: command: test - args: -p napi-rs --lib -- --nocapture + args: -p napi-sys --lib -- --nocapture - name: Unit tests run: | diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index 6df1f0f9..1f95d00a 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -58,7 +58,7 @@ jobs: timeout-minutes: 5 with: command: test - args: -p napi-rs --lib -- --nocapture + args: -p napi-sys --lib -- --nocapture - name: Unit tests run: | diff --git a/.github/workflows/napi3.yaml b/.github/workflows/napi3.yaml index 1cc3afc4..57564d79 100644 --- a/.github/workflows/napi3.yaml +++ b/.github/workflows/napi3.yaml @@ -54,7 +54,7 @@ jobs: timeout-minutes: 5 with: command: test - args: -p napi-rs --lib -- --nocapture + args: -p napi-sys --lib -- --nocapture - name: Unit tests run: | diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 1b2edc67..bcffd24f 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -57,14 +57,14 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: -p napi-rs -vvv + args: --all --bins --examples --tests -vvv - name: Tests uses: actions-rs/cargo@v1 timeout-minutes: 5 with: command: test - args: -p napi-rs --lib -- --nocapture + args: -p napi-sys --lib -- --nocapture - name: Unit tests run: | diff --git a/.gitignore b/.gitignore index 4ca1652d..890bcaab 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ build/LICENSE napi/LICENSE napi/README.md napi-derive/LICENSE +sys/LICENSE # Created by https://www.gitignore.io/api/node # Edit at https://www.gitignore.io/?templates=node diff --git a/Cargo.toml b/Cargo.toml index 9ff9fe0d..21784d41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "./build", "./napi", "./napi-derive", - "./napi-derive-example" + "./napi-derive-example", + "./sys" ] exclude = ["./test_module"] diff --git a/napi-derive-example/Cargo.toml b/napi-derive-example/Cargo.toml index 3191fc82..ffb64382 100644 --- a/napi-derive-example/Cargo.toml +++ b/napi-derive-example/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" crate-type = ["cdylib"] [dependencies] -napi-rs = { path = "../napi" } -napi-rs-derive = { path = "../napi-derive" } +napi = { path = "../napi" } +napi-derive = { path = "../napi-derive" } [build-dependencies] napi-build = { path = "../build" } diff --git a/napi-derive-example/src/lib.rs b/napi-derive-example/src/lib.rs index b283d0ae..739eb0d3 100644 --- a/napi-derive-example/src/lib.rs +++ b/napi-derive-example/src/lib.rs @@ -1,7 +1,7 @@ #[macro_use] -extern crate napi_rs as napi; +extern crate napi; #[macro_use] -extern crate napi_rs_derive; +extern crate napi_derive; use napi::{CallContext, Error, JsNumber, JsUnknown, Module, Result, Status}; use std::convert::TryInto; diff --git a/napi-derive/Cargo.toml b/napi-derive/Cargo.toml index e304b670..b8d025c9 100644 --- a/napi-derive/Cargo.toml +++ b/napi-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "napi-rs-derive" -version = "0.2.0" +name = "napi-derive" +version = "0.4.0" authors = ["LongYinan "] edition = "2018" description = "N-API procedural macros" diff --git a/napi-derive/src/lib.rs b/napi-derive/src/lib.rs index 85168638..77a0d350 100644 --- a/napi-derive/src/lib.rs +++ b/napi-derive/src/lib.rs @@ -82,24 +82,24 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream { #signature #(#fn_block)* #visibility extern "C" fn #fn_name( - raw_env: napi_rs::sys::napi_env, - cb_info: napi_rs::sys::napi_callback_info, - ) -> napi_rs::sys::napi_value { + raw_env: napi::sys::napi_env, + cb_info: napi::sys::napi_callback_info, + ) -> napi::sys::napi_value { use std::io::Write; use std::mem; use std::os::raw::c_char; use std::ptr; use std::ffi::CString; - use napi_rs::{JsUnknown, Env, Status, NapiValue, CallContext}; + use napi::{JsUnknown, Env, Status, NapiValue, CallContext}; let mut argc = #arg_len_span as usize; let mut raw_args = - unsafe { mem::MaybeUninit::<[napi_rs::sys::napi_value; #arg_len_span as usize]>::uninit().assume_init() }; + unsafe { mem::MaybeUninit::<[napi::sys::napi_value; #arg_len_span as usize]>::uninit().assume_init() }; let mut raw_this = ptr::null_mut(); let mut has_error = false; unsafe { - let status = napi_rs::sys::napi_get_cb_info( + let status = napi::sys::napi_get_cb_info( raw_env, cb_info, &mut argc as *mut usize as *mut u64, @@ -120,10 +120,10 @@ pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream { Err(e) => { let message = format!("{}", e); unsafe { - napi_rs::sys::napi_throw_error(raw_env, ptr::null(), CString::from_vec_unchecked(message.into()).as_ptr() as *const c_char); + napi::sys::napi_throw_error(raw_env, ptr::null(), CString::from_vec_unchecked(message.into()).as_ptr() as *const c_char); } let mut undefined = ptr::null_mut(); - unsafe { napi_rs::sys::napi_get_undefined(raw_env, &mut undefined) }; + unsafe { napi::sys::napi_get_undefined(raw_env, &mut undefined) }; undefined } } diff --git a/napi/Cargo.toml b/napi/Cargo.toml index cd0e23b5..a9ef3848 100644 --- a/napi/Cargo.toml +++ b/napi/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "napi-rs" -version = "0.3.1" +name = "napi" +version = "0.4.0" authors = ["Nathan Sobo ", "Yinan Long "] license = "MIT" description = "N-API bindings" @@ -13,6 +13,9 @@ edition = "2018" libuv = ["futures"] tokio_rt = ["futures", "tokio", "once_cell"] +[dependencies] +napi-sys = { version = "0.4", path = "../sys" } + [dependencies.futures] version = "0.3" optional = true @@ -27,14 +30,5 @@ version = "1.4" optional = true -[target.'cfg(windows)'.build-dependencies] -flate2 = "1.0" -reqwest = { version = "0.10", features = ["native-tls", "blocking"] } -tar = "0.4" - [build-dependencies] -bindgen = "0.54" -glob = "0.3" napi-build = { version = "0.1", path = "../build" } -regex = "1.3" -semver = "0.10" diff --git a/napi/build.rs b/napi/build.rs index f80d29ad..1f866b6a 100644 --- a/napi/build.rs +++ b/napi/build.rs @@ -1,118 +1,5 @@ -extern crate bindgen; -#[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() - ); - } - - 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"); -} - -#[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") } diff --git a/napi/src/lib.rs b/napi/src/lib.rs index 108336c6..2df52218 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -65,7 +65,6 @@ mod module; #[cfg(all(feature = "libuv", napi4))] mod promise; mod status; -pub mod sys; mod task; #[cfg(napi4)] pub mod threadsafe_function; @@ -75,6 +74,8 @@ mod tokio_rt; mod uv; mod version; +pub use napi_sys as sys; + pub use call_context::CallContext; pub use env::*; pub use error::{Error, Result}; diff --git a/sys/Cargo.toml b/sys/Cargo.toml new file mode 100644 index 00000000..a92e08a5 --- /dev/null +++ b/sys/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "napi-sys" +version = "0.4.0" +authors = ["LongYinan "] +edition = "2018" +readme = "README.md" +repository = "https://github.com/Brooooooklyn/napi-rs" +license = "MIT" +keywords = ["NodeJS", "FFI", "NAPI", "n-api"] + +[target.'cfg(windows)'.build-dependencies] +flate2 = "1.0" +reqwest = { version = "0.10", features = ["native-tls", "blocking"] } +tar = "0.4" + +[build-dependencies] +bindgen = "0.54" +glob = "0.3" +regex = "1.3" +semver = "0.10" diff --git a/sys/README.md b/sys/README.md new file mode 100644 index 00000000..171f1317 --- /dev/null +++ b/sys/README.md @@ -0,0 +1,5 @@ +# napi-sys + +Low-level N-API bindings for Node.js addons written in Rust. + +See the [napi](https://github.com/napi-rs/napi-rs) for the high-level API. diff --git a/sys/build.rs b/sys/build.rs new file mode 100644 index 00000000..aff9e17a --- /dev/null +++ b/sys/build.rs @@ -0,0 +1,115 @@ +extern crate bindgen; +#[cfg(windows)] +extern crate flate2; +extern crate glob; +#[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() { + 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/**/*.*").unwrap() { + println!( + "cargo:rerun-if-changed={}", + entry.unwrap().to_str().unwrap() + ); + } + + 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("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"); +} + +#[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") +} diff --git a/napi/src/sys/bindings.h b/sys/src/bindings.h similarity index 100% rename from napi/src/sys/bindings.h rename to sys/src/bindings.h diff --git a/sys/src/lib.rs b/sys/src/lib.rs new file mode 100644 index 00000000..644a4ab9 --- /dev/null +++ b/sys/src/lib.rs @@ -0,0 +1,2 @@ +mod sys; +pub use sys::*; diff --git a/napi/src/sys/mod.rs b/sys/src/sys.rs similarity index 100% rename from napi/src/sys/mod.rs rename to sys/src/sys.rs diff --git a/test_module/Cargo.toml b/test_module/Cargo.toml index 9d8705a5..c0e3f30d 100644 --- a/test_module/Cargo.toml +++ b/test_module/Cargo.toml @@ -9,8 +9,8 @@ crate-type = ["cdylib"] [dependencies] futures = "0.3" -napi-rs = { path = "../napi", features = ["libuv", "tokio_rt"] } -napi-rs-derive = { path = "../napi-derive" } +napi = { path = "../napi", features = ["libuv", "tokio_rt"] } +napi-derive = { path = "../napi-derive" } tokio = { version = "0.2", features = ["default", "fs"]} [build-dependencies] diff --git a/test_module/src/lib.rs b/test_module/src/lib.rs index e72ec243..b0711945 100644 --- a/test_module/src/lib.rs +++ b/test_module/src/lib.rs @@ -1,7 +1,7 @@ #[macro_use] -extern crate napi_rs as napi; +extern crate napi; #[macro_use] -extern crate napi_rs_derive; +extern crate napi_derive; use napi::{CallContext, Error, JsString, JsUnknown, Module, Result, Status};