JavaScript
v8
depot_tools
gn

GNを使ってV8を組み込む

Google V8は、depot_toolsの中のGNというツールを使ってビルドされています。GNはgenerate ninjaの略です。ニンジャナンデ!!!!

ninjaは並列ビルドシステムで、Make代替のものです。

GNはninja向けのファイルを生成するメタビルドシステムで、特徴としては、クロスプラットフォームやカスタマイズなどを行えるという点です。ninjaがMake代替だとすれば、GNはGNU autotools代替と言えるでしょう。

V8をビルドして(静的もしくは動的な)ライブラリを生成して、それをリンクして組み込むだけならGNを意識する必要はありませんが、せっかくのクロスプラットフォームな仕組みなので、そこに乗っかってみるという記事です。


前提

この記事では direnv を前提としてしています。特定ディレクトリ以下でのPATH設定に使っているだけなので、PATH設定さえ別の手段で行うなら、direnv は無くてもかまいません。

Macで動作確認をしています。LinuxやWindowsで行う場合、他に手順が必要なケースがあるかもしれません。


depot_toolsをインストールする

まずはtoybox-v8というディレクトリを作成し、その中にdepot_toolsをインストールします。ディストリビューションなどによってはdepot_toolsはパッケージ化されているようです。

mkdir toybox-v8

cd toybox-v8
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
echo "export PATH=`pwd`/depot_tools:$PATH" > .envrc
direnv allow .

最後の二行はパス設定のためのものです。depot_toolsはあまり汎用的とは言いがたいエコシステムなため、筆者はこのようにしています。


必要なファイルを用意する

fetch v8

もしもGNを使わずに手動でV8をリンクしたいということであれば、あとは、Getting started with embedding V8の手順に従うといいでしょう。

今回はhello_world.ccを使います。まず今回のディレクトリを整備します。

mkdir hello-world

cd hello-world

GNでV8を組み込む場合、とても多くの下準備が必要になります。

ln -s ../depot_tools .

ln -s ../v8 .
ln -s v8/build .
ln -s v8/build_overrides .
ln -s v8/buildtools .
ln -s v8/testing .

mkdir third_party
(cd third_party ; ln -s ../v8/third_party/googletest .)
(cd third_party ; ln -s ../v8/third_party/jinja2 .)
(cd third_party ; ln -s ../v8/third_party/llvm-build .)
(cd third_party ; ln -s ../v8/third_party/markupsafe .)

mkdir tools
(cd tools ; ln -s ../v8/tools/clang .)

cp v8/samples/hello-world.cc .

ここまでで toybox-v8の中身はこのようになっているはずです。

├── depot_tools

├── hello-world
│   ├── build -> v8/build
│   ├── build_overrides -> v8/build_overrides
│   ├── buildtools -> v8/buildtools
│   ├── depot_tools -> ../depot_tools
│   ├── hello-world.cc
│   ├── testing -> v8/testing
│   ├── third_party
│   │   ├── googletest -> ../v8/third_party/googletest
│   │   ├── jinja2 -> ../v8/third_party/jinja2
│   │   ├── llvm-build -> ../v8/third_party/llvm-build
│   │   └── markupsafe -> ../v8/third_party/markupsafe
│ ├── tools
│   │ └── clang -> ../v8/tools/clang
│   └── v8 -> ../v8
└── v8


GNファイルを作成する

GNファイルを2つ用意する必要があります。.gn というファイルと BUILD.gn です。


.gn

このファイルはV8のビルド情報を組み込んでビルド設定(引数)を定義するのが主目的です。

buildconfig = "//build/config/BUILDCONFIG.gn"

check_targets = []

default_args = {
cc_wrapper = "ccache"
v8_enable_i18n_support = false
clang_use_chrome_plugins = false
v8_monolithic = true
is_component_build = false
v8_use_external_startup_data = false
v8_experimental_extra_library_files = []
v8_extra_library_files = []
}

ここではcc_wrapperとして ccache というC/C++コンパイル結果をキャッシュするラッパーを使っています。V8のビルドでは、数百〜千のファイルをコンパイルする必要があるためキャッシュがあれば圧倒的に楽になります。

無い場合や使いたくない場合は、cc_wrapper = "ccache"の行を削りましょう。


BUILD.gn

BUILD.gnはhello-worldをmakeするためのファイルです。

import("//v8/gni/v8.gni")

group("default") {
deps = [
":hello_world",
]
}

v8_executable("hello_world") {
include_dirs =["v8"]
sources = [
"hello-world.cc",
]

configs = [ "v8:external_config" ]

deps = [
"//v8:v8_monolith",
]
}


実際にビルドする

gn gen out

ninja -C out

最初のgn gen outで、outというディレクトリの下にninjaコマンドで必要なファイルをすべて生成します。

ninja -C outで、実際にビルドを行います。

成功すれば、out/hello_worldというバイナリが生成されているはずです。

$ ./out/hello_world

Hello, World!
3 + 4 = 7

ちなみに1行目はJavaScriptのコードで、2行目の3+4という計算はWeb Assemblyで行われています。