napi cmake 将c++库编译为node包

napi cmake 编译node包

使用napi提供的方法把cmake c++项目打成node包,提供给javaScript用require引用

环境准备

第一步: 下载cmake-js
 npm i cmake-js -g 
第二步: 改造CMakeLists.txt文件
cmake_minimum_required(VERSION 3.9)

# 0042索引对应的策略使用新策略,具体策略未知
cmake_policy(SET CMP0042 NEW)
set (CMAKE_CXX_STANDARD 11)

project (build-node-addon-api-with-cmake)

# conan相关, 原项目的内容,不必理会, 如果不用conan,请删除下面两行
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

# 使用utf-8解析代码, 原项目的内容
if(MSVC)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /utf-8")
endif()

# napi 头文件目录
include_directories(${CMAKE_JS_INC})

aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC)

# 生成库时增加了cmake-js的东西
add_library(${PROJECT_NAME} SHARED ${SRC} ${CMAKE_JS_SRC})

# SUFFIX 导入.node后缀的库,而不仅仅是.dll
# PREFIX 导入库的前缀,而不仅仅是lib
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")

# 链接cmake-js的库和conan的库,如果不用conan不要加 ${CONAN_LIBS}
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB} ${CONAN_LIBS})

# 打印命令结果到变量中,命令会返回addon的c头文件目录
execute_process(COMMAND node -p "require('node-addon-api').include"
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        OUTPUT_VARIABLE NODE_ADDON_API_DIR
        )
#去除换行符
string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})

# 给该target指定头文件发现目录
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})

# define NPI_VERSION
add_definitions(-DNAPI_VERSION=3)

第三步: 增加package.json文件
{
  "name": "build-node-addon-api-with-cmake",
  "version": "0.0.0",
  "description": "Build N-API native addon with CMake and node-addon-api C++ wrapper.",
  "main": "hello.js",
  "private": true,
  "dependencies": {
    "bindings": "~1.2.1",
    "node-addon-api": "^1.0.0"
  },
  "scripts": {
    "install": "cmake-js compile",
    "test": "node hello.js"
  }
}

第四步: 增加给javaScript调用的接口
#include <napi.h>

#include "test.h"

static Napi::String MMethod(const Napi::CallbackInfo& info) {
 Napi::Env env = info.Env();
 return Napi::String::New(env, PrintHello());
}

static Napi::Boolean GetBool(const Napi::CallbackInfo& info) {
   Napi::Env env = info.Env();
   Compair(0,0);
   if (info.Length() < 2) {
       Napi::TypeError::New(env, "Wrong number of arguments")
           .ThrowAsJavaScriptException();
       return Napi::Boolean::New(env, Compair(0,2));
   }
   std::string arg0 = info[0].As<Napi::String>();
   std::string arg1 = info[1].As<Napi::String>();

   return Napi::Boolean::New(env, Compair(arg0,arg1));
}


static Napi::Object Init(Napi::Env env, Napi::Object exports) {
 exports.Set(Napi::String::New(env, "gethello"),
             Napi::Function::New(env, MMethod));
 exports.Set(Napi::String::New(env, "getbool"),
             Napi::Function::New(env, GetBool));
 return exports;
}

NODE_API_MODULE(addon, Init)

其中CNODE_API_MODULE(addon, Init)这一行, 第一个参数可以随便写,应该是暴露出去的类名, 第二个和Init函数同名, Init函数中调用exports.Set可以把想要暴露的接口和变量注册进去。

前面都是函数的定义:

第一个MMethod函数,接收0个参数,返回一个字符串,这个字符串是通过test.h中的c++函数PrintHello()生成的。
第二个函数GetBool接收两个字符串参数,如果参数不足2个,会向javaScript抛出一个异常,如果参数满足条件,会调用c++的Compair返回一个bool值。

需要注意的是,在c++函数中获取javaScript参数的方法:

如果是字符串, 直接使用std::string arg0 = info[0].As<Napi::String>()获取就可以了。
但是如果是其他类型,如double, 就需要用下面的方式获取

double arg0 = info[0].As<Napi::Number>().DoubleValue()

获取数字类型,有两个步骤,第一个是获取到Napi::Number类型,然后在调用DoubleValue()转换为c++的double类型。Napi::Number定义如下:


  /// A JavaScript number value.
   class Number : public Value {
      public:
           static Number New(
                   napi_env env, ///< N-API environment
                   double value  ///< Number value
                   );

           Number();                               ///< Creates a new _empty_ Number instance.
           Number(napi_env env, napi_value value); ///< Wraps a N-API value primitive.

           operator int32_t() const;  ///< Converts a Number value to a 32-bit signed integer value.
           operator uint32_t() const; ///< Converts a Number value to a 32-bit unsigned integer value.
           operator int64_t() const;  ///< Converts a Number value to a 64-bit signed integer value.
           operator float() const;    ///< Converts a Number value to a 32-bit floating-point value.
           operator double() const;   ///< Converts a Number value to a 64-bit floating-point value.

           int32_t Int32Value() const;   ///< Converts a Number value to a 32-bit signed integer value.
           uint32_t Uint32Value() const; ///< Converts a Number value to a 32-bit unsigned integer value.
           int64_t Int64Value() const;   ///< Converts a Number value to a 64-bit signed integer value.
           float FloatValue() const;     ///< Converts a Number value to a 32-bit floating-point value.
           double DoubleValue() const;   ///< Converts a Number value to a 64-bit floating-point value.
   };

还有一些特殊的参数,比如数组,对象,函数。数组和对象这里不做过多介绍,函数参数可以实现c++的回调函数,可以达到c++调用javaScript的目的,这里简单讲一下用法。

c++文件:

//这个函数接收一个javaScript的函数,参数,并且立即调用,
void RunCallback(const Napi::CallbackInfo& info) {
 Napi::Env env = info.Env();
 Napi::Function cb = info[0].As<Napi::Function>();
 cb.Call(env.Global(), {Napi::String::New(env, "hello world callback")});
}

javascript文件:

addon.runCallback(function(msg){
 console.log(msg);
});
第五步: 写一个hell.js测试
var addon = require('./build/lib/build-node-addon-api-with-cmake');

console.log(addon.gethello()); // 'world'
console.log(addon.getbool('111', '111')); // 'world'

其中require的路径,就看你产物的目录了,你可以把产物迁移到其他目录

第六步: 编译运行
npm i // 下载package.josn中声明的js依赖
cmake-js build //构建, 或者执行 npm run install
node hello.js //运行js测试代码, 或者执行 npm run test

构建输出:

➜  cpp_to_nodejs git:(master)npm run install                                                                                          [20/463]
                                                                                                                                                 
> build-node-addon-api-with-cmake@0.0.0 install /home/打码/work/cpp_to_nodejs                                                                     
> cmake-js compile                                                                                                                               
                                                                                                                                                 
[                                                                                                                                                
  '/node/v14.5.0/bin/node',                                                                                               
  '/home/打码/.nvm/versions/node/v14.5.0/bin/cmake-js',                                                                                           
  'compile'                                                                                                                                      
]                                                                                                                                                
info TOOL Using Unix Makefiles generator.                                                                                                        
info CMD CONFIGURE                                                                                                                               
info RUN cmake "/home/打码/work/cpp_to_nodejs" --no-warn-unused-cli -G"Unix Makefiles" -DCMAKE_JS_VERSION="6.1.0" -DCMAKE_BUILD_TYPE="Release" -DC
MAKE_LIBRARY_OUTPUT_DIRECTORY="/home/打码/work/cpp_to_nodejs/build/Release" -DCMAKE_JS_INC="/home/打码/.cmake-js/node-x64/v14.5.0/include/node" -DC
MAKE_JS_SRC="" -DNODE_RUNTIME="node" -DNODE_RUNTIMEVERSION="14.5.0" -DNODE_ARCH="x64"                                                            
Not searching for unused variables given on the command line.                                                                                    
-- The C compiler identification is GNU 10.2.0                                                                                                   
-- The CXX compiler identification is GNU 10.2.0                                                                                                 
-- Detecting C compiler ABI info                                                                                                                 
-- Detecting C compiler ABI info - done                                                                                                          
-- Check for working C compiler: /usr/bin/cc - skipped                                                                                           
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Conan: Adjusting output directories
-- Conan: Using cmake global configuration
-- Conan: Adjusting default RPATHs Conan policies
-- Conan: Adjusting language standard
-- Current conanbuildinfo.cmake directory: /home/打码/work/cpp_to_nodejs/build
-- Conan: Compiler GCC>=5, checking major version 10
-- Conan: Checking correct version: 10
-- Configuring done
-- Generating done
-- Build files have been written to: /home/打码/work/cpp_to_nodejs/build
info CMD BUILD
info RUN cmake --build "/home/打码/work/cpp_to_nodejs/build" --config Release
Scanning dependencies of target build-node-addon-api-with-cmake
[ 33%] Building CXX object CMakeFiles/build-node-addon-api-with-cmake.dir/src/test.cpp.o
[ 66%] Building CXX object CMakeFiles/build-node-addon-api-with-cmake.dir/src/test_node_api.cpp.o
[100%] Linking CXX shared library lib/build-node-addon-api-with-cmake.node
[100%] Built target build-node-addon-api-with-cmake

测试demo输出:

➜  cpp_to_nodejs git:(master)npm run test   

> build-node-addon-api-with-cmake@0.0.0 test /home/打码/work/cpp_to_nodejs
> node hello.js

11:05:11.194 [thread 47105][info] [test::main][PrintHello:14]: logger sink flags: 1
11:05:11.194 [thread 47105][info] [test::main][PrintHello:15]: echo hello world!
11:05:11.194 [thread 47105][info] [test::main][PrintHello:16]: json string {"pi":3.14}
hello world!
11:05:11.199 [thread 47105][info] [test::main][Compair:28]: arg1: 0.0
11:05:11.199 [thread 47105][info] [test::main][Compair:29]: arg2: 0.0
11:05:11.199 [thread 47105][info] [test::main][Compair:22]: arg1: 111
11:05:11.199 [thread 47105][info] [test::main][Compair:23]: arg2: 111
true

输出的带时间戳的日志是c++打印的, hello world!true是js打印的。

总结

总体来看,还是比较简单的,比其使用x86架构交叉编译arm架构,或者交叉编译安卓,这种编译node的包的方法会简单很多。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在 Node.js 中使用其他 C ,可以使用 N-API 提供的接口。 1. 首先,你需要在你的 C 代码中含所需的的头文件,并将其编译为共享。可以使用 Makefile 或其他构建工具来自动化这个过程。 2. 接下来,你需要使用 N-API 提供的接口来创建一个 Node.js 模块,并在其中加载你的 C 。这个过程括以下几个步骤: - 使用 `napi_module_define` 函数定义一个模块对象。 - 在模块对象中添加导出函数,这些函数将装你的 C 函数并使其可以从 JavaScript 中调用。 - 在模块初始化函数中加载你的 C ,通常使用 `dlopen` 函数。 下面是一个简单的示例代码,演示如何使用 N-API 来加载一个名为 `mylib` 的 C : ```c #include <node_api.h> #include <stdlib.h> #include <stdio.h> #include "mylib.h" napi_value my_c_function(napi_env env, napi_callback_info info) { // 将 N-API 参数转换为 C 参数 int argc = 1; napi_value argv[1]; napi_get_cb_info(env, info, &argc, argv, NULL, NULL); int arg; napi_get_value_int32(env, argv[0], &arg); // 调用你的 C 函数 int result = my_function(arg); // 将结果转换为 N-API 值并返回 napi_value ret; napi_create_int32(env, result, &ret); return ret; } napi_value Init(napi_env env, napi_value exports) { // 定义模块对象并添加导出函数 napi_property_descriptor desc[] = { {"my_c_function", NULL, my_c_function, NULL, NULL, NULL, napi_default, NULL}, }; napi_define_properties(env, exports, 1, desc); // 加载 C void* handle = dlopen("libmylib.so", RTLD_LAZY); return exports; } NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) ``` 这个示例中,我们定义了一个名为 `my_c_function` 的导出函数,它将一个整数作为参数传入 `my_function` 函数中,并将结果转换为 N-API 值返回。然后我们使用 `napi_define_properties` 函数将这个函数添加到模块对象中。最后,我们在模块初始化函数中使用 `dlopen` 函数加载 `libmylib.so` 。 需要注意的是,这个示例中使用的是 Linux 下的动态链接。如果你在其他操作系统上运行,可能需要使用不同的加载函数来加载。另外,如果你的 C 中使用了其他的第三方,也需要将这些一起编译为共享,并将它们加载到 Node.js 模块中。 希望这个示例能够帮助你了解如何在 Node.js 中使用其他 C

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值