概要
Google Chrome や Node.js で使われている JavaScript エンジンである V8 エンジンを、Unity のアプリ上で動かすためのあれこれをまとめていきたいと思います。長くなってしまうのでビルド編と使用編に分けて書きます。今回はビルド編です。V8 をどうやって Unity と連携させたかについては次回書きます。
これを利用して簡単なプロトタイプを作ってみた動画を Twitter に投稿しています。
こちらは Unity Editor 上で JavaScript を書いてそれを Cube の動きに適用している例です。
JSに更新処理を書いて、GameObjectに適用するプロトができた!! #Unity #madewithunity pic.twitter.com/3yFR7DRmuP
— edom18@XR / MESON CTO (@edo_m18) 2022年5月8日
こちらは Android の実機上で同じことをやっている例です。Android 実機でも現状のかんたんなサンプルでは 60FPS 出ているので、処理負荷的には問題なさそうです。
よぉぉぉぉぉし、Android でもアプリ内で JavaScript 動かすことできたぞ! #Unity #madewithunity pic.twitter.com/lFTiESXgcy
— edom18@XR / MESON CTO (@edo_m18) 2022年5月23日
開発環境
- Ubntsu 18.04.5 on WSL
現状、Android 向けのライブラリのビルドは Windows は対応していないようです。また、依存関係が強く、自分の Mac の環境ではインテルでも M1 でもどちらもビルドができませんでした。(かなり調べましたが、エラーに次ぐエラーで解決できず・・・)
なので今回は WSL を利用し、Ubuntsu 18.04.5 上でビルドを行いました。
V8 をビルドする準備
V8 は様々なツール群を連携させながらビルドする必要があります。公式サイトに手順が載っているものの、環境依存が強く、実際にビルドして利用できる形にするまでかなり苦戦しました。後半に、ビルドを試していく中で遭遇した問題のトラブルシューティングを掲載しています。
▼ V8 の公式サイト v8.dev
▼ V8 のビルド手順 v8.dev
ツールをインストール
V8 をビルドするためにはツールをインストールする必要があります。公式ドキュメントに沿ってセットアップしていきます。
depot_tools をインストール
まずはじめに depot_tools をインストールします。これは V8 エンジンのビルドに必要なファイルのダウンロードや依存関係の解決などをしてくれるツールです。
まずは Git から clone します。
$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
clone したツールを PATH
に追加します。
$ export PATH=/path/to/depot_tools:$PATH
depot_tools を update
インストールが終わったら最初に depot_tools を update しておきます。以下のコマンドで自動的に更新されます。
$ gclient
V8 用のディレクトリを作成する
次に、V8 エンジンのソースコードを取ってくるためディレクトリを作成します。
$ mkdir ~/v8 $ cd ~/v8
V8 のソースコードを fetch する
depot_tools を PATH
に追加してあれば fetch
コマンドが使えるようになっているため、以下のようにして V8 エンジンのソースコードを取得することができます。
$ fetch v8 $ cd v8
ターゲット OS を設定する
今回は Android 向けにビルドを行います。そのためツールに NDK などを含める必要があるので設定ファイルを更新します。
fetch
コマンドを実行したディレクトリに .gclient
ファイルがあるのでこれに target_os = ['android']
を追記します。以下は実際に追記した例です。
# solutions はデフォルトで記載されている solutions = [ { "name": "v8", "url": "https://chromium.googlesource.com/v8/v8.git", "deps_file": "DEPS", "managed": False, "custom_deps": {}, }, # 以下を追記 target_os = ['android']
Android 用ツールを更新する
上記のターゲットを追加した状態で以下のコマンドを実行すると、Android 向けにビルドするためのツールがインストールされます。
$ gclient sync
これでビルドの準備が整いました。
ビルドする
必要なファイルを生成し、ビルドを実行します。
ファイルを生成する
以下のツールを使うことで必要なファイルが生成されます。
$ tools/dev/v8gen.py arm.release
※1 Python2 系が必要なため、環境によってはインストールされていないかもしれません。自分は Linux 向けの Miniconda を利用して環境を作成し、ビルドを行いました。
※2 list
オプションを指定すると生成できる種類がリストされます。
$ tools/dev/v8gen.py list
ビルドの設定を調整する
ビルドの設定は以下のコマンドから変更することができます。
$ gn args out.gn/arm.release
上記を実行するとテキストエディタが起動します。自分の環境では以下の設定にすることでエラーなくビルドできました。
is_debug = false target_cpu = "arm64" v8_target_cpu = "arm64" target_os = "android" v8_monolithic = true v8_use_external_startup_data = false use_custom_libcxx = false
マングリングの問題
留意点があります。最後に記載している use_custom_libcxx = false
ですが、これを指定しないとビルドツールに同梱されている clang
コンパイラが利用され、Android Studio 側に持っていった際に undefined symbol
のエラーが表示されてしまうので注意してください。
これはコンパイラの仕様で、コンパイル時に関数などのシンボルをどう処理するかに依存します。Android Studio で利用しているコンパイラおよびリンカが異なるためにこうした問題が発生してしまいます。そのため、同梱されている clang
を利用しないことでコンパイラの処理内容が一致し、エラーが出なくなるわけです。いわゆるマングリングに関する問題です。
ビルドを実行する
ここまで準備ができたら以下のコマンドからビルドを実行します。
$ ninja -C out.gn/arm.release
数千ファイルにおよぶコンパイルが走るので、終わるまで少し待ちます。
ビルドが終わると out.gn/arm.release/obj
ディレクトリに libv8_monolith.a
というライブラリファイルが生成されています。これが V8 エンジンの機能をひとつにまとめたスタティックライブラリです。これを Android Studio 側にインポートすることで V8 エンジンの機能を C++ から利用することができるようになります。
と、さくっと書きましたがビルドが成功するまでに色々なエラーにぶつかりました。以下は自分が遭遇したエラーのトラブルシューティングです。
トラブルシューティング
自分がビルドを行う過程でいくつか遭遇した問題のトラブルシューティングを記載しておきます。(Windows 向け、Mac 向け、Mac 上でのビルドなどなど、今回のビルドとは関係ない部分でのトラブルシュートもありますが、なにかしらで役に立つと思うのでまとめておきます)
No such file or directory
ビルド中、いくつかの必要なファイルがなく No such file or directory
に類するエラーが発生しました。遭遇したエラーについてまとめておきます。
見つからないファイルを NDK フォルダから探す
これらの問題への対処は以下の記事を元に対処しました。
この問題の原因は、depot_tools に同梱されている NDK には含まれていないファイル があったためでした。なので自前で NDK をダウンロードしてきて、必要なファイルを depot_tools 同梱の NDK フォルダに適宜コピーする、という方法で対処しました。
depot_tools で利用されている NDK のバージョンは以下で確認できます。
$ cat <v8_directory>/third_party/android_ndk/source.properties
Pkg.Desc = Android NDK Pkg.Revision = 23.0.7599858
自身でダウンロードした NDK の中に該当ファイルを見つけたら、以下のような形で third_party
ディレクトリ側にコピーします。
cp -rv 23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/ /path/to/v8/v8/third_party/android_ndk/toolchains/llvm/prebuilt/darwin-x86_64/
ちなみに該当ファイルを見つける場合は find
コマンドを利用するとすぐに見つかります。
# ファイル検索の例 $ find . -name features.h ./toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/features.h
以下、遭遇した対象ファイル/エラーとその対策をリストしておきます。
fatal error: ‘features.h’ file not found
cp -rv ~/Library/Android/sdk/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64 /path/to/v8/v8/third_party/android_ndk/toolchains/llvm/prebuilt/darwin-x86_64
cp -rv ~/Library/Android/sdk/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/12.0.5/lib/linux ~/MyDesktop/GitRepo/v8-repo/v8/third_party/llvm-build/Release+Asserts/lib/clang/15.0.0/lib/linux
FileNotFoundError: [Errno 2] No such file or directory: 'llvm-strip'
cp -rv ~/Library/Android/sdk/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-strip ~/MyDesktop/GitRepo/v8-repo/v8/third_party/llvm-build/Release+Asserts/bin/llvm-strip
ld.lld: error: libclang_rt.builtins-aarch64-android.a: No such file or directory
find ./ -name libclang_rt.builtins-aarch64-android.a .//toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/12.0.5/lib/linux/libclang_rt.builtins-aarch64-android.a
cp ~/Library/Android/sdk/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/12.0.5/lib/linux/libclang_rt.builtins-aarch64-android.a /path/to/v8/v8/third_party/llvm-build/Release+Asserts/bin/
pkg_config でエラー
Python スクリプトを実行中、 pkg_config
関連でエラーが出ました。これは単純に対象のパッケージがインストールされていなかったのが問題なので、以下のようにしてインストールすることで回避できます。
sudo apt-get update && sudo apt-get install pkg-config
'GLIBCXX_3.4.26' not found
エラー全文は以下です。
/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.26' not found (required by ./clang_x64_v8_arm64/torque)
以下の記事を参考にしました。
https://scrapbox.io/tamago324vim/%2Fusr%2Flib%2Fx86_64-linux-gnu%2Flibstdc++.so.6:_version_%60GLIBCXX_3.4.26'_not_found_%E3%81%A3%E3%81%A6%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AB%E3%81%AA%E3%82%8Bscrapbox.io
strings
コマンドで確認してみると確かにバージョンが足らない。( GLIBCXX_3.4.25
までしかない)
$ strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX_3 GLIBCXX_3.4 GLIBCXX_3.4.1 # 中略 GLIBCXX_3.4.24 GLIBCXX_3.4.25
以下のコマンドで追加のバージョンをインストールしました。
$ sudo apt-get install software-properties-common $ sudo add-apt-repository ppa:ubuntu-toolchain-r/test $ sudo apt install gcc-10 g++-10 -y
インストール後、改めて確認するとバージョンが増えていました。
edom18:~/GitRepo/v8-repo/v8$ strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX_3 GLIBCXX_3.4 GLIBCXX_3.4.1 # 中略 GLIBCXX_3.4.24 GLIBCXX_3.4.25 GLIBCXX_3.4.26 GLIBCXX_3.4.27 GLIBCXX_3.4.28 GLIBCXX_3.4.29
最後に
C/C++ プロジェクトのビルドは依存関係が強く、毎回骨が折れます。今回はなんとか Android 向けのライブラリがビルドできてよかったです。このあとはさらに MacOS と iOS 向けにライブラリをビルドし、Unity アプリ内で JavaScript が使える環境を作っていこうと思っています。
次回は V8 を C++ プロジェクトから利用し、さらにそれをライブラリ化して Unity C# から利用する方法について書きたいと思います。