V8 编译踩坑 (v8.1-lkgr)

编译版本

由于当前 Chrome 最新稳定版本是 Chrome 81, 我们可以通过 chrome://version 查看,发现当前 Chrome 依赖的 v8 版本是 8.1.307.32

准备工作

安装 depot_tools

我们所需的 gn (配置生成工具) 和 nijia (构建工具) 都在这个仓库里。

# Clone depot_tools 仓库 
$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
# 导出环境变量
$ export PATH="$PATH:/path/to/depot_tools"

下载所需依赖

$ mkdir v8_build && cd v8_build
# 配置 v8 仓库
$ gclient config https://chromium.googlesource.com/v8/v8
# 需要科学上网,然后等待很长一段时间...
$ gclient sync

结束了之后,就会看到 v8 的仓库文件夹,配置一下 v8gen.py 的别名。

$ alias v8gen=/path/to/v8/tools/v8gen.py

编译

首先切换到我们需要编译的分支 8.1-lkgrlkgr 是最新通过测试的稳定版本,看下日志发现就是最新 Chrome 引用的 v8 版本 8.1.307.32

$ git checkout 8.1-lkgr

我们目的是编译出静态库 v8_monolith 供我们的 C++ 程序引用,按照官方教程走,这里需要 python 2 的环境,我用 pyenv 安装了 2.7.17 的版本,然后在 v8 目录下执行。

$ v8gen -vv x64.release.sample
'''
Traceback (most recent call last):
  File "/Users/Vincent/Common/v8/build/mac/find_sdk.py", line 97, in <module>
    print(main())
  File "/Users/Vincent/Common/v8/build/mac/find_sdk.py", line 80, in main
    raise Exception('No %s+ SDK found' % min_sdk_version)
Exception: No 10.15+ SDK found

这里报了个错误,说我们没有 10.15SDK,可是我用的是 Mojave (滑稽),看着 min_sdk_version 这个变量是写死在分支的文件里的,于是我修改了一下。

# build/config/mac/mac_sdk_overrides.gni
_sdk_min_from_env = getenv("FORCE_MAC_SDK_MIN")
declare_args() {
  # Minimum supported version of the Mac SDK.
  if (_sdk_min_from_env == "") {
    mac_sdk_min = "10.15" # 将这里修改成了10.14
  } else {
    mac_sdk_min = _sdk_min_from_env
  }
}

修改后再次执行,Done.

$ v8gen -vv x64.release.sample
'''
/Users/Vincent/.pyenv/versions/2.7.17/bin/python -u tools/mb/mb.py gen -f infra/mb/mb_config.pyl -m developer_default -b x64.release.sample out.gn/x64.release.sample

  Writing """\
  is_component_build = false
  is_debug = false
  target_cpu = "x64"
  use_custom_libcxx = false
  v8_monolithic = true
  v8_use_external_startup_data = false
  """ to /Users/Vincent/Common/v8/out.gn/x64.release.sample/args.gn.

  /Users/Vincent/Common/v8/buildtools/mac/gn gen out.gn/x64.release.sample --check
  Done. Made 140 targets from 81 files in 753ms

下面开始构建,不出意外的话应该会报下面的错。

$ ninja -C out.gn/x64.release.sample v8_monolith
'''
FAILED: obj/v8_base_without_compiler/js-number-format.o
../../src/objects/js-number-format.cc:1227:15: error: no member named 'getAllFieldPositions' in 'icu_67::number::FormattedNumber'
    formatted.getAllFieldPositions(*fp_iter, status);
    ~~~~~~~~~ ^
1 error generated.
ninja: build stopped: subcommand failed.

看一下这个文件 js-number-format.cc,发现确实没有 getAllFieldPositions 这个函数的实现。我切了 8.0-lkgr, 7.9-lkgr, 7.8-lkgr 这几个分支,发现编译都会报这个错,查了一下官方的 issue,发现 icu67 移除了 getAllFieldPositions 这个 API,需要后续添加一个 icu67.1patch

ICU 是一个国际化模块,下载地址

这个 bug 已在 2020/04/07 的一次 Commit 修复,这里我们直接 cherry-pick 会有冲突,但是又不想用太新的没有测试过的版本,反正也用不到国际化的模块,所以就将 i18n 从编译选项中移除了,方法如下:

# 编辑配置
$ gn args out.gn/x64.release.sample
# 增加一行:
# v8_enable_i18n_support = false

# 再次构建
$ ninja -C out.gn/x64.release.sample v8_monolith

Hello World

编译一下 samples/hello-world.cc

$ g++ samples/hello-world.cc -o hello-world \
-I. -I./include \
-L./out.gn/x64.release.sample/obj -lv8_monolith \
-std=c++11

执行一下,如果有如下输出就是编译成功了

$ ./hello-world
'''
Hello, World!
3 + 4 = 7

其他编译选项

上文已经演示了如何编译 V8 静态库并链接到你的 C++ 代码中,下面我们尝试编译可执行文件 d8

Release 版本

$ v8gen.py x64.release
$ ninja -C out.gn/x64.release

Debug 版本

$ v8gen.py x64.debug
$ ninja -C out.gn/x64.debug

参考文档

  1. Getting started with embedding V8
  2. ICU - International Components for Unicode