南宁建设集团招聘信息网站,可以做图的网站,河北精品网站建设,欧美做暧网站V8这个概念大家都不陌生了#xff0c;那么你动手编译过V8源码吗#xff1f;编译后有尝试去了解V8背后的一些概念吗#xff1f;如果没有#xff0c;那么也不用心慌#xff0c;下文将跟大家一一解释这些东西。在编译V8之前我们先要了解一个东西-构建系统1、构建系统1.1、构建…V8这个概念大家都不陌生了那么你动手编译过V8源码吗编译后有尝试去了解V8背后的一些概念吗如果没有那么也不用心慌下文将跟大家一一解释这些东西。在编译V8之前我们先要了解一个东西-构建系统1、构建系统1.1、构建系统是啥写惯前端的童鞋可能不是很明白这个东西是干啥用的但是其实平时你都会接触到只是概念不同而已。前端我们一般称其为打包构建类似工具诸如webpack、parcel做的事情。其实最后的目标都是想得到一些目标性的文件。这里可以简单地提及一下软件工程中的构建系统的历史。构建系统的需求是随着软件规模的增大而提出的。如果只是做简单的demo通常代码量比较小编写的源代码只有几个文件。比如你编写了一段代码放入helloworld.cpp文件中要编译这段代码只需要执行以下命令g helloworld.c -o helloworld
当软件规模逐渐增加这时可能有几十个源代码文件而且有了模块划分有的要编译成静态库有的要编译成动态库最后链接成可执行代码这时命令行方式就捉襟见肘需要一个构建系统。常见的构建系统有GNU Make。需要注意的是构建系统并不是取代gcc这样的工具链而是定义编译规则最终还是会调用工具链编译代码。当软件规模进一步扩大特别是有多平台支持需求的时候编写GNU Makefile将是一件繁琐和乏味的事情而且极容易出错。这时就出现了生成Makefile的工具比如Cmake、AutoMake等等这种构建系统称作元构建系统meta build system。在Linux上软件仓库的概念还没有普及的时候通常我们安装软件的步骤是./configure
make
make install
第一步就是调用一些自动化工具根据系统环境系统的版本众多软件安装情况也不一样生成GNU Makefile。然后第二步才使用gcc或者g命令去编译所有文件最后一步便是将所有文件链接起来成可执行命令并安装到系统的某个指定目录。一般后两个步骤都是比较固化的能提高工作效率的也就是在第一步了。于是V8团队针对自己的项目特点撸了一个叫做GYP(Generate Your Projects)的构建系统后面你要是看到node-gyp其实就是基于这个做的js版本。不过后面GYP被v8团队废弃掉改用GN(Generate Ninja)构建系统。二者的区别不是本文重点有兴趣的童鞋可以查看这篇文章 chromium中的GN构建系统。有意思的是尽管v8彻底废弃掉了GYP但是nodejs仍然在使用GYP这个R大在创建deno项目的时候有提及到Design Mistakes in Node。1.1.1、GN构建系统简介GN(Generate Ninja)是chromium project用来取代GYP的新工具由于GN是用C编写比起用 python写的GYP快了很多GN新的DSL的语法也被认为是比较好写以及维护的。在v8项目的根目录下有个.gn文件内容如下去掉所有注释了import(//build/dotfile_settings.gni)
buildconfig //build/config/BUILDCONFIG.gn
check_targets []
exec_script_whitelist build_dotfile_settings.exec_script_whitelist []
我们关注buildconfig这个配置。.gn所在的目录会被GN工具认定是项目的根目录.gn的内容基本就是用buildconfig来指定build config的位置其中//build//config/BUILDCONFIG.gn是相对于项目根目录下路径的配置文件。但是你会发现现在v8源码目录下并没有叫做build的目录这个目录要咋生成呢这些知识我们会在稍后的编译v8代码中提及。假设现在你有build目录了我们找到BUILDCONFIG.gn文件文件里面会根据系统和平台设置对应的编译工具链... ...if (custom_toolchain ! ) {set_default_toolchain(custom_toolchain)
} else if (_default_toolchain ! ) {set_default_toolchain(_default_toolchain)
}... ...
比如得到的_default_toolchain值为_default_toolchain //build/toolchain/linux:clang_x86那么你在build/toolchain/linux目录下的BUILD.gn可以找到这么一个配置clang_toolchain(clang_x86) {# Output linker map files for binary size analysis.enable_linker_map truetoolchain_args {current_cpu x86current_os linux}
}
因为GN没有内建的toolchain规则toolchain里的各种tool例如 cc,cxx,link等必须自己指定指定的文件是build/toolchain/gcc_toolchain.gni文件在文件中我们可以看到GN给定义的一些动作tool(cc) {depfile {{output}}.dprecompiled_header_type gcccommand $cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}${extra_cppflags}${extra_cflags} -c {{source}} -o {{output}}depsformat gccdescription CC {{output}}outputs [$object_subdir/{{source_name_part}}.o,]
}
最后项目根目录下会有一个BUILD.gn的文件指定生成可执行文件的指令比如v8_executable(v8_hello_world) {sources [samples/hello-world.cc,]configs [# Note: dont use :internal_config here because this target will get# the :external_config applied to it by virtue of depending on :v8, and# you cant have both applied to the same target.:internal_config_base,]deps [:v8,:v8_libbase,:v8_libplatform,//build/win:default_exe_manifest,]
}
这样一套完整的GN构建系统便完成了。1.1.2、Ninja构建系统有了GN为啥还要Ninja呢刚才我们知道GN的英文意思是Generator Ninja可见GN生成的东西并不是我们最终GNU Makefile形式。而Ninja才是最后生成Makefile的终极法器。Ninja 作为一个新型的编译工具小巧而又高效据谷歌官方的说法是速度有了好几倍的提升。这个时候我们还没有生成任何的Ninja文件需要我们使用GN命令去生成gn args out/foo这下子你在out/foo下就可以看到好多ninja文件Ninja使用build.ninja文件来定义构建规则和Makefile里的元编程不同build.ninja几乎是完全静态的动态生成依赖其他工具如gn或者CMake。build.ninjabuild.niinja相当于ninja的makefile一个简单的build.ninja文件如下分为rule和dependency两部分。phony: 可以创建其他target的别名。default: 如果没有在命令行中指定target可以使用default来指定默认的target。pools: 为了支持并发作业Ninja还支持pool的机制和用-j并行模式一样。Make vs Ninja Performance Comparison将Ninja和Make进行了测试对比。2、编译并测试V8代码接下来我们开始进行v8代码的编译操作。官网的文档给的已经很齐全了这里只是再简单说一下并提及一些官网没有给出的基本知识。2.1、下载v8代码这一步注意了不要直接从v8仓库使用git clone命令下载代码这样下载下来的代码是无效的会缺失很多东西要使用官方提供的工具depot_tools整个步骤汇总如下git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH$PATH:/path/to/depot_tools
gclient config https://chromium.googlesource.com/v8/v8
gclient sync
mkdir ~/v8
cd ~/v8
fetch v8
cd v8
2.2、编译v8代码编译v8代码官网同样给的很详细传送门这里总结一下而已有两种编译方式2.2.1、超便捷方式使用gm这个集成所有为一体的python脚本可以几个命令就搞定alias gm/path/to/v8/tools/dev/gm.py
gm x64.release
gm x64.release.check
2.2.2、手动编译方式按照我们之前说的流程我们需要使用GN去生成ninja文件再生成makefile最后才是编译因此可以使用gn args out/foo或者gn gen out/foo --argsis_debugfalse target_cpux64 v8_target_cpuarm64 use_gomatrue来生成ninja文件。这一行命令官网没有详细解释我在这里解释一下gn args out/foo 通过参数形式指定输出目录这个命令会弹出文本让你配置参数
gn gen out/foo 指定GN构建输出的目录, 可以指定参数 --argsis_debugfalse target_cpux64 v8_target_cpuarm64 use_gomatrue这个命令不会弹出文本窗让你配置
gn args out/foo --list 查看这个构建输出目录当时配置的参数
如果嫌上面的方式麻烦那么v8还提供了另外一个脚本来集成这些步骤v8gen命令如下alias v8gen/path/to/v8/tools/dev/v8gen.py
v8gen -b V8 Linux64 - debug builder -m client.v8 foo
v8gen的原理是借助mb_config.pyl文件。根据master配置(-m)和builder配置(-b)来生成编译文件我们在mb_config.pyl找到对应的配置最后一个参数foo是指定生成的二级目录默认一级目录是out.gn如下你也可以使用默认配置直接v8gen foo接下去使用ninja来编译ninja -C out/foo如果想要指定生成指定目标则ninja -C out/foo d8上述编译正常会报错goma/gomacc: No such file or directory。因为我们本地没有安装goma所以想要正常编译下去还需要安装一下gomagoma是什么东西呢从官网上看它是一个辅助编译加速的工具详细可以参考goma3、编译单个引用到v8库的C文件除了上述整体v8工程编译如果你想利用v8编译单个文件的话比如在官网提到的编译Hello.cc中使用到了g命令对于g命令有些参数是你必须了解的这里整理了一份请参考g -I. -Iinclude samples/hello-world.cc -o hello_world -lv8_monolith -Lout.gn/x64.release.sample/obj/ -pthread -stdc0xG命令解释如下-std决定使用的语言标准当编译C和C的时候该选择支持配置。上述命令中的c0x表示语言标准使用即将发布的ISO c 0x标准的工作草案。此选项支持可能包含在c 0x中的实验性特性。工作草案在不断地变化如果GCC的未来版本不属于c 0x标准那么由这个标志启用的任何特性都可能被删除。更多标准请参考[g](https://linux.die.net/man/1/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使用
这样上述命令想必一目了然了吧4、v8引擎基本概念简述在[译文]V8学习的高级进阶完整详细地介绍了很多概念这里只是再把这些概念简化掉让大家的记忆更加深刻。4.1、isolate这个概念在[译文]V8学习的高级进阶没有提及到它表示的一个独立的V8虚拟机拥有自己的堆栈。所以才取名isolate意为“隔离”。在v8中使用以下语法进行初始化Isolate* isolate Isolate::New(create_params);
4.2、handlehandle是指向对象的指针在V8中所有的对象都通过handle来引用handle主要用于V8的垃圾回收机制。在 V8 中handle 分为两种持久化 (Persistent)handle 和本地 (Local)handle持久化 handle 存放在堆上而本地 handle 存放在栈上。比如我要使用本地句柄句柄指向的内容是一个string那么你要这么定义LocalString source String::NewFromUtf8(isolate, Hello , World, NewStringType::kNormal).ToLocalChecked();鉴于一个个释放Handle比较麻烦v8又提供了HandleScope来批量处理你可以在handle之前声明好HandleScope handle_scope(isolate);4.3、contextcontext 是一个执行器环境使用 context 可以将相互分离的 JavaScript 脚本在同一个 V8 实例中运行而互不干涉。在运行 JavaScript 脚本是需要显式的指定 context 对象。创建上下文需要这样// 创建一个上下文
LocalContext context Context::New(isolate);// 进入上下文编译和运行脚本
Context::Scope context_scope(context);
4.4、V8的数据类型由于 C 原生数据类型与 JavaScript 中数据类型有很大差异因此 V8 提供了 Data 类从 JavaScript 到 C从 C 到 JavaScrpt 都会用到这个类及其子类比如String::NewFromUtf8(info.GetIsolate(), version).ToLocalChecked()
这里的String便是V8的数据类型。再比如v8::Integer::New(info.GetIsolate(), 10);
4.5、对象模板和函数模板这两个模板类用以定义 JavaScript 对象和 JavaScript 函数。我们在后续的小节部分将会接触到模板类的实例。通过使用 ObjectTemplate可以将 C 中的对象暴露给脚本环境类似的FunctionTemplate 用以将 C 函数暴露给脚本环境以供脚本使用。最后就此对于v8的了解应该有了一定的雏形了v8里面有很多重要的概念想要继续深入的可以参考另外一篇v8的实际应用文章了如何正确地使用v8嵌入到我们的C应用中参考chromium中的GN构建系统GYPGN和Ninjadepot_tools_tutorial(7) Manual PageGN Reference