e.blog

主にUnity/UE周りのことについてまとめていきます

V8 エンジンを Unity Android アプリ上で動かす(V8 ビルド編)

概要

Google Chrome や Node.js で使われている JavaScript エンジンである V8 エンジンを、Unity のアプリ上で動かすためのあれこれをまとめていきたいと思います。長くなってしまうのでビルド編と使用編に分けて書きます。今回はビルド編です。V8 をどうやって Unity と連携させたかについては次回書きます。

これを利用して簡単なプロトタイプを作ってみた動画を Twitter に投稿しています。

こちらは Unity Editor 上で JavaScript を書いてそれを Cube の動きに適用している例です。

こちらは Android の実機上で同じことをやっている例です。Android 実機でも現状のかんたんなサンプルでは 60FPS 出ているので、処理負荷的には問題なさそうです。



開発環境

  • 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 フォルダから探す

これらの問題への対処は以下の記事を元に対処しました。

www.jianshu.com

この問題の原因は、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 向けのライブラリがビルドできてよかったです。このあとはさらに MacOSiOS 向けにライブラリをビルドし、Unity アプリ内で JavaScript が使える環境を作っていこうと思っています。

次回は V8 を C++ プロジェクトから利用し、さらにそれをライブラリ化して Unity C# から利用する方法について書きたいと思います。