Mac下编译V8

V8是一个由Google开发的开源JavaScript引擎,用于Google Chrome及Chromium中。由于需要使用到V8,来在自己的C++项目中执行js,因此想要学习了解下V8的实现原理。打算先在Mac本地上编译运行起来,之后再尝试交叉编译。

下载V8源码

参考官方手册

注意:不要直接从v8仓库使用git clone命令下载代码,这样下载下来的代码是无效的,会缺失很多东西,要使用官方提供的工具depot_tools

安装 depot_tools

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

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

下载所需依赖

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

结束后会发现有v8库文件的文件夹

修改mini-SDK

由于对要求 MAC OSX SDK>10.15,而我的xcode带的是10.12。所以需要修改

查看sdk版本:

1
2
# Xcode中添加的SDK版本
$xcodebuild -showsdks

v8/build/config/mac/mac_sdk_overrides.gni文件

1
2
3
4
5
6
7
8
9
_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.12
} else {
mac_sdk_min = _sdk_min_from_env
}
}

编译

参考官方手册

方式一:gm脚本

1
2
3
4
#配置别名:
$ alias gm=/path/to/v8/tools/dev/gm.py
$ gm x64.release
$ gm x64.release.check

方式二:手动编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#生成ninja文件
$ gn args out/foo
或指定参数
$ gn gen out/foo --args='is_debug=false target_cpu="x64" v8_target_cpu="arm64" use_goma=true'

#或使用v8gen脚本
#配置别名:
$ alias v8gen=/path/to/v8/tools/dev/v8gen.py
$ v8gen -b 'V8 Linux64 - debug builder' -m client.v8 foo
或:
$ v8gen foo

$ v8gen x64.release.sample

#使用ninja来编译:
$ ninja -C out/foo
如果要在c++中嵌入v8,使用下面的
$ ninja -C out.gn/x64.release.sample v8_monolith

替换libtool

这里我编译出错

1
2
3
4
5
[48/1364] LIBTOOL-STATIC obj/libv8_libbase.a
FAILED: obj/libv8_libbase.a
rm -f obj/libv8_libbase.a && TOOL_VERSION=1594099062 python ../../build/toolchain/mac/filter_libtool.py /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -static -D -o obj/libv8_libbase.a -filelist obj/libv8_libbase.a.rsp
error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool: unknown option character `D' in: -D
Usage: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -static [-] file [...] [-filelist listfile[,dirname]] [-arch_only arch] [-sacLT] [-no_warning_for_no_symbols]

看样子是xcode的libtool工具出错,将新版本xcode(11.5)里带的libtool拷贝到原来的xcode 的/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool目录下成功解决问题

移除i18n

遇到报错

1
FAILED: obj/v8_base_without_compiler/js-number-format.o

icu67 移除了 getAllFieldPositions 这个 API,需要后续添加一个 icu67.1 的 patch

因为用不到国际化的模块,将 i18n 从编译选项中移除

1
2
3
4
5
6
# 编辑配置
$ gn args out.gn/x64.release.sample
# 增加一行:
# v8_enable_i18n_support = false
# 再次构建
$ ninja -C out.gn/x64.release.sample v8_monolith

测试

编译一下 samples/hello-world.cc

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

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

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

编译引用v8库的.cc文件

G++命令

sample:

1
g++ -I. -Iinclude samples/hello-world.cc -o hello_world -lv8_monolith -Lout.gn/x64.release.sample/obj/ -pthread -std=c++0x

命令解释:

-std= 决定使用的语言标准,当编译C和C++的时候该选择支持配置。 上述命令中的c++0x表示: 语言标准使用即将发布的ISO c++ 0x标准的工作草案。此选项支持可能包含在c++ 0x中的实验性特性。工作草案在不断地变化,如果GCC的未来版本不属于c++ 0x标准,那么由这个标志启用的任何特性都可能被删除。 更多标准请参考:g++

-pthread 使用POSIX线程库添加对多线程的支持。此选项为预处理器和链接器设置标志。它不影响编译器生成的目标代码的线程安全性,也不影响与其提供的库的线程安全性。这些是特定于HP-UX的标志。

-I dir 将目录dir添加到要搜索头文件的目录列表中。在系统标准包含目录之前,搜索由**-I*指定的目录。如果目录dir是标准的系统包含目录,则忽略该选项,以确保不会破坏系统目录的默认搜索顺序和对系统头文件的特殊处理。如果dir*以”=”开头,则”=”将被sysroot前缀替换。

-o file 指定输出文件。这与将file指定为cpp的第二个非选项参数相同。gcc 对第二个非选项参数的有另一种解释,因此必须使用-o指定输出文件

-llibrary -l library 链接时搜索名为library的库。(第二种指定库文件的方式仅适用于POSIX遵从性,不建议使用。) 在命令中编写这个选项的位置会有所不同;链接器按照指定的顺序搜索和处理库和目标文件。因此,foo.o -lz bar.o是在文件foo.o之后搜索库z。但在bar.o之前。如果bar.o是引用到了z库中的函数,这些函数是不能被加载。 链接器搜索库的标准目录列表,实际上是一个名为liblibrary.a的文件。然后链接器使用这个文件,就好像它是通过名称精确指定的一样。

搜索的目录包括几个标准系统目录,以及您使用-L指定的任何目录。 通常以这种方式找到的文件是库文件——其成员是目标文件的归档文件。链接器通过扫描成员来处理存档文件,这些成员定义了到目前为止已经引用但尚未定义的符号。但是,如果找到的文件是一个普通的对象文件,则以通常的方式链接它。

-Ldir 添加dir目录到搜索目录列表中去供-l使用

交叉编译 V8 for Raspberry Pi

下载toolchain for RPi on MAC

用法:

  1. 直接解压
  2. 加入环境变量export PATH=$PATH:/Users/guomengyuan/Downloads/xc-gcc_4.9.3_mac_rpi-master/arm-rpi-linux-gnueabihf/bin
  3. 修改名称,将所有的arm-rpi-linux-xxx 改为arm-linux-xxx

gn构建ninja文件

gn gen out.gn/arm --args='is_debug=false is_component_build=true symbol_level=0 target_os="linux" target_cpu="arm" v8_target_cpu="arm"'

编译

ninja -C out.gn/arm

v8引擎基本概念简述

isolate

表示的一个独立的V8虚拟机,拥有自己的堆栈。所以才取名isolate,意为“隔离”。在v8中使用以下语法进行初始化:

1
Isolate* isolate = Isolate::New(create_params); 

handle

handle是指向对象的指针,在V8中,所有的对象都通过handle来引用,handle主要用于V8的垃圾回收机制。在 V8 中,handle 分为两种:持久化 (Persistent)handle 和本地 (Local)handle,持久化 handle 存放在堆上,而本地 handle 存放在栈上。比如我要使用本地句柄,句柄指向的内容是一个string,那么你要这么定义:

1
Local<String> source = String::NewFromUtf8(isolate, "'Hello' + ', World'", NewStringType::kNormal).ToLocalChecked();

鉴于一个个释放Handle比较麻烦,v8又提供了HandleScope来批量处理,你可以在handle之前声明好:

1
HandleScope handle_scope(isolate);

context

context 是一个执行器环境,使用 context 可以将相互分离的 JavaScript 脚本在同一个 V8 实例中运行,而互不干涉。在运行 JavaScript 脚本是,需要显式的指定 context 对象。创建上下文,需要这样:

1
2
3
4
// 创建一个上下文 
Local<Context> context = Context::New(isolate);
// 进入上下文编译和运行脚本
Context::Scope context_scope(context);

V8的数据类型

由于 C++ 原生数据类型与 JavaScript 中数据类型有很大差异,因此 V8 提供了 Data 类,从 JavaScript 到 C++,从 C++ 到 JavaScrpt 都会用到这个类及其子类,比如:

1
String::NewFromUtf8(info.GetIsolate(), "version").ToLocalChecked() 

这里的String便是V8的数据类型。再比如:

1
v8::Integer::New(info.GetIsolate(), 10); 

对象模板和函数模板

这两个模板类用以定义 JavaScript 对象和 JavaScript 函数。我们在后续的小节部分将会接触到模板类的实例。通过使用 ObjectTemplate,可以将 C++ 中的对象暴露给脚本环境,类似的,FunctionTemplate 用以将 C++ 函数暴露给脚本环境,以供脚本使用。

Reference

https://www.cnblogs.com/wolfx/p/5920141.html
https://juejin.im/post/5d91a4d4f265da5b5f756c22
https://gist.github.com/Jiab77/e59841227d1f9c7d2877862ced673ec6
https://github.com/google/shaka-packager/issues/763
https://groups.google.com/forum/#!topic/v8-users/LTppUbqNrzI
豆瓣LLVM
rpi交叉编译工具
gn参数
http://blog.hszofficial.site/introduce/2018/02/22/%E6%A0%91%E8%8E%93%E6%B4%BE%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/