N-APIはホントにV8以外のJavaScriptエンジンでもリビルドなしでネイティブモジュールが動くのか
この記事はNode.js Advent Calendar 2018の16日目の記事です。 また、ここ数年チャレンジしている@kjunichiのパーソナルアドベントカレンダーの16日目の記事も兼ねてます。
まえがき
Node.jsとは直接関係ないが、V8をPostgreSQLで動かせるplv8にWindows対応のプルリク投げた前科がある。
このプルリクは残念がらマージに至らなかったが、作者の方が、対応して現在、少し頑張れば、Windowsでも 新しいplv8がビルドできるハズ。
v8以外のエンジンのnode.jsに切り替えられるバージョン管理ツールの導入
とりあえず、macOS mojaveで試す。
node.jsではnvmがメジャーなバージョン管理ツールですが、chakracoreなnode.jsを試すには、以下のnvsが 便利です。
READMEに従って以下のように入れた。
export NVS_HOME="$HOME/.nvs" git clone https://github.com/jasongin/nvs "$NVS_HOME" . "$NVS_HOME/nvs.sh" install
chakracoreを入れる
nvsコマンドで対話的にchakracoreを入れられる。
nvs ls
とする事で、現在インストールされているnode.jsの一覧と選択されているnode.jsが表示される。
node/10.14.2/x64 (Dubnium) node/10.13.0/x64 (Dubnium) node/10.12.0/x64 node/10.7.0/x64 >chakracore/10.13.0/x64 (Dubnium)
node-gypを入れる
N-APIに限らず、node.jsでネイティブモジュールを作る際に必要なnode-gypを入れる。
npm install -g node-gyp
N-APIを使ったモジュールを作る
以下のリポジトリをクローンしてN-APIのモジュールを用意。
git clone https://github.com/schahriar/n-api-article.git cd n-api-article/Getting_Started
以下のコマンドでモジュールをビルドする。
node-gyp configure build
最後に以下のように表示されればモジュールのビルドに成功している。
CC(target) Release/obj.target/module/src/module.o SOLINK_MODULE(target) Release/module.node gyp info ok
動かす
念の為、ここでどのnode.jsを使っているかを確認する。
node -p -e process.versions
{ http_parser: '2.8.0', node: '10.13.0', chakracore: '1.11.1.0', uv: '1.23.2', zlib: '1.2.11', ares: '1.14.0', modules: '64', nghttp2: '1.34.0', napi: '3', openssl: '1.1.0i', icu: '62.1', unicode: '11.0', cldr: '33.1', tz: '2018e' }
と、chakracoreの文字列が入っているので、v8エンジンでは無い事が確認できた。
それでは、実行してみる。
node module.js
JSエンジンを変更して実行してみる
今度はJSエンジンをv8に変更して実行。
nvsコマンドで通常のnode.jsを入れて、
nvs ls
>node/10.14.2/x64 (Dubnium) node/10.13.0/x64 (Dubnium) node/10.12.0/x64 node/10.7.0/x64 chakracore/10.13.0/x64 (Dubnium)
だめ押しで、
node -p -e process.versions
{ http_parser: '2.8.0', node: '10.14.2', v8: '6.8.275.32-node.45', uv: '1.23.2', zlib: '1.2.11', ares: '1.15.0', modules: '64', nghttp2: '1.34.0', napi: '3', openssl: '1.1.0j', icu: '62.1', unicode: '11.0', cldr: '33.1', tz: '2018e' }
node module.js
8 times 2 equals 16
動いた!
Windows10でもやってみた
Windowsでnodeのネイティブモジュールをビルドできるようにするのに手っ取り早いのは
npm install --global --production windows-build-tools --vs2017
としてwindows-build-toolsを入れてしまうのがお手軽。
Rustを入れた時、構成変更して、C++周りのツールやらライブラリを入れる必要があったが、 対応できるので、Node.js以外の処理系でC/C++のMSの処理系が必要なら、これで入れてしまうのが楽。
Windowsではnvsを常用しているものの、chakracoreをこれまで入れた事なかったw。
前述のmacOS同様にchakracoreなnode.jsをnvsで入れて、 node-gypを入れる。
トラブル発生!
以下のように、node-gypでモジュールをビルドしたら、
node-gyp configure build
以下のように、リンクでエラーが発生。
LINK : fatal error LNK1181: 入力ファイル 'chakracore.lib' を開けません。 [C:\Users\kjunichi\Documents\work\nod ejs\n-api-ar ticle\Getting_Started\build\module.vcxproj]
WindowsではMSVCでは共有ライブラリを利用する際に、ビルド時にリンカーに.libファイルを参照させる必要がある。 このエラーは、この.libファイルが見つからないというエラー。
対応方法
%homepath%\.node-gyp
配下にchakracore版のnode.jsの.libファイルをコピーする。
コピー元の.libファイルは%homepath%\AppData\Local\nvs\chakracore\配下にある。 この記事を書いた際は
%homepath%\AppData\Local\nvs\chakracore\10.13.0\x64\sdk\Release
に配置されていた。
コピー先の詳細なパスは後述のmodule.vcxprojのAdditionalDependenciesセクションのnode.libのパスの指定に従い、 そこにコピーする。
buildフォルダのmodule.vcxprojファイルのchakracore.libの指定個所を先ほどコピーしたするパスで指定する。
複数箇所あるので、検索して該当個所を書き換える。
変更前
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;DelayImp.lib;"C:\Users\kjunichi\.node-gyp\10.13.0\x64\node.lib";chakracore.lib</AdditionalDependencies>
変更後
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;DelayImp.lib;"C:\Users\kjunichi\.node-gyp\10.13.0\x64\node.lib";"C:\Users\kjunichi\.node-gyp\10.13.0\x64\chakracore.lib"</AdditionalDependencies>
configureをつけずに
node-gyp build
module.vcxproj -> C:\Users\kjunichi\Documents\work\nodejs\n-api-article\Getting_Started\build\Release\\modu le.node gyp info ok
あとは、macOSの時同様、JSのエンジンを切り替えてもリビルドなしで、動かせた。
まとめ
N-APIの謳い文句通りにリビルドなしで、v8エンジンでもMSのchakracoreエンジンでもネイティブモジュールが 動くとこが確認できた。
本命?のWindowsでは.libファイルの扱いがnvs側の考慮不足なのか、node-gyp側なのかとにかく上手く扱われていないので、 node-gyp一発で作業ができないことが分かった。
あと、
に書いてあるような状況なので、chakracoreの未来は暗いのかも。。
Rustで実装されているRapidusがN-APIに対応する事に期待。
関連記事
- Node.jsのネイティブモジュールでも使われているdlopen時に関数を実行する指定方法
- WindowsでV8のサンプルをコマンドラインでビルドして動かす
- アレクサで「iPhoneを探す」をpuppeteerを使って出来るようにした
- NAPI版のnode-ffi-napiを使ってNode.jsからPythonを動かす