nw.js@0.8.6からnode-ffiでネイティブモジュールを叩く

まず、nodeからネイティブモジュールを叩くには、[node-ffi](https://github.com/node-ffi/node-ffi "node-ffi/node-ffi")を使用します。

node-ffiですが、nodeのバージョンが0.10から0.12になるにかけて、ネイティブモジュールに関係する部分で後方互換性が失われてしまうという事件が起こっています。

現状0.11や0.12で動かすことは不可能ではないですが、私は落ち着いてから試すことにしました。

詳しい内容は、"NAN node"とかでググるか以下の記事をみるといいかもしれません。

* [node.jsのnative addonを作るときはNANを使おう。 - from scratch](http://yosuke-furukawa.hatenablog.com/entry/2014/04/07/205935 "node.jsのnative addonを作るときはNANを使おう。 - from scratch")
* [rvagg/nan](https://github.com/rvagg/nan "rvagg/nan")

今回は、このへんの話には、触れないでv0.10系を使うことにします。

# 環境

* Ubuntu 14.10 (x64)

OSXでも同じようにできる気がします。

# nw.jsのインストール

npmで古いnwをインストールできなかったので、tarを落とします。

[nwjs/nw.js](https://github.com/nwjs/nw.js/ "nwjs/nw.js")

OSに合わせてnw@0.8.6をダウンロードします。

ネイティブモジュールの後方互換性のために、nodeのv0.10系を使用している、v0.8系もメンテされているようです。

nw@0.8.6は、node@0.10.22/Chrome@30.0.1599.66です。

# 配置

まず、nvmで0.10.22をインストールします。(v.10でもいいかもしれません)

```bash
$ nvm install v0.10.22
```

サンプルを作成するために、お好みでディレクトリを掘って、package.jsonを作成します(npm init)

```bash
$ mkdir path/to/dir
$ cd path/to/dir
$ npm init
(index.jsの部分を、index.htmlにする、ほかはEnter)
```

node-ffiもインストールしておきます。

```
$ npm install -save ffi
```

さっきダウンロードしたnwを解凍して、node_modulesに配置しておきます(nwコマンドを叩くだけなので、別にどこでもいいです。)

```bash
$ mv path/to/node-webkit-v0.8.6-linux-x64 ./node_modules
```

## Linux系OSの場合のエラー

nwを実行すると、多分次のエラーが出ます。

```
error while loading shared libraries: libudev.so.0: cannot open shared object file: No such file or directory
```

それについては、ドキュメントでも言及されているので参考にします。

[The solution of lacking libudev.so.0 · nwjs/nw.js Wiki](https://github.com/nwjs/nw.js/wiki/The-solution-of-lacking-libudev.so.0 "The solution of lacking libudev.so.0 · nwjs/nw.js Wiki")

バイナリを書き換えてしまうのが手軽そうなのでそうします(他の方法もあります)

```
$ cd node_modules/node-webkit-v0.8.6-linux-x64
$ sed -i 's/udev\.so\.0/udev.so.1/g' nw
```

# node-ffiのビルド

nw@0.8.6向けにnode-ffiをリビルドします。

nodeのネイティブアドオンのビルドには、node-gypを使いますが、nw向けに便利化されたnw-gypがあるのでそちらを使います。

* [TooTallNate/node-gyp](https://github.com/TooTallNate/node-gyp "TooTallNate/node-gyp")
* [nwjs/nw-gyp](https://github.com/nwjs/nw-gyp "nwjs/nw-gyp")

nw-gypをグローバルにインストールします。

```bash
$ npm install -g nw-gyp
```

nw-gypを使ってnode-ffiをリビルドします。targetには今回使用するnw.jsのバージョン(0.8.6)を指定します。

```bash
$ cd node_modules/ffi
$ nw-gyp rebuild --target=0.8.6
```

ffiの依存関係で入っている"ref"もリビルドします。

```bash
$ cd node_modules/ffi/node_modules/ref
$ nw-gyp rebuild --target=0.8.6
```

さらに、依存関係で入っている"ref-struct"の依存関係で入っている"ref"もリビルドします。

```bash
$ cd node_modules/ffi/node_modules/ref-struct/node_modules/ref
$ nw-gyp rebuild --target=0.8.6
```

これら全部をビルドしないと、以下のようなエラーが出るはずです。

```
...(略)...
undefined symbol: _ZN2v86String3NewEPKci
```

# サンプルを動かしてみる

node-ffiのREADMEでlibmを動かしているの、同じように使えるか試してみます。

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Hello FFI!</title>
  </head>
  <body>
    <h1>Hello FFI!</h1>
    <script>
    var ffi = require("ffi");
    var math = ffi.Library('libm', {
      'sqrt': [ 'double', [ 'double' ] ]
    });
    var x = math.sqrt(2); // sqrt(2)=1.4142135623730951
    document.write(x.toString());
    </script>
  </body>
</html>
```

nwを起動すると、√2が表示されるはずです。

```bash
$ ./node_modules/node-webkit-v0.8.6-linux-x64/nw .
```
![Screenshot from 2015-03-22 16:01:21.png](https://qiita-image-store.s3.amazonaws.com/0/24784/a3288e89-72d8-c468-06d3-303608398bd7.png)

おお、ffi使えてます。

# まとめ

依存関係のrefまでリビルドしないといけないのに気づくのに少し時間がかかりましたが、nwでもffi動くようです。

調べていると、解決していないパターン([How to use node-ffi in node-webkit? · Issue #138 · node-ffi/node-ffi](https://github.com/node-ffi/node-ffi/issues/138 "How to use node-ffi in node-webkit? · Issue #138 · node-ffi/node-ffi"))が多くて困りました。

以下のサンプルも動かせそうですが、gruntとかを使ってて構成が少し複雑そうだったので試してません。

* [Node.js - Node Webkit から ネイティブDLLを呼ぶサンプル - Qiita](http://qiita.com/essa/items/5e16f20b2b680fbafb70 "Node.js - Node Webkit から ネイティブDLLを呼ぶサンプル - Qiita")
* [essa/nw_native_dll_sample](https://github.com/essa/nw_native_dll_sample "essa/nw_native_dll_sample")