Node.jsではrequire経由で呼び出す、ブラウザ向けにはbrowserifyで吐き出すようにしたら、わりとソースが整理できたという話。C言語前提になりますが、C++ではEmbindを使うと同じことができます。
Node.js向けにビルド
まず、とりあえずテキトーにCの関数を作ってコンパイルしてみます。
int foo() {
return 1;
}
emcc -O2 --closure 1 --memory-init-file 0 foo.c -o foo.js -s EXPORTED_FUNCTIONS="['_foo']"
コンパイルオプションを一応説明すると
-
-O2
: 最適化する(asm.jsとか) -
--closure 1
: closure compilerで圧縮する -
--memory-init-file 0
: 初期のメモリファイルを外出ししない -
-s EXPORTED_FUNCTIONS="['_foo']"
: 上のfoo関数をjs上で_fooとしてエクスポートする
といった具合です。これをnodeで実行してみると_foo関数が確かにエクスポートされています。
var Module = require('./foo');
Module._foo(); // 1
これを最終的にindex.js等でラップした関数を作って外部に公開するようにすれば、Node.js向けのライブラリが作れます。簡単ですね!
var Module = require('./foo');
module.exports.foo = function() {
return Module._foo();
};
ブラウザ向けビルド
ブラウザ向けではすこしだけ、コツが要ります。とりあえずwindow.fooにエクスポートしたい感じのコードを書いてみます。
var Module = require('./foo');
window['foo'] = function() {
return Module._foo();
};
browserify bar.js -o baz.js
ブラウザで実行してみるとエラーが出ます。
Uncaught TypeError: Module._foo is not a function(…)
これはEmscriptenはNode.js向けにはModuleをエクスポートしますがブラウザだとしてくれないからです。
emccするときに--post-js
で吐き出されるコードの後ろにjsを追加することで回避します。
module.exports = Module;
emcc -O2 --closure 1 --memory-init-file 0 foo.c -o foo.js --post-js post.js -s EXPORTED_FUNCTIONS="['_foo']"
browserify bar.js -o baz.js
これでブラウザ向けもビルドできますね。
minifyについて
ブラウザ向けにビルドする場合、minifyしたいと思いますが、Emscriptenが吐き出したコードまでminifyするとasm.js用に最適化している部分が消えるので無視するようにして下さい。以下、minifyifyを使った例。
browserify -p [minifyify --exclude=foo.js --no-map] bar.js -o baz.js
@printf_moriken 曰くuglify v2.5.0以降なら"use asm";
と書かれたスコープ内は空白削除だけということなので安心してminifyできます。
まとめ
Emscripten + browserifyでNode.js,ブラウザ向け対応を行う方法を紹介しました。実際にukyo/zlib-asmで同じことをしていますので、よければこちらも参照して下さい。