Node.jsでは、C++で拡張モジュールを作ることができる。
この拡張モジュールを経由して、Linuxならシステムコール、WindowsならWin32APIなどを呼び出すことができる。
GitHub - nodejs/node-gyp: Node.js native addon build tool
Node.jsのネイティブ拡張を作ってみよう 〜NAN, 非同期処理, npm公開まで〜 - Qiita
今回は、node-gypを用いて、簡単な拡張モジュールを作る。
まずは、npm init。
$ pwd /home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext $ ls $ npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (native_ext) version: (1.0.0) description: node-gyp sample entry point: (index.js) main.js test command: start git repository: - keywords: - author: B.G license: (ISC) MIT About to write to /home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/package.json: { "name": "native_ext", "version": "1.0.0", "description": "node-gyp sample", "main": "main.js", "scripts": { "test": "start" }, "repository": { "type": "git", "url": "-" }, "keywords": [ "-" ], "author": "B.G", "license": "MIT" } Is this OK? (yes) $
続いて、node-gypをインストール。
$ npm install node-gyp npm notice created a lockfile as package-lock.json. You should commit this file. + node-gyp@3.8.0 added 97 packages from 67 contributors and audited 183 packages in 6.812s found 0 vulnerabilities ╭───────────────────────────────────────────────────────────────╮ │ │ │ New minor version of npm available! 6.4.1 → 6.9.0 │ │ Changelog: https://github.com/npm/cli/releases/tag/v6.9.0 │ │ Run npm install -g npm to update! │ │ │ ╰───────────────────────────────────────────────────────────────╯ $
Python2.7(3系は非対応)が必要なのだが、入ってないので入れる。
$ which python /usr/bin/which: no python in (/home/bg1/.local/bin:/home/bg1/bin:/home/bg1/.local/bin:/home/bg1/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin) $ sudo yum install python メタデータの期限切れの最終確認: xx:xx:xx 時間前の 2019年xx月xx日 xx時xx分xx秒 に実施しました。 依存関係が解決しました。 ======================================================================================================= パッケージ アーキテクチャー バージョン リポジトリ サイズ ======================================================================================================= インストール: python-unversioned-command noarch 2.7.15-11.fc29 updates 13 k 依存関係をインストール中: python2 x86_64 2.7.15-11.fc29 updates 46 k python2-libs x86_64 2.7.15-11.fc29 updates 6.1 M compat-openssl10 x86_64 1:1.0.2o-3.fc29 fedora 1.1 M gdbm x86_64 1:1.18-1.fc29 fedora 116 k 弱い依存関係をインストール中: python2-pip noarch 18.1-1.fc29 updates 1.9 M python2-setuptools noarch 40.8.0-1.fc29 updates 644 k トランザクションの概要 ======================================================================================================= インストール 7 パッケージ ダウンロードサイズの合計: 10 M インストール済みのサイズ: 41 M これでよろしいですか? [y/N]: y パッケージのダウンロード中です: (1/7): python2-2.7.15-11.fc29.x86_64.rpm 24 kB/s | 46 kB 00:01 (2/7): python-unversioned-command-2.7.15-11.fc29.noarch.rpm 6.9 kB/s | 13 kB 00:01 (3/7): python2-setuptools-40.8.0-1.fc29.noarch.rpm 156 kB/s | 644 kB 00:04 (4/7): compat-openssl10-1.0.2o-3.fc29.x86_64.rpm 728 kB/s | 1.1 MB 00:01 (5/7): python2-libs-2.7.15-11.fc29.x86_64.rpm 694 kB/s | 6.1 MB 00:09 (6/7): gdbm-1.18-1.fc29.x86_64.rpm 58 kB/s | 116 kB 00:01 (7/7): python2-pip-18.1-1.fc29.noarch.rpm 228 kB/s | 1.9 MB 00:08 ------------------------------------------------------------------------------------------------------- 合計 732 kB/s | 10 MB 00:13 トランザクションの確認を実行中 トランザクションの確認に成功しました。 トランザクションのテストを実行中 トランザクションのテストに成功しました。 トランザクションを実行中 準備 : 1/1 Installed: gdbm-1:1.18-1.fc29.x86_64 インストール中 : gdbm-1:1.18-1.fc29.x86_64 1/7 Installed: gdbm-1:1.18-1.fc29.x86_64 Installed: compat-openssl10-1:1.0.2o-3.fc29.x86_64 インストール中 : compat-openssl10-1:1.0.2o-3.fc29.x86_64 2/7 scriptletの実行中: compat-openssl10-1:1.0.2o-3.fc29.x86_64 2/7 Installed: compat-openssl10-1:1.0.2o-3.fc29.x86_64 Installed: python2-libs-2.7.15-11.fc29.x86_64 インストール中 : python2-libs-2.7.15-11.fc29.x86_64 3/7 Installed: python2-libs-2.7.15-11.fc29.x86_64 Installed: python2-pip-18.1-1.fc29.noarch インストール中 : python2-pip-18.1-1.fc29.noarch 4/7 Installed: python2-pip-18.1-1.fc29.noarch Installed: python2-setuptools-40.8.0-1.fc29.noarch インストール中 : python2-setuptools-40.8.0-1.fc29.noarch 5/7 Installed: python2-setuptools-40.8.0-1.fc29.noarch Installed: python2-2.7.15-11.fc29.x86_64 インストール中 : python2-2.7.15-11.fc29.x86_64 6/7 Installed: python2-2.7.15-11.fc29.x86_64 Installed: python-unversioned-command-2.7.15-11.fc29.noarch インストール中 : python-unversioned-command-2.7.15-11.fc29.noarch 7/7 Installed: python-unversioned-command-2.7.15-11.fc29.noarch scriptletの実行中: python-unversioned-command-2.7.15-11.fc29.noarch 7/7 検証 : python-unversioned-command-2.7.15-11.fc29.noarch 1/7 検証 : python2-2.7.15-11.fc29.x86_64 2/7 検証 : python2-libs-2.7.15-11.fc29.x86_64 3/7 検証 : python2-pip-18.1-1.fc29.noarch 4/7 検証 : python2-setuptools-40.8.0-1.fc29.noarch 5/7 検証 : compat-openssl10-1:1.0.2o-3.fc29.x86_64 6/7 検証 : gdbm-1:1.18-1.fc29.x86_64 7/7 インストール済み: python-unversioned-command-2.7.15-11.fc29.noarch python2-pip-18.1-1.fc29.noarch python2-setuptools-40.8.0-1.fc29.noarch python2-2.7.15-11.fc29.x86_64 python2-libs-2.7.15-11.fc29.x86_64 compat-openssl10-1:1.0.2o-3.fc29.x86_64 gdbm-1:1.18-1.fc29.x86_64 完了しました! $
binding.gypというファイルに、
と書く。
native_ext.ccというのがC++ソースファイル。
native_ext.ccには、
get_abcという自作関数を用意し、args.GetReturnValue().Setで"ABC"をセットすることで、戻り値"ABC"を返してる。
(おまじないということで詳細は後で・・・。)
node-gypコマンドを使って、configureとbuildをする。
$ vi binding.gyp $ vi native_ext.cc $ ls binding.gyp build native_ext.cc node_modules package-lock.json package.json $ ./node_modules/.bin/node-gyp configure gyp info it worked if it ends with ok gyp info using node-gyp@3.8.0 gyp info using node@10.15.0 | linux | x64 gyp info spawn /usr/bin/python2 gyp info spawn args [ '/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/node_modules/node-gyp/gyp/gyp_main.py', gyp info spawn args 'binding.gyp', gyp info spawn args '-f', gyp info spawn args 'make', gyp info spawn args '-I', gyp info spawn args '/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/build/config.gypi', gyp info spawn args '-I', gyp info spawn args '/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/node_modules/node-gyp/addon.gypi', gyp info spawn args '-I', gyp info spawn args '/home/bg1/.node-gyp/10.15.0/include/node/common.gypi', gyp info spawn args '-Dlibrary=shared_library', gyp info spawn args '-Dvisibility=default', gyp info spawn args '-Dnode_root_dir=/home/bg1/.node-gyp/10.15.0', gyp info spawn args '-Dnode_gyp_dir=/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/node_modules/node-gyp', gyp info spawn args '-Dnode_lib_file=/home/bg1/.node-gyp/10.15.0/<(target_arch)/node.lib', gyp info spawn args '-Dmodule_root_dir=/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext', gyp info spawn args '-Dnode_engine=v8', gyp info spawn args '--depth=.', gyp info spawn args '--no-parallel', gyp info spawn args '--generator-output', gyp info spawn args 'build', gyp info spawn args '-Goutput_dir=.' ] gyp info ok $ ./node_modules/.bin/node-gyp build gyp info it worked if it ends with ok gyp info using node-gyp@3.8.0 gyp info using node@10.15.0 | linux | x64 gyp info spawn make gyp info spawn args [ 'BUILDTYPE=Release', '-C', 'build' ] make: ディレクトリ '/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/build' に入ります CXX(target) Release/obj.target/native_ext/native_ext.o In file included from ../native_ext.cc:2: /home/bg1/.node-gyp/10.15.0/include/node/node.h:570:43: 警告: cast between incompatible function types from ‘void (*)(v8::Local<v8::Object>)’ to ‘node::addon_register_func’ {aka ‘void (*)(v8::Local<v8::Object>, v8::Local<v8::Value>, void*)’} [-Wcast-function-type] (node::addon_register_func) (regfunc), \ ^ /home/bg1/.node-gyp/10.15.0/include/node/node.h:604:3: 備考: in expansion of macro ‘NODE_MODULE_X’ NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) ^~~~~~~~~~~~~ ../native_ext.cc:24:1: 備考: in expansion of macro ‘NODE_MODULE’ NODE_MODULE(native_ext, init); ^~~~~~~~~~~ SOLINK_MODULE(target) Release/obj.target/native_ext.node COPY Release/native_ext.node make: ディレクトリ '/home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/build' から出ます gyp info ok $
グローバルインストール( -g)じゃないので、"./node_modules/.bin/node-gyp"の"conigure"と"build"を使って、ビルドまで行う。
$ ls binding.gyp build native_ext.cc node_modules package-lock.json package.json $ cd build/ $ ls Makefile Release binding.Makefile config.gypi native_ext.target.mk $ cd Release/ $ ls native_ext.node obj.target $ pwd /home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext/build/Release $
build/Releaseの下の、"native_ext.node"がビルドで生成されたモジュール。
これを、main.jsから参照させる。
このようにnativeExt.get_abc()を呼び出せるので、戻り値の"ABC"をconsole.logで出力。
これでnodeコマンドで呼び出せるが、せっかくなので、package.jsonで、
"scripts"で"start"なら"node ."を実行するようにする。
$ vi package.json $ npm start > native_ext@1.0.0 start /home/bg1/project/cloud/github.com/Sample/node-gyp/node-gyp/node-gyp/src/native_ext > node . ABC $
npm startで起動して、取得した"ABC"を出力するようにできた。
Sample/node-gyp/node-gyp/node-gyp/src/native_ext at master · bg1bgst333/Sample · GitHub