OS:Windows 10/1903

Welcome~ N-API & CMake.js

目前NodeJS的LTS(长期支持)版本是10.16.x,在文档中该版本的N-API稳定性已经达到了Stability: 2 - Stable的程度,使用N-API已经可以逐步摆脱先锋实验性质,考虑基于此构建复杂项目了。

以往构建C++ Addon时惯用的构建工具是node-gyp,这一项目脱胎于Google的构建配置工具gyp(Generate your project)。由于谷歌已经在全面投入新的构建工具链,gyp也已如明日黄花。更搞笑的是,受到上游项目gyp的限制,node-gyp目前为止仍然不支持Python 3。这个从2013年开始提的issue,直到2019年7月才放出了实验性的支持py3更新。与此同时,CMake.js也在快速发展,其使用更加成熟的CMake进行配置,这一方案的潜力无需多言。

依赖环境

在Windows下编译项目依赖于MSVC编译器,直接安装一个VisualStudio 2019 Community就好了,同时会附带配置好MSBuild等环境。

安装CMake.js:

npm install -g cmake-js
cmake-js install

# 以下是安装后的输出
info TOOL Using Visual Studio 16 2019 generator.
info DIST Downloading distribution files.
http DIST       - https://nodejs.org/dist/v10.16.3/SHASUMS256.txt
http DIST       - https://nodejs.org/dist/v10.16.3/win-x64/node.lib
http DIST       - https://nodejs.org/dist/v10.16.3/node-v10.16.3-headers.tar.gz

另外还需要安装CMake,安装过程中记得选择添加环境变量的选项。到此为止,编译C++ Addon的环境已经准备好了。

小试牛刀

使用的代码同这篇文章 ,具体引入的node_api.h实际上是不在引用库中的,编译时cmake-js会负责链接正确的头文件。这一点实际上非常不方便,因为开发过程中调用的N-API函数就没有对应的补全和检查了。

使用的CMakeLists.txt文件如下:

cmake_minimum_required(VERSION 2.8)

# Name of the project (will be the name of the plugin)
project(addon)

# Build a shared library named after the project from the files in `src/`
file(GLOB SOURCE_FILES "src/*.c" "src/*.h")
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})

# Gives our library file a .node extension without any "lib" prefix
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")

# Essential include files to build a node addon,
# You should add this line in every CMake.js based project
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_JS_INC})

# Essential library files to link to a node addon
# You should add this line in every CMake.js based project
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})

最后运行cmake-js build,即可生成一个addon.node模块。

在JS中,可以使用binding这个模块来方便的引入自己编写的.node模块:

let my_mod = require('bindings')('addon');

console.log(my_mod.hello());

Bonus

使用纯C风格的原生N-API有诸多不便之处,因此我们可以使用基于N-API的封装:node-addon-api。只需要这样:仔细阅读这里,把该修改的加上。然后可以从node_modules里面翻到这个项目下附带的头文件,拷出来供开发的时候引用。使用C++封装之后的hello会变得非常简单:

#include <napi.h>

Napi::String Method(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();
    return Napi::String::New(env, "world");
}

Napi::Object Init(Napi::Env env, Napi::Object exports)
{
    exports.Set(Napi::String::New(env, "hello"), Napi::Function::New(env, Method));
    return exports;
}

NODE_API_MODULE(addon, Init)