背景
Rust で node の native modules を書くの、Rustを書いてみたいからっていう理由だけで試してみたい。 https://t.co/vek4YFjW5e https://t.co/uqpd95C4QQ— Yosuke FURUKAWA (@yosuke_furukawa) 2015, 12月 24
なんて、ツイートをたまたま見かけ、
原理的にちょっとまえのRustだとdleopn時に呼び出す関数を指定できなかったから直接.nodeファイルはRustが吐けないはずなんだけど、その辺りどうなってるのだろうか / “dherman/wc-demo · GitHub” https://t.co/pdAqgRMFbW— kjunichi (@kjunichi) 2015, 12月 24
なぜ原理的に無理そうなのか
-
- http://abrakatabura.hatenablog.com/entry/2015/12/14/214922
- https://users.rust-lang.org/t/library-onload-initialization/3079/2
やってみた
Rustがある時から、「attribute((constructor))」をサポートしたのかもと思い、調べるも見当たらず。
wc-demoのコードをたどり、依存モジュールのリポジトリのpackage.jsonをたどるも
node-ffiすら見当たらず、これは俺が知らない未知のテクノロジーを使って解決したのか!
と、期待を膨らませて実際に動かしてみた。
rustのアップデート
1.5以上とwc-demoに書いてあるので、
curl -sSf https://static.rust-lang.org/rustup.sh | sh
OSXではPkgも用意されているので、そこらへんはお好みで。
wc-demoをクローンして試す
$ nodebrew ls
v0.10.35
v0.12.0
v4.2.1
v5.1.1
v5.2.0
v5.3.0
current: v5.3.0
$ npm install
npm WARN prefer global node-gyp@3.0.3 should be installed with -g
> wc-demo@0.0.0 postinstall /Users/kjw_junichi/work/rust/neon/wc-demo
> neon build
ACTION binding_gyp_wc_demo_target_generate Release/obj.target/wc_demo/geni/binding.cc
ACTION binding_gyp_wc_demo_target_cargo ../target/release/libwc_demo.a
Unable to update file:///Users/kjw_junichi/work/rust/neon/wc-demo
Caused by:
Could not find `Cargo.toml` in `/Users/kjunichi/work/rust/neon/neon`
make: *** [../target/release/libwc_demo.a] Error 101
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/Users/kjunichi/work/rust/neon/wc-demo/node_modules/node-gyp/lib/build.js:270:23)
gyp ERR! stack at emitTwo (events.js:87:13)
gyp ERR! stack at ChildProcess.emit (events.js:172:7)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:200:12)
gyp ERR! System Darwin 15.2.0
gyp ERR! command "/Users/kjunichi/.nodebrew/node/v5.3.0/bin/node" "/Users/kjunichi/work/rust/neon/wc-demo/node_modules/.bin/node-gyp" "rebuild"
gyp ERR! cwd /Users/kjunichi/work/rust/neon/wc-demo
gyp ERR! node -v v5.3.0
gyp ERR! node-gyp -v v3.0.3
gyp ERR! not ok
npm ERR! Darwin 15.2.0
npm ERR! argv "/Users/kjunichi/.nodebrew/node/v5.3.0/bin/node" "/Users/kjunichi/.nodebrew/current/bin/npm" "install"
npm ERR! node v5.3.0
npm ERR! npm v3.3.12
npm ERR! code ELIFECYCLE
npm ERR! wc-demo@0.0.0 postinstall: `neon build`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the wc-demo@0.0.0 postinstall script 'neon build'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the wc-demo package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! neon build
npm ERR! You can get their info via:
npm ERR! npm owner ls wc-demo
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR! /Users/kjunichi/work/rust/neon/wc-demo/npm-debug.log
動かすには
cd ..
git clone https://github.com/dherman/neon.git
npm install -g neon-cli
cd wc-demo
cargo update
npm install
$ node -e 'require("./")'
node -e 'require("./")'
3092 [280ms, 914μs, 751ns]
3092 [40ms, 930μs, 825ns]
動きました!
$ cat binding.gyp
# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.
{
"target_defaults": {
"default_configuration": "Release"
},
"targets": [{
"target_name": "wc_demo",
"variables": {
"rust_inputs": ["src/lib.rs"],
"static_lib": "target/release/<(STATIC_LIB_PREFIX)wc_demo<(STATIC_LIB_SUFFIX)",
"shared_lib": "target/release/<(SHARED_LIB_PREFIX)wc_demo<(SHARED_LIB_SUFFIX)"
},
"sources": ["<(INTERMEDIATE_DIR)/binding.cc"],
"include_dirs": ["<!(neon include-path)"],
"libraries": ["../<(shared_lib)"],
"actions": [{
"action_name": "cargo",
"inputs": ["<@(rust_inputs)"],
"outputs": ["../<(static_lib)"],
"action": ["cargo", "rustc", "--release", "--", "--crate-type", "staticlib"]
}, {
"action_name": "shared_lib",
"inputs": ["<(static_lib)"],
"outputs": ["../<(shared_lib)"],
# FIXME: OSX-specific
"action": ["gcc", "-dynamiclib", "-Wl,-undefined,dynamic_lookup", "-Wl,-force_load,<(static_lib)", "-o", "<(shared_lib)"]
}, {
"action_name": "generate",
"inputs": [],
"outputs": ["<(INTERMEDIATE_DIR)/binding.cc"],
"action": ["neon", "generate", "<(INTERMEDIATE_DIR)/binding.cc"]
}]
}]
}
ということで、biding.ccをエントリポイントにして、その先がRustで記述されたモジュールとなっているようです。
関連
-
- Rustでコマンドラインアプリでキーが押されたかの判定しつつ、別の処理も進めるには
-
- RustでWebカメラの映像をコマンドプロンプトに出すコマンドを作った
-
- RustでGoのライブラリを使うときのCargoのbuild.rsの書き方
-
- Surface Goのカメラ映像をRustでコマンドプロンプトに表示した
- Rustで共有ライブラリを作る際に必要だったCのconst char*をRustの&strにする方法