From 97d5311b102070810cba7374bce7a1048c999722 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Mon, 12 Oct 2020 23:23:15 +0800 Subject: [PATCH 1/5] ci: add freebsd ci --- .cirrus.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .cirrus.yml diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 00000000..cb38f1b4 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,27 @@ +freebsd_instance: + image: freebsd-12-1-release-amd64 + +task: + name: FreeBSD 12.1 + env: + DEBUG: 'napi:*' + setup_script: + - pkg update + - pkg install -y -f curl node yarn npm libnghttp2 + - curl https://sh.rustup.rs -sSf --output rustup.sh + - sh rustup.sh -y --profile minimal --default-toolchain stable + - . $HOME/.cargo/env + - | + echo "~~~~ rustc --version ~~~~" + rustc --version + echo "~~~~ node -v ~~~~" + node -v + echo "~~~~ yarn --version ~~~~" + yarn --version + test_script: + - . $HOME/.cargo/env + - yarn install --frozen-lockfile --registry https://registry.npmjs.org + - yarn build + - cargo test -p napi-sys --lib -- --nocapture + - yarn build:test + - yarn test From b651be1becb33a4889ab93816045307990b90bcb Mon Sep 17 00:00:00 2001 From: LongYinan Date: Tue, 13 Oct 2020 22:58:29 +0800 Subject: [PATCH 2/5] ci: pass tests on the freebsd platform --- .cirrus.yml | 4 ++-- ava.config.js | 10 +++++++++- sys/build.rs | 24 ++++++++++++++++++++---- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index cb38f1b4..31b90518 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -7,7 +7,7 @@ task: DEBUG: 'napi:*' setup_script: - pkg update - - pkg install -y -f curl node yarn npm libnghttp2 + - pkg install -y -f curl node yarn npm libnghttp2 llvm - curl https://sh.rustup.rs -sSf --output rustup.sh - sh rustup.sh -y --profile minimal --default-toolchain stable - . $HOME/.cargo/env @@ -20,7 +20,7 @@ task: yarn --version test_script: - . $HOME/.cargo/env - - yarn install --frozen-lockfile --registry https://registry.npmjs.org + - yarn install --ignore-platform --frozen-lockfile --registry https://registry.npmjs.org - yarn build - cargo test -p napi-sys --lib -- --nocapture - yarn build:test diff --git a/ava.config.js b/ava.config.js index 38b8725c..dfc3550c 100644 --- a/ava.config.js +++ b/ava.config.js @@ -1,7 +1,15 @@ +import { platform } from 'os' + +const platformName = platform() + const configuration = { extensions: ['ts', 'tsx'], files: ['test_module/__test__/**/*.spec.ts'], - require: ['@swc-node/register'], + require: [ + platformName === 'freebsd' + ? 'ts-node/register/transpile-only' + : '@swc-node/register', + ], environmentVariables: { SWC_NODE_PROJECT: './test_module/tsconfig.json', }, diff --git a/sys/build.rs b/sys/build.rs index 7959c3c8..110bb6ff 100644 --- a/sys/build.rs +++ b/sys/build.rs @@ -28,21 +28,37 @@ fn main() { ); } - if node_major_version < 10 { + if node_major_version < 8 && node_version.minor < 9 { panic!("node version is too low") } - let node_include_path = find_node_include_path(node_full_version.trim_end()); + let node_include_path_buf = find_node_include_path(node_full_version.trim_end()); + + let node_include_path = match env::var("NODE_INCLUDE_PATH") { + Ok(node_include_path) => node_include_path, + Err(_) => node_include_path_buf.to_str().unwrap().to_owned(), + }; let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let mut sys_bindings_path = PathBuf::from("src"); sys_bindings_path.push("bindings.h"); - bindgen::Builder::default() + let mut bindgen_builder = bindgen::Builder::default() .derive_default(true) .header(sys_bindings_path.to_str().unwrap().to_owned()) - .clang_arg(String::from("-I") + node_include_path.to_str().unwrap()) + .clang_arg(format!("-I{}", node_include_path)); + + if let Ok(uv_include_path) = env::var("UV_INCLUDE_PATH") { + bindgen_builder = bindgen_builder.clang_arg(format!("-I{}", uv_include_path)); + } else if cfg!(target_os = "freebsd") { + bindgen_builder = bindgen_builder.clang_arg(format!( + "-I{}", + node_include_path_buf.parent().unwrap().to_str().unwrap() + )); + } + + bindgen_builder .rustified_enum("(napi_|uv_).+") .whitelist_function("(napi_|uv_|extras_).+") .whitelist_type("(napi_|uv_|extras_).+") From 283a4aaab9c0ef13e65546cbb537cbb05dc5ddce Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 14 Oct 2020 00:08:22 +0800 Subject: [PATCH 3/5] feat: support freebsd in napi build command --- ava.config.js => ava.config.cjs | 10 +++------- src/build.ts | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) rename ava.config.js => ava.config.cjs (64%) diff --git a/ava.config.js b/ava.config.cjs similarity index 64% rename from ava.config.js rename to ava.config.cjs index dfc3550c..b5b82e40 100644 --- a/ava.config.js +++ b/ava.config.cjs @@ -1,17 +1,13 @@ -import { platform } from 'os' - -const platformName = platform() - const configuration = { extensions: ['ts', 'tsx'], files: ['test_module/__test__/**/*.spec.ts'], require: [ - platformName === 'freebsd' + require('os').platform() === 'freebsd' ? 'ts-node/register/transpile-only' : '@swc-node/register', ], environmentVariables: { - SWC_NODE_PROJECT: './test_module/tsconfig.json', + TS_NODE_PROJECT: './test_module/tsconfig.json', }, } @@ -19,4 +15,4 @@ if (parseInt(process.versions.napi, 10) < 4) { configuration.compileEnhancements = false } -export default configuration +module.exports = configuration diff --git a/src/build.ts b/src/build.ts index ca4e124f..06667d36 100644 --- a/src/build.ts +++ b/src/build.ts @@ -84,6 +84,7 @@ export class BuildCommand extends Command { libExt = '.dll' break case 'linux': + case 'freebsd': dylibName = `lib${dylibName}` libExt = '.so' break From 826ebd9847e4586fbb51bfa00e3cab0819ee6d90 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 14 Oct 2020 11:29:51 +0800 Subject: [PATCH 4/5] refactor(napi): module register logic --- napi/src/lib.rs | 145 +++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 76 deletions(-) diff --git a/napi/src/lib.rs b/napi/src/lib.rs index d5352128..8ce80c4e 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -16,16 +16,16 @@ //! //! #[js_function(1)] //! pub fn uv_read_file(ctx: CallContext) -> Result { -//! let path = ctx.get::(0)?; -//! let (sender, receiver) = oneshot::channel(); -//! let p = path.as_str()?.to_owned(); -//! thread::spawn(|| { -//! let res = fs::read(p).map_err(|e| Error::new(Status::Unknown, format!("{}", e))); -//! sender.send(res).expect("Send data failed"); -//! }); -//! ctx.env.execute(receiver.map_err(|e| Error::new(Status::Unknown, format!("{}", e))).map(|x| x.and_then(|x| x)), |&mut env, data| { -//! env.create_buffer_with_data(data) -//! }) +//! let path = ctx.get::(0)?; +//! let (sender, receiver) = oneshot::channel(); +//! let p = path.as_str()?.to_owned(); +//! thread::spawn(|| { +//! let res = fs::read(p).map_err(|e| Error::new(Status::Unknown, format!("{}", e))); +//! sender.send(res).expect("Send data failed"); +//! }); +//! ctx.env.execute(receiver.map_err(|e| Error::new(Status::Unknown, format!("{}", e))).map(|x| x.and_then(|x| x)), |&mut env, data| { +//! env.create_buffer_with_data(data) +//! }) //! } //! ``` //! ### tokio_rt @@ -39,13 +39,13 @@ //! //! #[js_function(1)] //! pub fn tokio_readfile(ctx: CallContext) -> Result { -//! let js_filepath = ctx.get::(0)?; -//! let path_str = js_filepath.as_str()?; -//! ctx.env.execute_tokio_future( -//! tokio::fs::read(path_str.to_owned()) -//! .map(|v| v.map_err(|e| Error::new(Status::Unknown, format!("failed to read file, {}", e)))), -//! |&mut env, data| env.create_buffer_with_data(data), -//! ) +//! let js_filepath = ctx.get::(0)?; +//! let path_str = js_filepath.as_str()?; +//! ctx.env.execute_tokio_future( +//! tokio::fs::read(path_str.to_owned()) +//! .map(|v| v.map_err(|e| Error::new(Status::Unknown, format!("failed to read file, {}", e)))), +//! |&mut env, data| env.create_buffer_with_data(data), +//! ) //! } //! ``` //! @@ -70,22 +70,22 @@ //! ``` //! #[derive(Serialize, Debug, Deserialize)] //! struct AnObject { -//! a: u32, -//! b: Vec, -//! c: String, +//! a: u32, +//! b: Vec, +//! c: String, //! } //! //! #[js_function(1)] //! fn deserialize_from_js(ctx: CallContext) -> Result { -//! let arg0 = ctx.get::(0)?; -//! let de_serialized: AnObject = ctx.env.from_js_value(arg0)?; -//! ... +//! let arg0 = ctx.get::(0)?; +//! let de_serialized: AnObject = ctx.env.from_js_value(arg0)?; +//! ... //! } //! //! #[js_function] //! fn serialize(ctx: CallContext) -> Result { -//! let value = AnyObject { a: 1, b: vec![0.1, 2.22], c: "hello" }; -//! ctx.env.to_js_value(&value) +//! let value = AnyObject { a: 1, b: vec![0.1, 2.22], c: "hello" }; +//! ctx.env.to_js_value(&value) //! } //! ``` //! @@ -138,7 +138,7 @@ pub type ContextlessResult = Result>; /// register_module!(test_module, init); /// /// fn init(module: &mut Module) -> Result<()> { -/// module.create_named_method("nativeFunction", native_function)?; +/// module.create_named_method("nativeFunction", native_function)?; /// } /// ``` #[macro_export] @@ -154,11 +154,8 @@ macro_rules! register_module { _ => Err(Error::from_status(status)), } } - #[no_mangle] - #[cfg_attr(target_os = "linux", link_section = ".ctors")] - #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] - #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] - pub static __REGISTER_MODULE: extern "C" fn() = { + + extern "C" fn register_module() { use std::ffi::CString; use std::io::Write; use std::os::raw::c_char; @@ -167,58 +164,54 @@ macro_rules! register_module { #[cfg(all(feature = "tokio_rt", napi4))] use $crate::shutdown_tokio_rt; + static mut MODULE_DESCRIPTOR: Option = None; + unsafe { + MODULE_DESCRIPTOR = Some(sys::napi_module { + nm_version: 1, + nm_flags: 0, + nm_filename: concat!(file!(), "\0").as_ptr() as *const c_char, + nm_register_func: Some(napi_register_module_v1), + nm_modname: concat!(stringify!($module_name), "\0").as_ptr() as *const c_char, + nm_priv: 0 as *mut _, + reserved: [0 as *mut _; 4], + }); - extern "C" fn register_module() { - static mut MODULE_DESCRIPTOR: Option = None; - unsafe { - MODULE_DESCRIPTOR = Some(sys::napi_module { - nm_version: 1, - nm_flags: 0, - nm_filename: concat!(file!(), "\0").as_ptr() as *const c_char, - nm_register_func: Some(init_module), - nm_modname: concat!(stringify!($module_name), "\0").as_ptr() as *const c_char, - nm_priv: 0 as *mut _, - reserved: [0 as *mut _; 4], - }); + sys::napi_module_register(MODULE_DESCRIPTOR.as_mut().unwrap() as *mut sys::napi_module); + } - sys::napi_module_register(MODULE_DESCRIPTOR.as_mut().unwrap() as *mut sys::napi_module); - } + #[no_mangle] + pub unsafe extern "C" fn napi_register_module_v1( + raw_env: sys::napi_env, + raw_exports: sys::napi_value, + ) -> sys::napi_value { + let env = Env::from_raw(raw_env); + let mut exports: JsObject = JsObject::from_raw_unchecked(raw_env, raw_exports); + let mut cjs_module = Module { env, exports }; + let result = $init(&mut cjs_module); - extern "C" fn init_module( - raw_env: sys::napi_env, - raw_exports: sys::napi_value, - ) -> sys::napi_value { - let env = Env::from_raw(raw_env); - let mut exports: JsObject = JsObject::from_raw_unchecked(raw_env, raw_exports); - let mut cjs_module = Module { env, exports }; - let result = $init(&mut cjs_module); + #[cfg(all(feature = "tokio_rt", napi4))] + let hook_result = check_status(unsafe { + sys::napi_add_env_cleanup_hook(raw_env, Some(shutdown_tokio_rt), ptr::null_mut()) + }); - #[cfg(all(feature = "tokio_rt", napi4))] - let hook_result = check_status(unsafe { - sys::napi_add_env_cleanup_hook(raw_env, Some(shutdown_tokio_rt), ptr::null_mut()) - }); + #[cfg(not(all(feature = "tokio_rt", napi4)))] + let hook_result = Ok(()); - #[cfg(not(all(feature = "tokio_rt", napi4)))] - let hook_result = Ok(()); - - match hook_result.and_then(move |_| result) { - Ok(_) => cjs_module.exports.raw(), - Err(e) => { - unsafe { - sys::napi_throw_error( - raw_env, - ptr::null(), - CString::from_vec_unchecked(format!("Error initializing module: {}", e).into()) - .as_ptr() as *const _, - ) - }; - ptr::null_mut() - } + match hook_result.and_then(move |_| result) { + Ok(_) => cjs_module.exports.raw(), + Err(e) => { + unsafe { + sys::napi_throw_error( + raw_env, + ptr::null(), + CString::from_vec_unchecked(format!("Error initializing module: {}", e).into()) + .as_ptr() as *const _, + ) + }; + ptr::null_mut() } } } - - register_module - }; + } }; } From 24919d72be77ff3d8b2d4a66dc9bbea21e990464 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 14 Oct 2020 11:44:37 +0800 Subject: [PATCH 5/5] ci: write ava.config for ava@2 --- .eslintrc.yml | 8 ++++++++ .github/workflows/napi3.yaml | 5 +++++ tsconfig.root-lint.json | 4 ++++ write-ava-config.js | 10 ++++++++++ 4 files changed, 27 insertions(+) create mode 100644 tsconfig.root-lint.json create mode 100644 write-ava-config.js diff --git a/.eslintrc.yml b/.eslintrc.yml index 6b2d1eab..c78e1ab6 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -5,6 +5,7 @@ parserOptions: jsx: true ecmaVersion: 2020 sourceType: module + extraFileExtensions: ['.cjs'] project: ./tsconfig.json env: @@ -220,3 +221,10 @@ overrides: - '@typescript-eslint' parserOptions: project: ./bench/tsconfig.json + + - files: + - ./*.{cjs,js} + plugins: + - '@typescript-eslint' + parserOptions: + project: ./tsconfig.root-lint.json diff --git a/.github/workflows/napi3.yaml b/.github/workflows/napi3.yaml index 5548f8ce..b7e5924b 100644 --- a/.github/workflows/napi3.yaml +++ b/.github/workflows/napi3.yaml @@ -73,6 +73,11 @@ jobs: command: test args: -p napi-sys --lib -- --nocapture + - name: Write ava.config.js for ava@2 + run: | + node write-ava-config.js + cat ava.config.js + - name: Unit tests run: | yarn --cwd ./test_module --ignore-engines build diff --git a/tsconfig.root-lint.json b/tsconfig.root-lint.json new file mode 100644 index 00000000..a4f467cf --- /dev/null +++ b/tsconfig.root-lint.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "files": ["./ava.config.cjs", "./write-ava-config.js"] +} diff --git a/write-ava-config.js b/write-ava-config.js new file mode 100644 index 00000000..01842d84 --- /dev/null +++ b/write-ava-config.js @@ -0,0 +1,10 @@ +const { writeFileSync } = require('fs') +const { join } = require('path') + +const config = require('./ava.config.cjs') + +const code = ` +export default ${JSON.stringify(config, null, 2)} +` + +writeFileSync(join(__dirname, 'ava.config.js'), code)