用Cmake-js编译C++写的Nodejs扩展

你是不是写nodejs应用的时候,有过属于自己的业务需求,但这个需求你又找不到合适的类库,或者你这个业务需求独一无,根本不可能找到已经写好的类库

比如,你要调用一个自己写的DLL里的函数

这时候,你就需要用C++写个nodejs扩展了,但是c++又需要编译,这就需要个编译工具了

标题Nodejs扩展基本开发

按照Nodejs官方文档,编译扩展,两种方式

  • node-gyp
  • cmake-js

现在,分别简单介绍一下以上两种编译方式

1.node-gyp:这个gyp应该都比较熟悉,他是google的一个跨平台编译工具,比如编译chromium就需要用gyp,node-gyp就是用于nodejs扩展编译的gyp,基本上和gyp没有区别,node-gyp在写nodejs时候比较常用,比如你写sass时候如果安装了node-sass,就需要node-gyp编译

2.cmake-js:看名字也能知道,这东西大概率和cmake有关系。对,它是基于CMake来构建系统的,也能跨平台编译

以上两种方式都能编译nodejs扩展,但是从安装和使用上来讲,cmake-js比node-gyp要简单的多。

下面我来介绍一下cmake-js

cmake-js具体安装

测试环境,windows10,nodejs 12.6.3 , vs2015

1.首先你要有个nodejs环境,编译nodejs扩展没有nodejs环境,这就没法干了
2.安装cmake-js,安装很简单,npm直接安装,这有可能时间比较长,多等等

npm install -g cmake-js

3.安装cmake,如果你的用的cmake安装包的话,正常安装就行,啥都不用管,如果你用的压缩包的话,把他解压以后,打开这个文件夹
用Cmake-js编译C++写的Nodejs扩展_第1张图片
里面差不多就这些东西,要是你的文件和我的不一样,也无所谓,可能是版本差异,把这个路径复制一下,然后选中

我的电脑右键->属性->高级系统设置->环境变量->系统变量,然后点开path这个选项
用Cmake-js编译C++写的Nodejs扩展_第2张图片
然后点新建
用Cmake-js编译C++写的Nodejs扩展_第3张图片
把cmake那个bin目录路径写进去就行了

这个弄完,打开cmd,输入cmake,回车,输出如下
用Cmake-js编译C++写的Nodejs扩展_第4张图片
如果你看不见上面的输出,要么就是你的环境变量路径没有配对,要么就是环境变量没有生效,如果是环境变量没有生效的话,可能需要重启电脑

4.vs编译器,具体什么版本,要看看你安装的 cmake支持什么版本,理论上你能下载到的VS版本他应该都支持,如果要确认的话,打开cmake的bin目录,点击cmake-gui.exe,然后点击generate,再选下拉列表看看
用Cmake-js编译C++写的Nodejs扩展_第5张图片
以上,准备工作完成

camke-js具体使用

先创建package.json

npm init

创建好文件后,打开package.json,把下面代码复制到里面

 "dependencies": {
    "node-addon-api": "^3.0.0"
  },
  "cmake-js": {
    "runtime": "node",
    "runtimeVersion": "12.16.3",
    "arch": "x64"//64位
  }

这两个段很重要

修改好以后,然后用npm安装node-addon-api

npm i

这个下载很快,一下就好了

然后编写个CMakeLists.txt,你要是写过Cmake的话就很简单,下面是一个最简单的CMakeLists,直接复制出来用就可以

project (aaaa)
cmake_minimum_required(VERSION 3.16)

include_directories(${CMAKE_JS_INC})
file(GLOB SOURCE_FILES "*.cc")
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})


# Include N-API wrappers
execute_process(COMMAND node -p "require('node-addon-api').include"
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
        OUTPUT_VARIABLE NODE_ADDON_API_DIR
        )
string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR})
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})

# Define NAPI_VERSION
add_definitions(-DNAPI_VERSION=3)

project (aaaa)是我随便起了个项目名称aaaa,你随便改

然后就是重点了,要写c++代码了,创建a.cc文件,内容如下

#include 
#include 
#include 
#include 
using namespace std;
static napi_value MyFun(napi_env env, napi_callback_info info) {
  napi_status status;
  napi_value str;
  status = napi_create_string_utf8(env, "mytest", 6, &str);
  assert(status == napi_ok);
  return str;
}

static napi_value YouFun(napi_env env, napi_callback_info info) {
  napi_status status;
  size_t argc = 2;
  napi_value args[2];

  status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  
  size_t strleng;
  napi_get_value_string_utf8(env, args[0], 0, 0, &strleng);
  string p1;
  p1.reserve(strleng + 1);
  p1.resize(strleng);
  status = napi_get_value_string_utf8(env, args[0], &p1[0], p1.capacity(), nullptr);

  napi_get_value_string_utf8(env, args[1], 0, 0, &strleng);
  string p2;
  p2.reserve(strleng + 1);
  p2.resize(strleng);
  status = napi_get_value_string_utf8(env, args[1], &p2[0], p2.capacity(), nullptr);
  napi_value v;
  string dest = "p1:"+ p1 + "p2:" + p2;
  napi_create_string_utf8(env,dest.c_str(), strlen(dest.c_str()+1), &v);
  return v;

}
#define DECLARE_NAPI_METHOD(name, func)                                        \
  { name, 0, func, 0, 0, 0, napi_default, 0 }

static napi_value Init(napi_env env, napi_value exports) {
  napi_status status;
  napi_property_descriptor mytest = DECLARE_NAPI_METHOD("mytest", MyFun);
  status = napi_define_properties(env, exports, 1, &mytest);
  napi_property_descriptor youtest = DECLARE_NAPI_METHOD("youtest", YouFun);
  status = napi_define_properties(env, exports, 1, &youtest);
  assert(status == napi_ok);
  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

上面代码很简单,就是创建了两个函数,第一个函数名叫mytest,第二个函数名叫youtest,

第一个函数的功能就是输出字符串“mytest”,

第二个函数的功能就是输入两个字符串参数,然后输出p1:xxx,p2:xxx

都完事以后,就是执行编译了,执行cmd,进入你的工作目录,输入

cmake-js build

等他执行完,如果执行正确的话,你会发现工作目录中多了一个build文件夹,里面是VS项目,同时build/Release文件夹中有了aaaa.node文件,这就是你编译的nodejs扩展了

然后就是调用了,写个JS 文件,取名a.js,内容如下

var addon = require("./build/Release/aaaa.node");
console.log(addon.mytest());
console.log(addon.youtest('aaa','bbb'));

然后执行node调用它

node a.js

正确输出,应该可以看到如下
在这里插入图片描述

关于调试

程序肯定需要调试,怎么调试呢?

打开Build文件夹,用VS把项目文件打开,就是一个普通的VS项目,你也可以在这里改代码,然后编译
用Cmake-js编译C++写的Nodejs扩展_第6张图片
然后把aaaa项目,设为启动项目,在右键点击项目->属性->调试
用Cmake-js编译C++写的Nodejs扩展_第7张图片
按上面这么填写,然后F5就可以调试了

结束语

nodejs扩展,不仅可以用于nodejs服务器环境,还可以用于electron桌面开发,用法都一样

nodejs扩展开发也不太难,但是它API也不少,要写扩展还是要多看看API才行

你可能感兴趣的:(C++,c语言,c++,node.js)