はじめに
Emscripten は今流行りのコンパイラ基盤 LLVM を利用して C/C++ のコード(正確には LLVM-IR)を JavaScript に変換してくれるコンパイラです。フロントエンドである Clang を利用して C/C++ のコードを LLVM-IR という中間言語へ変換、Emscripten でこれを JavaScript に変換する流れになります。成果としては新しいどころだと、先月、 Unreal Engine 3 を JS にポーティング、WebGL でヌルサクするよ、というニュースが上がっていました。
また、gl.enchant.js と併用して使われる物理エンジンの ammo.js も Emscripten を利用しています。
はやりに乗っかろうと思い Emscriptenで C++ を JS に変換できる環境を整えてみました。
参考文献
llvm の導入
Mac は XCode で clang が入りますが、llvm-link などはいないので入れる必要があります。
$ brew install llvm
自分で取ってきてビルドしようかと思ったのですが brew で探したらいらっしゃいました。
後で LLVM / Clang のディレクトリ指定があり、1箇所にまとまっている必要があるので、導入した llvm と同一のディレクトリに clang のリンクを張ってきます。
$ cd /usr/local/Cellar/llvm/3.2/bin $ ln -s /usr/bin/clang* .
(追記:2013/04/16)
参考: C++のコードをブラウザ実行可能なJavaScriptに変換するEmscripten導入メモ - unstable diary
以下のコマンドで clang も一緒に入るようです。
$ brew install llvm --with-clang
emscripten の導入
$ git clone https://github.com/kripken/emscripten.git
$ cd emscripten
$ ./emcc
これで以下のようなメッセージが表示されます。
============================================================================== Welcome to Emscripten! This is the first time any of the Emscripten tools has been run. A settings file has been copied to ~/.emscripten, at absolute path: /Users/hecomi/.emscripten It contains our best guesses for the important paths, which are: LLVM_ROOT = /usr/bin PYTHON = /Library/Frameworks/Python.framework/Versions/2.7/bin/python NODE_JS = /Users/hecomi/.nodebrew/current/bin/node EMSCRIPTEN_ROOT = /Users/hecomi/tmp/emscripten/emscripten Please edit the file if any of those are incorrect. This command will now exit. When you are done editing those paths, re-run it. ==============================================================================
~/.emscripten を覗くと、python で EMSCRIPTEN_ROOT、LLVM_ROOT、PYTHON、NODE_JS、SPIDERMONKEY_ENGINE、V8_ENGINE、JAVA、TEMP_DIR の項目のパスの設定が書かれていますので適宜修正します。LLVM_ROOT を先ほど導入したパスに書き換え、EMSCRIPTEN_ROOT を emscripten のディレクトリを移動した場所へ書き換えました。
EMSCRIPTEN_ROOT = os.path.expanduser(os.getenv('EMSCRIPTEN') or '/Users/hecomi/Tools/emscripten') # directory LLVM_ROOT = os.path.expanduser(os.getenv('LLVM') or '/usr/local/Cellar/llvm/3.2/bin') # directory
Node.js が入っていない場合は、nodebrew 等を利用して Node.js を導入するか、d8 や spidermonkey をビルド、パスを通して下の方にある COMPILER_ENGINE をそれぞれの変数に書き換えて下さい。私の環境では Node.js を入れてあったので COMPILER_ENGINE は「NODE_JS」になっていました。
次に環境変数に Emscripten のパスを追加します。私は zsh を使っているので以下の指定を追加してシェルを再起動しました。
# Emscripten local EMSCRIPTEN_PATH="$HOME/Tools/emscripten" export PATH="$EMSCRIPTEN_PATH:$PATH"
em++ でエラーが出ないかチェックします。
$ em++ -v emcc (Emscripten GCC-like replacement + linker emulating GNU ld ) 2.0 Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn) Target: x86_64-apple-darwin12.2.1 Thread model: posix
これで準備完了です。
Hello, world!
#include <iostream> int main() { std::cout << "Hello, world!" << std::endl; return 0; }
これを変換して node に食わせてみます。
$ em++ -o helloworld.js helloworld.cpp
$ node helloworld.js
Hello, world!
無事、動きました!変換後のコードは「123538 行」あったので貼り付けるのはやめておきます。iostream も含めて全部変換しているのかな...。
HTML に変換することもできます。
$ em++ -o helloworld.html helloworld.cpp
$ open helloworld.html
おわりに
どんなものが変換できるのか、パフォーマンスがどうなのか等についても時間がアレば調べてみようと思います。
LLVM を利用して言語間の相互変換が簡単にできる未来とか来ると面白そうですね。